Skip to content

Commit 013aad1

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 35fd887 commit 013aad1

21 files changed

+397
-63
lines changed

include/swift/AST/Attr.def

+4
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess,
245245
SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface,
246246
OnProtocol | UserInaccessible, 62)
247247

248+
DECL_ATTR(_cdecl, CDecl,
249+
OnFunc | LongAttribute | UserInaccessible, 63)
250+
251+
248252
#undef TYPE_ATTR
249253
#undef DECL_ATTR_ALIAS
250254
#undef SIMPLE_DECL_ATTR

include/swift/AST/Attr.h

+18
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,24 @@ class SILGenNameAttr : public DeclAttribute {
516516
}
517517
};
518518

519+
/// Defines the @_cdecl attribute.
520+
class CDeclAttr : public DeclAttribute {
521+
public:
522+
CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
523+
: DeclAttribute(DAK_CDecl, AtLoc, Range, Implicit),
524+
Name(Name) {}
525+
526+
CDeclAttr(StringRef Name, bool Implicit)
527+
: CDeclAttr(Name, SourceLoc(), SourceRange(), /*Implicit=*/true) {}
528+
529+
/// The symbol name.
530+
const StringRef Name;
531+
532+
static bool classof(const DeclAttribute *DA) {
533+
return DA->getKind() == DAK_CDecl;
534+
}
535+
};
536+
519537
/// Defines the @_semantics attribute.
520538
class SemanticsAttr : public DeclAttribute {
521539
public:

include/swift/AST/DiagnosticsSema.def

+9-1
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,14 @@ ERROR(no_objc_tagged_pointer_not_class_protocol,none,
836836
ERROR(swift_native_objc_runtime_base_not_on_root_class,none,
837837
"@_swift_native_objc_runtime_base_not_on_root_class can only be applied "
838838
"to root classes", ())
839+
840+
ERROR(cdecl_not_at_top_level,none,
841+
"@_cdecl can only be applied to global functions", ())
842+
ERROR(cdecl_empty_name,none,
843+
"@_cdecl symbol name cannot be empty", ())
844+
ERROR(cdecl_throws,none,
845+
"raising errors from @_cdecl functions is not supported", ())
846+
839847
ERROR(attr_methods_only,none,
840848
"only methods can be declared %0", (DeclAttribute))
841849
ERROR(access_control_in_protocol,none,
@@ -2487,7 +2495,7 @@ ERROR(objc_enum_case_multi,none,
24872495
"'@objc' enum case declaration defines multiple enum cases with the same Objective-C name", ())
24882496

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

24922500
ERROR(objc_invalid_on_var,none,
24932501
"property cannot be %" OBJC_ATTR_SELECT "0 "

include/swift/Serialization/ModuleFormat.h

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

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

4949
/// Serialized module format minor version number.
@@ -53,7 +53,8 @@ const uint16_t VERSION_MAJOR = 0;
5353
/// in source control, you should also update the comment to briefly
5454
/// describe what change you made. The content of this comment isn't important;
5555
/// it just ensures a conflict if two people change the module format.
56-
const uint16_t VERSION_MINOR = 239; // multiple results for SILFunctionType
56+
/// describe what change you made.
57+
const uint16_t VERSION_MINOR = 240; // Last change: @_cdecl
5758

5859
using DeclID = PointerEmbeddedInt<unsigned, 31>;
5960
using DeclIDField = BCFixed<31>;
@@ -1223,6 +1224,13 @@ namespace decls_block {
12231224
BCFixed<1>, // implicit flag
12241225
BCBlob // _silgen_name
12251226
>;
1227+
1228+
using CDeclDeclAttrLayout = BCRecordLayout<
1229+
CDecl_DECL_ATTR,
1230+
BCFixed<1>, // implicit flag
1231+
BCBlob // _silgen_name
1232+
>;
1233+
12261234

12271235
using AlignmentDeclAttrLayout = BCRecordLayout<
12281236
Alignment_DECL_ATTR,

lib/AST/ASTContext.cpp

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

19091909
Optional<ForeignErrorConvention>
19101910
AbstractFunctionDecl::getForeignErrorConvention() const {
1911-
if (!isObjC() || !isBodyThrowing()) return None;
1911+
if (!isObjC() && !getAttrs().hasAttribute<CDeclAttr>())
1912+
return None;
1913+
if (!isBodyThrowing())
1914+
return None;
19121915
auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions;
19131916
auto it = conventionsMap.find(this);
19141917
if (it == conventionsMap.end()) return None;

lib/AST/ASTVerifier.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -2200,8 +2200,9 @@ struct ASTNodeBase {};
22002200
abort();
22012201
}
22022202

2203-
if (AFD->getForeignErrorConvention() && !AFD->isObjC()) {
2204-
Out << "foreign error convention on non-@objc function\n";
2203+
if (AFD->getForeignErrorConvention()
2204+
&& !AFD->isObjC() && !AFD->getAttrs().hasAttribute<CDeclAttr>()) {
2205+
Out << "foreign error convention on non-@objc, non-@_cdecl function\n";
22052206
AFD->dump(Out);
22062207
abort();
22072208
}

lib/AST/Attr.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options)
372372
if (cast<AutoClosureAttr>(this)->isEscaping())
373373
Printer << "(escaping)";
374374
break;
375+
376+
case DAK_CDecl:
377+
Printer << "@_cdecl(\"" << cast<CDeclAttr>(this)->Name << "\")";
378+
break;
379+
375380
case DAK_ObjC: {
376381
Printer.printAttrName("@objc");
377382
llvm::SmallString<32> scratch;
@@ -487,6 +492,8 @@ StringRef DeclAttribute::getAttrName() const {
487492
return "_silgen_name";
488493
case DAK_Alignment:
489494
return "_alignment";
495+
case DAK_CDecl:
496+
return "_cdecl";
490497
case DAK_SwiftNativeObjCRuntimeBase:
491498
return "_swift_native_objc_runtime_base";
492499
case DAK_Semantics:

lib/IRGen/Linking.cpp

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

233233
// entity ::= declaration // other declaration
234234
case Kind::Function:
235-
// As a special case, functions can have external asm names.
235+
// As a special case, functions can have manually mangled names.
236236
if (auto AsmA = getDecl()->getAttrs().getAttribute<SILGenNameAttr>()) {
237237
mangler.append(AsmA->Name);
238238
return mangler.finalize(buffer);

lib/Parse/ParseDecl.cpp

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

568+
case DAK_CDecl:
568569
case DAK_SILGenName: {
569570
if (!consumeIf(tok::l_paren)) {
570571
diagnose(Loc, diag::attr_expected_lparen, AttrName,
@@ -601,9 +602,16 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
601602
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
602603
}
603604

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

608616
break;
609617
}

0 commit comments

Comments
 (0)