Skip to content

Commit 4f0e3bf

Browse files
Merge pull request #69207 from kateinoigakukun/yt/cdecl-without-body
Introduce `@extern(c)` to declare C function without Clang module
2 parents 7b5db49 + 6947993 commit 4f0e3bf

32 files changed

+734
-87
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -437,18 +437,34 @@ the export name.
437437

438438
It's the equivalent of clang's `__attribute__((export_name))`.
439439

440-
## `@_extern(<language>)`
440+
## `@extern(<language>)`
441441

442442
Indicates that a particular declaration should be imported
443443
from the external environment.
444444

445-
### `@_extern(wasm, module: <"moduleName">, name: <"fieldName">)`
445+
### `@extern(wasm, module: <"moduleName">, name: <"fieldName">)`
446446

447447
Indicates that a particular declaration should be imported
448448
through WebAssembly's import interface.
449449

450450
It's the equivalent of clang's `__attribute__((import_module("module"), import_name("field")))`.
451451

452+
### `@extern(c, [, <"cName">])`
453+
454+
Indicates that a particular declaration should refer to a
455+
C declaration with the given name. If the optional "cName"
456+
string is not specified, the Swift function name is used
457+
without Swift name mangling. Platform-specific mangling
458+
rules (leading underscore on Darwin) are still applied.
459+
460+
Similar to `@_cdecl`, but this attribute is used to reference
461+
C declarations from Swift, while `@_cdecl` is used to define
462+
Swift functions that can be referenced from C.
463+
464+
Also similar to `@_silgen_name`, but a function declared with
465+
`@extern(c)` is assumed to use the C ABI, while `@_silgen_name`
466+
assumes the Swift ABI.
467+
452468
## `@_fixed_layout`
453469

454470
Same as `@frozen` but also works for classes.

include/swift/AST/Attr.def

+2-2
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ DECL_ATTR(_section, Section,
421421
DECL_ATTR(_rawLayout, RawLayout,
422422
OnStruct | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
423423
146)
424-
DECL_ATTR(_extern, Extern,
425-
OnFunc | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
424+
DECL_ATTR(extern, Extern,
425+
OnFunc | AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
426426
147)
427427
SIMPLE_DECL_ATTR(_nonEscapable, NonEscapable,
428428
OnNominalType | UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,

include/swift/AST/Attr.h

+38-8
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
);
@@ -2335,22 +2339,48 @@ class ExposeAttr : public DeclAttribute {
23352339
}
23362340
};
23372341

2338-
/// Define the `@_extern` attribute, used to import external declarations in
2342+
/// Define the `@extern` attribute, used to import external declarations in
23392343
/// the specified way to interoperate with Swift.
23402344
class ExternAttr : public DeclAttribute {
2345+
SourceLoc LParenLoc, RParenLoc;
2346+
23412347
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) {}
2348+
ExternAttr(llvm::Optional<StringRef> ModuleName,
2349+
llvm::Optional<StringRef> Name, SourceLoc AtLoc,
2350+
SourceLoc LParenLoc, SourceLoc RParenLoc, SourceRange Range,
2351+
ExternKind Kind, bool Implicit)
2352+
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit), LParenLoc(LParenLoc),
2353+
RParenLoc(RParenLoc), ModuleName(ModuleName), Name(Name) {
2354+
Bits.ExternAttr.kind = static_cast<unsigned>(Kind);
2355+
}
23452356

2346-
ExternAttr(StringRef ModuleName, StringRef Name, bool Implicit)
2347-
: ExternAttr(ModuleName, Name, SourceLoc(), SourceRange(), Implicit) {}
2357+
ExternAttr(llvm::Optional<StringRef> ModuleName,
2358+
llvm::Optional<StringRef> Name, ExternKind Kind, bool Implicit)
2359+
: ExternAttr(ModuleName, Name, SourceLoc(), SourceLoc(), SourceLoc(),
2360+
SourceRange(), Kind, Implicit) {}
23482361

23492362
/// The module name to import the named declaration in it
2350-
const StringRef ModuleName;
2363+
/// Used for Wasm import declaration.
2364+
const llvm::Optional<StringRef> ModuleName;
23512365

