Skip to content

Commit 529cc3c

Browse files
authored
Merge pull request #82039 from xymus/cdecl-enum
PrintAsClang: Introduce `@cdecl` enums
2 parents f2c820a + bd110a0 commit 529cc3c

18 files changed

+440
-50
lines changed

include/swift/AST/DeclAttr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface,
365365
62)
366366

367367
DECL_ATTR(_cdecl, CDecl,
368-
OnFunc | OnAccessor,
368+
OnFunc | OnAccessor | OnEnum,
369369
LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
370370
63)
371371
DECL_ATTR_ALIAS(cdecl, CDecl)

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,9 @@ ERROR(cdecl_empty_name,none,
20892089
"%0 symbol name cannot be empty", (DeclAttribute))
20902090
ERROR(cdecl_throws,none,
20912091
"raising errors from %0 functions is not supported", (DeclAttribute))
2092+
ERROR(cdecl_incompatible_with_objc,none,
2093+
"cannot apply both '@cdecl' and '@objc' to %kindonly0",
2094+
(const Decl *))
20922095
ERROR(cdecl_feature_required,none,
20932096
"'@cdecl' requires '-enable-experimental-feature CDecl'",
20942097
())
@@ -3771,9 +3774,9 @@ ERROR(enum_with_raw_type_case_with_argument,none,
37713774
NOTE(enum_raw_type_here,none,
37723775
"declared raw type %0 here", (Type))
37733776
ERROR(objc_enum_no_raw_type,none,
3774-
"'@objc' enum must declare an integer raw type", ())
3777+
"'%0' enum must declare an integer raw type", (DeclAttribute))
37753778
ERROR(objc_enum_raw_type_not_integer,none,
3776-
"'@objc' enum raw type %0 is not an integer type", (Type))
3779+
"'%0' enum raw type %1 is not an integer type", (DeclAttribute, Type))
37773780
ERROR(enum_non_integer_raw_value_auto_increment,none,
37783781
"enum case must declare a raw value when the preceding raw value is not an integer", ())
37793782
ERROR(enum_non_integer_convertible_raw_type_no_value,none,
@@ -6630,6 +6633,10 @@ NOTE(not_objc_swift_struct,none,
66306633
(ForeignLanguage))
66316634
NOTE(not_objc_swift_enum,none,
66326635
"non-'@objc' enums cannot be represented in Objective-C", ())
6636+
NOTE(not_cdecl_or_objc_swift_enum,none,
6637+
"Swift enums not marked '@cdecl'%select{| or '@objc'}0 cannot be "
6638+
"represented in %" FOREIGN_LANG_SELECT "0",
6639+
(ForeignLanguage))
66336640
NOTE(not_objc_generic_type_param,none,
66346641
"generic type parameters cannot be represented in "
66356642
"%" FOREIGN_LANG_SELECT "0", (ForeignLanguage))

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4875,9 +4875,9 @@ class TypeCheckObjCImplementationRequest
48754875
bool isCached() const { return true; }
48764876
};
48774877

4878-
/// Check @cdecl-style attributes for compatibility with the foreign language.
4879-
class TypeCheckCDeclAttributeRequest
4880-
: public SimpleRequest<TypeCheckCDeclAttributeRequest,
4878+
/// Check @cdecl functions for compatibility with the foreign language.
4879+
class TypeCheckCDeclFunctionRequest
4880+
: public SimpleRequest<TypeCheckCDeclFunctionRequest,
48814881
evaluator::SideEffect(FuncDecl *FD,
48824882
CDeclAttr *attr),
48834883
RequestFlags::Cached> {
@@ -4894,6 +4894,25 @@ class TypeCheckCDeclAttributeRequest
48944894
bool isCached() const { return true; }
48954895
};
48964896

4897+
/// Check @cdecl enums for compatibility with C.
4898+
class TypeCheckCDeclEnumRequest
4899+
: public SimpleRequest<TypeCheckCDeclEnumRequest,
4900+
evaluator::SideEffect(EnumDecl *ED,
4901+
CDeclAttr *attr),
4902+
RequestFlags::Cached> {
4903+
public:
4904+
using SimpleRequest::SimpleRequest;
4905+
4906+
private:
4907+
friend SimpleRequest;
4908+
4909+
evaluator::SideEffect
4910+
evaluate(Evaluator &evaluator, EnumDecl *ED, CDeclAttr *attr) const;
4911+
4912+
public:
4913+
bool isCached() const { return true; }
4914+
};
4915+
48974916
void simple_display(llvm::raw_ostream &out, ASTNode node);
48984917
void simple_display(llvm::raw_ostream &out, Type value);
48994918
void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR);

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,11 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest,
554554
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
555555
unsigned(ExtensionDecl *),
556556
Cached, NoLocationInfo)
557-
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclAttributeRequest,
558-
evaluator::SideEffect(FuncDecl *, CDeclAttr *),
557+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclFunctionRequest,
558+
evaluator::SideEffect(FunctionDecl *, CDeclAttr *),
559+
Cached, NoLocationInfo)
560+
SWIFT_REQUEST(TypeChecker, TypeCheckCDeclEnumRequest,
561+
evaluator::SideEffect(EnumDecl *, CDeclAttr *),
559562
Cached, NoLocationInfo)
560563
SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest,
561564
bool(AbstractStorageDecl *), Cached,

