diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 38bad9b1f2504..d7a15f985eb78 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -245,6 +245,10 @@ SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess, SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface, OnProtocol | UserInaccessible, 62) +DECL_ATTR(_cdecl, CDecl, + OnFunc | LongAttribute | UserInaccessible, 63) + + #undef TYPE_ATTR #undef DECL_ATTR_ALIAS #undef SIMPLE_DECL_ATTR diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index bfcf7ab97b811..6d5267268d8a2 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -516,6 +516,24 @@ class SILGenNameAttr : public DeclAttribute { } }; +/// Defines the @_cdecl attribute. +class CDeclAttr : public DeclAttribute { +public: + CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit) + : DeclAttribute(DAK_CDecl, AtLoc, Range, Implicit), + Name(Name) {} + + CDeclAttr(StringRef Name, bool Implicit) + : CDeclAttr(Name, SourceLoc(), SourceRange(), /*Implicit=*/true) {} + + /// The symbol name. + const StringRef Name; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_CDecl; + } +}; + /// Defines the @_semantics attribute. class SemanticsAttr : public DeclAttribute { public: diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e17896565197c..f7ad542daa9a1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -836,6 +836,14 @@ ERROR(no_objc_tagged_pointer_not_class_protocol,none, ERROR(swift_native_objc_runtime_base_not_on_root_class,none, "@_swift_native_objc_runtime_base_not_on_root_class can only be applied " "to root classes", ()) + +ERROR(cdecl_not_at_top_level,none, + "@_cdecl can only be applied to global functions", ()) +ERROR(cdecl_empty_name,none, + "@_cdecl symbol name cannot be empty", ()) +ERROR(cdecl_throws,none, + "raising errors from @_cdecl functions is not supported", ()) + ERROR(attr_methods_only,none, "only methods can be declared %0", (DeclAttribute)) ERROR(access_control_in_protocol,none, @@ -2487,7 +2495,7 @@ ERROR(objc_enum_case_multi,none, "'@objc' enum case declaration defines multiple enum cases with the same Objective-C name", ()) // If you change this, also change enum ObjCReason -#define OBJC_ATTR_SELECT "select{marked dynamic|marked @objc|marked @IBOutlet|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override}" +#define OBJC_ATTR_SELECT "select{marked @_cdecl|marked dynamic|marked @objc|marked @IBOutlet|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override}" ERROR(objc_invalid_on_var,none, "property cannot be %" OBJC_ATTR_SELECT "0 " diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index af745d21bec29..fe36cae428ef3 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -43,7 +43,7 @@ const unsigned char MODULE_DOC_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x07 }; /// Serialized module format major version number. /// -/// Always 0 for Swift 1.0. +/// Always 0 for Swift 1.x and 2.x. const uint16_t VERSION_MAJOR = 0; /// Serialized module format minor version number. @@ -53,7 +53,8 @@ const uint16_t VERSION_MAJOR = 0; /// in source control, you should also update the comment to briefly /// 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. -const uint16_t VERSION_MINOR = 239; // multiple results for SILFunctionType +/// describe what change you made. +const uint16_t VERSION_MINOR = 240; // Last change: @_cdecl using DeclID = PointerEmbeddedInt; using DeclIDField = BCFixed<31>; @@ -1223,6 +1224,13 @@ namespace decls_block { BCFixed<1>, // implicit flag BCBlob // _silgen_name >; + + using CDeclDeclAttrLayout = BCRecordLayout< + CDecl_DECL_ATTR, + BCFixed<1>, // implicit flag + BCBlob // _silgen_name + >; + using AlignmentDeclAttrLayout = BCRecordLayout< Alignment_DECL_ATTR, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f0d33d3460ad2..6db9fe927ba65 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1908,7 +1908,10 @@ void AbstractFunctionDecl::setForeignErrorConvention( Optional AbstractFunctionDecl::getForeignErrorConvention() const { - if (!isObjC() || !isBodyThrowing()) return None; + if (!isObjC() && !getAttrs().hasAttribute()) + return None; + if (!isBodyThrowing()) + return None; auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions; auto it = conventionsMap.find(this); if (it == conventionsMap.end()) return None; diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 8a1db86400a10..c5ed2bba76c98 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2200,8 +2200,9 @@ struct ASTNodeBase {}; abort(); } - if (AFD->getForeignErrorConvention() && !AFD->isObjC()) { - Out << "foreign error convention on non-@objc function\n"; + if (AFD->getForeignErrorConvention() + && !AFD->isObjC() && !AFD->getAttrs().hasAttribute()) { + Out << "foreign error convention on non-@objc, non-@_cdecl function\n"; AFD->dump(Out); abort(); } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index cb470ce69bff4..7d69f2e53d632 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -372,6 +372,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options) if (cast(this)->isEscaping()) Printer << "(escaping)"; break; + + case DAK_CDecl: + Printer << "@_cdecl(\"" << cast(this)->Name << "\")"; + break; + case DAK_ObjC: { Printer.printAttrName("@objc"); llvm::SmallString<32> scratch; @@ -487,6 +492,8 @@ StringRef DeclAttribute::getAttrName() const { return "_silgen_name"; case DAK_Alignment: return "_alignment"; + case DAK_CDecl: + return "_cdecl"; case DAK_SwiftNativeObjCRuntimeBase: return "_swift_native_objc_runtime_base"; case DAK_Semantics: diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 93b51864b8dd3..4e67190f66625 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -232,7 +232,7 @@ void LinkEntity::mangle(raw_ostream &buffer) const { // entity ::= declaration // other declaration case Kind::Function: - // As a special case, functions can have external asm names. + // As a special case, functions can have manually mangled names. if (auto AsmA = getDecl()->getAttrs().getAttribute()) { mangler.append(AsmA->Name); return mangler.finalize(buffer); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7b9a3030e9d23..408a385fc750c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -565,6 +565,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + case DAK_CDecl: case DAK_SILGenName: { if (!consumeIf(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -601,9 +602,16 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); } - if (!DiscardAttribute) - Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc, + if (!DiscardAttribute) { + if (DK == DAK_SILGenName) + Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc, + AttrRange, /*Implicit=*/false)); + else if (DK == DAK_CDecl) + Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc, AttrRange, /*Implicit=*/false)); + else + llvm_unreachable("out of sync with switch"); + } break; } diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index dcd1178e050de..56df3cf48f8f5 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -91,7 +91,8 @@ static Identifier getNameForObjC(const ValueDecl *VD, namespace { class ObjCPrinter : private DeclVisitor, private TypeVisitor> { + Optional> +{ friend ASTVisitor; friend TypeVisitor; @@ -120,7 +121,8 @@ class ObjCPrinter : private DeclVisitor, } bool shouldInclude(const ValueDecl *VD) { - return VD->isObjC() && VD->getFormalAccess() >= minRequiredAccess && + return (VD->isObjC() || VD->getAttrs().hasAttribute()) && + VD->getFormalAccess() >= minRequiredAccess && !(isa(VD) && cast(VD)->hasStubImplementation()); } @@ -333,48 +335,52 @@ class ObjCPrinter : private DeclVisitor, return true; } - void printAbstractFunction(AbstractFunctionDecl *AFD, bool isClassMethod, - bool isNSUIntegerSubscript = false) { - printDocumentationComment(AFD); - if (isClassMethod) - os << "+ ("; - else - os << "- ("; - - const clang::ObjCMethodDecl *clangMethod = nullptr; - if (!isNSUIntegerSubscript) { - if (const AbstractFunctionDecl *clangBase = findClangBase(AFD)) { - clangMethod = - dyn_cast_or_null(clangBase->getClangDecl()); - } - } - - Type rawMethodTy = AFD->getType()->castTo()->getResult(); - auto methodTy = rawMethodTy->castTo(); - + Type getForeignResultType(AbstractFunctionDecl *AFD, + FunctionType *methodTy, + Optional errorConvention) { // A foreign error convention can affect the result type as seen in // Objective-C. - Optional errorConvention - = AFD->getForeignErrorConvention(); - Type resultTy = methodTy->getResult(); if (errorConvention) { switch (errorConvention->getKind()) { case ForeignErrorConvention::ZeroResult: case ForeignErrorConvention::NonZeroResult: // The error convention provides the result type. - resultTy = errorConvention->getResultType(); - break; + return errorConvention->getResultType(); case ForeignErrorConvention::NilResult: // Errors are propagated via 'nil' returns. - resultTy = OptionalType::get(resultTy); - break; + return OptionalType::get(methodTy->getResult()); case ForeignErrorConvention::NonNilError: case ForeignErrorConvention::ZeroPreservedResult: break; } } + return methodTy->getResult(); + } + + void printAbstractFunctionAsMethod(AbstractFunctionDecl *AFD, + bool isClassMethod, + bool isNSUIntegerSubscript = false) { + printDocumentationComment(AFD); + if (isClassMethod) + os << "+ ("; + else + os << "- ("; + + const clang::ObjCMethodDecl *clangMethod = nullptr; + if (!isNSUIntegerSubscript) { + if (const AbstractFunctionDecl *clangBase = findClangBase(AFD)) { + clangMethod = + dyn_cast_or_null(clangBase->getClangDecl()); + } + } + + Optional errorConvention + = AFD->getForeignErrorConvention(); + Type rawMethodTy = AFD->getType()->castTo()->getResult(); + auto methodTy = rawMethodTy->castTo(); + auto resultTy = getForeignResultType(AFD, methodTy, errorConvention); // Constructors and methods returning DynamicSelf return // instancetype. @@ -464,15 +470,63 @@ class ObjCPrinter : private DeclVisitor, os << ";\n"; } + + void printSingleFunctionParam(const ParamDecl *param) { + // The type name may be multi-part. + PrintMultiPartType multiPart(*this); + visitPart(param->getType(), OTK_None); + auto name = param->getName(); + if (name.empty()) + return; + + os << ' ' << name; + if (isClangKeyword(name)) + os << "_"; + } + void printAbstractFunctionAsFunction(FuncDecl *FD) { + printDocumentationComment(FD); + Optional errorConvention + = FD->getForeignErrorConvention(); + auto resultTy = getForeignResultType(FD, + FD->getType()->castTo(), + errorConvention); + + // The result type may be a partial function type we need to close + // up later. + PrintMultiPartType multiPart(*this); + visitPart(resultTy, OTK_None); + + assert(FD->getAttrs().hasAttribute() + && "not a cdecl function"); + + os << ' ' << FD->getAttrs().getAttribute()->Name << '('; + + assert(FD->getParameterLists().size() == 1 && "not a C-compatible func"); + auto params = FD->getParameterLists().back(); + interleave(*params, + [&](const ParamDecl *param) { + printSingleFunctionParam(param); + }, + [&]{ os << ", "; }); + + os << ')'; + + // Finish the result type. + multiPart.finish(); + + os << ';'; + } + void visitFuncDecl(FuncDecl *FD) { - assert(FD->getDeclContext()->isTypeContext() && - "cannot handle free functions right now"); - printAbstractFunction(FD, FD->isStatic()); + if (FD->getDeclContext()->isTypeContext()) + printAbstractFunctionAsMethod(FD, FD->isStatic()); + else + printAbstractFunctionAsFunction(FD); } void visitConstructorDecl(ConstructorDecl *CD) { - printAbstractFunction(CD, false); + printAbstractFunctionAsMethod(CD, false); } bool maybePrintIBOutletCollection(Type ty) { @@ -514,9 +568,9 @@ class ObjCPrinter : private DeclVisitor, if (VD->isStatic()) { // Objective-C doesn't have class properties. Just print the accessors. - printAbstractFunction(VD->getGetter(), true); + printAbstractFunctionAsMethod(VD->getGetter(), true); if (auto setter = VD->getSetter()) - printAbstractFunction(setter, true); + printAbstractFunctionAsMethod(setter, true); return; } @@ -635,9 +689,9 @@ class ObjCPrinter : private DeclVisitor, isNSUIntegerSubscript = isNSUInteger(indexParam->getType()); } - printAbstractFunction(SD->getGetter(), false, isNSUIntegerSubscript); + printAbstractFunctionAsMethod(SD->getGetter(), false, isNSUIntegerSubscript); if (auto setter = SD->getSetter()) - printAbstractFunction(setter, false, isNSUIntegerSubscript); + printAbstractFunctionAsMethod(setter, false, isNSUIntegerSubscript); } /// Visit part of a type, such as the base of a pointer type. @@ -1169,6 +1223,33 @@ class ObjCPrinter : private DeclVisitor, Optional optionalKind) { visitPart(RST->getReferentType(), optionalKind); } + + /// RAII class for printing multi-part C types, such as functions and arrays. + class PrintMultiPartType { + ObjCPrinter &Printer; + decltype(ObjCPrinter::openFunctionTypes) savedFunctionTypes; + + PrintMultiPartType(const PrintMultiPartType &) = delete; + public: + PrintMultiPartType(ObjCPrinter &Printer) + : Printer(Printer) { + savedFunctionTypes.swap(Printer.openFunctionTypes); + } + + void finish() { + auto &openFunctionTypes = Printer.openFunctionTypes; + while (!openFunctionTypes.empty()) { + const FunctionType *openFunctionTy = openFunctionTypes.pop_back_val(); + Printer.finishFunctionType(openFunctionTy); + } + openFunctionTypes = std::move(savedFunctionTypes); + savedFunctionTypes.clear(); + } + + ~PrintMultiPartType() { + finish(); + } + }; /// Print a full type, optionally declaring the given \p name. /// @@ -1180,9 +1261,7 @@ class ObjCPrinter : private DeclVisitor, Identifier name = Identifier()) { PrettyStackTraceType trace(M.getASTContext(), "printing", ty); - decltype(openFunctionTypes) savedFunctionTypes; - savedFunctionTypes.swap(openFunctionTypes); - + PrintMultiPartType multiPart(*this); visitPart(ty, optionalKind); if (!name.empty()) { os << ' ' << name; @@ -1190,12 +1269,6 @@ class ObjCPrinter : private DeclVisitor, os << '_'; } } - while (!openFunctionTypes.empty()) { - const FunctionType *openFunctionTy = openFunctionTypes.pop_back_val(); - finishFunctionType(openFunctionTy); - } - - openFunctionTypes = std::move(savedFunctionTypes); } }; @@ -1485,6 +1558,14 @@ class ModuleWriter { printer.print(CD); return true; } + + bool writeFunc(const FuncDecl *FD) { + if (addImport(FD)) + return true; + + printer.print(FD); + return true; + } bool writeProtocol(const ProtocolDecl *PD) { if (addImport(PD)) @@ -1834,6 +1915,8 @@ class ModuleWriter { success = writeProtocol(PD); else if (auto ED = dyn_cast(D)) success = writeEnum(ED); + else if (auto ED = dyn_cast(D)) + success = writeFunc(ED); else llvm_unreachable("unknown top-level ObjC value decl"); diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 23a8c3c7d2792..3239c3667dbce 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -325,7 +325,10 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { // Currying and calling convention thunks have shared linkage. if (isThunk()) - return SILLinkage::Shared; + // If a function declares a @_cdecl name, its native-to-foreign thunk is + // exported with the visibility of the function. + if (!isNativeToForeignThunk() || !d->getAttrs().hasAttribute()) + return SILLinkage::Shared; // Enum constructors are essentially the same as thunks, they are // emitted by need and have shared linkage. @@ -483,13 +486,20 @@ static std::string mangleConstant(SILDeclRef c, StringRef prefix) { return mangler.finalize(); } - // As a special case, functions can have external asm names. - // Use the asm name only for the original non-thunked, non-curried entry + // As a special case, functions can have manually mangled names. + // Use the SILGen name only for the original non-thunked, non-curried entry // point. - if (auto AsmA = c.getDecl()->getAttrs().getAttribute()) + if (auto NameA = c.getDecl()->getAttrs().getAttribute()) if (!c.isForeignToNativeThunk() && !c.isNativeToForeignThunk() && !c.isCurried) { - mangler.append(AsmA->Name); + mangler.append(NameA->Name); + return mangler.finalize(); + } + + // Use a given cdecl name for native-to-foreign thunks. + if (auto CDeclA = c.getDecl()->getAttrs().getAttribute()) + if (c.isNativeToForeignThunk()) { + mangler.append(CDeclA->Name); return mangler.finalize(); } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index f3978dd92e674..278609f1926be 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -435,6 +435,14 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { if (!Captures.empty()) TopLevelSGF->B.createMarkFunctionEscape(AFD, Captures); } + + // If the declaration is exported as a C function, emit its native-to-foreign + // thunk too, if it wasn't already forced. + if (AFD->getAttrs().hasAttribute()) { + auto thunk = SILDeclRef(AFD).asForeign(); + if (!hasFunction(thunk)) + emitNativeToForeignThunk(thunk); + } } static bool hasSILBody(FuncDecl *fd) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index f0b9778a5502e..c2716604b7ba8 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -47,6 +47,7 @@ class AttributeEarlyChecker : public AttributeVisitor { bool visitDeclAttribute(DeclAttribute *A) = delete; #define IGNORED_ATTR(X) void visit##X##Attr(X##Attr *) {} + IGNORED_ATTR(CDecl) IGNORED_ATTR(SILGenName) IGNORED_ATTR(Available) IGNORED_ATTR(Convenience) @@ -652,6 +653,8 @@ class AttributeChecker : public AttributeVisitor { #undef IGNORED_ATTR void visitAvailableAttr(AvailableAttr *attr); + + void visitCDeclAttr(CDeclAttr *attr); void visitFinalAttr(FinalAttr *attr); void visitIBActionAttr(IBActionAttr *attr); @@ -853,6 +856,18 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) { } } +void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) { + // Only top-level func decls are currently supported. + if (D->getDeclContext()->isTypeContext()) + TC.diagnose(attr->getLocation(), + diag::cdecl_not_at_top_level); + + // The name must not be empty. + if (attr->Name.empty()) + TC.diagnose(attr->getLocation(), + diag::cdecl_empty_name); +} + void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr( UnsafeNoObjCTaggedPointerAttr *attr) { // Only class protocols can have the attribute. diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 0fc9248c47eab..bc2340f57c9e5 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -4414,6 +4414,18 @@ class DeclChecker : public DeclVisitor { markAsObjC(TC, FD, isObjC, errorConvention); } + // If the function is exported to C, it must be representable in (Obj-)C. + if (auto CDeclAttr = FD->getAttrs().getAttribute()) { + Optional errorConvention; + if (TC.isRepresentableInObjC(FD, ObjCReason::ExplicitlyCDecl, + errorConvention)) { + if (FD->isBodyThrowing()) { + FD->setForeignErrorConvention(*errorConvention); + TC.diagnose(CDeclAttr->getLocation(), diag::cdecl_throws); + } + } + } + inferDynamic(TC.Context, FD); TC.checkDeclAttributes(FD); @@ -5056,6 +5068,7 @@ class DeclChecker : public DeclVisitor { UNINTERESTING_ATTR(Accessibility) UNINTERESTING_ATTR(Alignment) + UNINTERESTING_ATTR(CDecl) UNINTERESTING_ATTR(SILGenName) UNINTERESTING_ATTR(Exported) UNINTERESTING_ATTR(IBAction) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 3f9686330e7a4..54516250d0ba0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2633,9 +2633,10 @@ bool TypeChecker::isRepresentableInObjC( if (auto *FD = dyn_cast(AFD)) { if (FD->isAccessor()) { // Accessors can only be @objc if the storage declaration is. + // Global computed properties may however @_cdecl their accessors. auto storage = FD->getAccessorStorageDecl(); validateDecl(storage); - if (!storage->isObjC()) { + if (!storage->isObjC() && Reason != ObjCReason::ExplicitlyCDecl) { if (Diagnose) { auto error = FD->isGetter() ? (isa(storage) @@ -2690,7 +2691,8 @@ bool TypeChecker::isRepresentableInObjC( isSpecialInit = init->isObjCZeroParameterWithLongSelector(); if (!isSpecialInit && - !isParamListRepresentableInObjC(*this, AFD, AFD->getParameterList(1), + !isParamListRepresentableInObjC(*this, AFD, + AFD->getParameterLists().back(), Reason)) { if (!Diagnose) { // Return as soon as possible if we are not producing diagnostics. @@ -2826,7 +2828,7 @@ bool TypeChecker::isRepresentableInObjC( // If the selector did not provide an index for the error, find // the last parameter that is not a trailing closure. if (!foundErrorParameterIndex) { - auto *paramList = AFD->getParameterList(1); + auto *paramList = AFD->getParameterLists().back(); errorParameterIndex = paramList->size(); while (errorParameterIndex > 0) { // Skip over trailing closures. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 048ddd9380fe9..e01122b4de2ec 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -399,6 +399,7 @@ withoutContext(TypeResolutionOptions options) { /// the OBJC_ATTR_SELECT macro in DiagnosticsSema.def. enum class ObjCReason { DoNotDiagnose, + ExplicitlyCDecl, ExplicitlyDynamic, ExplicitlyObjC, ExplicitlyIBOutlet, diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4269e8e2a46e2..7a06cda69622e 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1884,6 +1884,14 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional ForcedContext) { break; } + case decls_block::CDecl_DECL_ATTR: { + bool isImplicit; + serialization::decls_block::CDeclDeclAttrLayout::readRecord( + scratch, isImplicit); + Attr = new (ctx) CDeclAttr(blobData, isImplicit); + break; + } + case decls_block::Alignment_DECL_ATTR: { bool isImplicit; unsigned alignment; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 94d2e16244d2f..efbba010b4efb 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -1621,7 +1621,16 @@ void Serializer::writeDeclAttribute(const DeclAttribute *DA) { theAttr->Name); return; } - + + case DAK_CDecl: { + auto *theAttr = cast(DA); + auto abbrCode = DeclTypeAbbrCodes[CDeclDeclAttrLayout::Code]; + CDeclDeclAttrLayout::emitRecord(Out, ScratchRecord, abbrCode, + theAttr->isImplicit(), + theAttr->Name); + return; + } + case DAK_Alignment: { auto *theAlignment = cast(DA); auto abbrCode = DeclTypeAbbrCodes[AlignmentDeclAttrLayout::Code]; diff --git a/test/PrintAsObjC/cdecl.swift b/test/PrintAsObjC/cdecl.swift new file mode 100644 index 0000000000000..83b313e625750 --- /dev/null +++ b/test/PrintAsObjC/cdecl.swift @@ -0,0 +1,30 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-source-import -emit-module -emit-module-doc -o %t %s -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %t/cdecl.swiftmodule -parse -emit-objc-header-path %t/cdecl.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module +// RUN: FileCheck %s < %t/cdecl.h +// RUN: %check-in-clang %t/cdecl.h +// RUN: %check-in-clang -fno-modules %t/cdecl.h -include Foundation.h -include ctypes.h -include CoreFoundation.h + +// REQUIRES: objc_interop + +// CHECK-LABEL: /// What a nightmare! +// CHECK-LABEL: double (^ _Nonnull block_nightmare(float (^ _Nonnull x)(NSInteger)))(char); + +/// What a nightmare! +@_cdecl("block_nightmare") +func block_nightmare(x: @convention(block) Int -> Float) + -> @convention(block) CChar -> Double { return { _ in 0 } } + +// CHECK-LABEL: void foo_bar(NSInteger x, NSInteger y); +@_cdecl("foo_bar") +func foo(x: Int, bar y: Int) {} + +// CHECK-LABEL: double (* _Nonnull function_pointer_nightmare(float (* _Nonnull x)(NSInteger)))(char); +@_cdecl("function_pointer_nightmare") +func function_pointer_nightmare(x: @convention(c) Int -> Float) + -> @convention(c) CChar -> Double { return { _ in 0 } } + +// CHECK-LABEL: void has_keyword_arg_names(NSInteger auto_, NSInteger union_); +@_cdecl("has_keyword_arg_names") +func keywordArgNames(auto: Int, union: Int) {} diff --git a/test/SILGen/cdecl.swift b/test/SILGen/cdecl.swift new file mode 100644 index 0000000000000..c5344bb04e315 --- /dev/null +++ b/test/SILGen/cdecl.swift @@ -0,0 +1,43 @@ +// RUN: %target-swift-frontend -emit-silgen %s | FileCheck %s + +// CHECK-LABEL: sil hidden @pear : $@convention(c) +// CHECK: function_ref @_TF5cdecl5apple +// CHECK-LABEL: sil hidden @_TF5cdecl5apple +@_cdecl("pear") +func apple(f: @convention(c) Int -> Int) { +} + +// CHECK-LABEL: sil hidden @_TF5cdecl16forceCEntryPoint +// CHECK: function_ref @grapefruit +func forceCEntryPoint() { + apple(orange) +} + +// CHECK-LABEL: sil hidden @grapefruit : $@convention(c) +// CHECK: function_ref @_TF5cdecl6orange +// CHECK-LABEL: sil hidden @_TF5cdecl6orange +@_cdecl("grapefruit") +func orange(x: Int) -> Int { + return x +} + +// CHECK-LABEL: sil @cauliflower : $@convention(c) +// CHECK: function_ref @_TF5cdecl8broccoli +// CHECK-LABEL: sil @_TF5cdecl8broccoli +@_cdecl("cauliflower") +public func broccoli(x: Int) -> Int { + return x +} + +// CHECK-LABEL: sil private @collard_greens : $@convention(c) +// CHECK: function_ref @_TF5cdeclP[[PRIVATE:.*]]4kale +// CHECK: sil private @_TF5cdeclP[[PRIVATE:.*]]4kale +@_cdecl("collard_greens") +private func kale(x: Int) -> Int { + return x +} + +/* TODO: Handle error conventions +@_cdecl("vomits") +func barfs() throws {} + */ diff --git a/test/attr/attr_cdecl.swift b/test/attr/attr_cdecl.swift new file mode 100644 index 0000000000000..f10f69679607e --- /dev/null +++ b/test/attr/attr_cdecl.swift @@ -0,0 +1,55 @@ +// RUN: %target-parse-verify-swift + +@_cdecl("cdecl_foo") func foo(x: Int) -> Int { return x } + +@_cdecl("") // expected-error{{symbol name cannot be empty}} +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}} +var property: Int + +var computed: Int { + @_cdecl("get_computed") get { return 0 } + @_cdecl("set_computed") set { return } +} + +struct SwiftStruct { var x, y: Int } +enum SwiftEnum { case A, B } +@objc enum CEnum: Int { case A, B } + +@_cdecl("swiftStruct") +func swiftStruct(x: SwiftStruct) {} // expected-error{{cannot be represented}} expected-note{{Swift struct}} + +@_cdecl("swiftEnum") +func swiftEnum(x: SwiftEnum) {} // expected-error{{cannot be represented}} expected-note{{non-'@objc' enum}} + +@_cdecl("cEnum") +func cEnum(x: CEnum) {} + +class Foo { + @_cdecl("Foo_foo") // expected-error{{can only be applied to global functions}} + func foo(x: Int) -> Int { return x } + + @_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'}} + init() {} + + @_cdecl("Foo_deinit") // expected-error{{may only be used on 'func'}} + deinit {} +} + +func hasNested() { + @_cdecl("nested") // expected-error{{can only be used in a non-local scope}} + func nested() { } +} + +// TODO: Handle error conventions in SILGen for toplevel functions. +@_cdecl("throwing") // expected-error{{raising errors from @_cdecl functions is not supported}} +func throwing() throws { } + +// TODO: cdecl name collisions