Skip to content

Commit 138e2da

Browse files
committed
PrintAsClang: Print @cdecl enums in the compatibility header
Print @cdecl enums in the C section of the compatibility header. Use and extend the macros to support C compiler clients. The macro is adapted to the features supported by the client compiler. It uses an Objective-C style macro with raw type when available and fallbacks to a simple typedef for C compatibility.
1 parent 6d09112 commit 138e2da

File tree

5 files changed

+217
-11
lines changed

5 files changed

+217
-11
lines changed

include/swift/PrintAsClang/ClangMacros.def

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,20 +176,37 @@ 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+
193210
CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))")
194211
CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))")
195212

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,8 +2385,14 @@ class DeclAndTypePrinter::Implementation
23852385
}
23862386

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

@@ -3016,9 +3022,17 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
30163022
(outputLang == OutputLanguageMode::C))
30173023
return false;
30183024

3019-
// C output mode only accepts @cdecl functions.
3025+
// C output mode only prints @cdecl functions and enums.
30203026
if (outputLang == OutputLanguageMode::C &&
3021-
!cdeclKind) {
3027+
!cdeclKind && !isa<EnumDecl>(VD)) {
3028+
return false;
3029+
}
3030+
3031+
// The C mode prints @cdecl enums and reject other enums,
3032+
// while other modes accept other enums and reject @cdecl ones.
3033+
if (isa<EnumDecl>(VD) &&
3034+
VD->getAttrs().hasAttribute<CDeclAttr>() !=
3035+
(outputLang == OutputLanguageMode::C)) {
30223036
return false;
30233037
}
30243038

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,10 @@ class ModuleWriter {
530530
}
531531

