diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 03ff13ae7fac5..07436d56358be 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -949,11 +949,12 @@ BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed( BridgedSourceRange cRange, BridgedPlatformKind cPlatform, BridgedVersionTuple cVersion); -SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:)") +SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:underscored:)") BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext, BridgedSourceLoc cAtLoc, BridgedSourceRange cRange, - BridgedStringRef cName); + BridgedStringRef cName, + bool underscored); SWIFT_NAME( "BridgedCustomAttr.createParsed(_:atLoc:type:initContext:argumentList:)") diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index a0e2bf98f2f9a..399f6b29d191b 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -691,22 +691,29 @@ class SectionAttr : public DeclAttribute { /// Defines the @_cdecl attribute. class CDeclAttr : public DeclAttribute { public: - CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit) - : DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit), Name(Name) { + CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit, + bool Underscored) + : DeclAttribute(DeclAttrKind::CDecl, AtLoc, Range, Implicit), + Name(Name), Underscored(Underscored) { } - CDeclAttr(StringRef Name, bool Implicit) - : CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit) {} + CDeclAttr(StringRef Name, bool Implicit, bool Underscored) + : CDeclAttr(Name, SourceLoc(), SourceRange(), Implicit, Underscored) {} /// The symbol name. const StringRef Name; + /// Is this the version of the attribute that's underscored? + /// Used to preserve retro compatibility with early adopters. + const bool Underscored; + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::CDecl; } CDeclAttr *clone(ASTContext &ctx) const { - return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit()); + return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit(), + Underscored); } bool isEquivalent(const CDeclAttr *other, Decl *attachedTo) const { diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 18efc38168122..74c6ef4418dbb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -8172,6 +8172,11 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// instance method. bool isObjCInstanceMethod() const; + /// Get the foreign language targeted by a @cdecl-style attribute, if any. + /// Used to abstract away the change in meaning of @cdecl vs @_cdecl while + /// formalizing the attribute. + std::optional getCDeclKind() const; + /// Determine whether the name of an argument is an API name by default /// depending on the function context. bool argumentNameIsAPIByDefault() const; diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index ba2e8c28a28c6..6c7a9a0a8d85b 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -365,9 +365,10 @@ SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface, 62) DECL_ATTR(_cdecl, CDecl, - OnFunc | OnAccessor, + OnFunc | OnAccessor | OnEnum, LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 63) +DECL_ATTR_ALIAS(cdecl, CDecl) SIMPLE_DECL_ATTR(usableFromInline, UsableFromInline, OnAbstractFunction | OnVar | OnSubscript | OnNominalType | OnTypeAlias, diff --git a/include/swift/AST/DiagnosticsCommon.h b/include/swift/AST/DiagnosticsCommon.h index 80d0c725ffe46..bf4b26eaedb91 100644 --- a/include/swift/AST/DiagnosticsCommon.h +++ b/include/swift/AST/DiagnosticsCommon.h @@ -48,6 +48,7 @@ namespace swift { } // end namespace detail enum class StaticSpellingKind : uint8_t; + enum class ForeignLanguage : uint8_t; namespace diag { diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 37136b7d8c7b8..e7fbc7fc0a5f2 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1529,6 +1529,9 @@ ERROR(attr_expected_comma,none, ERROR(attr_expected_string_literal,none, "expected string literal in '%0' attribute", (StringRef)) +ERROR(attr_expected_cname,none, + "expected C identifier in '%0' attribute", (StringRef)) + ERROR(attr_expected_option_such_as,none, "expected '%0' option such as '%1'", (StringRef, StringRef)) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0c646d2c93458..f1b76bd40378e 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2051,11 +2051,17 @@ WARNING(wrap_objc_implementation_will_become_error,none, (DiagnosticInfo *)) ERROR(cdecl_not_at_top_level,none, - "@_cdecl can only be applied to global functions", ()) + "%0 can only be applied to global functions", (DeclAttribute)) ERROR(cdecl_empty_name,none, - "@_cdecl symbol name cannot be empty", ()) + "%0 symbol name cannot be empty", (DeclAttribute)) ERROR(cdecl_throws,none, - "raising errors from '@_cdecl' functions is not supported", ()) + "raising errors from %0 functions is not supported", (DeclAttribute)) +ERROR(cdecl_incompatible_with_objc,none, + "cannot apply both '@cdecl' and '@objc' to %kindonly0", + (const Decl *)) +ERROR(cdecl_feature_required,none, + "@cdecl requires '-enable-experimental-feature CDecl'", + ()) // @_used and @_section ERROR(section_linkage_markers_disabled,none, @@ -3736,9 +3742,9 @@ ERROR(enum_with_raw_type_case_with_argument,none, NOTE(enum_raw_type_here,none, "declared raw type %0 here", (Type)) ERROR(objc_enum_no_raw_type,none, - "'@objc' enum must declare an integer raw type", ()) + "'%0' enum must declare an integer raw type", (DeclAttribute)) ERROR(objc_enum_raw_type_not_integer,none, - "'@objc' enum raw type %0 is not an integer type", (Type)) + "'%0' enum raw type %1 is not an integer type", (DeclAttribute, Type)) ERROR(enum_non_integer_raw_value_auto_increment,none, "enum case must declare a raw value when the preceding raw value is not an integer", ()) ERROR(enum_non_integer_convertible_raw_type_no_value,none, @@ -5513,10 +5519,14 @@ FIXIT(insert_globalactor_attr, "@%0 ", (Type)) ERROR(main_function_must_be_mainActor,none, "main() must be '@MainActor'", ()) +// Keep aligned with enum ForeignLanguage +#define FOREIGN_LANG_SELECT "select{C|Objective-C}" + ERROR(not_objc_function_async,none, "'async' %0 cannot be represented in Objective-C", (DescriptiveDeclKind)) NOTE(not_objc_function_type_async,none, - "'async' function types cannot be represented in Objective-C", ()) + "'async' function types cannot be represented " + "in %" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) ERROR(actor_isolated_objc,none, "actor-isolated %kind0 cannot be '@objc'", (const ValueDecl *)) @@ -6523,7 +6533,7 @@ ERROR(objc_cannot_infer_name_raw_identifier,none, (DescriptiveDeclKind)) // If you change this, also change enum ObjCReason -#define OBJC_ATTR_SELECT "select{marked '@_cdecl'|marked dynamic|marked '@objc'|marked '@objcMembers'|marked '@IBOutlet'|marked '@IBAction'|marked '@IBSegueAction'|marked '@NSManaged'|a member of an '@objc' protocol|implicitly '@objc'|an '@objc' override|an implementation of an '@objc' requirement|marked '@IBInspectable'|marked '@GKInspectable'|in an '@objc' extension of a class (without '@nonobjc')|in an '@objc @implementation' extension of a class (without final or '@nonobjc')|marked '@objc' by an access note}" +#define OBJC_ATTR_SELECT "select{marked '@cdecl'|marked '@_cdecl'|marked dynamic|marked '@objc'|marked '@objcMembers'|marked '@IBOutlet'|marked '@IBAction'|marked '@IBSegueAction'|marked '@NSManaged'|a member of an '@objc' protocol|implicitly '@objc'|an '@objc' override|an implementation of an '@objc' requirement|marked '@IBInspectable'|marked '@GKInspectable'|in an '@objc' extension of a class (without '@nonobjc')|in an '@objc @implementation' extension of a class (without final or '@nonobjc')|marked '@objc' by an access note}" ERROR(objc_invalid_on_var,none, "property cannot be %" OBJC_ATTR_SELECT "0 " @@ -6556,25 +6566,36 @@ NOTE(not_objc_error_protocol_composition,none, "protocol-constrained type containing 'Error' cannot be represented " "in Objective-C", ()) NOTE(not_objc_empty_tuple,none, - "empty tuple type cannot be represented in Objective-C", ()) + "empty tuple type cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_non_trivial_cxx_class,none, - "non-trivial C++ classes cannot be represented in Objective-C", ()) + "non-trivial C++ classes cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_tuple,none, - "tuples cannot be represented in Objective-C", ()) + "tuples cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_swift_class,none, "classes not annotated with '@objc' cannot be represented " "in Objective-C", ()) NOTE(not_objc_swift_struct,none, - "Swift structs cannot be represented in Objective-C", ()) + "Swift structs cannot be represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_swift_enum,none, "non-'@objc' enums cannot be represented in Objective-C", ()) +NOTE(not_cdecl_or_objc_swift_enum,none, + "Swift enums not marked '@cdecl'%select{| or '@objc'}0 cannot be " + "represented in %" FOREIGN_LANG_SELECT "0", + (ForeignLanguage)) NOTE(not_objc_generic_type_param,none, - "generic type parameters cannot be represented in Objective-C", ()) + "generic type parameters cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) NOTE(not_objc_function_type_param,none, - "function types cannot be represented in Objective-C unless their " - "parameters and returns can be", ()) + "function types cannot be represented in %" FOREIGN_LANG_SELECT "0 " + "unless their parameters and returns can be", (ForeignLanguage)) NOTE(not_objc_function_type_throwing,none, - "throwing function types cannot be represented in Objective-C", ()) + "throwing function types cannot be represented in " + "%" FOREIGN_LANG_SELECT "0", (ForeignLanguage)) NOTE(objc_inferring_on_objc_protocol_member,none, "inferring '@objc' because the declaration is a member of " "an '@objc' protocol", ()) @@ -6584,6 +6605,11 @@ NOTE(objc_witness_objc_requirement,none, "satisfying requirement for %kind0 in protocol %1", (const ValueDecl *, const ProtocolDecl *)) +NOTE(cdecl_incompatible_with_protocols,none, + "protocols cannot be represented in C", ()) +NOTE(cdecl_incompatible_with_classes,none, + "classes cannot be represented in C", ()) + ERROR(no_opaque_return_type_of,none, "unable to resolve type for _opaqueReturnTypeOf attribute", ()) @@ -6596,20 +6622,24 @@ ERROR(objc_addressor, none, ERROR(objc_coroutine_accessor, none, "'read' and 'modify' accessors are not allowed to be marked '@objc'", ()) ERROR(objc_invalid_on_func_variadic,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic " - "parameter", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because it has a variadic " + "parameter", (const AbstractFunctionDecl*, unsigned)) ERROR(objc_invalid_on_func_inout,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because inout " - "parameters cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because inout " + "parameters cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_param_type,none, - "method cannot be %" OBJC_ATTR_SELECT "1 because the type of the " - "parameter %0 cannot be represented in Objective-C", (unsigned, unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "2 because the type of the " + "parameter %1 cannot be represented in %" FOREIGN_LANG_SELECT "3", + (const AbstractFunctionDecl*, unsigned, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_single_param_type,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because the type of the " - "parameter cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because the type of the " + "parameter cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_func_result_type,none, - "method cannot be %" OBJC_ATTR_SELECT "0 because its result type " - "cannot be represented in Objective-C", (unsigned)) + "%kindonly0 cannot be %" OBJC_ATTR_SELECT "1 because its result type " + "cannot be represented in %" FOREIGN_LANG_SELECT "2", + (const AbstractFunctionDecl*, unsigned, ForeignLanguage)) ERROR(objc_invalid_on_foreign_class,none, "method cannot be %" OBJC_ATTR_SELECT "0 because Core Foundation " "types are not classes in Objective-C", (unsigned)) @@ -6735,6 +6765,7 @@ ERROR(nonobjc_not_allowed,none, #undef OBJC_DIAG_SELECT_2 #undef OBJC_DIAG_SELECT #undef OBJC_ATTR_SELECT +#undef FOREIGN_LANG_SELECT //------------------------------------------------------------------------------ // MARK: @exclusivity diff --git a/include/swift/AST/Type.h b/include/swift/AST/Type.h index 7591f14e8520e..84a77b9f81560 100644 --- a/include/swift/AST/Type.h +++ b/include/swift/AST/Type.h @@ -161,7 +161,7 @@ inline SubstOptions operator|(SubstFlags lhs, SubstFlags rhs) { /// Enumeration describing foreign languages to which Swift may be /// bridged. -enum class ForeignLanguage { +enum class ForeignLanguage : uint8_t { C, ObjectiveC, }; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 8fa4dfd751459..b8294e77daf0e 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -4875,6 +4875,44 @@ class TypeCheckObjCImplementationRequest bool isCached() const { return true; } }; +/// Check @cdecl functions for compatibility with the foreign language. +class TypeCheckCDeclFunctionRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + evaluator::SideEffect + evaluate(Evaluator &evaluator, FuncDecl *FD, CDeclAttr *attr) const; + +public: + bool isCached() const { return true; } +}; + +/// Check @cdecl enums for compatibility with C. +class TypeCheckCDeclEnumRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + evaluator::SideEffect + evaluate(Evaluator &evaluator, EnumDecl *ED, CDeclAttr *attr) const; + +public: + bool isCached() const { return true; } +}; + void simple_display(llvm::raw_ostream &out, ASTNode node); void simple_display(llvm::raw_ostream &out, Type value); void simple_display(llvm::raw_ostream &out, const TypeRepr *TyR); diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index a5812410ee000..22c449bdb754e 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -554,6 +554,12 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest, SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest, unsigned(ExtensionDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeCheckCDeclFunctionRequest, + evaluator::SideEffect(FunctionDecl *, CDeclAttr *), + Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, TypeCheckCDeclEnumRequest, + evaluator::SideEffect(EnumDecl *, CDeclAttr *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest, bool(AbstractStorageDecl *), Cached, NoLocationInfo) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index c6b3de80207eb..ebfce784b07bc 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -520,6 +520,9 @@ EXPERIMENTAL_FEATURE(ClosureBodyMacro, true) /// Allow declarations of Swift runtime symbols using @_silgen_name. EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true) +/// Allow use of `@cdecl` +EXPERIMENTAL_FEATURE(CDecl, true) + /// Optimize copies of ObjectiveC blocks. EXPERIMENTAL_FEATURE(CopyBlockOptimization, true) diff --git a/include/swift/PrintAsClang/ClangMacros.def b/include/swift/PrintAsClang/ClangMacros.def index e62fbe039412c..aa70254a06c4e 100644 --- a/include/swift/PrintAsClang/ClangMacros.def +++ b/include/swift/PrintAsClang/ClangMacros.def @@ -176,20 +176,46 @@ 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_BODY("SWIFT_ENUM_FWD_DECL", \ + "# if (defined(__cplusplus) && __cplusplus >= 201103L) || " \ + " (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || " \ + " __has_feature(objc_fixed_enum)\n" \ + "# define SWIFT_ENUM_FWD_DECL(_type, _name) enum _name : _type _name;\n" \ + "# else\n" \ + "# define SWIFT_ENUM_FWD_DECL(_type, _name) _type _name;\n" \ + "# endif") + CLANG_MACRO("SWIFT_UNAVAILABLE", , "__attribute__((unavailable))") CLANG_MACRO("SWIFT_UNAVAILABLE_MSG", "(msg)", "__attribute__((unavailable(msg)))") diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index b275161608f95..9808bf7b56ec0 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1799,7 +1799,9 @@ StringRef DeclAttribute::getAttrName() const { case DeclAttrKind::Alignment: return "_alignment"; case DeclAttrKind::CDecl: - return "_cdecl"; + if (cast(this)->Underscored) + return "_cdecl"; + return "cdecl"; case DeclAttrKind::SwiftNativeObjCRuntimeBase: return "_swift_native_objc_runtime_base"; case DeclAttrKind::Semantics: diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 02dce91e53e55..f0fe953aef711 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -247,10 +247,11 @@ BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed( BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext, BridgedSourceLoc cAtLoc, BridgedSourceRange cRange, - BridgedStringRef cName) { + BridgedStringRef cName, + bool underscored) { return new (cContext.unbridged()) CDeclAttr(cName.unbridged(), cAtLoc.unbridged(), cRange.unbridged(), - /*Implicit=*/false); + /*Implicit=*/false, /*Underscored*/underscored); } BridgedCustomAttr BridgedCustomAttr_createParsed( diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 603700c0135d3..68093f14c83ee 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -649,12 +649,13 @@ clang::QualType ClangTypeConverter::visitEnumType(EnumType *type) { if (type->isUninhabited()) return convert(Context.TheEmptyTupleType); - if (!type->getDecl()->isObjC()) - // Can't translate something not marked with @objc + auto ED = type->getDecl(); + if (!ED->isObjC() && !ED->getAttrs().hasAttribute()) + // Can't translate something not marked with @objc or @cdecl. return clang::QualType(); // @objc enums lower to their raw types. - return convert(type->getDecl()->getRawType()); + return convert(ED->getRawType()); } clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f96299cf1c09f..0c628f23eee87 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4575,7 +4575,10 @@ StringRef ValueDecl::getCDeclName() const { // Handle explicit cdecl attributes. if (auto cdeclAttr = getAttrs().getAttribute()) { - return cdeclAttr->Name; + if (!cdeclAttr->Name.empty()) + return cdeclAttr->Name; + else + return getBaseIdentifier().str(); } return ""; @@ -10629,6 +10632,15 @@ bool AbstractFunctionDecl::isObjCInstanceMethod() const { return isInstanceMember() || isa(this); } +std::optional AbstractFunctionDecl::getCDeclKind() const { + auto attr = getAttrs().getAttribute(); + if (!attr) + return std::nullopt; + + return attr->Underscored ? ForeignLanguage::ObjectiveC + : ForeignLanguage::C; +} + bool AbstractFunctionDecl::needsNewVTableEntry() const { auto &ctx = getASTContext(); return evaluateOrDefault( diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 7cbdba9d0141b..1d94865633101 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -453,6 +453,11 @@ static bool usesFeatureClosureBodyMacro(Decl *decl) { return false; } +static bool usesFeatureCDecl(Decl *decl) { + auto attr = decl->getAttrs().getAttribute(); + return attr && !attr->Underscored; +} + static bool usesFeatureMemorySafetyAttributes(Decl *decl) { if (decl->getAttrs().hasAttribute() || decl->getAttrs().hasAttribute()) diff --git a/lib/AST/SwiftNameTranslation.cpp b/lib/AST/SwiftNameTranslation.cpp index 41e29f5cceeee..832f06c147baf 100644 --- a/lib/AST/SwiftNameTranslation.cpp +++ b/lib/AST/SwiftNameTranslation.cpp @@ -16,6 +16,7 @@ #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/Attr.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSema.h" @@ -49,6 +50,10 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) { } } + if (auto cdeclAttr = VD->getAttrs().getAttribute()) + if (!customNamesOnly || !cdeclAttr->Name.empty()) + return VD->getCDeclName(); + if (customNamesOnly) return StringRef(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index c76a5ed6965fa..e45f0585102fb 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -3056,6 +3056,12 @@ getForeignRepresentable(Type type, ForeignLanguage language, // Imported classes and protocols are not representable in C. if (isa(nominal) || isa(nominal)) return failure(); + + // @objc enums are not representable in C, @cdecl ones and imported ones + // are ok. + if (!nominal->hasClangNode()) + return failure(); + LLVM_FALLTHROUGH; case ForeignLanguage::ObjectiveC: @@ -3094,6 +3100,11 @@ getForeignRepresentable(Type type, ForeignLanguage language, } } + // @cdecl enums are representable in C and Objective-C. + if (nominal->getAttrs().getAttribute()) { + return { ForeignRepresentableKind::Trivial, nullptr }; + } + // Pointers may be representable in ObjC. PointerTypeKind pointerKind; if (auto pointerElt = type->getAnyPointerElementType(pointerKind)) { diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 268b9f74d259f..13d9dc4f5664a 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -531,20 +531,37 @@ extension ASTGenVisitor { /// E.g.: /// ``` /// @_cdecl("c_function_name") + /// @cdecl(c_function_name) + /// @cdecl /// ``` func generateCDeclAttr(attribute node: AttributeSyntax) -> BridgedCDeclAttr? { - self.generateWithLabeledExprListArguments(attribute: node) { args in - guard let name = self.generateConsumingSimpleStringLiteralAttrOption(args: &args) else { + let attrName = node.attributeName.as(IdentifierTypeSyntax.self)?.name.text + let underscored = attrName?.hasPrefix("_") ?? false + + var name: BridgedStringRef? = nil + if node.arguments != nil || underscored { + name = self.generateWithLabeledExprListArguments(attribute: node) { + args in + if underscored { + self.generateConsumingSimpleStringLiteralAttrOption(args: &args) + } else { + self.generateConsumingPlainIdentifierAttrOption(args: &args) { + return $0.rawText.bridged + } + } + } + guard name != nil else { return nil } - - return .createParsed( - self.ctx, - atLoc: self.generateSourceLoc(node.atSign), - range: self.generateAttrSourceRange(node), - name: name - ) } + + return .createParsed( + self.ctx, + atLoc: self.generateSourceLoc(node.atSign), + range: self.generateAttrSourceRange(node), + name: name ?? "", + underscored: underscored + ) } struct GeneratedDerivativeOriginalDecl { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 492fa70d7a206..b2ad4da6b46d5 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2988,7 +2988,38 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, break; } - case DeclAttrKind::CDecl: + case DeclAttrKind::CDecl: { + if (!AttrName.starts_with("_")) { + std::optional CName; + if (consumeIfAttributeLParen()) { + // Custom C name. + if (Tok.isNot(tok::identifier)) { + diagnose(Loc, diag::attr_expected_cname, AttrName); + return makeParserSuccess(); + } + + CName = Tok.getText(); + consumeToken(tok::identifier); + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + + if (!consumeIf(tok::r_paren)) { + diagnose(Loc, diag::attr_expected_rparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + } else { + AttrRange = SourceRange(Loc); + } + + Attributes.add(new (Context) CDeclAttr(CName.value_or(StringRef()), AtLoc, + AttrRange, /*Implicit=*/false, + /*isUnderscored*/false)); + break; + } + + // Leave legacy @_cdecls to the logic expecting a string. + LLVM_FALLTHROUGH; + } case DeclAttrKind::Expose: case DeclAttrKind::SILGenName: { if (!consumeIfAttributeLParen()) { @@ -3068,10 +3099,11 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, if (DK == DeclAttrKind::SILGenName) Attributes.add(new (Context) SILGenNameAttr(AsmName.value(), Raw, AtLoc, AttrRange, /*Implicit=*/false)); - else if (DK == DeclAttrKind::CDecl) + else if (DK == DeclAttrKind::CDecl) { Attributes.add(new (Context) CDeclAttr(AsmName.value(), AtLoc, - AttrRange, /*Implicit=*/false)); - else if (DK == DeclAttrKind::Expose) { + AttrRange, /*Implicit=*/false, + /*isUnderscored*/true)); + } else if (DK == DeclAttrKind::Expose) { for (auto *EA : Attributes.getAttributes()) { // A single declaration cannot have two @_exported attributes with // the same exposure kind. diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index e396d698454c8..8935bf1727129 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -2290,6 +2290,14 @@ class DeclAndTypePrinter::Implementation return false; } + std::optional + getKnownType(const TypeDecl *typeDecl) { + if (outputLang == OutputLanguageMode::C) + return owningPrinter.typeMapping.getKnownCTypeInfo(typeDecl); + + return owningPrinter.typeMapping.getKnownObjCTypeInfo(typeDecl); + } + /// If \p typeDecl is one of the standard library types used to map in Clang /// primitives and basic types, print out the appropriate spelling and /// return true. @@ -2298,8 +2306,7 @@ class DeclAndTypePrinter::Implementation /// for interfacing with C and Objective-C. bool printIfKnownSimpleType(const TypeDecl *typeDecl, std::optional optionalKind) { - auto knownTypeInfo = - owningPrinter.typeMapping.getKnownObjCTypeInfo(typeDecl); + auto knownTypeInfo = getKnownType(typeDecl); if (!knownTypeInfo) return false; os << knownTypeInfo->name; @@ -2379,8 +2386,14 @@ class DeclAndTypePrinter::Implementation } void maybePrintTagKeyword(const TypeDecl *NTD) { - if (isa(NTD) && !NTD->hasClangNode()) { - os << "enum "; + if (auto *ED = dyn_cast(NTD); !NTD->hasClangNode()) { + if (ED->getAttrs().hasAttribute()) { + // 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; } @@ -3000,6 +3013,30 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { return false; } + // In C output mode print only the C variant `@cdecl` (no `@_cdecl`), + // while in other modes print only `@_cdecl`. + std::optional cdeclKind = std::nullopt; + if (auto *FD = dyn_cast(VD)) + cdeclKind = FD->getCDeclKind(); + if (cdeclKind && + (*cdeclKind == ForeignLanguage::C) != + (outputLang == OutputLanguageMode::C)) + return false; + + // C output mode only prints @cdecl functions and enums. + if (outputLang == OutputLanguageMode::C && + !cdeclKind && !isa(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(VD) && + VD->getAttrs().hasAttribute() != + (outputLang == OutputLanguageMode::C)) { + return false; + } + if (VD->getAttrs().hasAttribute()) return false; diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index 665bf42894308..782206a353a60 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -457,6 +457,14 @@ class ModuleWriter { } } + if (isa(D) && !D->hasClangNode() && + outputLangMode != OutputLanguageMode::Cxx) { + // We don't want to add an import for a @cdecl or @objc enum declared + // in Swift. We either do nothing for special enums like Optional as + // done in the prologue here, or we forward declare them. + return false; + } + imports.insert(otherModule); return true; } @@ -530,12 +538,20 @@ class ModuleWriter { } void forwardDeclare(const EnumDecl *ED) { - assert(ED->isObjC() || ED->hasClangNode()); + assert(ED->isObjC() || ED->getAttrs().getAttribute() || + ED->hasClangNode()); forwardDeclare(ED, [&]{ - os << "enum " << getNameForObjC(ED) << " : "; - printer.print(ED->getRawType()); - os << ";\n"; + if (ED->getASTContext().LangOpts.hasFeature(Feature::CDecl)) { + // Forward declare in a way to be compatible with older C standards. + os << "typedef SWIFT_ENUM_FWD_DECL("; + printer.print(ED->getRawType()); + os << ", " << getNameForObjC(ED) << ")\n"; + } else { + os << "enum " << getNameForObjC(ED) << " : "; + printer.print(ED->getRawType()); + os << ";\n"; + } }); } @@ -605,6 +621,7 @@ class ModuleWriter { } else if (addImport(TD)) { return; } else if (auto ED = dyn_cast(TD)) { + // Treat this after addImport to filter out special enums from the stdlib. forwardDeclare(ED); } else if (isa(TD)) { llvm_unreachable("should not see generic parameters here"); @@ -865,7 +882,7 @@ class ModuleWriter { SmallVector 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(), @@ -1119,6 +1136,17 @@ void swift::printModuleContentsAsObjC( .write(); } +void swift::printModuleContentsAsC( + raw_ostream &os, llvm::SmallPtrSetImpl &imports, + ModuleDecl &M, SwiftToClangInteropContext &interopContext) { + llvm::raw_null_ostream prologueOS; + llvm::StringSet<> exposedModules; + ModuleWriter(os, prologueOS, imports, M, interopContext, getRequiredAccess(M), + /*requiresExposedAttribute=*/false, exposedModules, + OutputLanguageMode::C) + .write(); +} + EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx( raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext, bool requiresExposedAttribute, llvm::StringSet<> &exposedModules) { diff --git a/lib/PrintAsClang/ModuleContentsWriter.h b/lib/PrintAsClang/ModuleContentsWriter.h index f6a175fc4b6e4..0e7edca689caa 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.h +++ b/lib/PrintAsClang/ModuleContentsWriter.h @@ -37,6 +37,11 @@ void printModuleContentsAsObjC(raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext); +void printModuleContentsAsC(raw_ostream &os, + llvm::SmallPtrSetImpl &imports, + ModuleDecl &M, + SwiftToClangInteropContext &interopContext); + struct EmittedClangHeaderDependencyInfo { /// The set of imported modules used by this module. SmallPtrSet imports; diff --git a/lib/PrintAsClang/OutputLanguageMode.h b/lib/PrintAsClang/OutputLanguageMode.h index 5828aa0306239..b39c625c044a1 100644 --- a/lib/PrintAsClang/OutputLanguageMode.h +++ b/lib/PrintAsClang/OutputLanguageMode.h @@ -15,7 +15,7 @@ namespace swift { -enum class OutputLanguageMode { ObjC, Cxx }; +enum class OutputLanguageMode { ObjC, Cxx, C }; } // end namespace swift diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 0cfdf1c389708..e62087e33a4d3 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -58,6 +58,17 @@ static void emitObjCConditional(raw_ostream &out, out << "#endif\n"; } +static void emitExternC(raw_ostream &out, + llvm::function_ref cCase) { + emitCxxConditional(out, [&] { + out << "extern \"C\" {\n"; + }); + cCase(); + emitCxxConditional(out, [&] { + out << "} // extern \"C\"\n"; + }); +} + static void writePtrauthPrologue(raw_ostream &os, ASTContext &ctx) { emitCxxConditional(os, [&]() { ClangSyntaxPrinter(ctx, os).printIgnoredDiagnosticBlock( @@ -207,6 +218,21 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); + + if (ctx.LangOpts.hasFeature(Feature::CDecl)) { + // For C compilers which don’t support nullability attributes, ignore them; + // for ones which do, suppress warnings about them being an extension. + out << "#if !__has_feature(nullability)\n" + "# define _Nonnull\n" + "# define _Nullable\n" + "# define _Null_unspecified\n" + "#elif !defined(__OBJC__)\n" + "# pragma clang diagnostic ignored \"-Wnullability-extension\"\n" + "#endif\n" + "#if !__has_feature(nullability_nullable_result)\n" + "# define _Nullable_result _Nullable\n" + "#endif\n"; + } } static int compareImportModulesByName(const ImportModuleTy *left, @@ -383,7 +409,10 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, const FrontendOptions &frontendOpts, clang::HeaderSearch &clangHeaderSearchInfo, const llvm::StringMap &exposedModuleHeaderNames, - bool useCxxImport = false) { + bool useCxxImport = false, + bool useNonModularIncludes = false) { + useNonModularIncludes |= frontendOpts.EmitClangHeaderWithNonModularIncludes; + // Note: we can't use has_feature(modules) as it's always enabled in C++20 // mode. out << "#if __has_feature(objc_modules)\n"; @@ -420,7 +449,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem(); llvm::ErrorOr cwd = fileSystem.getCurrentWorkingDirectory(); - if (frontendOpts.EmitClangHeaderWithNonModularIncludes) { + if (useNonModularIncludes) { assert(cwd && "Access to current working directory required"); for (auto searchDir = clangHeaderSearchInfo.search_dir_begin(); @@ -472,7 +501,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, } if (seenImports.insert(Name).second) { out << importDirective << ' ' << Name.str() << importDirectiveLineEnd; - if (frontendOpts.EmitClangHeaderWithNonModularIncludes) { + if (useNonModularIncludes) { if (const clang::Module *underlyingClangModule = swiftModule->findUnderlyingClangModule()) { collectClangModuleHeaderIncludes( @@ -495,7 +524,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, out << importDirective << ' '; ModuleDecl::ReverseFullNameIterator(clangModule).printForward(out); out << importDirectiveLineEnd; - if (frontendOpts.EmitClangHeaderWithNonModularIncludes) { + if (useNonModularIncludes) { collectClangModuleHeaderIncludes( clangModule, fileManager, requiredTextualIncludes, visitedModules, includeDirs, cwd.get()); @@ -503,11 +532,13 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, } } - if (frontendOpts.EmitClangHeaderWithNonModularIncludes) { - out << "#else\n"; - for (auto header : requiredTextualIncludes) { + if (useNonModularIncludes && !requiredTextualIncludes.empty()) { + out << "#elif defined(__OBJC__)\n"; + for (auto header : requiredTextualIncludes) out << "#import <" << header << ">\n"; - } + out << "#else\n"; + for (auto header : requiredTextualIncludes) + out << "#include <" << header << ">\n"; } out << "#endif\n\n"; for (const auto header : textualIncludes) { @@ -518,8 +549,13 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl &imports, if (bridgingHeader.empty()) out << "#import <" << M.getName().str() << '/' << M.getName().str() << ".h>\n\n"; - else - out << "#import \"" << bridgingHeader << "\"\n\n"; + else { + out << "#if defined(__OBJC__)\n"; + out << "#import \"" << bridgingHeader << "\"\n"; + out << "#else\n"; + out << "#include \"" << bridgingHeader << "\"\n"; + out << "#endif\n\n"; + } } } @@ -577,12 +613,28 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, llvm::PrettyStackTraceString trace("While generating Clang header"); SwiftToClangInteropContext interopContext(*M, irGenOpts); + writePrologue(os, M->getASTContext(), computeMacroGuard(M)); + + // C content (@cdecl) + std::string moduleContentsScratch; + if (M->getASTContext().LangOpts.hasFeature(Feature::CDecl)) { + SmallPtrSet imports; + llvm::raw_string_ostream cModuleContents{moduleContentsScratch}; + printModuleContentsAsC(cModuleContents, imports, *M, interopContext); + + llvm::StringMap exposedModuleHeaderNames; + writeImports(os, imports, *M, bridgingHeader, frontendOpts, + clangHeaderSearchInfo, exposedModuleHeaderNames, + /*useCxxImport=*/false, /*useNonModularIncludes*/true); + + emitExternC(os, [&] { os << "\n" << cModuleContents.str(); }); + moduleContentsScratch.clear(); + } + // Objective-C content SmallPtrSet imports; - std::string objcModuleContentsBuf; - llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf}; + llvm::raw_string_ostream objcModuleContents{moduleContentsScratch}; printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext); - writePrologue(os, M->getASTContext(), computeMacroGuard(M)); emitObjCConditional(os, [&] { llvm::StringMap exposedModuleHeaderNames; writeImports(os, imports, *M, bridgingHeader, frontendOpts, @@ -591,6 +643,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, writePostImportPrologue(os, *M); emitObjCConditional(os, [&] { os << "\n" << objcModuleContents.str(); }); writeObjCEpilogue(os); + + // C++ content emitCxxConditional(os, [&] { // FIXME: Expose Swift with @expose by default. bool enableCxx = frontendOpts.ClangHeaderExposedDecls.has_value() || diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 5c809011d2c53..07cd8f84365b5 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -1298,7 +1298,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { } // Use a given cdecl name for native-to-foreign thunks. - if (auto CDeclA = getDecl()->getAttrs().getAttribute()) + if (getDecl()->getAttrs().hasAttribute()) if (isNativeToForeignThunk()) { // If this is an @implementation @_cdecl, mangle it like the clang // function it implements. @@ -1307,7 +1307,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { if (!clangMangling.empty()) return clangMangling; } - return CDeclA->Name.str(); + return getDecl()->getCDeclName().str(); } if (SKind == ASTMangler::SymbolKind::DistributedThunk) { diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 05f8a78be6edf..1810bb82d35c5 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -644,8 +644,8 @@ configureInheritedDesignatedInitAttributes(ClassDecl *classDecl, std::optional asyncConvention; std::optional errorConvention; if (superclassCtor->isObjC() && - !isRepresentableInObjC(ctor, ObjCReason::MemberOfObjCSubclass, - asyncConvention, errorConvention)) + !isRepresentableInLanguage(ctor, ObjCReason::MemberOfObjCSubclass, + asyncConvention, errorConvention)) ctor->getAttrs().add(new (ctx) NonObjCAttr(/*isImplicit=*/true)); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index eab64da3e5ea1..1c5d515a67d42 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2364,17 +2364,36 @@ static bool canDeclareSymbolName(StringRef symbol, ModuleDecl *fromModule) { void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) { // Only top-level func decls are currently supported. if (D->getDeclContext()->isTypeContext()) - diagnose(attr->getLocation(), diag::cdecl_not_at_top_level); + diagnose(attr->getLocation(), diag::cdecl_not_at_top_level, + attr); - // The name must not be empty. - if (attr->Name.empty()) - diagnose(attr->getLocation(), diag::cdecl_empty_name); + // @_cdecl name must not be empty. + if (attr->Name.empty() && attr->Underscored) + diagnose(attr->getLocation(), diag::cdecl_empty_name, + attr); // The standard library can use @_cdecl to implement runtime functions. if (!canDeclareSymbolName(attr->Name, D->getModuleContext())) { diagnose(attr->getLocation(), diag::reserved_runtime_symbol_name, attr->Name); } + + // @_cdecl was never accepted on enums. Keep the previous diagnostic. + if (isa(D) && attr->Underscored) { + diagnose(attr->getLocation(), diag::attr_only_one_decl_kind, + attr, "func"); + } + + // Reject using both @cdecl and @objc on the same decl. + if (D->getAttrs().getAttribute()) { + diagnose(attr->getLocation(), diag::cdecl_incompatible_with_objc, D); + } + + // @cdecl needs to be enabled via a feature flag. + if (!attr->Underscored && + !Ctx.LangOpts.hasFeature(Feature::CDecl)) { + diagnose(attr->getLocation(), diag::cdecl_feature_required); + } } void AttributeChecker::visitExposeAttr(ExposeAttr *attr) { diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index a8cb7249c458f..b4194dfaa5d87 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -810,8 +810,9 @@ CaptureInfo CaptureInfoRequest::evaluate(Evaluator &evaluator, std::optional asyncConvention; std::optional errorConvention; if (!AFD->isObjC() && - isRepresentableInObjC(AFD, ObjCReason::MemberOfObjCMembersClass, - asyncConvention, errorConvention)) { + isRepresentableInLanguage(AFD, + ObjCReason::MemberOfObjCMembersClass, + asyncConvention, errorConvention)) { AFD->diagnose( diag::objc_generic_extension_using_type_parameter_try_objc) .fixItInsert(AFD->getAttributeInsertionLoc(false), "@objc "); diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index ec38710102b22..3e2049dae7da3 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -48,6 +48,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) { LLVM_FALLTHROUGH; case ObjCReason::ExplicitlyCDecl: + case ObjCReason::ExplicitlyUnderscoreCDecl: case ObjCReason::ExplicitlyDynamic: case ObjCReason::ExplicitlyObjC: case ObjCReason::ExplicitlyObjCMembers: @@ -81,6 +82,7 @@ swift::behaviorLimitForObjCReason(ObjCReason reason, ASTContext &ctx) { unsigned swift::getObjCDiagnosticAttrKind(ObjCReason reason) { switch (reason) { case ObjCReason::ExplicitlyCDecl: + case ObjCReason::ExplicitlyUnderscoreCDecl: case ObjCReason::ExplicitlyDynamic: case ObjCReason::ExplicitlyObjC: case ObjCReason::ExplicitlyObjCMembers: @@ -132,6 +134,7 @@ void ObjCReason::describe(const Decl *D) const { case ObjCReason::ExplicitlyObjCByAccessNote: case ObjCReason::ExplicitlyCDecl: + case ObjCReason::ExplicitlyUnderscoreCDecl: case ObjCReason::ExplicitlyDynamic: case ObjCReason::ExplicitlyObjC: case ObjCReason::ExplicitlyObjCMembers: @@ -166,17 +169,19 @@ void ObjCReason::setAttrInvalid() const { static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, Type T, SourceRange TypeRange, - DiagnosticBehavior behavior) { + DiagnosticBehavior behavior, + ObjCReason reason) { auto &diags = DC->getASTContext().Diags; + auto language = reason.getForeignLanguage(); // Special diagnostic for tuples. if (T->is()) { if (T->isVoid()) - diags.diagnose(TypeRange.Start, diag::not_objc_empty_tuple) + diags.diagnose(TypeRange.Start, diag::not_objc_empty_tuple, language) .highlight(TypeRange) .limitBehavior(behavior); else - diags.diagnose(TypeRange.Start, diag::not_objc_tuple) + diags.diagnose(TypeRange.Start, diag::not_objc_tuple, language) .highlight(TypeRange) .limitBehavior(behavior); return; @@ -184,6 +189,13 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, // Special diagnostic for classes. if (auto *CD = T->getClassOrBoundGenericClass()) { + if (language == ForeignLanguage::C) { + diags.diagnose(TypeRange.Start, diag::cdecl_incompatible_with_classes) + .highlight(TypeRange) + .limitBehavior(behavior); + return; + } + if (!CD->isObjC()) diags.diagnose(TypeRange.Start, diag::not_objc_swift_class) .highlight(TypeRange) @@ -195,12 +207,14 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, if (auto *SD = T->getStructOrBoundGenericStruct()) { if (isa_and_nonnull(SD->getClangDecl())) { // This can be a non-trivial C++ record. - diags.diagnose(TypeRange.Start, diag::not_objc_non_trivial_cxx_class) + diags.diagnose(TypeRange.Start, diag::not_objc_non_trivial_cxx_class, + language) .highlight(TypeRange) .limitBehavior(behavior); return; } - diags.diagnose(TypeRange.Start, diag::not_objc_swift_struct) + diags.diagnose(TypeRange.Start, diag::not_objc_swift_struct, + language) .highlight(TypeRange) .limitBehavior(behavior); return; @@ -208,14 +222,30 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, // Special diagnostic for enums. if (T->is()) { - diags.diagnose(TypeRange.Start, diag::not_objc_swift_enum) - .highlight(TypeRange) - .limitBehavior(behavior); + if (DC->getASTContext().LangOpts.hasFeature(Feature::CDecl)) { + // New dialog mentioning @cdecl. + diags.diagnose(TypeRange.Start, diag::not_cdecl_or_objc_swift_enum, + language) + .highlight(TypeRange) + .limitBehavior(behavior); + } else { + diags.diagnose(TypeRange.Start, diag::not_objc_swift_enum) + .highlight(TypeRange) + .limitBehavior(behavior); + } return; } // Special diagnostic for protocols and protocol compositions. if (T->isExistentialType()) { + // No protocol is representable in C. + if (language == ForeignLanguage::C) { + diags.diagnose(TypeRange.Start, diag::cdecl_incompatible_with_protocols) + .highlight(TypeRange) + .limitBehavior(behavior); + return; + } + if (T->isAny()) { // Any is not @objc. diags.diagnose(TypeRange.Start, @@ -263,7 +293,8 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, } if (T->is() || T->isTypeParameter()) { - diags.diagnose(TypeRange.Start, diag::not_objc_generic_type_param) + diags.diagnose(TypeRange.Start, diag::not_objc_generic_type_param, + language) .highlight(TypeRange) .limitBehavior(behavior); return; @@ -271,20 +302,23 @@ static void diagnoseTypeNotRepresentableInObjC(const DeclContext *DC, if (auto fnTy = T->getAs()) { if (fnTy->getExtInfo().isAsync()) { - diags.diagnose(TypeRange.Start, diag::not_objc_function_type_async) + diags.diagnose(TypeRange.Start, diag::not_objc_function_type_async, + language) .highlight(TypeRange) .limitBehavior(behavior); return; } if (fnTy->getExtInfo().isThrowing()) { - diags.diagnose(TypeRange.Start, diag::not_objc_function_type_throwing) + diags.diagnose(TypeRange.Start, diag::not_objc_function_type_throwing, + language) .highlight(TypeRange) .limitBehavior(behavior); return; } - diags.diagnose(TypeRange.Start, diag::not_objc_function_type_param) + diags.diagnose(TypeRange.Start, diag::not_objc_function_type_param, + language) .highlight(TypeRange) .limitBehavior(behavior); return; @@ -295,16 +329,19 @@ static void diagnoseFunctionParamNotRepresentable( const AbstractFunctionDecl *AFD, unsigned NumParams, unsigned ParamIndex, const ParamDecl *P, ObjCReason Reason) { auto behavior = behaviorLimitForObjCReason(Reason, AFD->getASTContext()); + auto language = Reason.getForeignLanguage(); if (NumParams == 1) { softenIfAccessNote(AFD, Reason.getAttr(), AFD->diagnose(diag::objc_invalid_on_func_single_param_type, - getObjCDiagnosticAttrKind(Reason)) + AFD, getObjCDiagnosticAttrKind(Reason), + language) .limitBehavior(behavior)); } else { softenIfAccessNote(AFD, Reason.getAttr(), AFD->diagnose(diag::objc_invalid_on_func_param_type, - ParamIndex + 1, getObjCDiagnosticAttrKind(Reason)) + AFD, ParamIndex + 1, getObjCDiagnosticAttrKind(Reason), + language) .limitBehavior(behavior)); } SourceRange SR; @@ -312,22 +349,24 @@ static void diagnoseFunctionParamNotRepresentable( if (P->hasAttachedPropertyWrapper()) { auto wrapperTy = P->getPropertyWrapperBackingPropertyType(); SR = P->getOutermostAttachedPropertyWrapper()->getRange(); - diagnoseTypeNotRepresentableInObjC(AFD, wrapperTy, SR, behavior); + diagnoseTypeNotRepresentableInObjC(AFD, wrapperTy, SR, behavior, Reason); } else { if (auto typeRepr = P->getTypeRepr()) SR = typeRepr->getSourceRange(); - diagnoseTypeNotRepresentableInObjC(AFD, P->getTypeInContext(), SR, behavior); + diagnoseTypeNotRepresentableInObjC(AFD, P->getTypeInContext(), SR, + behavior, Reason); } Reason.describe(AFD); } -static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, - const ParameterList *PL, - ObjCReason Reason) { +static bool isParamListRepresentableInLanguage(const AbstractFunctionDecl *AFD, + const ParameterList *PL, + ObjCReason Reason) { // If you change this function, you must add or modify a test in PrintAsClang. ASTContext &ctx = AFD->getASTContext(); auto &diags = ctx.Diags; auto behavior = behaviorLimitForObjCReason(Reason, ctx); + auto language = Reason.getForeignLanguage(); bool IsObjC = true; unsigned NumParams = PL->size(); for (unsigned ParamIndex = 0; ParamIndex != NumParams; ++ParamIndex) { @@ -337,7 +376,7 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, if (param->isVariadic()) { softenIfAccessNote(AFD, Reason.getAttr(), diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_variadic, - getObjCDiagnosticAttrKind(Reason)) + AFD, getObjCDiagnosticAttrKind(Reason)) .highlight(param->getSourceRange()) .limitBehavior(behavior)); Reason.describe(AFD); @@ -349,7 +388,8 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, if (param->isInOut()) { softenIfAccessNote(AFD, Reason.getAttr(), diags.diagnose(param->getStartLoc(), diag::objc_invalid_on_func_inout, - getObjCDiagnosticAttrKind(Reason)) + AFD, getObjCDiagnosticAttrKind(Reason), + language) .highlight(param->getSourceRange()) .limitBehavior(behavior)); Reason.describe(AFD); @@ -362,11 +402,11 @@ static bool isParamListRepresentableInObjC(const AbstractFunctionDecl *AFD, if (param->hasAttachedPropertyWrapper()) { if (param->getPropertyWrapperBackingPropertyType()->isRepresentableIn( - ForeignLanguage::ObjectiveC, + language, const_cast(AFD))) continue; } else if (param->getTypeInContext()->isRepresentableIn( - ForeignLanguage::ObjectiveC, + language, const_cast(AFD))) { continue; } @@ -646,14 +686,14 @@ static bool isValidObjectiveCErrorResultType(DeclContext *dc, Type type) { llvm_unreachable("Unhandled ForeignRepresentableKind in switch."); } -bool swift::isRepresentableInObjC( +bool swift::isRepresentableInLanguage( const AbstractFunctionDecl *AFD, ObjCReason Reason, std::optional &asyncConvention, std::optional &errorConvention) { auto abiRole = ABIRoleInfo(AFD); if (!abiRole.providesAPI() && abiRole.getCounterpart()) - return isRepresentableInObjC(abiRole.getCounterpart(), Reason, - asyncConvention, errorConvention); + return isRepresentableInLanguage(abiRole.getCounterpart(), Reason, + asyncConvention, errorConvention); // Clear out the async and error conventions. They will be added later if // needed. @@ -663,7 +703,7 @@ bool swift::isRepresentableInObjC( // If you change this function, you must add or modify a test in PrintAsClang. ASTContext &ctx = AFD->getASTContext(); DiagnosticStateRAII diagState(ctx.Diags); - + auto language = Reason.getForeignLanguage(); if (checkObjCInForeignClassContext(AFD, Reason)) return false; @@ -689,6 +729,7 @@ bool swift::isRepresentableInObjC( auto storage = accessor->getStorage(); bool storageIsObjC = storage->isObjC() || Reason == ObjCReason::ExplicitlyCDecl + || Reason == ObjCReason::ExplicitlyUnderscoreCDecl || Reason == ObjCReason::WitnessToObjC || Reason == ObjCReason::MemberOfObjCProtocol; @@ -775,9 +816,9 @@ bool swift::isRepresentableInObjC( isSpecialInit = init->isObjCZeroParameterWithLongSelector(); if (!isSpecialInit && - !isParamListRepresentableInObjC(AFD, - AFD->getParameters(), - Reason)) { + !isParamListRepresentableInLanguage(AFD, + AFD->getParameters(), + Reason)) { return false; } @@ -787,15 +828,16 @@ bool swift::isRepresentableInObjC( !ResultType->hasError() && !ResultType->isVoid() && !ResultType->isUninhabited() && - !ResultType->isRepresentableIn(ForeignLanguage::ObjectiveC, + !ResultType->isRepresentableIn(language, const_cast(FD))) { softenIfAccessNote(AFD, Reason.getAttr(), AFD->diagnose(diag::objc_invalid_on_func_result_type, - getObjCDiagnosticAttrKind(Reason)) + FD, getObjCDiagnosticAttrKind(Reason), + language) .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(FD, ResultType, FD->getResultTypeSourceRange(), - behavior); + behavior, Reason); Reason.describe(FD); return false; } @@ -844,15 +886,15 @@ bool swift::isRepresentableInObjC( completionHandlerParams.push_back(AnyFunctionType::Param(type)); // Make sure that the parameter type is representable in Objective-C. - if (!type->isRepresentableIn( - ForeignLanguage::ObjectiveC, const_cast(FD))) { + if (!type->isRepresentableIn(language, const_cast(FD))) { softenIfAccessNote(AFD, Reason.getAttr(), AFD->diagnose(diag::objc_invalid_on_func_result_type, - getObjCDiagnosticAttrKind(Reason)) + FD, getObjCDiagnosticAttrKind(Reason), + language) .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(FD, type, FD->getResultTypeSourceRange(), - behavior); + behavior, Reason); Reason.describe(FD); return true; @@ -1160,7 +1202,7 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { .limitBehavior(behavior)); diagnoseTypeNotRepresentableInObjC(VD->getDeclContext(), VD->getInterfaceType(), - TypeRange, behavior); + TypeRange, behavior, Reason); Reason.describe(VD); } @@ -1249,7 +1291,7 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { diagnoseTypeNotRepresentableInObjC(SD->getDeclContext(), !IndexResult ? IndexType : ElementType, - TypeRange, behavior); + TypeRange, behavior, Reason); Reason.describe(SD); } @@ -1264,8 +1306,9 @@ bool swift::canBeRepresentedInObjC(const ValueDecl *decl) { if (auto func = dyn_cast(decl)) { std::optional asyncConvention; std::optional errorConvention; - return isRepresentableInObjC(func, ObjCReason::MemberOfObjCMembersClass, - asyncConvention, errorConvention); + return isRepresentableInLanguage(func, + ObjCReason::MemberOfObjCMembersClass, + asyncConvention, errorConvention); } if (auto var = dyn_cast(decl)) @@ -1736,19 +1779,18 @@ static bool isCIntegerType(Type type) { } /// Determine whether the given enum should be @objc. -static bool isEnumObjC(EnumDecl *enumDecl) { +static bool isEnumObjC(EnumDecl *enumDecl, DeclAttribute *attr) { // FIXME: Use shouldMarkAsObjC once it loses it's TypeChecker argument. - // If there is no @objc attribute, it's not @objc. - auto attr = enumDecl->getAttrs().getAttribute(); + // If there is no @objc or @cdecl attribute, skip it. if (!attr) return false; Type rawType = enumDecl->getRawType(); - // @objc enums must have a raw type. + // @objc/@cdecl enums must have a raw type. if (!rawType) { - enumDecl->diagnose(diag::objc_enum_no_raw_type); + enumDecl->diagnose(diag::objc_enum_no_raw_type, attr); return false; } @@ -1761,7 +1803,7 @@ static bool isEnumObjC(EnumDecl *enumDecl) { SourceRange errorRange; if (!enumDecl->getInherited().empty()) errorRange = enumDecl->getInherited().getEntry(0).getSourceRange(); - enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, rawType) + enumDecl->diagnose(diag::objc_enum_raw_type_not_integer, attr, rawType) .highlight(errorRange); return false; } @@ -1771,7 +1813,8 @@ static bool isEnumObjC(EnumDecl *enumDecl) { enumDecl->diagnose(diag::empty_enum_raw_type); } - checkObjCNameValidity(enumDecl, attr); + if (auto objcAttr = dyn_cast(attr)) + checkObjCNameValidity(enumDecl, objcAttr); return true; } @@ -1812,9 +1855,9 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { } else if (auto enumDecl = dyn_cast(VD)) { // Enums can be @objc so long as they have a raw type that is representable // as an arithmetic type in C. - if (isEnumObjC(enumDecl)) - isObjC = objCReasonForObjCAttr( - enumDecl->getAttrs().getAttribute()); + auto attr = enumDecl->getAttrs().getAttribute(); + if (attr && isEnumObjC(enumDecl, attr)) + isObjC = objCReasonForObjCAttr(attr); } else if (auto enumElement = dyn_cast(VD)) { // Enum elements can be @objc so long as the containing enum is @objc. if (enumElement->getParentEnum()->isObjC()) { @@ -1873,7 +1916,7 @@ bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { } else if (isa(VD)) { // Destructors need no additional checking. } else if (auto func = dyn_cast(VD)) { - if (!isRepresentableInObjC( + if (!isRepresentableInLanguage( func, *isObjC, asyncConvention, errorConvention)) { isObjC->setAttrInvalid(); return false; @@ -4154,3 +4197,43 @@ evaluate(Evaluator &evaluator, Decl *D) const { return evaluator::SideEffect(); } + +evaluator::SideEffect +TypeCheckCDeclFunctionRequest::evaluate(Evaluator &evaluator, + FuncDecl *FD, + CDeclAttr *attr) const { + auto &ctx = FD->getASTContext(); + + auto lang = FD->getCDeclKind(); + assert(lang && "missing @cdecl?"); + auto kind = lang == ForeignLanguage::ObjectiveC + ? ObjCReason::ExplicitlyUnderscoreCDecl + : ObjCReason::ExplicitlyCDecl; + ObjCReason reason(kind, attr); + + std::optional asyncConvention; + std::optional errorConvention; + if (isRepresentableInLanguage(FD, reason, asyncConvention, errorConvention)) { + if (FD->hasAsync()) { + FD->setForeignAsyncConvention(*asyncConvention); + ctx.Diags.diagnose(attr->getLocation(), diag::attr_decl_async, + attr, FD); + } else if (FD->hasThrows()) { + FD->setForeignErrorConvention(*errorConvention); + ctx.Diags.diagnose(attr->getLocation(), diag::cdecl_throws, + attr); + } + } else { + reason.setAttrInvalid(); + } + return {}; +} + +evaluator::SideEffect +TypeCheckCDeclEnumRequest::evaluate(Evaluator &evaluator, + EnumDecl *ED, + CDeclAttr *attr) const { + // Apply @objc's logic. + isEnumObjC(ED, attr); + return {}; +} diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 5bb7d285379c1..9305e56eba938 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -3288,6 +3288,13 @@ class DeclChecker : public DeclVisitor { } } + // If the enum is exported to C, it must be representable in C. + if (auto CDeclAttr = ED->getAttrs().getAttribute()) { + evaluateOrDefault(Ctx.evaluator, + TypeCheckCDeclEnumRequest{ED, CDeclAttr}, + {}); + } + // ----- // NonCopyableChecks // @@ -3819,24 +3826,10 @@ class DeclChecker : public DeclVisitor { } // If the function is exported to C, it must be representable in (Obj-)C. - // FIXME: This needs to be moved to its own request if we want to - // productize @_cdecl. if (auto CDeclAttr = FD->getAttrs().getAttribute()) { - std::optional asyncConvention; - std::optional errorConvention; - ObjCReason reason(ObjCReason::ExplicitlyCDecl, CDeclAttr); - if (isRepresentableInObjC(FD, reason, asyncConvention, errorConvention)) { - if (FD->hasAsync()) { - FD->setForeignAsyncConvention(*asyncConvention); - Ctx.Diags.diagnose(CDeclAttr->getLocation(), diag::attr_decl_async, - CDeclAttr, FD); - } else if (FD->hasThrows()) { - FD->setForeignErrorConvention(*errorConvention); - Ctx.Diags.diagnose(CDeclAttr->getLocation(), diag::cdecl_throws); - } - } else { - reason.setAttrInvalid(); - } + evaluateOrDefault(Ctx.evaluator, + TypeCheckCDeclFunctionRequest{FD, CDeclAttr}, + {}); } TypeChecker::checkObjCImplementation(FD); diff --git a/lib/Sema/TypeCheckObjC.h b/lib/Sema/TypeCheckObjC.h index 9e74c6f228f11..5c7a22e681f35 100644 --- a/lib/Sema/TypeCheckObjC.h +++ b/lib/Sema/TypeCheckObjC.h @@ -43,6 +43,8 @@ class ObjCReason { enum Kind { /// Has the '@cdecl' attribute. ExplicitlyCDecl, + /// Has the '@_cdecl' attribute. + ExplicitlyUnderscoreCDecl, /// Has the 'dynamic' modifier. ExplicitlyDynamic, /// Has an explicit '@objc' attribute. @@ -101,6 +103,7 @@ class ObjCReason { static bool requiresAttr(Kind kind) { switch (kind) { case ExplicitlyCDecl: + case ExplicitlyUnderscoreCDecl: case ExplicitlyDynamic: case ExplicitlyObjC: case ExplicitlyObjCMembers: @@ -162,6 +165,13 @@ class ObjCReason { return declOrAttr.get(); } + // The foreign language targeted by the context. + ForeignLanguage getForeignLanguage() const { + if (kind == ExplicitlyCDecl) + return ForeignLanguage::C; + return ForeignLanguage::ObjectiveC; + } + void setAttrInvalid() const; /// Emit an additional diagnostic describing why we are applying @objc to the @@ -183,7 +193,7 @@ unsigned getObjCDiagnosticAttrKind(ObjCReason reason); /// Determine whether the given function can be represented in Objective-C, /// and figure out its foreign error convention (if any). -bool isRepresentableInObjC( +bool isRepresentableInLanguage( const AbstractFunctionDecl *AFD, ObjCReason Reason, std::optional &asyncConvention, std::optional &errorConvention); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index ab03112fb6752..019a924db5b8c 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -6001,9 +6001,10 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { case decls_block::CDecl_DECL_ATTR: { bool isImplicit; + bool isUnderscored; serialization::decls_block::CDeclDeclAttrLayout::readRecord( - scratch, isImplicit); - Attr = new (ctx) CDeclAttr(blobData, isImplicit); + scratch, isImplicit, isUnderscored); + Attr = new (ctx) CDeclAttr(blobData, isImplicit, isUnderscored); break; } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 004131ee7bdb7..dd9ba4ad4afe3 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 944; // nonisolated(nonsending) isolation was moved +const uint16_t SWIFTMODULE_VERSION_MINOR = 945; // cdecl isUnderscored /// A standard hash seed used for all string hashes in a serialized module. /// @@ -2239,7 +2239,8 @@ namespace decls_block { using CDeclDeclAttrLayout = BCRecordLayout< CDecl_DECL_ATTR, BCFixed<1>, // implicit flag - BCBlob // _silgen_name + BCFixed<1>, // underscored flag + BCBlob // cname >; using ImplementsDeclAttrLayout = BCRecordLayout< diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index b4b1c6f6bbf97..86b5db9759b62 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2990,6 +2990,7 @@ class Serializer::DeclSerializer : public DeclVisitor { auto abbrCode = S.DeclTypeAbbrCodes[CDeclDeclAttrLayout::Code]; CDeclDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, theAttr->isImplicit(), + theAttr->Underscored, theAttr->Name); return; } diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index b438faa8a0e15..66d22db87457a 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -5,6 +5,7 @@ // RUN: -enable-experimental-feature Lifetimes \ // RUN: -enable-experimental-feature RawLayout \ // RUN: -enable-experimental-feature SymbolLinkageMarkers \ +// RUN: -enable-experimental-feature CDecl \ // RUN: -enable-experimental-concurrency \ // RUN: -enable-experimental-move-only \ // RUN: -enable-experimental-feature ParserASTGen \ @@ -15,6 +16,7 @@ // RUN: -enable-experimental-feature Lifetimes \ // RUN: -enable-experimental-feature RawLayout \ // RUN: -enable-experimental-feature SymbolLinkageMarkers \ +// RUN: -enable-experimental-feature CDecl \ // RUN: -enable-experimental-concurrency \ // RUN: -enable-experimental-move-only \ // RUN: | %sanitize-address > %t/cpp-parser.ast @@ -28,6 +30,7 @@ // RUN: -enable-experimental-feature Lifetimes \ // RUN: -enable-experimental-feature RawLayout \ // RUN: -enable-experimental-feature SymbolLinkageMarkers \ +// RUN: -enable-experimental-feature CDecl \ // RUN: -enable-experimental-concurrency \ // RUN: -enable-experimental-move-only @@ -39,6 +42,7 @@ // REQUIRES: swift_feature_Lifetimes // REQUIRES: swift_feature_RawLayout // REQUIRES: swift_feature_SymbolLinkageMarkers +// REQUIRES: swift_feature_CDecl // rdar://116686158 // UNSUPPORTED: asan @@ -95,7 +99,9 @@ func fn(_: Int) {} @_disallowFeatureSuppression(NoncopyableGenerics) public struct LoudlyNC {} -@_cdecl("c_function_name") func foo(x: Int) {} +@_cdecl("c_function_name") func cdeclUnderscore(x: Int) {} +@cdecl(c_function_name_official) func cdecl(x: Int) {} +@cdecl func cdeclDefault() {} struct StaticProperties { dynamic var property: Int { return 1 } diff --git a/test/Interpreter/cdecl_official_run.swift b/test/Interpreter/cdecl_official_run.swift new file mode 100644 index 0000000000000..2ed14a1b6022d --- /dev/null +++ b/test/Interpreter/cdecl_official_run.swift @@ -0,0 +1,66 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate an internal cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Build and run a binary from Swift and C code. +// RUN: %clang-no-modules -c %t/Client.c -o %t/Client.o -target %target-triple \ +// RUN: -I %t -I %clang-include-dir -Werror -isysroot %sdk +// RUN: %target-build-swift %t/Lib.swift %t/Client.o -O -o %t/a.out \ +// RUN: -enable-experimental-feature CDecl -parse-as-library +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out > %t/run.log +// RUN: %FileCheck %s --input-file %t/run.log + +// REQUIRES: swift_feature_CDecl +// REQUIRES: executable_test + +//--- Lib.swift + +/// My documentation +@cdecl(simple) public func simpleNameSwiftSide(x: CInt, bar y: CInt) -> CInt { + print(x, y) + return x +} + +@cdecl func defaultName(x: Int) { + print(x) +} + +@cdecl public func primitiveTypes(i: Int, ci: CInt, l: CLong, c: CChar, f: Float, d: Double, b: Bool) { + print(i, ci, l, c, f, d, b) +} + +@cdecl enum CEnum: CInt { case A, B } + +@cdecl func useEnum(e: CEnum) -> CEnum { + print(e) + return e +} + +//--- Client.c + +#include +#include "cdecl.h" + +int main() { + int x = simple(42, 43); + // CHECK: 42 43 + printf("%d\n", x); + // CHECK-NEXT: 42 + + defaultName(121); + // CHECK-NEXT: 121 + + primitiveTypes(1, 2, 3, 'a', 1.0f, 2.0, true); + // CHECK-NEXT: 1 2 3 97 1.0 2.0 true + + CEnum e = useEnum(CEnumB); + // CHECK-NEXT: B + printf("%d\n", e); + // CHECK-NEXT: 1 +} diff --git a/test/PrintAsObjC/cdecl-enum-reference.swift b/test/PrintAsObjC/cdecl-enum-reference.swift new file mode 100644 index 0000000000000..3ccc38de35295 --- /dev/null +++ b/test/PrintAsObjC/cdecl-enum-reference.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +/// Build CoreLib defining a @cdecl enum. +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/CoreLib.swift -emit-module -verify -o %t \ +// RUN: -emit-clang-header-path %t/CoreLib.h \ +// RUN: -enable-experimental-feature CDecl +// RUN: %check-in-clang-c %t/CoreLib.h -I %t + +/// Build MiddleLib using the @cdecl enum in API. +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/MiddleLib.swift -emit-module -verify -o %t -I %t \ +// RUN: -emit-clang-header-path %t/MiddleLib.h \ +// RUN: -enable-experimental-feature CDecl +// RUN: %FileCheck %s --input-file %t/MiddleLib.h +// RUN: %check-in-clang-c %t/MiddleLib.h -I %t + +/// Build a client. +// RUN: %clang-no-modules -c %t/Client.c -I %t \ +// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ +// RUN: -I %clang-include-dir -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl + +//--- CoreLib.swift +@cdecl(CEnum) +public enum CEnum: CInt { case A, B } + +//--- MiddleLib.swift +import CoreLib + +@cdecl(CFunc) +public func CFunc(e: CEnum) {} +// CHECK: typedef SWIFT_ENUM_FWD_DECL(int, CEnum) +// CHECK: SWIFT_EXTERN void CFunc(SWIFT_ENUM_TAG CEnum e) SWIFT_NOEXCEPT; + +//--- Client.c +#include "CoreLib.h" +#include "MiddleLib.h" + +int main() { + CFunc(CEnumA); +} diff --git a/test/PrintAsObjC/cdecl-enums.swift b/test/PrintAsObjC/cdecl-enums.swift new file mode 100644 index 0000000000000..37bc6a4d726c1 --- /dev/null +++ b/test/PrintAsObjC/cdecl-enums.swift @@ -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(unsigned int, ExplicitValues, closed) { +// CHECK-NEXT: ExplicitValuesZim = 0, +// CHECK-NEXT: ExplicitValuesZang = 219, +// CHECK-NEXT: ExplicitValuesZung = 220, +// CHECK-NEXT: }; +// NEGATIVE-NOT: ExplicitValuesDomain + +@cdecl 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 +} diff --git a/test/PrintAsObjC/cdecl-imports.swift b/test/PrintAsObjC/cdecl-imports.swift index 41540286ccc23..7b153be5e6883 100644 --- a/test/PrintAsObjC/cdecl-imports.swift +++ b/test/PrintAsObjC/cdecl-imports.swift @@ -2,6 +2,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -parse-as-library -typecheck -verify -emit-objc-header-path %t/swift.h // RUN: %FileCheck %s < %t/swift.h // RUN: %check-in-clang %t/swift.h +// RUN: %check-in-clang-c %t/swift.h // RUN: %check-in-clang-cxx %t/swift.h // REQUIRES: objc_interop diff --git a/test/PrintAsObjC/cdecl-includes-with-objc.swift b/test/PrintAsObjC/cdecl-includes-with-objc.swift new file mode 100644 index 0000000000000..1e9c109ec8494 --- /dev/null +++ b/test/PrintAsObjC/cdecl-includes-with-objc.swift @@ -0,0 +1,82 @@ +/// Include module for use from both C and Objective-C @cdecl variants. + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate the compatibility header cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/Lib.swift \ +// RUN: -emit-module -o %t -verify -F %t \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Check compatibility header directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang %t/cdecl.h -F %t +// RUN: %check-in-clang-c %t/cdecl.h -F %t +// RUN: %check-in-clang-cxx %t/cdecl.h -F %t + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop + +//--- CFramework.framework/Modules/module.modulemap + +framework module CFramework { + umbrella header "CFramework.h" +} + +//--- CFramework.framework/Headers/CFramework.h + +typedef int IntFromCFramework; + +//--- Lib.swift + +import CFramework +import CoreGraphics +import Foundation + +// CHECK-NOT: Foundation; + +/// Imports for C variant to @_cdecl + +// CHECK: #if __has_feature(objc_modules) +// CHECK: @import CFramework; +// CHECK-NEXT: @import CoreGraphics; +// CHECK-NEXT: #elif defined(__OBJC__) +// CHECK-NEXT: #import +// CHECK-NEXT: #import +// CHECK-NEXT: #else +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #endif + +// CHECK: #if defined(__cplusplus) +// CHECK: extern "C" { +// CHECK: #endif + +@cdecl(get_int_alias) +public func getIntAlias() -> IntFromCFramework { 42 } +// CHECK: SWIFT_EXTERN IntFromCFramework get_int_alias(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl(imports_cgpoint) +public func importsCGPoint(pt: CGPoint) { } +// CHECK: SWIFT_EXTERN void imports_cgpoint(CGPoint pt) SWIFT_NOEXCEPT; + +// CHECK: #if defined(__cplusplus) +// CHECK: } // extern "C" +// CHECK: #endif + +/// Imports for Objective-C variant to @_cdecl + +@_cdecl("imports_cgpoint_objc") +public func importsCGPointObjC(pt: CGPoint) { } +// CHECK: #if defined(__OBJC__) +// CHECK: #if __has_feature(objc_modules) +// CHECK-NEXT: #if __has_warning("-Watimport-in-framework-header") +// CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header" +// CHECK-NEXT: #endif +// CHECK-NEXT: @import CoreGraphics; +// CHECK-NEXT: #endif + +// CHECK: #if defined(__OBJC__) +// CHECK: SWIFT_EXTERN void imports_cgpoint_objc(CGPoint pt) SWIFT_NOEXCEPT; +// CHECK: #endif diff --git a/test/PrintAsObjC/cdecl-includes.swift b/test/PrintAsObjC/cdecl-includes.swift new file mode 100644 index 0000000000000..7ca05de945fbd --- /dev/null +++ b/test/PrintAsObjC/cdecl-includes.swift @@ -0,0 +1,110 @@ +/// Print #includes for C clients and reference imported types. +/// This test shouldn't require the objc runtime. + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate the compatibility header cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %t/Lib.swift \ +// RUN: -emit-module -verify -o %t -I %t \ +// RUN: -import-bridging-header %t/BridgingHeader.h \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -disable-objc-interop \ +// RUN: -enable-experimental-feature CDecl + +/// Check compatibility header directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang-c -I %t %t/cdecl.h \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +/// Compile a client against the compatibility header +// RUN: %clang-no-modules -c %t/Client.c -I %t -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl + +//--- module.modulemap + +module CModule { + header "CModule_FileA.h" + header "sub/CModule_FileB.h" +} + +//--- CModule_FileA.h + +struct CStruct { int a; }; + +//--- sub/CModule_FileB.h + +union CUnion { long a; float b; }; + +//--- Dependency.h + +typedef enum TKTimeSetting { + TKTimeSettingLight, + TKTimeSettingNormal, + TKTimeSettingDark +} TKTimeSetting; + +//--- BridgingHeader.h + +#include "Dependency.h" + +//--- Lib.swift + +import CModule + +// CHECK: #if __has_feature(objc_modules) +// CHECK: @import CModule; +// CHECK-NEXT: #elif defined(__OBJC__) +// CHECK-NEXT: #import +// CHECK-NEXT: #import +// CHECK-NEXT: #else +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #endif + +// CHECK: #if defined(__OBJC__) +// CHECK: #import " +// CHECK-SAME: BridgingHeader.h" +// CHECK-NEXT: #else +// CHECK-NEXT: #include " +// CHECK-SAME: BridgingHeader.h" +// CHECK-NEXT: #endif + +// CHECK-NOT: BridgingHeader + +// CHECK: #if defined(__cplusplus) +// CHECK: extern "C" { +// CHECK: #endif + +@cdecl(mirror_struct) +public func a_mirrorStruct(_ a: CStruct) -> CStruct { a } +// CHECK: SWIFT_EXTERN struct CStruct mirror_struct(struct CStruct a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl(mirror_union) +public func b_mirrorStruct(_ a: CUnion) -> CUnion { a } +// CHECK: SWIFT_EXTERN union CUnion mirror_union(union CUnion a) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + + +@cdecl(TKGetDefaultToastSetting) +public func c_defaultToastSetting() -> TKTimeSetting { TKTimeSettingNormal } // It would be nice to import TKTimeSettingNormal as a member. +// CHECK: SWIFT_EXTERN TKTimeSetting TKGetDefaultToastSetting(void) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +// CHECK: #if defined(__cplusplus) +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +//--- Client.c + +#include "cdecl.h" + +int main() { + struct CStruct s = { 42 }; + struct CStruct s_out = mirror_struct(s); + + union CUnion u = { 43 }; + union CUnion u_out = mirror_union(u); + + TKTimeSetting def = TKGetDefaultToastSetting(); +} diff --git a/test/PrintAsObjC/cdecl-official-for-objc-clients.swift b/test/PrintAsObjC/cdecl-official-for-objc-clients.swift new file mode 100644 index 0000000000000..94be91e72f2c0 --- /dev/null +++ b/test/PrintAsObjC/cdecl-official-for-objc-clients.swift @@ -0,0 +1,23 @@ +/// Similar test to cdecl-official but gated to objc-interop compatibility + +// RUN: %empty-directory(%t) +// RUN: split-file %S/cdecl-official.swift %t --leading-lines + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang-cxx %t/cdecl.h + +/// Build an Objective-C client against cdecl.h +// RUN: %clang -c %t/Client.c -fmodules -I %t \ +// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ +// RUN: -I %clang-include-dir -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop diff --git a/test/PrintAsObjC/cdecl-official-visibility.swift b/test/PrintAsObjC/cdecl-official-visibility.swift new file mode 100644 index 0000000000000..a40bbe713d276 --- /dev/null +++ b/test/PrintAsObjC/cdecl-official-visibility.swift @@ -0,0 +1,64 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate cdecl.h for an app +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h -package-name pkg \ +// RUN: -enable-experimental-feature CDecl +// RUN: %FileCheck %s --input-file %t/cdecl.h --check-prefixes PUBLIC-AND-INTERNAL,INTERNAL-ONLY +// RUN: %check-in-clang-c %t/cdecl.h + +/// Generate cdecl.h for a library +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h -package-name pkg \ +// RUN: -enable-experimental-feature CDecl +// RUN: %FileCheck %s --input-file %t/cdecl.h --check-prefixes PUBLIC-AND-INTERNAL +// RUN: %FileCheck %s --input-file %t/cdecl.h --implicit-check-not INTERNAL-ONLY +// RUN: %check-in-clang-c %t/cdecl.h + +// REQUIRES: swift_feature_CDecl + +//--- Lib.swift + +@cdecl private enum PrivateEnum: CInt { case A, B } +// PUBLIC-AND-INTERNAL-NOT: PrivateEnum + +@cdecl internal enum InternalEnum: CInt { case A, B } +// INTERNAL-ONLY: typedef SWIFT_ENUM(int, InternalEnum, closed) { +// INTERNAL-ONLY: InternalEnumA = 0, +// INTERNAL-ONLY: InternalEnumB = 1, +// INTERNAL-ONLY: }; + +@cdecl package enum PackageEnum: CInt { case A, B } +// INTERNAL-ONLY: typedef SWIFT_ENUM(int, PackageEnum, closed) { +// INTERNAL-ONLY: PackageEnumA = 0, +// INTERNAL-ONLY: PackageEnumB = 1, +// INTERNAL-ONLY: }; + +@cdecl public enum PublicEnum: CInt { case A, B } +// PUBLIC-AND-INTERNAL: typedef SWIFT_ENUM(int, PublicEnum, closed) { +// PUBLIC-AND-INTERNAL: PublicEnumA = 0, +// PUBLIC-AND-INTERNAL: PublicEnumB = 1, +// PUBLIC-AND-INTERNAL: }; + +/// Private documentation +@cdecl private func a_private() {} +// PUBLIC-AND-INTERNAL-NOT: // Private documentation +// PUBLIC-AND-INTERNAL-NOT: a_private + +/// Internal documentation +@cdecl internal func b_internal() {} +// INTERNAL-ONLY: // Internal documentation +// INTERNAL-ONLY: b_internal + +/// Package documentation +@cdecl package func c_package() {} +// INTERNAL-ONLY: // Package documentation +// INTERNAL-ONLY: c_package + +/// Public documentation +@cdecl public func d_public() {} +// PUBLIC-AND-INTERNAL: // Public documentation +// PUBLIC-AND-INTERNAL: d_public diff --git a/test/PrintAsObjC/cdecl-official.swift b/test/PrintAsObjC/cdecl-official.swift new file mode 100644 index 0000000000000..982db28f53873 --- /dev/null +++ b/test/PrintAsObjC/cdecl-official.swift @@ -0,0 +1,127 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t --leading-lines + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %t/Lib.swift -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-clang-header-path %t/cdecl.h \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion + +/// Build a client against cdecl.h +// RUN: %clang-no-modules -c %t/Client.c -I %t \ +// RUN: -F %S/../Inputs/clang-importer-sdk-path/frameworks \ +// RUN: -I %clang-include-dir -Werror \ +// RUN: -isysroot %S/../Inputs/clang-importer-sdk + +// REQUIRES: swift_feature_CDecl + +//--- Lib.swift + +// CHECK-NOT: assume_nonnull + +// CHECK: #if defined(__cplusplus) +// CHECK: extern "C" { +// CHECK: #endif + +// CHECK: /// Enums +// CHECK: typedef SWIFT_ENUM(int, 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 a0_simple(x: Int, bar y: Int) -> Int { return x } +// CHECK-LABEL: // My documentation +// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t simple(ptrdiff_t x, ptrdiff_t y) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl +func a1_defaultName(x: Int) -> Int { return x } +// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t a1_defaultName(ptrdiff_t x) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT; + +@cdecl(primitiveTypes) +public func b_primitiveTypes(i: Int, ci: CInt, l: CLong, c: CChar, f: Float, d: Double, b: Bool) {} +// CHECK-LABEL: SWIFT_EXTERN void primitiveTypes(ptrdiff_t i, int ci, long l, char c, float f, double d, bool b) SWIFT_NOEXCEPT; + +@cdecl(has_keyword_arg_names) +func c_keywordArgNames(auto: Int, union: Int) {} +// CHECK-LABEL: SWIFT_EXTERN void has_keyword_arg_names(ptrdiff_t auto_, ptrdiff_t union_) SWIFT_NOEXCEPT; + +@cdecl(return_never) +func d_returnNever() -> Never { fatalError() } +// CHECK-LABEL: SWIFT_EXTERN void return_never(void) SWIFT_NOEXCEPT SWIFT_NORETURN; + +/// Pointer types +// CHECK: /// Pointer types + +@cdecl(pointers) +func f_pointers(_ x: UnsafeMutablePointer, + y: UnsafePointer, + z: UnsafeMutableRawPointer, + w: UnsafeRawPointer, + u: OpaquePointer) {} +// CHECK: SWIFT_EXTERN void pointers(ptrdiff_t * _Nonnull x, ptrdiff_t const * _Nonnull y, void * _Nonnull z, void const * _Nonnull w, void * _Nonnull u) SWIFT_NOEXCEPT; + +@cdecl(nullable_pointers) +func g_nullablePointers(_ x: UnsafeMutableRawPointer, + y: UnsafeMutableRawPointer?, + z: UnsafeMutableRawPointer!) {} +// CHECK: SWIFT_EXTERN void nullable_pointers(void * _Nonnull x, void * _Nullable y, void * _Null_unspecified z) SWIFT_NOEXCEPT; + +/// Enums + +@cdecl +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 + +//--- Client.c + +#include "cdecl.h" + +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(); +} diff --git a/test/PrintAsObjC/cdecl-with-objc.swift b/test/PrintAsObjC/cdecl-with-objc.swift new file mode 100644 index 0000000000000..5ceb98dae89cd --- /dev/null +++ b/test/PrintAsObjC/cdecl-with-objc.swift @@ -0,0 +1,33 @@ +/// Ensure we print @cdecl and @_cdecl only once. + +// RUN: %empty-directory(%t) + +/// Generate cdecl.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) \ +// RUN: %s -emit-module -verify -o %t -emit-module-doc \ +// RUN: -emit-objc-header-path %t/cdecl.h \ +// RUN: -disable-objc-attr-requires-foundation-module \ +// RUN: -enable-experimental-feature CDecl + +/// Check cdecl.h directly +// RUN: %FileCheck %s --input-file %t/cdecl.h +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang-c %t/cdecl.h -Wnullable-to-nonnull-conversion +// RUN: %check-in-clang-cxx %t/cdecl.h + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop + +@cdecl +func cFunc() { } +// CHECK: cFunc +// CHECK-NOT: cFunc + +/// The class would break C parsing if printed in wrong block +@objc +class ObjCClass {} + +@_cdecl("objcFunc") +func objcFunc() -> ObjCClass! { return ObjCClass() } +// CHECK: objcFunc +// CHECK-NOT: objcFunc diff --git a/test/PrintAsObjC/cdecl.swift b/test/PrintAsObjC/cdecl.swift index 307159d956171..780121bb97d68 100644 --- a/test/PrintAsObjC/cdecl.swift +++ b/test/PrintAsObjC/cdecl.swift @@ -4,6 +4,7 @@ // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-in-clang %t/cdecl.h // RUN: %check-in-clang -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h +// RUN: %check-in-clang-c -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h // RUN: %check-in-clang-cxx -fno-modules -Qunused-arguments %t/cdecl.h -include ctypes.h -include CoreFoundation.h // REQUIRES: objc_interop diff --git a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-mock-sdk.swift b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-mock-sdk.swift index fa2fe7cef598a..d053c463c5488 100644 --- a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-mock-sdk.swift +++ b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-mock-sdk.swift @@ -4,6 +4,7 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -F %S/Inputs/ -typecheck -verify -emit-objc-header-path %t/textual-imports.h -emit-clang-header-nonmodular-includes %s // RUN: %FileCheck %s < %t/textual-imports.h // RUN: %check-in-clang -fno-modules -Qunused-arguments %t/textual-imports.h -F %S/Inputs +// RUN: %check-in-clang-c %t/textual-imports.h -F %S/Inputs import Foundation import Mixed @@ -30,9 +31,14 @@ public class HelloWorld: NSObject { // CHECK-NEXT: @import CoreGraphics; // CHECK-NEXT: @import Mixed; // CHECK-NEXT: @import ObjectiveC; -// CHECK-NEXT: #else +// CHECK-NEXT: #elif defined(__OBJC__) // CHECK-NEXT: #import // CHECK-NEXT: #import // CHECK-NEXT: #import // CHECK-NEXT: #import +// CHECK-NEXT: #else +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #include // CHECK-NEXT: #endif diff --git a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-modulemap-not-in-include-dir.swift b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-modulemap-not-in-include-dir.swift index 5c073aba1e779..d2e5454b787df 100644 --- a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-modulemap-not-in-include-dir.swift +++ b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-modulemap-not-in-include-dir.swift @@ -16,6 +16,8 @@ public class Bar : Baz {} // CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header" // CHECK-NEXT: #endif // CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest; -// CHECK-NEXT: #else -// CHECK: #import +// CHECK-NEXT: #elif defined(__OBJC__) +// CHECK-NEXT: #import +// CHECK: #else +// CHECK-NEXT: #include // CHECK: #endif diff --git a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-path-normalization.swift b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-path-normalization.swift index abd61424ed432..3e4ca18b47648 100644 --- a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-path-normalization.swift +++ b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-path-normalization.swift @@ -14,6 +14,8 @@ public class Bar : Baz {} // CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header" // CHECK-NEXT: #endif // CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest; -// CHECK-NEXT: #else -// CHECK: #import +// CHECK-NEXT: #elif defined(__OBJC__) +// CHECK-NEXT: #import +// CHECK: #else +// CHECK-NEXT: #include // CHECK: #endif diff --git a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-symlinked-header.swift b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-symlinked-header.swift index 9e4547a83c430..4ac059c77112c 100644 --- a/test/PrintAsObjC/emit-clang-header-nonmodular-includes-symlinked-header.swift +++ b/test/PrintAsObjC/emit-clang-header-nonmodular-includes-symlinked-header.swift @@ -16,6 +16,8 @@ public class Bar : Foo {} // CHECK-NEXT: #pragma clang diagnostic ignored "-Watimport-in-framework-header" // CHECK-NEXT: #endif // CHECK-NEXT: @import EmitClangHeaderNonmodularIncludesStressTest; -// CHECK-NEXT: #else +// CHECK-NEXT: #elif defined(__OBJC__) // CHECK: #import +// CHECK-NEXT: #else +// CHECK: #include // CHECK: #endif diff --git a/test/PrintAsObjC/lit.local.cfg b/test/PrintAsObjC/lit.local.cfg index a517a4a80ca14..c7f4cd06a3431 100644 --- a/test/PrintAsObjC/lit.local.cfg +++ b/test/PrintAsObjC/lit.local.cfg @@ -10,6 +10,14 @@ config.substitutions.insert(0, ('%check-in-clang', '-I %%clang-include-dir ' '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) +config.substitutions.insert(0, ('%check-in-clang-c', + '%%clang-no-modules -fsyntax-only -x c-header -std=c99 ' + '-Weverything -Werror -Wno-unused-macros -Wno-incomplete-module ' + '-Wno-auto-import -Wno-poison-system-directories ' + '-F %%clang-importer-sdk-path/frameworks ' + '-I %%clang-include-dir ' + '-isysroot %r/Inputs/clang-importer-sdk' % config.test_source_root) ) + config.substitutions.insert(0, ('%check-in-clang-cxx', '%%clang -fsyntax-only -x objective-c++-header -std=c++17 ' '-fobjc-arc -fmodules -fmodules-validate-system-headers ' diff --git a/test/SILGen/cdecl-official.swift b/test/SILGen/cdecl-official.swift new file mode 100644 index 0000000000000..cc78642297d20 --- /dev/null +++ b/test/SILGen/cdecl-official.swift @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-emit-silgen %s -module-name cdecl \ +// RUN: -enable-experimental-feature CDecl > %t/out.sil +// RUN: %FileCheck %s -input-file %t/out.sil + +// REQUIRES: swift_feature_CDecl + +// CHECK-LABEL: sil hidden [thunk] [ossa] @pear : $@convention(c) +// CHECK: function_ref @$s5cdecl5apple{{[_0-9a-zA-Z]*}}F +// CHECK-LABEL: sil hidden [ossa] @$s5cdecl5apple{{[_0-9a-zA-Z]*}}F +@cdecl(pear) +func apple(_ f: @convention(c) (Int) -> Int) { } + +// CHECK-LABEL: sil hidden [ossa] @$s5cdecl16forceCEntryPoint{{[_0-9a-zA-Z]*}}F +// CHECK: function_ref @grapefruit : $@convention(c) (Int) -> Int +// CHECK: function_ref apple(_:) +// CHECK: function_ref @$s5cdecl5appleyyS2iXCF : $@convention(thin) (@convention(c) (Int) -> Int) -> () +// FIXME should it be function_ref @apple? +func forceCEntryPoint() { + apple(orange) +} + +// CHECK-LABEL: sil hidden [thunk] [ossa] @grapefruit : $@convention(c) +// CHECK: function_ref @$s5cdecl6orange{{[_0-9a-zA-Z]*}}F +// CHECK-LABEL: sil hidden [ossa] @$s5cdecl6orange{{[_0-9a-zA-Z]*}}F +@cdecl(grapefruit) +func orange(_ x: Int) -> Int { + return x +} + +// CHECK-LABEL: sil [serialized] [thunk] [ossa] @cauliflower : $@convention(c) +// CHECK: function_ref @$s5cdecl8broccoli{{[_0-9a-zA-Z]*}}F +// CHECK-LABEL: sil [ossa] @$s5cdecl8broccoli{{[_0-9a-zA-Z]*}}F +// FIXME should it be `sil hidden`? +@cdecl(cauliflower) +public func broccoli(_ x: Int) -> Int { + return x +} + +// CHECK-LABEL: sil private [thunk] [ossa] @collard_greens : $@convention(c) +// CHECK: function_ref @$s5cdecl4kale[[PRIVATE:.*]] +// CHECK: sil private [ossa] @$s5cdecl4kale[[PRIVATE:.*]] +@cdecl(collard_greens) +private func kale(_ x: Int) -> Int { + return x +} + +// CHECK-LABEL: sil private [thunk] [ossa] @defaultName : $@convention(c) +// CHECK: function_ref @$s5cdecl11defaultName[[PRIVATE:.*]] +// CHECK: sil private [ossa] @$s5cdecl11defaultName[[PRIVATE:.*]] +@cdecl +private func defaultName(_ x: Int) -> Int { + return x +} diff --git a/test/Sema/access-notes-invalid.swift b/test/Sema/access-notes-invalid.swift index bb2fe2b39e1b6..a4b0bd497d3bd 100644 --- a/test/Sema/access-notes-invalid.swift +++ b/test/Sema/access-notes-invalid.swift @@ -21,10 +21,10 @@ class Extant { // GOOD-REMARK-DAG: note: add '@objc' explicitly to silence this warning func bad(_: Int?) {} // expected-remark * {{}} - // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: ignored access note: method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax - // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: error: ignored access note: method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax - // BAD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: ignored access note: method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax - // BAD-ERROR-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: error: ignored access note: method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-1]]:{{[0-9]+}}: remark: ignored access note: instance method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-IGNORE-NOT: access-notes-invalid.swift:[[@LINE-2]]:{{[0-9]+}}: error: ignored access note: instance method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-REMARK-DAG: access-notes-invalid.swift:[[@LINE-3]]:{{[0-9]+}}: remark: ignored access note: instance method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax + // BAD-ERROR-DAG: access-notes-invalid.swift:[[@LINE-4]]:{{[0-9]+}}: error: ignored access note: instance method cannot be marked '@objc' by an access note because the type of the parameter cannot be represented in Objective-C; did not implicitly add '@objc' to this instance method, even though it was specified by access note for Access notes containing future, unknown syntax } // FIXME: Should diagnose multiple access notes for the same decl diff --git a/test/attr/attr_cdecl.swift b/test/attr/attr_cdecl.swift index 2705646b8fdac..c2d9110a50896 100644 --- a/test/attr/attr_cdecl.swift +++ b/test/attr/attr_cdecl.swift @@ -8,7 +8,7 @@ func emptyName(x: Int) -> Int { return x } @_cdecl("noBody") func noBody(x: Int) -> Int // expected-error{{expected '{' in body of function}} -@_cdecl("property") // expected-error{{may only be used on 'func' declarations}} +@_cdecl("property") // expected-error{{'@_cdecl' attribute cannot be applied to this declaration}} var property: Int var computed: Int { @@ -24,6 +24,9 @@ enum SwiftEnum { case A, B } @objc enum CEnum: Int { case A, B } #endif +@_cdecl("enum") // expected-error {{@_cdecl may only be used on 'func' declarations}} +enum UnderscoreCDeclEnum: CInt { case A, B } + @_cdecl("swiftStruct") func swiftStruct(x: SwiftStruct) {} // expected-error{{cannot be represented}} expected-note{{Swift struct}} @@ -40,10 +43,10 @@ class Foo { @_cdecl("Foo_foo_2") // expected-error{{can only be applied to global functions}} static func foo(x: Int) -> Int { return x } - @_cdecl("Foo_init") // expected-error{{may only be used on 'func'}} + @_cdecl("Foo_init") // expected-error{{'@_cdecl' attribute cannot be applied to this declaration}} init() {} - @_cdecl("Foo_deinit") // expected-error{{may only be used on 'func'}} + @_cdecl("Foo_deinit") // expected-error{{'@_cdecl' attribute cannot be applied to this declaration}} deinit {} } @@ -53,7 +56,7 @@ func hasNested() { } // TODO: Handle error conventions in SILGen for toplevel functions. -@_cdecl("throwing") // expected-error{{raising errors from '@_cdecl' functions is not supported}} +@_cdecl("throwing") // expected-error{{raising errors from @_cdecl functions is not supported}} func throwing() throws { } // TODO: cdecl name collisions diff --git a/test/attr/attr_cdecl_official.swift b/test/attr/attr_cdecl_official.swift new file mode 100644 index 0000000000000..80cc19ed3c2c5 --- /dev/null +++ b/test/attr/attr_cdecl_official.swift @@ -0,0 +1,204 @@ +/// @cdecl attribute +/// This test shouldn't require the objc runtime. + +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s \ +// RUN: -enable-experimental-feature CDecl -disable-objc-interop + +// REQUIRES: swift_feature_CDecl + +@cdecl(cdecl_foo) func foo(x: Int) -> Int { return x } + +@cdecl(not an identifier) func invalidName() {} +// expected-error @-1 {{expected ')' in 'cdecl' attribute}} +// expected-error @-2 {{expected declaration}} + +@cdecl() func emptyParen() {} +// expected-error @-1 {{expected C identifier in 'cdecl' attribute}} +// expected-error @-2 {{expected declaration}} + +@cdecl(42) func aNumber() {} +// expected-error @-1 {{expected C identifier in 'cdecl' attribute}} +// expected-error @-2 {{expected declaration}} + +@cdecl(a:selector:) func selectordName() {} +// expected-error @-1 {{expected ')' in 'cdecl' attribute}} +// expected-error @-2 {{expected declaration}} + +@cdecl("oldStringStyle") func oldStringStyle() {} +// expected-error @-1 {{expected C identifier in 'cdecl' attribute}} +// expected-error @-2 {{expected declaration}} + +@cdecl func defaultName() {} + +@cdecl(noBody) +func noBody(x: Int) -> Int // expected-error{{expected '{' in body of function}} + +@cdecl(property) // expected-error{{'@cdecl' attribute cannot be applied to this declaration}} +var property: Int + +var computed: Int { + @cdecl(get_computed) get { return 0 } + @cdecl(set_computed) set { return } +} + +@cdecl(`inout`) +func noBody(x: inout Int) { } // expected-error{{global function cannot be marked '@cdecl' because inout parameters cannot be represented in C}} + +struct SwiftStruct { var x, y: Int } +enum SwiftEnum { case A, B } + +#if os(Windows) && (arch(x86_64) || arch(arm64)) +@cdecl(CEnum) enum CEnum: Int32 { case A, B } +#else +@cdecl(CEnum) enum CEnum: Int { case A, B } +#endif + +@cdecl enum CEnumDefaultName: CInt { case A, B } + +@cdecl(CEnumNoRawType) enum CEnumNoRawType { case A, B } +// expected-error @-1 {{'@cdecl' enum must declare an integer raw type}} + +@cdecl(CEnumStringRawType) enum CEnumStringRawType: String { case A, B } +// expected-error @-1 {{'@cdecl' enum raw type 'String' is not an integer type}} + +@cdecl +func swiftStruct(x: SwiftStruct) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{Swift structs cannot be represented in C}} + +@cdecl +func swiftEnum(x: SwiftEnum) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{Swift enums not marked '@cdecl' cannot be represented in C}} + +@cdecl(cEnum) +func cEnum(x: CEnum) {} + +@cdecl(CDeclAndObjC) // expected-error {{cannot apply both '@cdecl' and '@objc' to enum}} +@objc +enum CDeclAndObjC: CInt { case A, B } + +@cdecl(TwoCDecls) // expected-note {{attribute already specified here}} +@_cdecl("TwoCDecls") // expected-error {{duplicate attribute}} +func TwoCDecls() {} + +class Foo { + @cdecl(Foo_foo) // expected-error{{@cdecl can only be applied to global functions}} + func foo(x: Int) -> Int { return x } + + @cdecl(Foo_foo_2) // expected-error{{@cdecl can only be applied to global functions}} + static func foo(x: Int) -> Int { return x } + + @cdecl(Foo_init) // expected-error{{'@cdecl' attribute cannot be applied to this declaration}} + init() {} + + @cdecl(Foo_deinit) // expected-error{{'@cdecl' attribute cannot be applied to this declaration}} + deinit {} +} + +@cdecl(throwing) // expected-error{{raising errors from @cdecl functions is not supported}} +func throwing() throws { } + +@cdecl(acceptedPointers) +func acceptedPointers(_ x: UnsafeMutablePointer, + y: UnsafePointer, + z: UnsafeMutableRawPointer, + w: UnsafeRawPointer, + u: OpaquePointer) {} + +@cdecl(rejectedPointers) +func rejectedPointers( // expected-error 6 {{global function cannot be marked '@cdecl' because the type of the parameter}} + x: UnsafePointer, // expected-note {{Swift structs cannot be represented in C}} + y: CVaListPointer, // expected-note {{Swift structs cannot be represented in C}} + z: UnsafeBufferPointer, // expected-note {{Swift structs cannot be represented in C}} + u: UnsafeMutableBufferPointer, // expected-note {{Swift structs cannot be represented in C}} + v: UnsafeRawBufferPointer, // expected-note {{Swift structs cannot be represented in C}} + t: UnsafeMutableRawBufferPointer) {} // expected-note {{Swift structs cannot be represented in C}} + +@cdecl +func genericParam(i: I) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because it has generic parameters}} + +@cdecl +func variadic(_: Int...) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because it has a variadic parameter}} + +@cdecl +func tupleParamEmpty(a: ()) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{empty tuple type cannot be represented in C}} + +@cdecl +func tupleParam(a: (Int, Float)) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{tuples cannot be represented in C}} + +@cdecl(emptyTupleReturn) +func emptyTupleReturn() -> () {} + +@cdecl(tupleReturn) +func tupleReturn() -> (Int, Float) { (1, 2.0) } +// expected-error @-1 {{global function cannot be marked '@cdecl' because its result type cannot be represented in C}} +// expected-note @-2 {{tuples cannot be represented in C}} + +@cdecl +func funcAcceptsThrowingFunc(fn: (String) throws -> Int) { } +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{throwing function types cannot be represented in C}} + +@cdecl +func funcAcceptsThrowingFuncReturn() -> (String) throws -> Int { fatalError() } +// expected-error @-1 {{global function cannot be marked '@cdecl' because its result type cannot be represented in C}} +// expected-note @-2 {{throwing function types cannot be represented in C}} + +@cdecl +func bar(f: (SwiftEnum) -> SwiftStruct) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{function types cannot be represented in C unless their parameters and returns can be}} + +@cdecl +func bas(f: (SwiftEnum) -> ()) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{function types cannot be represented in C unless their parameters and returns can be}} + +@cdecl +func zim(f: () -> SwiftStruct) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{function types cannot be represented in C unless their parameters and returns can be}} + +@cdecl +func zang(f: (SwiftEnum, SwiftStruct) -> ()) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{function types cannot be represented in C unless their parameters and returns can be}} + +@cdecl(zang_zang) + func zangZang(f: (Int...) -> ()) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{function types cannot be represented in C unless their parameters and returns can be}} + +@cdecl +func array(i: [Int]) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{Swift structs cannot be represented in C}} + +class SwiftClass {} +@cdecl +func swiftClass(p: SwiftClass) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{classes cannot be represented in C}} + +protocol SwiftProtocol {} +@cdecl +func swiftProtocol(p: SwiftProtocol) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{protocols cannot be represented in C}} + +@cdecl +func swiftErrorProtocol(e: Error) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{protocols cannot be represented in C}} + +@cdecl +func anyParam(e:Any) {} +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{protocols cannot be represented in C}} diff --git a/test/attr/attr_cdecl_official_async.swift b/test/attr/attr_cdecl_official_async.swift new file mode 100644 index 0000000000000..dc19a94c2c4f4 --- /dev/null +++ b/test/attr/attr_cdecl_official_async.swift @@ -0,0 +1,17 @@ +// RUN: %target-typecheck-verify-swift -enable-objc-interop \ +// RUN: -disable-availability-checking \ +// RUN: -enable-experimental-feature CDecl + +// REQUIRES: concurrency +// REQUIRES: swift_feature_CDecl + +@_cdecl("async") // expected-error{{@_cdecl global function cannot be asynchronous}} +func asynchronous() async { } + +@cdecl(async2) // expected-error{{@cdecl global function cannot be asynchronous}} +func asynchronous2() async { } + +@cdecl(asyncParam) +func asynchronousParam(fn: (String) async -> Int) { } +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{'async' function types cannot be represented in C}} diff --git a/test/attr/attr_cdecl_official_rejected.swift b/test/attr/attr_cdecl_official_rejected.swift new file mode 100644 index 0000000000000..42cd1e5fc40a5 --- /dev/null +++ b/test/attr/attr_cdecl_official_rejected.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift +// RUN: %target-swift-frontend -typecheck %s \ +// RUN: -enable-experimental-feature CDecl + +// REQUIRES: swift_feature_CDecl + +@cdecl(cdecl_foo) func foo() { } // expected-error {{@cdecl requires '-enable-experimental-feature CDecl'}} + +var computed: Int { + @cdecl(get_computed) get { return 0 } // expected-error {{@cdecl requires '-enable-experimental-feature CDecl'}} + @cdecl(set_computed) set { } // expected-error {{@cdecl requires '-enable-experimental-feature CDecl'}} +} diff --git a/test/attr/attr_cdecl_official_with_objc.swift b/test/attr/attr_cdecl_official_with_objc.swift new file mode 100644 index 0000000000000..e3d53ede5b4b5 --- /dev/null +++ b/test/attr/attr_cdecl_official_with_objc.swift @@ -0,0 +1,42 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -verify %s \ +// RUN: -enable-experimental-feature CDecl + +// REQUIRES: swift_feature_CDecl +// REQUIRES: objc_interop + +import Foundation + +@objc +class ObjCClass: NSObject { } + +@cdecl func objcClassReturn() -> ObjCClass { fatalError() } +// expected-error @-1 {{global function cannot be marked '@cdecl' because its result type cannot be represented in C}} +// expected-note @-2 {{classes cannot be represented in C}} + +@cdecl func objcClassParams(a: ObjCClass, b: ObjCClass) { } +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter 1 cannot be represented in C}} +// expected-error @-2 {{global function cannot be marked '@cdecl' because the type of the parameter 2 cannot be represented in C}} +// expected-note @-3 2 {{classes cannot be represented in C}} + +@objc +protocol ObjCProtocol {} + +@cdecl func objcProtocol(a: ObjCProtocol) { } +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{protocols cannot be represented in C}} + +@objc +enum ObjCEnum: Int { case A, B } +@cdecl func objcEnumUseInCDecl(a: ObjCEnum) { } +// expected-error @-1 {{global function cannot be marked '@cdecl' because the type of the parameter cannot be represented in C}} +// expected-note @-2 {{Swift enums not marked '@cdecl' cannot be represented in C}} + +/// Objective-C accepts @cdecl enums. +@cdecl(CEnum) +enum CEnum: Int { case A, B } +@_cdecl("cdeclEnumUseInObjc") func cdeclEnumUseInObjc(a: CEnum) { } + +enum SwiftEnum { case A, B } +@_cdecl("swiftEnumUseInObjc") func swiftEnumUseInObjc(a: SwiftEnum) { } +// expected-error @-1 {{global function cannot be marked '@_cdecl' because the type of the parameter cannot be represented in Objective-C}} +// expected-note @-2 {{Swift enums not marked '@cdecl' or '@objc' cannot be represented in Objective-C}} diff --git a/test/attr/attr_dynamic.swift b/test/attr/attr_dynamic.swift index 1e7b9b3212c7d..626f4d8bc431d 100644 --- a/test/attr/attr_dynamic.swift +++ b/test/attr/attr_dynamic.swift @@ -11,7 +11,7 @@ dynamic prefix operator +!+ // expected-error{{unexpected attribute 'dynamic' i class Foo { @objc dynamic init() {} - @objc dynamic init(x: NotObjCAble) {} // expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{Swift structs cannot be represented in Objective-C}} + @objc dynamic init(x: NotObjCAble) {} // expected-error{{initializer cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{Swift structs cannot be represented in Objective-C}} @objc dynamic var x: Int diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 33c528c34fac1..13af1e83a76eb 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -421,39 +421,39 @@ class ConcreteContext3 { func genericParams() -> [T] { return [] } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because it has generic parameters}} @objc // bad-access-note-move{{ConcreteContext3.returnObjCProtocolMetatype()}} - func returnObjCProtocolMetatype() -> NSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnObjCProtocolMetatype() -> NSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} typealias AnotherNSCoding = NSCoding typealias MetaNSCoding1 = NSCoding.Protocol typealias MetaNSCoding2 = AnotherNSCoding.Protocol @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype1()}} - func returnObjCAliasProtocolMetatype1() -> AnotherNSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnObjCAliasProtocolMetatype1() -> AnotherNSCoding.Protocol { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype2()}} - func returnObjCAliasProtocolMetatype2() -> MetaNSCoding1 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnObjCAliasProtocolMetatype2() -> MetaNSCoding1 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} @objc // bad-access-note-move{{ConcreteContext3.returnObjCAliasProtocolMetatype3()}} - func returnObjCAliasProtocolMetatype3() -> MetaNSCoding2 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnObjCAliasProtocolMetatype3() -> MetaNSCoding2 { return NSCoding.self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} typealias Composition = NSCopying & NSCoding @objc // bad-access-note-move{{ConcreteContext3.returnCompositionMetatype1()}} - func returnCompositionMetatype1() -> Composition.Protocol { return Composition.self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnCompositionMetatype1() -> Composition.Protocol { return Composition.self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} @objc // bad-access-note-move{{ConcreteContext3.returnCompositionMetatype2()}} - func returnCompositionMetatype2() -> (NSCopying & NSCoding).Protocol { return (NSCopying & NSCoding).self } // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + func returnCompositionMetatype2() -> (NSCopying & NSCoding).Protocol { return (NSCopying & NSCoding).self } // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} typealias NSCodingExistential = NSCoding.Type @objc // bad-access-note-move{{ConcreteContext3.inoutFunc(a:)}} - func inoutFunc(a: inout Int) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because inout parameters cannot be represented in Objective-C}} + func inoutFunc(a: inout Int) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because inout parameters cannot be represented in Objective-C}} @objc // bad-access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram1(a:)}} - func metatypeOfExistentialMetatypePram1(a: NSCodingExistential.Protocol) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + func metatypeOfExistentialMetatypePram1(a: NSCodingExistential.Protocol) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} @objc // bad-access-note-move{{ConcreteContext3.metatypeOfExistentialMetatypePram2(a:)}} - func metatypeOfExistentialMetatypePram2(a: NSCoding.Type.Protocol) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + func metatypeOfExistentialMetatypePram2(a: NSCoding.Type.Protocol) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} } func genericContext1(_: T) { @@ -718,7 +718,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func7_(a:)}} func func7_(a: PlainClass) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with '@objc' cannot be represented in Objective-C}} func func7m(a: PlainClass.Type) {} @@ -726,14 +726,14 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func7m_(a:)}} func func7m_(a: PlainClass.Type) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} func func8() -> PlainClass {} // CHECK-LABEL: {{^}} func func8() -> PlainClass { @objc // bad-access-note-move{{infer_instanceFunc1.func8_()}} func func8_() -> PlainClass {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} // expected-note@-2 {{classes not annotated with '@objc' cannot be represented in Objective-C}} func func8m() -> PlainClass.Type {} @@ -741,14 +741,14 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func8m_()}} func func8m_() -> PlainClass.Type {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} func func9(a: PlainStruct) {} // CHECK-LABEL: {{^}} func func9(a: PlainStruct) { @objc // bad-access-note-move{{infer_instanceFunc1.func9_(a:)}} func func9_(a: PlainStruct) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} func func10() -> PlainStruct {} @@ -756,7 +756,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func10_()}} func func10_() -> PlainStruct {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} func func11(a: PlainEnum) {} @@ -764,7 +764,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func11_(a:)}} func func11_(a: PlainEnum) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{non-'@objc' enums cannot be represented in Objective-C}} func func12(a: PlainProtocol) {} @@ -772,7 +772,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func12_(a:)}} func func12_(a: PlainProtocol) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'PlainProtocol' cannot be represented in Objective-C}} func func13(a: Class_ObjC1) {} @@ -786,7 +786,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func14_(a:)}} func func14_(a: Protocol_Class1) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{protocol-constrained type containing protocol 'Protocol_Class1' cannot be represented in Objective-C}} func func15(a: Protocol_ObjC1) {} @@ -878,9 +878,9 @@ class infer_instanceFunc1 { // Check that we produce diagnostics for every parameter and return type. @objc // bad-access-note-move{{infer_instanceFunc1.func_MultipleDiags(a:b:)}} func func_MultipleDiags(a: PlainStruct, b: PlainEnum) -> Any {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter 1 cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter 1 cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} - // access-note-adjust{{@objc}} expected-error@-3 {{method cannot be marked '@objc' because the type of the parameter 2 cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-3 {{instance method cannot be marked '@objc' because the type of the parameter 2 cannot be represented in Objective-C}} // expected-note@-4 {{non-'@objc' enums cannot be represented in Objective-C}} // Produces an extra: expected-note@-5 * {{attribute 'objc' was added by access note for fancy tests}} @@ -889,7 +889,7 @@ class infer_instanceFunc1 { @objc // bad-access-note-move{{infer_instanceFunc1.func_UnnamedParam2(_:)}} func func_UnnamedParam2(_: PlainStruct) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{Swift structs cannot be represented in Objective-C}} @objc // access-note-move{{infer_instanceFunc1.func_varParam1(a:)}} @@ -1723,17 +1723,17 @@ class infer_instanceVar2< @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Unconstrained_(a:)}} func func_GP_Unconstrained_(a: GP_Unconstrained) {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Unconstrained_()}} func func_GP_Unconstrained_() -> GP_Unconstrained {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} @objc // bad-access-note-move{{infer_instanceVar2.func_GP_Class_ObjC__()}} func func_GP_Class_ObjC__() -> GP_Class_ObjC {} - // access-note-adjust{{@objc}} expected-error@-1 {{method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1 {{instance method cannot be marked '@objc' because its result type cannot be represented in Objective-C}} // expected-note@-2 {{generic type parameters cannot be represented in Objective-C}} } @@ -2159,18 +2159,18 @@ class ClosureArguments { func foo(f: (Int) -> ()) {} // CHECK: @objc func bar @objc // bad-access-note-move{{ClosureArguments.bar(f:)}} - func bar(f: (NotObjCEnum) -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + func bar(f: (NotObjCEnum) -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func bas @objc // bad-access-note-move{{ClosureArguments.bas(f:)}} - func bas(f: (NotObjCEnum) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + func bas(f: (NotObjCEnum) -> ()) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func zim @objc // bad-access-note-move{{ClosureArguments.zim(f:)}} - func zim(f: () -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + func zim(f: () -> NotObjCStruct) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: @objc func zang @objc // bad-access-note-move{{ClosureArguments.zang(f:)}} - func zang(f: (NotObjCEnum, NotObjCStruct) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + func zang(f: (NotObjCEnum, NotObjCStruct) -> ()) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} @objc // bad-access-note-move{{ClosureArguments.zangZang(f:)}} - func zangZang(f: (Int...) -> ()) {} // access-note-adjust{{@objc}} expected-error{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} + func zangZang(f: (Int...) -> ()) {} // access-note-adjust{{@objc}} expected-error{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} expected-note{{function types cannot be represented in Objective-C unless their parameters and returns can be}} // CHECK: {{^}} func fooImplicit func fooImplicit(f: (Int) -> ()) {} // CHECK: {{^}} func barImplicit @@ -2305,7 +2305,7 @@ class ClassThrows1 { @objc // bad-access-note-move{{ClassThrows1.methodAcceptsThrowingFunc(fn:)}} func methodAcceptsThrowingFunc(fn: (String) throws -> Int) { } - // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{throwing function types cannot be represented in Objective-C}} @objc // bad-access-note-move{{ClassThrows1.init(radians:)}} @@ -2316,7 +2316,7 @@ class ClassThrows1 { @objc // bad-access-note-move{{ClassThrows1.fooWithErrorEnum1(x:)}} func fooWithErrorEnum1(x: ErrorEnum) {} - // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{non-'@objc' enums cannot be represented in Objective-C}} // CHECK: {{^}} func fooWithErrorEnum2(x: ErrorEnum) @@ -2324,7 +2324,7 @@ class ClassThrows1 { @objc // bad-access-note-move{{ClassThrows1.fooWithErrorProtocolComposition1(x:)}} func fooWithErrorProtocolComposition1(x: Error & Protocol_ObjC1) { } - // access-note-adjust{{@objc}} expected-error@-1{{method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} + // access-note-adjust{{@objc}} expected-error@-1{{instance method cannot be marked '@objc' because the type of the parameter cannot be represented in Objective-C}} // expected-note@-2{{protocol-constrained type containing 'Error' cannot be represented in Objective-C}} // CHECK: {{^}} func fooWithErrorProtocolComposition2(x: any Error & Protocol_ObjC1) diff --git a/test/lit.cfg b/test/lit.cfg index f824949630a0f..8a7841b8a52f2 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -837,6 +837,11 @@ config.substitutions.append( ('%clangxx', "%r %s" % (config.clangxx, clang_mcp_opt)) ) +# Alternative to %clang that doesn't require -fmodules. +config.substitutions.append( ('%clang-no-modules', + "%r" % + (config.clang)) ) + # This must come after all substitutions containing "%clang". # Note: %clang is the locally-built clang. # To get Xcode's clang, use %target-clang.