Skip to content

Commit 4be74c7

Browse files
committed
[SE-0495, SILGen] Only emit C-compatible entrypoints for @c definitions.
When defining a C-compatible function with `@c`, we were emitting a Swift function along with a C-compatible thunk. Stop doing that, and instead only produce the C-compatible function. All uses of the function will go through that C interface, just like if the function were declared in C. This also applies to `@c @implementation` functions, which are declared in C but implemented in Swift. It does *not* apply to `@_cdecl`, which will continue to produce both the Swift function and C thunk to prevent an ABI break. Fixes rdar://158888024.
1 parent 79fc0f1 commit 4be74c7

File tree

13 files changed

+94
-55
lines changed

13 files changed

+94
-55
lines changed

include/swift/AST/Attr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3107,7 +3107,7 @@ class ExternAttr : public DeclAttribute {
31073107
StringRef getCName(const FuncDecl *forDecl) const;
31083108

31093109
/// Find an ExternAttr with the given kind in the given DeclAttributes.
3110-
static ExternAttr *find(DeclAttributes &attrs, ExternKind kind);
3110+
static const ExternAttr *find(const DeclAttributes &attrs, ExternKind kind);
31113111

31123112
static bool classof(const DeclAttribute *DA) {
31133113
return DA->getKind() == DeclAttrKind::Extern;

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
10701070
std::optional<ObjCSelector>
10711071
getExplicitObjCName(bool allowInvalid = false) const;
10721072

1073+
/// Whether this declaration is exposed to C and does not expose a Swift
1074+
/// counterpart.
1075+
bool hasOnlyCEntryPoint() const;
1076+
10731077
/// True if this declaration provides an implementation for an imported
10741078
/// Objective-C declaration. This implies various restrictions and special
10751079
/// behaviors for it and, if it's an extension, its members.

lib/AST/Attr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,8 +3270,8 @@ StringRef ExternAttr::getCName(const FuncDecl *D) const {
32703270
return D->getBaseIdentifier().str();
32713271
}
32723272

3273-
ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) {
3274-
for (DeclAttribute *attr : attrs) {
3273+
const ExternAttr *ExternAttr::find(const DeclAttributes &attrs, ExternKind kind) {
3274+
for (const DeclAttribute *attr : attrs) {
32753275
if (auto *externAttr = dyn_cast<ExternAttr>(attr)) {
32763276
if (externAttr->getExternKind() == kind)
32773277
return externAttr;

lib/AST/Decl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,6 +2256,20 @@ ExtensionDecl::isAddingConformanceToInvertible() const {
22562256
return std::nullopt;
22572257
}
22582258

2259+
bool Decl::hasOnlyCEntryPoint() const {
2260+
if (ExternAttr::find(getAttrs(), ExternKind::C))
2261+
return true;
2262+
2263+
if (auto cdeclAttr = getAttrs().getAttribute<CDeclAttr>()) {
2264+
// The @c syntax only provides C entrypoints, not Swift ones. The historical
2265+
// @_cdecl introduces both Swift and C entrypoints.
2266+
if (!cdeclAttr->Underscored)
2267+
return true;
2268+
}
2269+
2270+
return false;
2271+
}
2272+
22592273
bool Decl::isObjCImplementation() const {
22602274
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
22612275
}

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ bool swift::requiresForeignToNativeThunk(ValueDecl *vd) {
8989
if (proto->isObjC())
9090
return true;
9191

92+
// If there is only a C entrypoint from a Swift function, we will need
93+
// foreign-to-native thunks to deal with them.
94+
if (vd->hasOnlyCEntryPoint())
95+
return true;
96+
9297
if (auto fd = dyn_cast<FuncDecl>(vd))
9398
return fd->hasClangNode();
9499

@@ -111,7 +116,7 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) {
111116
if (vd->hasClangNode())
112117
return true;
113118

114-
if (ExternAttr::find(vd->getAttrs(), ExternKind::C))
119+
if (vd->hasOnlyCEntryPoint())
115120
return true;
116121

117122
if (auto *accessor = dyn_cast<AccessorDecl>(vd)) {
@@ -131,7 +136,9 @@ SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign,
131136
bool isRuntimeAccessible,
132137
SILDeclRef::BackDeploymentKind backDeploymentKind,
133138
AutoDiffDerivativeFunctionIdentifier *derivativeId)
134-
: loc(vd), kind(kind), isForeign(isForeign), distributedThunk(isDistributedThunk),
139+
: loc(vd), kind(kind),
140+
isForeign(isForeign),
141+
distributedThunk(isDistributedThunk),
135142
isKnownToBeLocal(isKnownToBeLocal),
136143
isRuntimeAccessible(isRuntimeAccessible),
137144
backDeploymentKind(backDeploymentKind), defaultArgIndex(0),
@@ -470,7 +477,8 @@ static LinkageLimit getLinkageLimit(SILDeclRef constant) {
470477
// Native-to-foreign thunks for methods are always just private, since
471478
// they're anchored by Objective-C metadata.
472479
auto &attrs = fn->getAttrs();
473-
if (constant.isNativeToForeignThunk() && !attrs.hasAttribute<CDeclAttr>()) {
480+
if (constant.isNativeToForeignThunk() &&
481+
!(attrs.hasAttribute<CDeclAttr>() && !fn->hasOnlyCEntryPoint())) {
474482
auto isTopLevel = fn->getDeclContext()->isModuleScopeContext();
475483
return isTopLevel ? Limit::OnDemand : Limit::Private;
476484
}
@@ -1268,6 +1276,10 @@ bool SILDeclRef::isNativeToForeignThunk() const {
12681276
if (getDecl()->getAttrs().hasAttribute<ExternAttr>())
12691277
return false;
12701278

1279+
// No thunk is required if the decl directly exposes a C entry point.
1280+
if (getDecl()->hasOnlyCEntryPoint())
1281+
return false;
1282+
12711283
// Only certain kinds of SILDeclRef can expose native-to-foreign thunks.
12721284
return kind == Kind::Func || kind == Kind::Initializer ||
12731285
kind == Kind::Deallocator;
@@ -1433,7 +1445,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
14331445

14341446
// Use a given cdecl name for native-to-foreign thunks.
14351447
if (getDecl()->getAttrs().hasAttribute<CDeclAttr>())
1436-
if (isNativeToForeignThunk()) {
1448+
if (isNativeToForeignThunk() || isForeign) {
14371449
// If this is an @implementation @_cdecl, mangle it like the clang
14381450
// function it implements.
14391451
if (auto objcInterface = getDecl()->getImplementedObjCDecl()) {

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4899,8 +4899,18 @@ getAbstractionPatternForConstant(TypeConverter &converter, ASTContext &ctx,
48994899
return AbstractionPattern(fnType);
49004900

49014901
const clang::Decl *clangDecl = bridgedFn->getClangDecl();
4902-
if (!clangDecl)
4902+
if (!clangDecl) {
4903+
// If this function only has a C entrypoint, create a Clang type to
4904+
// use when referencing it.
4905+
if (bridgedFn->hasOnlyCEntryPoint()) {
4906+
auto clangType = ctx.getClangFunctionType(
4907+
fnType->getParams(), fnType->getResult(),
4908+
FunctionTypeRepresentation::CFunctionPointer);
4909+
return AbstractionPattern(fnType, clangType);
4910+
}
4911+
49034912
return AbstractionPattern(fnType);
4913+
}
49044914

49054915
// Don't implicitly turn non-optional results to optional if
49064916
// we're going to apply a foreign error convention that checks

lib/SIL/IR/SILSymbolVisitor.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
489489
}
490490
}
491491

492-
addFunction(SILDeclRef(AFD));
492+
addFunction(SILDeclRef(AFD).asForeign(AFD->hasOnlyCEntryPoint()));
493493

494494
ASSERT(ABIRoleInfo(AFD).providesAPI()
495495
&& "SILSymbolVisitorImpl visiting ABI-only decl?");
@@ -499,10 +499,12 @@ class SILSymbolVisitorImpl : public ASTVisitor<SILSymbolVisitorImpl> {
499499
Visitor.addDynamicFunction(AFD, *dynKind);
500500
}
501501

502-
if (AFD->getAttrs().hasAttribute<CDeclAttr>()) {
503-
// A @_cdecl("...") function has an extra symbol, with the name from the
504-
// attribute.
505-
addFunction(SILDeclRef(AFD).asForeign());
502+
if (auto cdeclAttr = AFD->getAttrs().getAttribute<CDeclAttr>()) {
503+
if (cdeclAttr->Underscored) {
504+
// A @_cdecl("...") function has an extra symbol, with the name from the
505+
// attribute.
506+
addFunction(SILDeclRef(AFD).asForeign());
507+
}
506508
}
507509

508510
if (auto distributedThunk = AFD->getDistributedThunk()) {

lib/SILGen/SILGen.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,10 +1559,12 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
15591559

15601560
// If the declaration is exported as a C function, emit its native-to-foreign
15611561
// thunk too, if it wasn't already forced.
1562-
if (AFD->getAttrs().hasAttribute<CDeclAttr>()) {
1563-
auto thunk = SILDeclRef(AFD).asForeign();
1564-
if (!hasFunction(thunk))
1565-
emitNativeToForeignThunk(thunk);
1562+
if (auto cdeclAttr = AFD->getAttrs().getAttribute<CDeclAttr>()) {
1563+
if (cdeclAttr->Underscored) {
1564+
auto thunk = SILDeclRef(AFD).asForeign();
1565+
if (!hasFunction(thunk))
1566+
emitNativeToForeignThunk(thunk);
1567+
}
15661568
}
15671569

15681570
emitDistributedThunkForDecl(AFD);
@@ -1591,7 +1593,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) {
15911593

15921594
if (shouldEmitFunctionBody(fd)) {
15931595
Types.setCaptureTypeExpansionContext(SILDeclRef(fd), M);
1594-
emitOrDelayFunction(SILDeclRef(decl));
1596+
emitOrDelayFunction(SILDeclRef(decl, fd->hasOnlyCEntryPoint()));
15951597
}
15961598
}
15971599

lib/SILGen/SILGenExpr.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -996,9 +996,6 @@ emitRValueForDecl(SILLocation loc, ConcreteDeclRef declRef, Type ncRefType,
996996
// If the referenced decl isn't a VarDecl, it should be a constant of some
997997
// sort.
998998
SILDeclRef silDeclRef(decl);
999-
if (ExternAttr::find(decl->getAttrs(), ExternKind::C))
1000-
silDeclRef = silDeclRef.asForeign();
1001-
1002999
assert(silDeclRef.getParameterListCount() == 1);
10031000
auto substType = cast<AnyFunctionType>(refType);
10041001
auto typeContext = getFunctionTypeInfo(substType);

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,11 +2565,6 @@ InterfaceTypeRequest::evaluate(Evaluator &eval, ValueDecl *D) const {
25652565
infoBuilder = infoBuilder.withSendingResult();
25662566
}
25672567

2568-
if (ExternAttr::find(D->getAttrs(), ExternKind::C)) {
2569-
infoBuilder = infoBuilder.withRepresentation(
2570-
FunctionTypeRepresentation::CFunctionPointer);
2571-
}
2572-
25732568
// Lifetime dependencies only apply to the outer function type.
25742569
if (!hasSelf && lifetimeDependenceInfo.has_value()) {
25752570
infoBuilder =

0 commit comments

Comments
 (0)