23522366
/// The declaration name to import
2353-
const StringRef Name;
2367+
/// std::nullopt if the declaration name is not specified with @extern(c)
2368+
const llvm::Optional<StringRef> Name;
2369+
2370+
SourceLoc getLParenLoc() const { return LParenLoc; }
2371+
SourceLoc getRParenLoc() const { return RParenLoc; }
2372+
2373+
/// Returns the kind of extern.
2374+
ExternKind getExternKind() const {
2375+
return static_cast<ExternKind>(Bits.ExternAttr.kind);
2376+
}
2377+
2378+
/// Returns the C name of the given declaration.
2379+
/// \p forDecl is the func decl that the attribute belongs to.
2380+
StringRef getCName(const FuncDecl *forDecl) const;
2381+
2382+
/// Find an ExternAttr with the given kind in the given DeclAttributes.
2383+
static ExternAttr *find(DeclAttributes &attrs, ExternKind kind);
23542384

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

include/swift/AST/AttrKind.h

+18
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/DiagnosticsParse.def

+1-1
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ ERROR(attr_rawlayout_expected_params,none,
18651865
"expected %1 argument after %0 argument in @_rawLayout attribute", (StringRef, StringRef))
18661866

18671867
ERROR(attr_extern_expected_label,none,
1868-
"expected %0 argument to @_extern attribute", (StringRef))
1868+
"expected %0 argument to @extern attribute", (StringRef))
18691869
//------------------------------------------------------------------------------
18701870
// MARK: Generics parsing diagnostics
18711871
//------------------------------------------------------------------------------

include/swift/AST/DiagnosticsSema.def

+24-2
Original file line numberDiff line numberDiff line change
@@ -1871,9 +1871,31 @@ ERROR(section_not_at_top_level,none,
18711871
ERROR(section_empty_name,none,
18721872
"@_section section name cannot be empty", ())
18731873

1874-
// @_extern
1874+
// @extern
1875+
ERROR(attr_extern_experimental,none,
1876+
"@extern requires '-enable-experimental-feature Extern'", ())
18751877
ERROR(extern_not_at_top_level_func,none,
1876-
"@_extern attribute can only be applied to global functions", ())
1878+
"@extern attribute can only be applied to global functions", ())
1879+
ERROR(extern_empty_c_name,none,
1880+
"expected non-empty C name in @extern attribute", ())
1881+
ERROR(extern_only_non_other_attr,none,
1882+
"@extern attribute cannot be applied to an '@%0' declaration", (StringRef))
1883+
WARNING(extern_c_maybe_invalid_name, none,
1884+
"C name '%0' may be invalid; explicitly specify the name in @extern(c) to suppress this warning",
1885+
(StringRef))
1886+
1887+
ERROR(c_func_variadic, none,
1888+
"cannot declare variadic argument %0 in %kind1",
1889+
(DeclName, const ValueDecl *))
1890+
ERROR(c_func_unsupported_specifier, none,
1891+
"cannot declare '%0' argument %1 in %kind2",
1892+
(StringRef, DeclName, const ValueDecl *))
1893+
ERROR(c_func_unsupported_type, none, "%0 cannot be represented in C",
1894+
(Type))
1895+
ERROR(c_func_async,none,
1896+
"async functions cannot be represented in C", ())
1897+
ERROR(c_func_throws,none,
1898+
"raising errors from C functions is not supported", ())
18771899

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

include/swift/AST/TypeCheckRequests.h

+20
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

+3
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)

include/swift/Basic/Features.def

+3
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ EXPERIMENTAL_FEATURE(StructLetDestructuring, true)
257257
/// lifetime-dependent results.
258258
EXPERIMENTAL_FEATURE(NonEscapableTypes, false)
259259

260+
/// Enable the `@extern` attribute.
261+
EXPERIMENTAL_FEATURE(Extern, true)
262+
260263
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
261264
#undef EXPERIMENTAL_FEATURE
262265
#undef UPCOMING_FEATURE

