Skip to content

Commit 5a57e57

Browse files
committed
Initial implementation of a @_cdecl attribute to export top-level functions to C.
There's an immediate need for this in the core libs, and we have most of the necessary pieces on hand to make it easy to implement. This is an unpolished initial implementation, with the following limitations, among others: - It doesn't support bridging error conventions, - It relies on ObjC interop, - It doesn't check for symbol name collisions, - It has an underscored name with required symbol name `@cdecl("symbol_name")`, awaiting official bikeshed painting.
1 parent 7c68fc1 commit 5a57e57

21 files changed

+404
-63
lines changed

Diff for: include/swift/AST/Attr.def

+3
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ SIMPLE_DECL_ATTR(indirect, Indirect,
239239
SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess,
240240
OnFunc /*| OnVar*/ | LongAttribute, 61)
241241

242+
DECL_ATTR(_cdecl, CDecl,
243+
OnFunc | LongAttribute | UserInaccessible, 62)
244+
242245
#undef TYPE_ATTR
243246
#undef DECL_ATTR_ALIAS
244247
#undef SIMPLE_DECL_ATTR

Diff for: include/swift/AST/Attr.h

+18
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,24 @@ class SILGenNameAttr : public DeclAttribute {
571571
}
572572
};
573573

574+
/// Defines the @_cdecl attribute.
575+
class CDeclAttr : public DeclAttribute {
576+
public:
577+
CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
578+
: DeclAttribute(DAK_CDecl, AtLoc, Range, Implicit),
579+
Name(Name) {}
580+
581+
CDeclAttr(StringRef Name, bool Implicit)
582+
: CDeclAttr(Name, SourceLoc(), SourceRange(), /*Implicit=*/true) {}
583+
584+
/// The symbol name.
585+
const StringRef Name;
586+
587+
static bool classof(const DeclAttribute *DA) {
588+
return DA->getKind() == DAK_CDecl;
589+
}
590+
};
591+
574592
/// Defines the @_semantics attribute.
575593
class SemanticsAttr : public DeclAttribute {
576594
public:

Diff for: include/swift/AST/DiagnosticsSema.def

+9-1
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,14 @@ ERROR(no_objc_tagged_pointer_not_class_protocol,sema_tcd,none,
742742
ERROR(swift_native_objc_runtime_base_not_on_root_class,sema_tcd,none,
743743
"@_swift_native_objc_runtime_base_not_on_root_class can only be applied "
744744
"to root classes", ())
745+
746+
ERROR(cdecl_not_at_top_level,sema_tcd,none,
747+
"@_cdecl can only be applied to global functions", ())
748+
ERROR(cdecl_empty_name,sema_tcd,none,
749+
"@_cdecl symbol name cannot be empty", ())
750+
ERROR(cdecl_throws,sema_tcd,none,
751+
"raising errors from @_cdecl functions is not supported", ())
752+
745753
ERROR(attr_methods_only,sema_tcd,none,
746754
"only methods can be declared %0", (DeclAttribute))
747755
ERROR(access_control_in_protocol,sema_tcd,none,
@@ -2344,7 +2352,7 @@ ERROR(objc_name_func_mismatch,sema_objc,none,
23442352
(bool, unsigned, bool, unsigned, bool, bool))
23452353

23462354
// If you change this, also change enum ObjCReason
2347-
#define OBJC_ATTR_SELECT "select{marked dynamic|marked @objc|marked @IBOutlet|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override}"
2355+
#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}"
23482356

23492357
ERROR(objc_invalid_on_var,sema_objc,none,
23502358
"property cannot be %" OBJC_ATTR_SELECT "0 "

Diff for: include/swift/Serialization/ModuleFormat.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const unsigned char MODULE_DOC_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x07 };
4242

4343
/// Serialized module format major version number.
4444
///
45-
/// Always 0 for Swift 1.0.
45+
/// Always 0 for Swift 1.x and 2.x.
4646
const uint16_t VERSION_MAJOR = 0;
4747

4848
/// Serialized module format minor version number.
@@ -51,7 +51,7 @@ const uint16_t VERSION_MAJOR = 0;
5151
/// To ensure that two separate changes don't silently get merged into one
5252
/// in source control, you should also update the comment to briefly
5353
/// describe what change you made.
54-
const uint16_t VERSION_MINOR = 222; // Last change: @_fixed_layout
54+
const uint16_t VERSION_MINOR = 223; // Last change: @_cdecl
5555

5656
using DeclID = Fixnum<31>;
5757
using DeclIDField = BCFixed<31>;
@@ -1203,6 +1203,13 @@ namespace decls_block {
12031203
BCFixed<1>, // implicit flag
12041204
BCBlob // _silgen_name
12051205
>;
1206+
1207+
using CDeclDeclAttrLayout = BCRecordLayout<
1208+
CDecl_DECL_ATTR,
1209+
BCFixed<1>, // implicit flag
1210+
BCBlob // _silgen_name
1211+
>;
1212+
12061213

12071214
using AlignmentDeclAttrLayout = BCRecordLayout<
12081215
Alignment_DECL_ATTR,

Diff for: lib/AST/ASTContext.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1875,7 +1875,10 @@ void AbstractFunctionDecl::setForeignErrorConvention(
18751875

18761876
Optional<ForeignErrorConvention>
18771877
AbstractFunctionDecl::getForeignErrorConvention() const {
1878-
if (!isObjC() || !isBodyThrowing()) return None;
1878+
if (!isObjC() && !getAttrs().hasAttribute<CDeclAttr>())
1879+
return None;
1880+
if (!isBodyThrowing())
1881+
return None;
18791882
auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions;
18801883
auto it = conventionsMap.find(this);
18811884
if (it == conventionsMap.end()) return None;

Diff for: lib/AST/Attr.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ void DeclAttribute::print(ASTPrinter &Printer,
327327
if (cast<AutoClosureAttr>(this)->isEscaping())
328328
Printer << "(escaping)";
329329
break;
330+
331+
case DAK_CDecl:
332+
Printer << "@_cdecl(\"" << cast<CDeclAttr>(this)->Name << "\")";
333+
break;
334+
330335
case DAK_ObjC: {
331336
if (Options.PrintForSIL && isImplicit())
332337
break;
@@ -421,6 +426,8 @@ StringRef DeclAttribute::getAttrName() const {
421426
return "_silgen_name";
422427
case DAK_Alignment:
423428
return "_alignment";
429+
case DAK_CDecl:
430+
return "_cdecl";
424431
case DAK_SwiftNativeObjCRuntimeBase:
425432
return "_swift_native_objc_runtime_base";
426433
case DAK_Semantics:

Diff for: lib/AST/Verifier.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -2249,8 +2249,9 @@ struct ASTNodeBase {};
22492249
abort();
22502250
}
22512251

2252-
if (AFD->getForeignErrorConvention() && !AFD->isObjC()) {
2253-
Out << "foreign error convention on non-@objc function\n";
2252+
if (AFD->getForeignErrorConvention()
2253+
&& !AFD->isObjC() && !AFD->getAttrs().hasAttribute<CDeclAttr>()) {
2254+
Out << "foreign error convention on non-@objc, non-@_cdecl function\n";
22542255
AFD->dump(Out);
22552256
abort();
22562257
}

Diff for: lib/IRGen/Linking.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ void LinkEntity::mangle(raw_ostream &buffer) const {
220220

221221
// entity ::= declaration // other declaration
222222
case Kind::Function:
223-
// As a special case, functions can have external asm names.
223+
// As a special case, functions can have manually mangled names.
224224
if (auto AsmA = getDecl()->getAttrs().getAttribute<SILGenNameAttr>()) {
225225
buffer << AsmA->Name;
226226
return;

Diff for: lib/Parse/ParseDecl.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
563563
break;
564564
}
565565

566+
case DAK_CDecl:
566567
case DAK_SILGenName: {
567568
if (!consumeIf(tok::l_paren)) {
568569
diagnose(Loc, diag::attr_expected_lparen, AttrName,
@@ -599,9 +600,16 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
599600
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
600601
}
601602

602-
if (!DiscardAttribute)
603-
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
603+
if (!DiscardAttribute) {
604+
if (DK == DAK_SILGenName)
605+
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
606+
AttrRange, /*Implicit=*/false));
607+
else if (DK == DAK_CDecl)
608+
Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc,
604609
AttrRange, /*Implicit=*/false));
610+
else
611+
llvm_unreachable("out of sync with switch");
612+
}
605613

606614
break;
607615
}

0 commit comments

Comments
 (0)