Skip to content

Commit b27fd9a

Browse files
[c-interop] Add @_extern(c) attribute to declare C function in Swift
1 parent 1132152 commit b27fd9a

20 files changed

+571
-55
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ DECL_ATTR(_rawLayout, RawLayout,
421421
OnStruct | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
422422
146)
423423
DECL_ATTR(_extern, Extern,
424-
OnFunc | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
424+
OnFunc | AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
425425
147)
426426
SIMPLE_DECL_ATTR(_nonEscapable, NonEscapable,
427427
OnNominalType | UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,

include/swift/AST/Attr.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ class DeclAttribute : public AttributeBase {
176176
kind : NumExposureKindBits
177177
);
178178

179+
SWIFT_INLINE_BITFIELD(ExternAttr, DeclAttribute, NumExternKindBits,
180+
kind : NumExternKindBits
181+
);
182+
179183
SWIFT_INLINE_BITFIELD(SynthesizedProtocolAttr, DeclAttribute, 1,
180184
isUnchecked : 1
181185
);
@@ -2339,18 +2343,33 @@ class ExposeAttr : public DeclAttribute {
23392343
/// the specified way to interoperate with Swift.
23402344
class ExternAttr : public DeclAttribute {
23412345
public:
2342-
ExternAttr(StringRef ModuleName, StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
2343-
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit),
2344-
ModuleName(ModuleName), Name(Name) {}
2346+
ExternAttr(std::optional<StringRef> ModuleName, std::optional<StringRef> Name,
2347+
SourceLoc AtLoc, SourceRange Range, ExternKind Kind, bool Implicit)
2348+
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit),
2349+
ModuleName(ModuleName), Name(Name) {
2350+
Bits.ExternAttr.kind = static_cast<unsigned>(Kind);
2351+
}
23452352

2346-
ExternAttr(StringRef ModuleName, StringRef Name, bool Implicit)
2347-
: ExternAttr(ModuleName, Name, SourceLoc(), SourceRange(), Implicit) {}
2353+
ExternAttr(std::optional<StringRef> ModuleName, std::optional<StringRef> Name,
2354+
ExternKind Kind, bool Implicit)
2355+
: ExternAttr(ModuleName, Name, SourceLoc(), SourceRange(), Kind,
2356+
Implicit) {}
23482357

23492358
/// The module name to import the named declaration in it
2350-
const StringRef ModuleName;
2359+
/// Used for Wasm import declaration.
2360+
const std::optional<StringRef> ModuleName;
23512361

23522362
/// The declaration name to import
2353-
const StringRef Name;
2363+
/// std::nullopt if the declaration name is not specified with @_extern(c)
2364+
const std::optional<StringRef> Name;
2365+
2366+
/// Returns the kind of extern.
2367+
ExternKind getExternKind() const {
2368+
return static_cast<ExternKind>(Bits.ExternAttr.kind);
2369+
}
2370+
2371+
/// Find an ExternAttr with the given kind in the given DeclAttributes.
2372+
static ExternAttr *find(DeclAttributes &attrs, ExternKind kind);
23542373

23552374
static bool classof(const DeclAttribute *DA) {
23562375
return DA->getKind() == DAK_Extern;

include/swift/AST/AttrKind.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ enum class ExposureKind: uint8_t {
112112
enum : unsigned { NumExposureKindBits =
113113
countBitsUsed(static_cast<unsigned>(ExposureKind::Last_ExposureKind)) };
114114

115+
/// This enum represents the possible values of the @_extern attribute.
116+
enum class ExternKind: uint8_t {
117+
/// Reference an externally defined C function.
118+
/// The imported function has C function pointer representation,
119+
/// and is called using the C calling convention.
120+
C,
121+
/// Reference an externally defined function through WebAssembly's
122+
/// import mechanism.
123+
/// This does not specify the calling convention and can be used
124+
/// with other extern kinds together.
125+
/// Effectively, this is no-op on non-WebAssembly targets.
126+
Wasm,
127+
Last_ExternKind = Wasm
128+
};
129+
130+
enum : unsigned { NumExternKindBits =
131+
countBitsUsed(static_cast<unsigned>(ExternKind::Last_ExternKind)) };
132+
115133
enum DeclAttrKind : unsigned {
116134
#define DECL_ATTR(_, NAME, ...) DAK_##NAME,
117135
#include "swift/AST/Attr.def"

include/swift/AST/DiagnosticsSema.def

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,6 +1874,22 @@ ERROR(section_empty_name,none,
18741874
// @_extern
18751875
ERROR(extern_not_at_top_level_func,none,
18761876
"@_extern attribute can only be applied to global functions", ())
1877+
ERROR(extern_empty_c_name,none,
1878+
"expected non-empty C name in @_extern attribute", ())
1879+
ERROR(extern_only_non_other_attr,none,
1880+
"@_extern attribute cannot be applied to an '%0' declaration", (StringRef))
1881+
ERROR(c_func_variadic, none,
1882+
"cannot declare variadic argument %0 in %kind1",
1883+
(DeclName, const ValueDecl *))
1884+
ERROR(c_func_unsupported_specifier, none,
1885+
"cannot declare '%0' argument %1 in %kind2",
1886+
(StringRef, DeclName, const ValueDecl *))
1887+
ERROR(c_func_unsupported_type, none, "%0 cannot be represented in C",
1888+
(Type))
1889+
ERROR(c_func_async,none,
1890+
"async functions cannot be represented in C", ())
1891+
ERROR(c_func_throws,none,
1892+
"raising errors from C functions is not supported", ())
18771893

18781894
ERROR(expose_wasm_not_at_top_level_func,none,
18791895
"@_expose attribute with 'wasm' can only be applied to global functions", ())

include/swift/AST/TypeCheckRequests.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,26 @@ class IsObjCRequest :
205205
void cacheResult(bool value) const;
206206
};
207207

208+
/// Determine whether the given declaration has
209+
/// a C-compatible interface.
210+
class IsCCompatibleFuncDeclRequest :
211+
public SimpleRequest<IsCCompatibleFuncDeclRequest,
212+
bool(FuncDecl *),
213+
RequestFlags::Cached> {
214+
public:
215+
using SimpleRequest::SimpleRequest;
216+
217+
private:
218+
friend SimpleRequest;
219+
220+
// Evaluation.
221+
bool evaluate(Evaluator &evaluator, FuncDecl *decl) const;
222+
223+
public:
224+
// Caching.
225+
bool isCached() const { return true; }
226+
};
227+
208228
void simple_display(llvm::raw_ostream &out, CtorInitializerKind initKind);
209229

210230
/// Computes the kind of initializer for a given \c ConstructorDecl

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,3 +520,6 @@ SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
520520
SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest,
521521
bool(const AbstractFunctionDecl *),
522522
SeparatelyCached, NoLocationInfo)
523+
SWIFT_REQUEST(TypeChecker, IsCCompatibleFuncDeclRequest,
524+
bool(const FuncDecl *),
525+
Cached, NoLocationInfo)

lib/AST/Attr.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,8 +1142,21 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11421142
case DAK_Extern: {
11431143
auto *Attr = cast<ExternAttr>(this);
11441144
Printer.printAttrName("@_extern");
1145-
// For now, it accepts only "wasm" as its kind.
1146-
Printer << "(wasm, module: \"" << Attr->ModuleName << "\", name: \"" << Attr->Name << "\")";
1145+
Printer << "(";
1146+
switch (Attr->getExternKind()) {
1147+
case ExternKind::C:
1148+
Printer << "c";
1149+
// Symbol name can be omitted for C.
1150+
if (auto cName = Attr->Name)
1151+
Printer << ", \"" << *cName << "\"";
1152+
break;
1153+
case ExternKind::Wasm:
1154+
Printer << "wasm";
1155+
// @_extern(wasm) always has names.
1156+
Printer << ", module: \"" << *Attr->ModuleName << "\"";
1157+
Printer << ", name: \"" << *Attr->Name << "\")";
1158+
break;
1159+
}
11471160
break;
11481161
}
11491162

@@ -2638,6 +2651,16 @@ bool MacroRoleAttr::hasNameKind(MacroIntroducedDeclNameKind kind) const {
26382651
}) != getNames().end();
26392652
}
26402653

2654+
ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) {
2655+
for (DeclAttribute *attr : attrs) {
2656+
if (auto *externAttr = dyn_cast<ExternAttr>(attr)) {
2657+
if (externAttr->getExternKind() == kind)
2658+
return externAttr;
2659+
}
2660+
}
2661+
return nullptr;
2662+
}
2663+
26412664
const DeclAttribute *
26422665
DeclAttributes::getEffectiveSendableAttr() const {
26432666
const NonSendableAttr *assumedAttr = nullptr;

lib/Parse/ParseDecl.cpp

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,55 +1442,75 @@ bool Parser::parseExternAttribute(DeclAttributes &Attributes,
14421442
return false;
14431443
}
14441444
auto diagnoseExpectLanguage = [&]() {
1445-
diagnose(Tok.getLoc(), diag::attr_expected_option_such_as, AttrName,
1446-
"wasm");
1445+
diagnose(Tok.getLoc(), diag::attr_expected_option_such_as, AttrName, "c");
14471446
};
14481447
if (Tok.isNot(tok::identifier)) {
14491448
diagnoseExpectLanguage();
14501449
return false;
14511450
}
14521451

1453-
if (Tok.getText() != "wasm") {
1454-
diagnoseExpectLanguage();
1455-
DiscardAttribute = true;
1456-
}
1457-
consumeToken(tok::identifier);
1458-
1459-
// Parse @_extern(wasm, module: "x", name: "y")
1460-
auto parseStringLiteralArgument = [&](StringRef fieldName, StringRef &fieldValue) {
1461-
if (!consumeIf(tok::comma) || Tok.isNot(tok::identifier) || Tok.getText() != fieldName) {
1462-
diagnose(Loc, diag::attr_extern_expected_label, fieldName);
1463-
return false;
1452+
auto parseStringLiteralArgument =
1453+
[&](std::optional<StringRef> fieldName) -> std::optional<StringRef> {
1454+
if (!consumeIf(tok::comma)) {
1455+
diagnose(Loc, diag::attr_expected_comma, AttrName,
1456+
DeclAttribute::isDeclModifier(DAK_Extern));
1457+
return std::nullopt;
14641458
}
1465-
consumeToken(tok::identifier);
1459+
if (fieldName) {
1460+
if (Tok.isNot(tok::identifier) || Tok.getText() != fieldName) {
1461+
diagnose(Loc, diag::attr_extern_expected_label, *fieldName);
1462+
return std::nullopt;
1463+
}
1464+
consumeToken(tok::identifier);
14661465

1467-
if (!consumeIf(tok::colon)) {
1468-
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, fieldName);
1469-
return false;
1466+
if (!consumeIf(tok::colon)) {
1467+
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, *fieldName);
1468+
return std::nullopt;
1469+
}
14701470
}
14711471

14721472
if (Tok.isNot(tok::string_literal)) {
14731473
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
1474-
return false;
1474+
return std::nullopt;
14751475
}
14761476
llvm::Optional<StringRef> importModuleName =
14771477
getStringLiteralIfNotInterpolated(Loc, ("'" + AttrName + "'").str());
14781478
consumeToken(tok::string_literal);
14791479

14801480
if (!importModuleName.has_value()) {
14811481
DiscardAttribute = true;
1482-
return false;
1482+
return std::nullopt;
14831483
}
1484-
fieldValue = importModuleName.value();
1485-
return true;
1484+
return importModuleName.value();
14861485
};
14871486

1488-
StringRef importModuleName, importName;
1489-
if (!parseStringLiteralArgument("module", importModuleName))
1490-
DiscardAttribute = true;
1487+
auto kindTok = Tok;
1488+
consumeToken(tok::identifier);
1489+
1490+
// Parse @_extern(wasm, module: "x", name: "y") or @_extern(c[, "x"])
1491+
ExternKind kind;
1492+
std::optional<StringRef> importModuleName, importName;
14911493

1492-
if (!parseStringLiteralArgument("name", importName))
1494+
if (kindTok.getText() == "wasm") {
1495+
kind = ExternKind::Wasm;
1496+
if (!(importModuleName = parseStringLiteralArgument("module")))
1497+
DiscardAttribute = true;
1498+
if (!(importName = parseStringLiteralArgument("name")))
1499+
DiscardAttribute = true;
1500+
} else if (kindTok.getText() == "c") {
1501+
kind = ExternKind::C;
1502+
if (Tok.is(tok::comma)) {
1503+
if (!(importName = parseStringLiteralArgument(std::nullopt))) {
1504+
DiscardAttribute = true;
1505+
}
1506+
}
1507+
} else {
1508+
diagnoseExpectLanguage();
14931509
DiscardAttribute = true;
1510+
while (Tok.isNot(tok::r_paren)) {
1511+
consumeToken();
1512+
}
1513+
}
14941514

14951515
if (!consumeIf(tok::r_paren)) {
14961516
diagnose(Loc, diag::attr_expected_rparen, AttrName,
@@ -1500,9 +1520,15 @@ bool Parser::parseExternAttribute(DeclAttributes &Attributes,
15001520

15011521
auto AttrRange = SourceRange(Loc, Tok.getLoc());
15021522

1523+
// Reject duplicate attributes with the same kind.
1524+
if (ExternAttr::find(Attributes, kind)) {
1525+
diagnose(Loc, diag::duplicate_attribute, false);
1526+
DiscardAttribute = true;
1527+
}
1528+
15031529
if (!DiscardAttribute) {
15041530
Attributes.add(new (Context) ExternAttr(importModuleName, importName, AtLoc,
1505-
AttrRange,
1531+
AttrRange, kind,
15061532
/*Implicit=*/false));
15071533
}
15081534
return false;

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,9 @@ bool SILDeclRef::isNativeToForeignThunk() const {
10041004
// onto.
10051005
if (getDecl()->hasClangNode())
10061006
return false;
1007+
// No thunk is required if the decl directly references an external decl.
1008+
if (getDecl()->getAttrs().hasAttribute<ExternAttr>())
1009+
return false;
10071010

10081011
// Only certain kinds of SILDeclRef can expose native-to-foreign thunks.
10091012
return kind == Kind::Func || kind == Kind::Initializer ||
@@ -1144,7 +1147,12 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
11441147
if (!NameA->Name.empty() && !isThunk()) {
11451148
return NameA->Name.str();
11461149
}
1147-
1150+
1151+
if (auto *ExternA = ExternAttr::find(getDecl()->getAttrs(), ExternKind::C)) {
1152+
if (auto cName = ExternA->Name)
1153+
return cName->str();
1154+
}
1155+
11481156
// Use a given cdecl name for native-to-foreign thunks.
11491157
if (auto CDeclA = getDecl()->getAttrs().getAttribute<CDeclAttr>())
11501158
if (isNativeToForeignThunk()) {

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,9 @@ void SILFunctionBuilder::addFunctionAttributes(
180180
}
181181
}
182182

183-
if (auto *EA = Attrs.getAttribute<ExternAttr>()) {
184-
F->setWasmImportModuleAndField(EA->ModuleName, EA->Name);
183+
if (auto *EA = ExternAttr::find(Attrs, ExternKind::Wasm)) {
184+
// @_extern(wasm) always has explicit names
185+
F->setWasmImportModuleAndField(*EA->ModuleName, *EA->Name);
185186
}
186187

187188
if (Attrs.hasAttribute<UsedAttr>())

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4081,6 +4081,8 @@ TypeConverter::getDeclRefRepresentation(SILDeclRef c) {
40814081
case SILDeclRef::Kind::Func:
40824082
if (c.getDecl()->getDeclContext()->isTypeContext())
40834083
return SILFunctionTypeRepresentation::Method;
4084+
if (ExternAttr::find(c.getDecl()->getAttrs(), ExternKind::C))
4085+
return SILFunctionTypeRepresentation::CFunctionPointer;
40844086
return SILFunctionTypeRepresentation::Thin;
40854087

40864088
case SILDeclRef::Kind::Destroyer:

0 commit comments

Comments
 (0)