include/swift/Parse/Parser.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ class Parser {
10951095
ParserResult<DifferentiableAttr> parseDifferentiableAttribute(SourceLoc AtLoc,
10961096
SourceLoc Loc);
10971097

1098-
/// Parse the @_extern attribute.
1098+
/// Parse the @extern attribute.
10991099
bool parseExternAttribute(DeclAttributes &Attributes, bool &DiscardAttribute,
11001100
StringRef AttrName, SourceLoc AtLoc, SourceLoc Loc);
11011101

include/swift/SIL/SILFunction.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ class SILFunction
303303
/// empty.
304304
StringRef WasmExportName;
305305

306-
/// Name of a Wasm import module and field if @_extern(wasm) attribute
306+
/// Name of a Wasm import module and field if @extern(wasm) attribute
307307
llvm::Optional<std::pair<StringRef, StringRef>> WasmImportModuleAndField;
308308

309309
/// Has value if there's a profile for this function
@@ -1297,14 +1297,14 @@ class SILFunction
12971297
StringRef wasmExportName() const { return WasmExportName; }
12981298
void setWasmExportName(StringRef value) { WasmExportName = value; }
12991299

1300-
/// Return Wasm import module name if @_extern(wasm) was used otherwise empty
1300+
/// Return Wasm import module name if @extern(wasm) was used otherwise empty
13011301
StringRef wasmImportModuleName() const {
13021302
if (WasmImportModuleAndField)
13031303
return WasmImportModuleAndField->first;
13041304
return StringRef();
13051305
}
13061306

1307-
/// Return Wasm import field name if @_extern(wasm) was used otherwise empty
1307+
/// Return Wasm import field name if @extern(wasm) was used otherwise empty
13081308
StringRef wasmImportFieldName() const {
13091309
if (WasmImportModuleAndField)
13101310
return WasmImportModuleAndField->second;

lib/AST/ASTPrinter.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3612,6 +3612,10 @@ static bool usesFeatureTypedThrows(Decl *decl) {
36123612
return false;
36133613
}
36143614

3615+
static bool usesFeatureExtern(Decl *decl) {
3616+
return decl->getAttrs().hasAttribute<ExternAttr>();
3617+
}
3618+
36153619
/// Suppress the printing of a particular feature.
36163620
static void suppressingFeature(PrintOptions &options, Feature feature,
36173621
llvm::function_ref<void()> action) {

lib/AST/Attr.cpp

+35-4
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,22 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11411141

11421142
case DAK_Extern: {
11431143
auto *Attr = cast<ExternAttr>(this);
1144-
Printer.printAttrName("@_extern");
1145-
// For now, it accepts only "wasm" as its kind.
1146-
Printer << "(wasm, module: \"" << Attr->ModuleName << "\", name: \"" << Attr->Name << "\")";
1144+
Printer.printAttrName("@extern");
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

@@ -1718,7 +1731,7 @@ StringRef DeclAttribute::getAttrName() const {
17181731
case DAK_RawLayout:
17191732
return "_rawLayout";
17201733
case DAK_Extern:
1721-
return "_extern";
1734+
return "extern";
17221735
}
17231736
llvm_unreachable("bad DeclAttrKind");
17241737
}
@@ -2638,6 +2651,24 @@ bool MacroRoleAttr::hasNameKind(MacroIntroducedDeclNameKind kind) const {
26382651
}) != getNames().end();
26392652
}
26402653

2654+
StringRef ExternAttr::getCName(const FuncDecl *D) const {
2655+
if (auto cName = this->Name)
2656+
return cName.value();
2657+
// If no name was specified, fall back on the Swift base name without mangling.
2658+
// Base name is always available and non-empty for FuncDecl.
2659+
return D->getBaseIdentifier().str();
2660+
}
2661+
2662+
ExternAttr *ExternAttr::find(DeclAttributes &attrs, ExternKind kind) {
2663+
for (DeclAttribute *attr : attrs) {
2664+
if (auto *externAttr = dyn_cast<ExternAttr>(attr)) {
2665+
if (externAttr->getExternKind() == kind)
2666+
return externAttr;
2667+
}
2668+
}
2669+
return nullptr;
2670+
}
2671+
26412672
const DeclAttribute *
26422673
DeclAttributes::getEffectiveSendableAttr() const {
26432674
const NonSendableAttr *assumedAttr = nullptr;

0 commit comments

Comments
 (0)