diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index c177927cdff08..8fb837ee5d6b8 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -3107,7 +3107,7 @@ class ExternAttr : public DeclAttribute { StringRef getCName(const FuncDecl *forDecl) const; /// Find an ExternAttr with the given kind in the given DeclAttributes. - static ExternAttr *find(DeclAttributes &attrs, ExternKind kind); + static const ExternAttr *find(const DeclAttributes &attrs, ExternKind kind); static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::Extern; diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2f1a80cf1b1f0..026dce1948458 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1070,6 +1070,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi std::optional getExplicitObjCName(bool allowInvalid = false) const; + /// Whether this declaration is exposed to C and does not expose a Swift + /// counterpart. + bool hasOnlyCEntryPoint() const; + /// True if this declaration provides an implementation for an imported /// Objective-C declaration. This implies various restrictions and special /// behaviors for it and, if it's an extension, its members. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 3a86d411294af..bf764339ff8d0 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -3270,8 +3270,8 @@ StringRef ExternAttr::getCName(const FuncDecl *D) const { return D->getBaseIdentifier().str(); } -ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) { - for (DeclAttribute *attr : attrs) { +const ExternAttr *ExternAttr::find(const DeclAttributes &attrs, ExternKind kind) { + for (const DeclAttribute *attr : attrs) { if (auto *externAttr = dyn_cast(attr)) { if (externAttr->getExternKind() == kind) return externAttr; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 819be4e914035..18712bf1b96b6 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2256,6 +2256,20 @@ ExtensionDecl::isAddingConformanceToInvertible() const { return std::nullopt; } +bool Decl::hasOnlyCEntryPoint() const { + if (ExternAttr::find(getAttrs(), ExternKind::C)) + return true; + + if (auto cdeclAttr = getAttrs().getAttribute()) { + // The @c syntax only provides C entrypoints, not Swift ones. The historical + // @_cdecl introduces both Swift and C entrypoints. + if (!cdeclAttr->Underscored) + return true; + } + + return false; +} + bool Decl::isObjCImplementation() const { return getAttrs().hasAttribute(/*AllowInvalid=*/true); } diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 2fb6b27fdb023..096260f622623 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -89,6 +89,11 @@ bool swift::requiresForeignToNativeThunk(ValueDecl *vd) { if (proto->isObjC()) return true; + // If there is only a C entrypoint from a Swift function, we will need + // foreign-to-native thunks to deal with them. + if (vd->hasOnlyCEntryPoint()) + return true; + if (auto fd = dyn_cast(vd)) return fd->hasClangNode(); @@ -111,7 +116,7 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) { if (vd->hasClangNode()) return true; - if (ExternAttr::find(vd->getAttrs(), ExternKind::C)) + if (vd->hasOnlyCEntryPoint()) return true; if (auto *accessor = dyn_cast(vd)) { @@ -131,7 +136,9 @@ SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, bool isRuntimeAccessible, SILDeclRef::BackDeploymentKind backDeploymentKind, AutoDiffDerivativeFunctionIdentifier *derivativeId) - : loc(vd), kind(kind), isForeign(isForeign), distributedThunk(isDistributedThunk), + : loc(vd), kind(kind), + isForeign(isForeign), + distributedThunk(isDistributedThunk), isKnownToBeLocal(isKnownToBeLocal), isRuntimeAccessible(isRuntimeAccessible), backDeploymentKind(backDeploymentKind), defaultArgIndex(0), @@ -470,7 +477,8 @@ static LinkageLimit getLinkageLimit(SILDeclRef constant) { // Native-to-foreign thunks for methods are always just private, since // they're anchored by Objective-C metadata. auto &attrs = fn->getAttrs(); - if (constant.isNativeToForeignThunk() && !attrs.hasAttribute()) { + if (constant.isNativeToForeignThunk() && + !(attrs.hasAttribute() && !fn->hasOnlyCEntryPoint())) { auto isTopLevel = fn->getDeclContext()->isModuleScopeContext(); return isTopLevel ? Limit::OnDemand : Limit::Private; } @@ -1268,6 +1276,10 @@ bool SILDeclRef::isNativeToForeignThunk() const { if (getDecl()->getAttrs().hasAttribute()) return false; + // No thunk is required if the decl directly exposes a C entry point. + if (getDecl()->hasOnlyCEntryPoint()) + return false; + // Only certain kinds of SILDeclRef can expose native-to-foreign thunks. return kind == Kind::Func || kind == Kind::Initializer || kind == Kind::Deallocator; @@ -1433,7 +1445,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { // Use a given cdecl name for native-to-foreign thunks. if (getDecl()->getAttrs().hasAttribute()) - if (isNativeToForeignThunk()) { + if (isNativeToForeignThunk() || isForeign) { // If this is an @implementation @_cdecl, mangle it like the clang // function it implements. if (auto objcInterface = getDecl()->getImplementedObjCDecl()) { diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index f945630fd9c49..6de71146d80db 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -4899,8 +4899,18 @@ getAbstractionPatternForConstant(TypeConverter &converter, ASTContext &ctx, return AbstractionPattern(fnType); const clang::Decl *clangDecl = bridgedFn->getClangDecl(); - if (!clangDecl) + if (!clangDecl) { + // If this function only has a C entrypoint, create a Clang type to + // use when referencing it. + if (bridgedFn->hasOnlyCEntryPoint()) { + auto clangType = ctx.getClangFunctionType( + fnType->getParams(), fnType->getResult(), + FunctionTypeRepresentation::CFunctionPointer); + return AbstractionPattern(fnType, clangType); + } + return AbstractionPattern(fnType); + } // Don't implicitly turn non-optional results to optional if // we're going to apply a foreign error convention that checks diff --git a/lib/SIL/IR/SILSymbolVisitor.cpp b/lib/SIL/IR/SILSymbolVisitor.cpp index 550290d51e589..7bfead36de128 100644 --- a/lib/SIL/IR/SILSymbolVisitor.cpp +++ b/lib/SIL/IR/SILSymbolVisitor.cpp @@ -489,7 +489,7 @@ class SILSymbolVisitorImpl : public ASTVisitor { } } - addFunction(SILDeclRef(AFD)); + addFunction(SILDeclRef(AFD).asForeign(AFD->hasOnlyCEntryPoint())); ASSERT(ABIRoleInfo(AFD).providesAPI() && "SILSymbolVisitorImpl visiting ABI-only decl?"); @@ -499,10 +499,12 @@ class SILSymbolVisitorImpl : public ASTVisitor { Visitor.addDynamicFunction(AFD, *dynKind); } - if (AFD->getAttrs().hasAttribute()) { - // A @_cdecl("...") function has an extra symbol, with the name from the - // attribute. - addFunction(SILDeclRef(AFD).asForeign()); + if (auto cdeclAttr = AFD->getAttrs().getAttribute()) { + if (cdeclAttr->Underscored) { + // A @_cdecl("...") function has an extra symbol, with the name from the + // attribute. + addFunction(SILDeclRef(AFD).asForeign()); + } } if (auto distributedThunk = AFD->getDistributedThunk()) { diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index a9aa7c7c5ecd1..6c9d3d38c5848 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1559,10 +1559,12 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { // 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); + if (auto cdeclAttr = AFD->getAttrs().getAttribute()) { + if (cdeclAttr->Underscored) { + auto thunk = SILDeclRef(AFD).asForeign(); + if (!hasFunction(thunk)) + emitNativeToForeignThunk(thunk); + } } emitDistributedThunkForDecl(AFD); @@ -1591,7 +1593,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) { if (shouldEmitFunctionBody(fd)) { Types.setCaptureTypeExpansionContext(SILDeclRef(fd), M); - emitOrDelayFunction(SILDeclRef(decl)); + emitOrDelayFunction(SILDeclRef(decl, fd->hasOnlyCEntryPoint())); } } diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index b5a287f5f497b..e7d07dd2cffeb 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -996,9 +996,6 @@ emitRValueForDecl(SILLocation loc, ConcreteDeclRef declRef, Type ncRefType, // If the referenced decl isn't a VarDecl, it should be a constant of some // sort. SILDeclRef silDeclRef(decl); - if (ExternAttr::find(decl->getAttrs(), ExternKind::C)) - silDeclRef = silDeclRef.asForeign(); - assert(silDeclRef.getParameterListCount() == 1); auto substType = cast(refType); auto typeContext = getFunctionTypeInfo(substType); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index aa3482315b2a4..b4de9d0e0c106 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2565,11 +2565,6 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const { infoBuilder = infoBuilder.withSendingResult(); } - if (ExternAttr::find(D->getAttrs(), ExternKind::C)) { - infoBuilder = infoBuilder.withRepresentation( - FunctionTypeRepresentation::CFunctionPointer); - } - // Lifetime dependencies only apply to the outer function type. if (!hasSelf && lifetimeDependenceInfo.has_value()) { infoBuilder = diff --git a/test/IRGen/cdecl_implementation.swift b/test/IRGen/cdecl_implementation.swift index 58c553dfa885d..bdf6e1c9c50aa 100644 --- a/test/IRGen/cdecl_implementation.swift +++ b/test/IRGen/cdecl_implementation.swift @@ -28,14 +28,12 @@ public func fn() { /// implFunc(_:) // CHECK-LABEL: define{{.*}} void @implFunc -// FIXME: We'd like this to be internal or hidden, not public. -// CHECK: define{{.*}} swiftcc void @"$s20cdecl_implementation8implFuncyys5Int32VF" +// CHECK-NOT: define{{.*}} swiftcc void @"$s20cdecl_implementation8implFuncyys5Int32VF" /// inplFuncCName(_:) // CHECK-LABEL: define{{.*}} void @"\01_implFuncAsmName" -// FIXME: We'd like this to be internal or hidden, not public. -// CHECK: define{{.*}} swiftcc void @"$s20cdecl_implementation13implFuncCNameyys5Int32VF" +// CHECK-NOT: define{{.*}} swiftcc void @"$s20cdecl_implementation13implFuncCNameyys5Int32VF" /// fn() // CHECK-LABEL: define{{.*}} swiftcc void @"$s20cdecl_implementation2fnyyF" diff --git a/test/SILGen/cdecl-official.swift b/test/SILGen/cdecl-official.swift index 5cea5e93208a7..b9b2943205056 100644 --- a/test/SILGen/cdecl-official.swift +++ b/test/SILGen/cdecl-official.swift @@ -6,49 +6,55 @@ // 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 +// CHECK-LABEL: sil hidden [ossa] @pear : $@convention(c) (@convention(c) (Int) -> Int) -> () { @c(pear) func apple(_ f: @convention(c) (Int) -> Int) { } +func acceptSwiftFunc(_ f: (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? +// CHECK: [[GRAPEFRUIT:%[0-9]+]] = function_ref @grapefruit : $@convention(c) (Int) -> Int +// CHECK: [[PEAR:%[0-9]+]] = function_ref @pear : $@convention(c) (@convention(c) (Int) -> Int) -> () +// CHECK: apply [[PEAR]]([[GRAPEFRUIT]]) 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 +// CHECK-LABEL: sil hidden [ossa] @grapefruit : $@convention(c) (Int) -> Int { @c(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`? +// CHECK-LABEL: sil hidden [ossa] @$s5cdecl13requiresThunkyyF : $@convention(thin) () -> () { +// CHECK: [[ORANGE:%[0-9]+]] = function_ref @$s5cdecl6orangeyS2iFTO : $@convention(thin) (Int) -> Int +// CHECK: [[THICK_ORANGE:%[0-9]+]] = thin_to_thick_function [[ORANGE]] to $@callee_guaranteed (Int) -> Int +// CHECK: [[NOESCAPE_ORANGE:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[THICK_ORANGE]] to $@noescape @callee_guaranteed (Int) -> Int +// CHECK: [[ACCEPT:%[0-9]+]] = function_ref @$s5cdecl15acceptSwiftFuncyyS2iXEF : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> () +// CHECK: apply [[ACCEPT]]([[NOESCAPE_ORANGE]]) +func requiresThunk() { + acceptSwiftFunc(orange) +} + +// CHECK-LABEL: sil [serialized] [ossa] @cauliflower : $@convention(c) (Int) -> Int { +// CHECK-NOT: apply +// CHECK: return @c(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:.*]] +// CHECK-LABEL: sil private [ossa] @collard_greens : $@convention(c) (Int) -> Int { +// CHECK-NOT: apply +// CHECK: return @c(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:.*]] +// CHECK-LABEL: sil private [ossa] @defaultName : $@convention(c) (Int) -> Int { +// CHECK-NOT: apply +// CHECK: return @c private func defaultName(_ x: Int) -> Int { return x diff --git a/test/SILGen/extern_c.swift b/test/SILGen/extern_c.swift index 054d063492014..f863f8da5b6f4 100644 --- a/test/SILGen/extern_c.swift +++ b/test/SILGen/extern_c.swift @@ -51,12 +51,11 @@ func main() { // CHECK-DAG: apply [[F7]]([[DEFAULT_V]]) : $@convention(c) (Int) -> () defaultArg() - // CHECK-DAG: [[CREF:%[0-9]+]] = function_ref @$s8extern_c16publicVisibilityyS2iFTo : $@convention(c) (Int) -> Int - // CHECK-DAG: [[THUNK:%[0-9]+]] = function_ref @$sS2iIetCyd_S2iIegyd_TR : $@convention(thin) (Int, @convention(c) (Int) -> Int) -> Int - // CHECK-DAG: [[APPLIED:%[0-9]+]] = partial_apply [callee_guaranteed] [[THUNK]]([[CREF]]) : $@convention(thin) (Int, @convention(c) (Int) -> Int) -> Int - // CHECK-DAG: [[NOESCAPE_APPLIED:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[APPLIED]] to $@noescape @callee_guaranteed (Int) -> Int + // CHECK-DAG: [[CREF:%[0-9]+]] = function_ref @$s8extern_c16publicVisibilityyS2iFTO : $@convention(thin) (Int) -> Int + // CHECK-DAG: [[THICK_CREF:%[0-9]+]] = thin_to_thick_function [[CREF]] to $@callee_guaranteed (Int) -> Int + // CHECK-DAG: [[NOESCAPE_CREF:%[0-9]+]] = convert_escape_to_noescape [not_guaranteed] [[THICK_CREF]] to $@noescape @callee_guaranteed (Int) -> Int // CHECK-DAG: [[CALL_ME:%[0-9]+]] = function_ref @$s8extern_c6callMe4bodyS3iXE_tF - // CHECK-DAG: apply [[CALL_ME]]([[NOESCAPE_APPLIED]]) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> Int + // CHECK-DAG: apply [[CALL_ME]]([[NOESCAPE_CREF]]) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed (Int) -> Int) -> Int _ = callMe(body: publicVisibility) }