include/swift/PrintAsClang/ClangMacros.def

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,20 +176,46 @@ CLANG_MACRO_CONDITIONAL("SWIFT_ENUM_ATTR", "(_extensibility)", \
176176
"__attribute__((enum_extensibility(_extensibility)))")
177177

178178
CLANG_MACRO_BODY("SWIFT_ENUM", \
179-
"# define SWIFT_ENUM(_type, _name, _extensibility) " \
179+
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
180+
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
181+
" __has_feature(objc_fixed_enum)\n" \
182+
"# define SWIFT_ENUM(_type, _name, _extensibility) " \
180183
"enum _name : _type _name; " \
181184
"enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type\n" \
182-
"# if __has_feature(generalized_swift_name)\n" \
183-
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
184-
"enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \
185-
"enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \
186-
"SWIFT_ENUM_EXTRA _name : _type\n" \
185+
"# if __has_feature(generalized_swift_name)\n" \
186+
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
187+
"enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \
188+
"enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \
189+
"SWIFT_ENUM_EXTRA _name : _type\n" \
190+
"# else\n" \
191+
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
192+
"SWIFT_ENUM(_type, _name, _extensibility)\n" \
193+
"# endif\n" \
187194
"# else\n" \
195+
"# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum \n" \
188196
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
189197
"SWIFT_ENUM(_type, _name, _extensibility)\n" \
190198
"# endif")
191199
CLANG_MACRO_DEFINED("SWIFT_ENUM_NAMED")
192200

201+
CLANG_MACRO_BODY("SWIFT_ENUM_TAG", \
202+
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
203+
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
204+
" __has_feature(objc_fixed_enum)\n" \
205+
"# define SWIFT_ENUM_TAG enum\n" \
206+
"# else\n" \
207+
"# define SWIFT_ENUM_TAG\n" \
208+
"# endif")
209+
210+
CLANG_MACRO_BODY("SWIFT_ENUM_FWD_DECL", \
211+
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
212+
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
213+
" __has_feature(objc_fixed_enum)\n" \
214+
"# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type _name;\n" \
215+
"# else\n" \
216+
"# define SWIFT_ENUM_FWD_DECL(_type, _name) _type _name;\n" \
217+
"# endif")
218+
193219
CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))")
194220
CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))")
195221

lib/AST/SwiftNameTranslation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/AST/SwiftNameTranslation.h"
1818
#include "swift/AST/ASTContext.h"
19+
#include "swift/AST/Attr.h"
1920
#include "swift/AST/Decl.h"
2021
#include "swift/AST/DiagnosticsSema.h"
2122
#include "swift/AST/LazyResolver.h"
@@ -48,6 +49,9 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) {
4849
}
4950
}
5051

52+
if (auto cdeclAttr = VD->getAttrs().getAttribute<CDeclAttr>())
53+
return cdeclAttr->Name;
54+
5155
if (customNamesOnly)
5256
return StringRef();
5357

lib/AST/Type.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3096,6 +3096,12 @@ getForeignRepresentable(Type type, ForeignLanguage language,
30963096
// Imported classes and protocols are not representable in C.
30973097
if (isa<ClassDecl>(nominal) || isa<ProtocolDecl>(nominal))
30983098
return failure();
3099+
3100+
// @objc enums are not representable in C, @cdecl ones and imported ones
3101+
// are ok.
3102+
if (!nominal->hasClangNode())
3103+
return failure();
3104+
30993105
LLVM_FALLTHROUGH;
31003106

31013107
case ForeignLanguage::ObjectiveC:
@@ -3134,6 +3140,11 @@ getForeignRepresentable(Type type, ForeignLanguage language,
31343140
}
31353141
}
31363142

3143+
// @cdecl enums are representable in C and Objective-C.
3144+
if (nominal->getAttrs().getAttribute<CDeclAttr>()) {
3145+
return { ForeignRepresentableKind::Trivial, nullptr };
3146+
}
3147+
31373148
// Pointers may be representable in ObjC.
31383149
PointerTypeKind pointerKind;
31393150
if (auto pointerElt = type->getAnyPointerElementType(pointerKind)) {

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,8 +2388,14 @@ class DeclAndTypePrinter::Implementation
23882388
}
23892389

