mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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.
This commit is contained in:
@@ -176,20 +176,37 @@ CLANG_MACRO_CONDITIONAL("SWIFT_ENUM_ATTR", "(_extensibility)", \
|
||||
"__attribute__((enum_extensibility(_extensibility)))")
|
||||
|
||||
CLANG_MACRO_BODY("SWIFT_ENUM", \
|
||||
"# define SWIFT_ENUM(_type, _name, _extensibility) " \
|
||||
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
|
||||
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
|
||||
" __has_feature(objc_fixed_enum)\n" \
|
||||
"# define SWIFT_ENUM(_type, _name, _extensibility) " \
|
||||
"enum _name : _type _name; " \
|
||||
"enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type\n" \
|
||||
"# if __has_feature(generalized_swift_name)\n" \
|
||||
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
|
||||
"enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \
|
||||
"enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \
|
||||
"SWIFT_ENUM_EXTRA _name : _type\n" \
|
||||
"# if __has_feature(generalized_swift_name)\n" \
|
||||
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
|
||||
"enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); " \
|
||||
"enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) " \
|
||||
"SWIFT_ENUM_EXTRA _name : _type\n" \
|
||||
"# else\n" \
|
||||
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
|
||||
"SWIFT_ENUM(_type, _name, _extensibility)\n" \
|
||||
"# endif\n" \
|
||||
"# else\n" \
|
||||
"# define SWIFT_ENUM(_type, _name, _extensibility) _type _name; enum \n" \
|
||||
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) " \
|
||||
"SWIFT_ENUM(_type, _name, _extensibility)\n" \
|
||||
"# endif")
|
||||
CLANG_MACRO_DEFINED("SWIFT_ENUM_NAMED")
|
||||
|
||||
CLANG_MACRO_BODY("SWIFT_ENUM_TAG", \
|
||||
"# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \
|
||||
" (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \
|
||||
" __has_feature(objc_fixed_enum)\n" \
|
||||
"# define SWIFT_ENUM_TAG enum\n" \
|
||||
"# else\n" \
|
||||
"# define SWIFT_ENUM_TAG\n" \
|
||||
"# endif")
|
||||
|
||||
CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))")
|
||||
CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))")
|
||||
|
||||
|
||||
@@ -2385,8 +2385,14 @@ private:
|
||||
}
|
||||
|
||||
void maybePrintTagKeyword(const TypeDecl *NTD) {
|
||||
if (isa<EnumDecl>(NTD) && !NTD->hasClangNode()) {
|
||||
os << "enum ";
|
||||
if (auto *ED = dyn_cast<EnumDecl>(NTD); !NTD->hasClangNode()) {
|
||||
if (ED->getAttrs().hasAttribute<CDeclAttr>()) {
|
||||
// We should be able to use the tag macro for all printed enums but
|
||||
// for now restrict it to @cdecl to guard it behind the feature flag.
|
||||
os << "SWIFT_ENUM_TAG ";
|
||||
} else {
|
||||
os << "enum ";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3016,9 +3022,17 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
|
||||
(outputLang == OutputLanguageMode::C))
|
||||
return false;
|
||||
|
||||
// C output mode only accepts @cdecl functions.
|
||||
// C output mode only prints @cdecl functions and enums.
|
||||
if (outputLang == OutputLanguageMode::C &&
|
||||
!cdeclKind) {
|
||||
!cdeclKind && !isa<EnumDecl>(VD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The C mode prints @cdecl enums and reject other enums,
|
||||
// while other modes accept other enums and reject @cdecl ones.
|
||||
if (isa<EnumDecl>(VD) &&
|
||||
VD->getAttrs().hasAttribute<CDeclAttr>() !=
|
||||
(outputLang == OutputLanguageMode::C)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -530,6 +530,10 @@ public:
|
||||
}
|
||||
|
||||
void forwardDeclare(const EnumDecl *ED) {
|
||||
// Don't forward declare C enums.
|
||||
if (ED->getAttrs().getAttribute<CDeclAttr>())
|
||||
return;
|
||||
|
||||
assert(ED->isObjC() || ED->hasClangNode());
|
||||
|
||||
forwardDeclare(ED, [&]{
|
||||
@@ -864,7 +868,7 @@ public:
|
||||
|
||||
SmallVector<ProtocolConformance *, 1> conformances;
|
||||
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
|
||||
if (outputLangMode != OutputLanguageMode::Cxx
|
||||
if (outputLangMode == OutputLanguageMode::ObjC
|
||||
&& ED->lookupConformance(errorTypeProto, conformances)) {
|
||||
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
|
||||
ED->getAllElements().end(),
|
||||
|
||||
126
test/PrintAsObjC/cdecl-enums.swift
Normal file
126
test/PrintAsObjC/cdecl-enums.swift
Normal file
@@ -0,0 +1,126 @@
|
||||
/// Variant of PrintAsObjC/enums.swift for @cdecl enums.
|
||||
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-source-import \
|
||||
// RUN: -emit-module -emit-module-doc -o %t %s \
|
||||
// RUN: -import-objc-header %S/Inputs/enums.h \
|
||||
// RUN: -emit-objc-header-path %t/enums.h \
|
||||
// RUN: -disable-objc-attr-requires-foundation-module \
|
||||
// RUN: -enable-experimental-feature CDecl
|
||||
|
||||
// RUN: %FileCheck %s --input-file %t/enums.h
|
||||
// RUN: %FileCheck -check-prefix=NEGATIVE %s --input-file %t/enums.h
|
||||
// RUN: %check-in-clang %t/enums.h
|
||||
|
||||
// REQUIRES: swift_feature_CDecl
|
||||
// REQUIRES: objc_interop
|
||||
|
||||
import Foundation
|
||||
|
||||
// NEGATIVE-NOT: enum EnumNamed
|
||||
|
||||
/// No error domains in C mode.
|
||||
// NEGATIVE-NOT: @"main.
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, ObjcEnumNamed, "EnumNamed", closed) {
|
||||
// CHECK-NEXT: ObjcEnumNamedA = 0,
|
||||
// CHECK-NEXT: ObjcEnumNamedB = 1,
|
||||
// CHECK-NEXT: ObjcEnumNamedC = 2,
|
||||
// CHECK-NEXT: ObjcEnumNamedD = 3,
|
||||
// CHECK-NEXT: ObjcEnumNamedHelloDolly = 4,
|
||||
// CHECK-NEXT: };
|
||||
|
||||
@cdecl("ObjcEnumNamed") enum EnumNamed: Int {
|
||||
case A, B, C, d, helloDolly
|
||||
}
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(unsigned int, ExplicitValues, "ExplicitValues", closed) {
|
||||
// CHECK-NEXT: ExplicitValuesZim = 0,
|
||||
// CHECK-NEXT: ExplicitValuesZang = 219,
|
||||
// CHECK-NEXT: ExplicitValuesZung = 220,
|
||||
// CHECK-NEXT: };
|
||||
// NEGATIVE-NOT: ExplicitValuesDomain
|
||||
|
||||
@cdecl("ExplicitValues") enum ExplicitValues: CUnsignedInt {
|
||||
case Zim, Zang = 219, Zung
|
||||
|
||||
func methodNotExportedToC() {}
|
||||
}
|
||||
|
||||
// CHECK: /// Foo: A feer, a female feer.
|
||||
// CHECK-NEXT: typedef SWIFT_ENUM_NAMED(int, FooComments, "FooComments", closed) {
|
||||
// CHECK: /// Zim: A zeer, a female zeer.
|
||||
// CHECK-NEXT: FooCommentsZim = 0,
|
||||
// CHECK-NEXT: FooCommentsZang = 1,
|
||||
// CHECK-NEXT: FooCommentsZung = 2,
|
||||
// CHECK-NEXT: }
|
||||
|
||||
/// Foo: A feer, a female feer.
|
||||
@cdecl("FooComments") public enum FooComments: CInt {
|
||||
/// Zim: A zeer, a female zeer.
|
||||
case Zim
|
||||
case Zang, Zung
|
||||
}
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(int16_t, NegativeValues, "NegativeValues", closed) {
|
||||
// CHECK-NEXT: Zang = -219,
|
||||
// CHECK-NEXT: Zung = -218,
|
||||
// CHECK-NEXT: };
|
||||
@cdecl("NegativeValues") enum NegativeValues: Int16 {
|
||||
case Zang = -219, Zung
|
||||
|
||||
func methodNotExportedToC() {}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, SomeError, "SomeError", closed) {
|
||||
// CHECK-NEXT: SomeErrorBadness = 9001,
|
||||
// CHECK-NEXT: SomeErrorWorseness = 9002,
|
||||
// CHECK-NEXT: };
|
||||
@cdecl("SomeError") enum SomeError: Int, Error {
|
||||
case Badness = 9001
|
||||
case Worseness
|
||||
}
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, SomeOtherError, "SomeOtherError", closed) {
|
||||
// CHECK-NEXT: SomeOtherErrorDomain = 0,
|
||||
// CHECK-NEXT: };
|
||||
@cdecl("SomeOtherError") enum SomeOtherError: Int, Error {
|
||||
case Domain
|
||||
}
|
||||
|
||||
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(ptrdiff_t, ObjcErrorType, "SomeRenamedErrorType", closed) {
|
||||
// CHECK-NEXT: ObjcErrorTypeBadStuff = 0,
|
||||
// CHECK-NEXT: };
|
||||
@cdecl("ObjcErrorType") enum SomeRenamedErrorType: Int, Error {
|
||||
case BadStuff
|
||||
}
|
||||
|
||||
@cdecl("acceptMemberImported") func acceptMemberImported(a: Wrapper.Raw, b: Wrapper.Enum, c: Wrapper.Options, d: Wrapper.Typedef, e: Wrapper.Anon, ee: Wrapper.Anon2) {}
|
||||
// CHECK-LABEL: SWIFT_EXTERN void acceptMemberImported(enum MemberRaw a, enum MemberEnum b, MemberOptions c, enum MemberTypedef d, MemberAnon e, MemberAnon2 ee) SWIFT_NOEXCEPT;
|
||||
|
||||
@cdecl("acceptPlainEnum") func acceptPlainEnum(_: NSMalformedEnumMissingTypedef) {}
|
||||
// CHECK-LABEL: SWIFT_EXTERN void acceptPlainEnum(enum NSMalformedEnumMissingTypedef) SWIFT_NOEXCEPT;
|
||||
|
||||
@cdecl("acceptTopLevelImported") func acceptTopLevelImported(a: TopLevelRaw, b: TopLevelEnum, c: TopLevelOptions, d: TopLevelTypedef, e: TopLevelAnon) {}
|
||||
// CHECK-LABEL: SWIFT_EXTERN void acceptTopLevelImported(enum TopLevelRaw a, TopLevelEnum b, TopLevelOptions c, TopLevelTypedef d, TopLevelAnon e) SWIFT_NOEXCEPT;
|
||||
|
||||
@cdecl("takeAndReturnEnumC") func takeAndReturnEnumC(_ foo: FooComments) -> NegativeValues {
|
||||
return .Zung
|
||||
}
|
||||
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG NegativeValues takeAndReturnEnumC(SWIFT_ENUM_TAG FooComments foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
@cdecl("takeAndReturnRenamedEnum") func takeAndReturnRenamedEnum(_ foo: EnumNamed) -> EnumNamed {
|
||||
return .A
|
||||
}
|
||||
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG ObjcEnumNamed takeAndReturnRenamedEnum(SWIFT_ENUM_TAG ObjcEnumNamed foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
/// Objective-C user.
|
||||
|
||||
// CHECK-LABEL: SWIFT_ENUM_FWD_DECL(int, FooComments)
|
||||
// CHECK-LABEL: SWIFT_ENUM_FWD_DECL(int16_t, NegativeValues)
|
||||
|
||||
// CHECK-LABEL: SWIFT_EXTERN SWIFT_ENUM_TAG NegativeValues takeAndReturnEnumObjC(SWIFT_ENUM_TAG FooComments foo) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
@_cdecl("takeAndReturnEnumObjC") func takeAndReturnEnumObjC(_ foo: FooComments) -> NegativeValues {
|
||||
return .Zung
|
||||
}
|
||||
@@ -27,6 +27,22 @@
|
||||
// CHECK: extern "C" {
|
||||
// CHECK: #endif
|
||||
|
||||
// CHECK: /// Enums
|
||||
// CHECK: typedef SWIFT_ENUM_NAMED(int, CEnum, "CEnum", closed) {
|
||||
// CHECK: CEnumA = 0,
|
||||
// CHECK: CEnumB = 1,
|
||||
// CHECK: };
|
||||
|
||||
// CHECK: typedef SWIFT_ENUM_NAMED(long, CEnumRenamed_CName, "CEnumRenamed", closed) {
|
||||
// CHECK: CEnumRenamed_CNameA = 0,
|
||||
// CHECK: CEnumRenamed_CNameB = 1,
|
||||
// CHECK: };
|
||||
|
||||
// CHECK: typedef SWIFT_ENUM_NAMED(char, zCEnumDefinedLate, "zCEnumDefinedLate", closed) {
|
||||
// CHECK: CEnumDefinedLateA = 0,
|
||||
// CHECK: CEnumDefinedLateB = 1,
|
||||
// CHECK: };
|
||||
|
||||
/// My documentation
|
||||
@cdecl("simple")
|
||||
func a_simple(x: Int, bar y: Int) -> Int { return x }
|
||||
@@ -62,6 +78,30 @@ func g_nullablePointers(_ x: UnsafeMutableRawPointer,
|
||||
z: UnsafeMutableRawPointer!) {}
|
||||
// CHECK: SWIFT_EXTERN void nullable_pointers(void * _Nonnull x, void * _Nullable y, void * _Null_unspecified z) SWIFT_NOEXCEPT;
|
||||
|
||||
/// Enums
|
||||
|
||||
@cdecl("CEnum")
|
||||
enum CEnum: CInt { case A, B }
|
||||
|
||||
@cdecl("CEnumRenamed_CName")
|
||||
enum CEnumRenamed: CLong { case A, B }
|
||||
|
||||
@cdecl("use_enum")
|
||||
func h_useCEnum(e: CEnum) -> CEnum { return e }
|
||||
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG CEnum use_enum(SWIFT_ENUM_TAG CEnum e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
@cdecl("use_enum_renamed")
|
||||
func i_useCEnumLong(e: CEnumRenamed) -> CEnumRenamed { return e }
|
||||
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG CEnumRenamed_CName use_enum_renamed(SWIFT_ENUM_TAG CEnumRenamed_CName e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
@cdecl("use_enum_late")
|
||||
func j_useCEnumChar(e: zCEnumDefinedLate) -> zCEnumDefinedLate { return e }
|
||||
// CHECK: SWIFT_EXTERN SWIFT_ENUM_TAG zCEnumDefinedLate use_enum_late(SWIFT_ENUM_TAG zCEnumDefinedLate e) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
|
||||
|
||||
/// Declare this enum late in the source file and in alphabetical order.
|
||||
@cdecl("zCEnumDefinedLate")
|
||||
enum zCEnumDefinedLate: CChar { case A, B }
|
||||
|
||||
// CHECK: #if defined(__cplusplus)
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: #endif
|
||||
@@ -74,5 +114,10 @@ int main() {
|
||||
ptrdiff_t x = simple(42, 43);
|
||||
primitiveTypes(1, 2, 3, 'a', 1.0f, 2.0, true);
|
||||
has_keyword_arg_names(1, 2);
|
||||
|
||||
(void)use_enum(CEnumA);
|
||||
(void)use_enum_renamed(CEnumRenamed_CNameB);
|
||||
(void)use_enum_late(zCEnumDefinedLateA);
|
||||
|
||||
return_never();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user