532532
void forwardDeclare(const EnumDecl *ED) {
533+
// Don't forward declare C enums.
534+
if (ED->getAttrs().getAttribute<CDeclAttr>())
535+
return;
536+
533537
assert(ED->isObjC() || ED->hasClangNode());
534538

535539
forwardDeclare(ED, [&]{
@@ -864,7 +868,7 @@ class ModuleWriter {
864868

865869
SmallVector<ProtocolConformance *, 1> conformances;
866870
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
867-
if (outputLangMode != OutputLanguageMode::Cxx
871+
if (outputLangMode == OutputLanguageMode::ObjC
868872
&& ED->lookupConformance(errorTypeProto, conformances)) {
869873
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
870874
ED->getAllElements().end(),

test/PrintAsObjC/cdecl-enums.swift

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/// Variant of PrintAsObjC/enums.swift for @cdecl enums.
2+
3+
// RUN: %empty-directory(%t)
4+
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-source-import \
6+
// RUN: -emit-module -emit-module-doc -o %t %s \
7+
// RUN: -import-objc-header %S/Inputs/enums.h \
8+
// RUN: -emit-objc-header-path %t/enums.h \
9+
// RUN: -disable-objc-attr-requires-foundation-module \
10+
// RUN: -enable-experimental-feature CDecl
11+
12+
// RUN: %FileCheck %s --input-file %t/enums.h
13+
// RUN: %FileCheck -check-prefix=NEGATIVE %s --input-file %t/enums.h
14+
// RUN: %check-in-clang %t/enums.h
15+
16+
// REQUIRES: swift_feature_CDecl
17+
// REQUIRES: objc_interop
18+
19+
import Foundation
20+
21+
// NEGATIVE-NOT: enum EnumNamed
22+
23+
/// No error domains in C mode.
24+
// NEGATIVE-NOT: @"main.
25+
26+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, ObjcEnumNamed, "EnumNamed", closed) {
27+
// CHECK-NEXT: ObjcEnumNamedA = 0,
28+
// CHECK-NEXT: ObjcEnumNamedB = 1,
29+
// CHECK-NEXT: ObjcEnumNamedC = 2,
30+
// CHECK-NEXT: ObjcEnumNamedD = 3,
31+
// CHECK-NEXT: ObjcEnumNamedHelloDolly = 4,
32+
// CHECK-NEXT: };
33+
34+
@cdecl("ObjcEnumNamed") enum EnumNamed: Int {
35+
case A, B, C, d, helloDolly
36+
}
37+
38+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(unsigned int, ExplicitValues, "ExplicitValues", closed) {
39+
// CHECK-NEXT: ExplicitValuesZim = 0,
40+
// CHECK-NEXT: ExplicitValuesZang = 219,
41+
// CHECK-NEXT: ExplicitValuesZung = 220,
42+
// CHECK-NEXT: };
43+
// NEGATIVE-NOT: ExplicitValuesDomain
44+
45+
@cdecl("ExplicitValues") enum ExplicitValues: CUnsignedInt {
46+
case Zim, Zang = 219, Zung
47+
48+
func methodNotExportedToC() {}
49+
}
50+
51+
// CHECK: /// Foo: A feer, a female feer.
52+
// CHECK-NEXT: typedef SWIFT_ENUM_NAMED(int, FooComments, "FooComments", closed) {
53+
// CHECK: /// Zim: A zeer, a female zeer.
54+
// CHECK-NEXT: FooCommentsZim = 0,
55+
// CHECK-NEXT: FooCommentsZang = 1,
56+
// CHECK-NEXT: FooCommentsZung = 2,
57+
// CHECK-NEXT: }
58+
59+
/// Foo: A feer, a female feer.
60+
@cdecl("FooComments") public enum FooComments: CInt {
61+
/// Zim: A zeer, a female zeer.
62+
case Zim
63+
case Zang, Zung
64+
}
65+
66+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(int16_t, NegativeValues, "NegativeValues", closed) {
67+
// CHECK-NEXT: Zang = -219,
68+
// CHECK-NEXT: Zung = -218,
69+
// CHECK-NEXT: };
70+
@cdecl("NegativeValues") enum NegativeValues: Int16 {
71+
case Zang = -219, Zung
72+
73+
func methodNotExportedToC() {}
74+
}
75+
76+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, SomeError, "SomeError", closed) {
77+
// CHECK-NEXT: SomeErrorBadness = 9001,
78+
// CHECK-NEXT: SomeErrorWorseness = 9002,
79+
// CHECK-NEXT: };
80+
@cdecl("SomeError") enum SomeError: Int, Error {
81+
case Badness = 9001
82+
case Worseness
83+
}
84+
85+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, SomeOtherError, "SomeOtherError", closed) {
86+
// CHECK-NEXT: SomeOtherErrorDomain = 0,
87+
// CHECK-NEXT: };
88+
@cdecl("SomeOtherError") enum SomeOtherError: Int, Error {
89+
case Domain
90+
}
91+
92+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, ObjcErrorType, "SomeRenamedErrorType", closed) {
93+
// CHECK-NEXT: ObjcErrorTypeBadStuff = 0,
94+
// CHECK-NEXT: };
95+
@cdecl("ObjcErrorType") enum SomeRenamedErrorType: Int, Error {
96+
case BadStuff
97+
}
98+
99+
@cdecl("acceptMemberImported") func acceptMemberImported(a: Wrapper.Raw, b: Wrapper.Enum, c: Wrapper.Options, d: Wrapper.Typedef, e: Wrapper.Anon, ee: Wrapper.Anon2) {}
100+
// CHECK-LABEL: SWIFT_EXTERN void acceptMemberImported(enum MemberRaw a, enum MemberEnum b, MemberOptions c, enum MemberTypedef d, MemberAnon e, MemberAnon2 ee) SWIFT_NOEXCEPT;
101+
102+
@cdecl("acceptPlainEnum") func acceptPlainEnum(_: NSMalformedEnumMissingTypedef) {}
103+
// CHECK-LABEL: SWIFT_EXTERN void acceptPlainEnum(enum NSMalformedEnumMissingTypedef) SWIFT_NOEXCEPT;
104+
105+
@cdecl("acceptTopLevelImported") func acceptTopLevelImported(a: TopLevelRaw, b: TopLevelEnum, c: TopLevelOptions, d: TopLevelTypedef, e: TopLevelAnon) {}
106+
// CHECK-LABEL: SWIFT_EXTERN void acceptTopLevelImported(enum TopLevelRaw a, TopLevelEnum b, TopLevelOptions c, TopLevelTypedef d, TopLevelAnon e) SWIFT_NOEXCEPT;
107+
108+
@cdecl("takeAndReturnEnumC") func takeAndReturnEnumC(_ foo: FooComments) -> NegativeValues {
109+
return .Zung
110+
}
111+
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG NegativeValues takeAndReturnEnumC(SWIFT_ENUM_TAG FooComments foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
112+
113+
@cdecl("takeAndReturnRenamedEnum") func takeAndReturnRenamedEnum(_ foo: EnumNamed) -> EnumNamed {
114+
return .A
115+
}
116+
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG ObjcEnumNamed takeAndReturnRenamedEnum(SWIFT_ENUM_TAG ObjcEnumNamed foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
117+
118+
/// Objective-C user.
119+
120+
// CHECK-LABEL: SWIFT_ENUM_FWD_DECL(int, FooComments)
121+
// CHECK-LABEL: SWIFT_ENUM_FWD_DECL(int16_t, NegativeValues)
122+
123+
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG NegativeValues takeAndReturnEnumObjC(SWIFT_ENUM_TAG FooComments foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
124+
@_cdecl("takeAndReturnEnumObjC") func takeAndReturnEnumObjC(_ foo: FooComments) -> NegativeValues {
125+
return .Zung
126+
}

test/PrintAsObjC/cdecl-official.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@
2727
// CHECK: extern "C" {
2828
// CHECK: #endif
2929

30+
// CHECK: /// Enums
31+
// CHECK: typedef SWIFT_ENUM_NAMED(int, CEnum, "CEnum", closed) {
32+
// CHECK: CEnumA = 0,
33+
// CHECK: CEnumB = 1,
34+
// CHECK: };
35+
36+
// CHECK: typedef SWIFT_ENUM_NAMED(long, CEnumRenamed_CName, "CEnumRenamed", closed) {
37+
// CHECK: CEnumRenamed_CNameA = 0,
38+
// CHECK: CEnumRenamed_CNameB = 1,
39+
// CHECK: };
40+
41+
// CHECK: typedef SWIFT_ENUM_NAMED(char, zCEnumDefinedLate, "zCEnumDefinedLate", closed) {
42+
// CHECK: CEnumDefinedLateA = 0,
43+
// CHECK: CEnumDefinedLateB = 1,
44+
// CHECK: };
45+
3046
/// My documentation
3147
@cdecl("simple")
3248
func a_simple(x: Int, bar y: Int) -> Int { return x }
@@ -62,6 +78,30 @@ func g_nullablePointers(_ x: UnsafeMutableRawPointer,
6278
z: UnsafeMutableRawPointer!) {}
6379
// CHECK: SWIFT_EXTERN void nullable_pointers(void * _Nonnull x, void * _Nullable y, void * _Null_unspecified z) SWIFT_NOEXCEPT;
6480

81+
/// Enums
82+
83+
@cdecl("CEnum")
84+
enum CEnum: CInt { case A, B }
85+
86+
@cdecl("CEnumRenamed_CName")
87+
enum CEnumRenamed: CLong { case A, B }
88+
89+
@cdecl("use_enum")
90+
func h_useCEnum(e: CEnum) -> CEnum { return e }
91+
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG CEnum use_enum(SWIFT_ENUM_TAG CEnum e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
92+
93+
@cdecl("use_enum_renamed")
94+
func i_useCEnumLong(e: CEnumRenamed) -> CEnumRenamed { return e }
95+
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG CEnumRenamed_CName use_enum_renamed(SWIFT_ENUM_TAG CEnumRenamed_CName e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
96+
97+
@cdecl("use_enum_late")
98+
func j_useCEnumChar(e: zCEnumDefinedLate) -> zCEnumDefinedLate { return e }
99+
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG zCEnumDefinedLate use_enum_late(SWIFT_ENUM_TAG zCEnumDefinedLate e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
100+
101+
/// Declare this enum late in the source file and in alphabetical order.
102+
@cdecl("zCEnumDefinedLate")
103+
enum zCEnumDefinedLate: CChar { case A, B }
104+
65105
// CHECK: #if defined(__cplusplus)
66106
// CHECK-NEXT: }
67107
// CHECK-NEXT: #endif
@@ -74,5 +114,10 @@ int main() {
74114
ptrdiff_t x = simple(42, 43);
75115
primitiveTypes(1, 2, 3, 'a', 1.0f, 2.0, true);
76116
has_keyword_arg_names(1, 2);
117+
118+
(void)use_enum(CEnumA);
119+
(void)use_enum_renamed(CEnumRenamed_CNameB);
120+
(void)use_enum_late(zCEnumDefinedLateA);
121+
77122
return_never();
78123
}

0 commit comments

Comments
 (0)