23902390
void maybePrintTagKeyword(const TypeDecl *NTD) {
2391-
if (isa<EnumDecl>(NTD) && !NTD->hasClangNode()) {
2392-
os << "enum ";
2391+
if (auto *ED = dyn_cast<EnumDecl>(NTD); !NTD->hasClangNode()) {
2392+
if (ED->getAttrs().hasAttribute<CDeclAttr>()) {
2393+
// We should be able to use the tag macro for all printed enums but
2394+
// for now restrict it to @cdecl to guard it behind the feature flag.
2395+
os << "SWIFT_ENUM_TAG ";
2396+
} else {
2397+
os << "enum ";
2398+
}
23932399
return;
23942400
}
23952401

@@ -3019,9 +3025,17 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
30193025
(outputLang == OutputLanguageMode::C))
30203026
return false;
30213027

3022-
// C output mode only accepts @cdecl functions.
3028+
// C output mode only prints @cdecl functions and enums.
30233029
if (outputLang == OutputLanguageMode::C &&
3024-
!cdeclKind) {
3030+
!cdeclKind && !isa<EnumDecl>(VD)) {
3031+
return false;
3032+
}
3033+
3034+
// The C mode prints @cdecl enums and reject other enums,
3035+
// while other modes accept other enums and reject @cdecl ones.
3036+
if (isa<EnumDecl>(VD) &&
3037+
VD->getAttrs().hasAttribute<CDeclAttr>() !=
3038+
(outputLang == OutputLanguageMode::C)) {
30253039
return false;
30263040
}
30273041

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,14 @@ class ModuleWriter {
457457
}
458458
}
459459

460+
if (isa<EnumDecl>(D) && !D->hasClangNode() &&
461+
outputLangMode != OutputLanguageMode::Cxx) {
462+
// We don't want to add an import for a @cdecl or @objc enum declared
463+
// in Swift. We either do nothing for special enums like Optional as
464+
// done in the prologue here, or we forward declare them.
465+
return false;
466+
}
467+
460468
imports.insert(otherModule);
461469
return true;
462470
}
@@ -530,12 +538,20 @@ class ModuleWriter {
530538
}
531539

532540
void forwardDeclare(const EnumDecl *ED) {
533-
assert(ED->isObjC() || ED->hasClangNode());
541+
assert(ED->isObjC() || ED->getAttrs().getAttribute<CDeclAttr>() ||
542+
ED->hasClangNode());
534543

535544
forwardDeclare(ED, [&]{
536-
os << "enum " << getNameForObjC(ED) << " : ";
537-
printer.print(ED->getRawType());
538-
os << ";\n";
545+
if (ED->getASTContext().LangOpts.hasFeature(Feature::CDecl)) {
546+
// Forward declare in a way to be compatible with older C standards.
547+
os << "typedef SWIFT_ENUM_FWD_DECL(";
548+
printer.print(ED->getRawType());
549+
os << ", " << getNameForObjC(ED) << ")\n";
550+
} else {
551+
os << "enum " << getNameForObjC(ED) << " : ";
552+
printer.print(ED->getRawType());
553+
os << ";\n";
554+
}
539555
});
540556
}
541557

@@ -604,6 +620,7 @@ class ModuleWriter {
604620
} else if (addImport(TD)) {
605621
return;
606622
} else if (auto ED = dyn_cast<EnumDecl>(TD)) {
623+
// Treat this after addImport to filter out special enums from the stdlib.
607624
forwardDeclare(ED);
608625
} else if (isa<GenericTypeParamDecl>(TD)) {
609626
llvm_unreachable("should not see generic parameters here");
@@ -864,7 +881,7 @@ class ModuleWriter {
864881

865882
SmallVector<ProtocolConformance *, 1> conformances;
866883
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
867-
if (outputLangMode != OutputLanguageMode::Cxx
884+
if (outputLangMode == OutputLanguageMode::ObjC
868885
&& ED->lookupConformance(errorTypeProto, conformances)) {
869886
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
870887
ED->getAllElements().end(),

lib/Sema/TypeCheckAttr.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2398,9 +2398,21 @@ void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) {
23982398
attr->Name);
23992399
}
24002400

2401+
// @_cdecl was never accepted on enums. Keep the previous diagnostic.
2402+
if (isa<EnumDecl>(D) && attr->Underscored) {
2403+
diagnose(attr->getLocation(), diag::attr_only_one_decl_kind,
2404+
attr, "func");
2405+
}
2406+
2407+
// Reject using both @cdecl and @objc on the same decl.
2408+
if (D->getAttrs().getAttribute<ObjCAttr>()) {
2409+
diagnose(attr->getLocation(), diag::cdecl_incompatible_with_objc, D);
2410+
}
2411+
2412+
// @cdecl needs to be enabled via a feature flag.
24012413
if (!attr->Underscored &&
24022414
!Ctx.LangOpts.hasFeature(Feature::CDecl)) {
2403-
Ctx.Diags.diagnose(attr->getLocation(), diag::cdecl_feature_required);
2415+
diagnose(attr->getLocation(), diag::cdecl_feature_required);
24042416
}
24052417
}
24062418

0 commit comments

Comments
 (0)