Skip to content

Commit

Permalink
Initial implementation of a @_cdecl attribute to export top-level fun…
Browse files Browse the repository at this point in the history
…ctions 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.
  • Loading branch information
jckarter committed Nov 18, 2015
1 parent 7c68fc1 commit 5a57e57
Show file tree
Hide file tree
Showing 21 changed files with 404 additions and 63 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ SIMPLE_DECL_ATTR(indirect, Indirect,
SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess,
OnFunc /*| OnVar*/ | LongAttribute, 61)

DECL_ATTR(_cdecl, CDecl,
OnFunc | LongAttribute | UserInaccessible, 62)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef SIMPLE_DECL_ATTR
Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,24 @@ class SILGenNameAttr : public DeclAttribute {
}
};

/// Defines the @_cdecl attribute.
class CDeclAttr : public DeclAttribute {
public:
CDeclAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
: DeclAttribute(DAK_CDecl, AtLoc, Range, Implicit),
Name(Name) {}

CDeclAttr(StringRef Name, bool Implicit)
: CDeclAttr(Name, SourceLoc(), SourceRange(), /*Implicit=*/true) {}

/// The symbol name.
const StringRef Name;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_CDecl;
}
};

/// Defines the @_semantics attribute.
class SemanticsAttr : public DeclAttribute {
public:
Expand Down
10 changes: 9 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,14 @@ ERROR(no_objc_tagged_pointer_not_class_protocol,sema_tcd,none,
ERROR(swift_native_objc_runtime_base_not_on_root_class,sema_tcd,none,
"@_swift_native_objc_runtime_base_not_on_root_class can only be applied "
"to root classes", ())

ERROR(cdecl_not_at_top_level,sema_tcd,none,
"@_cdecl can only be applied to global functions", ())
ERROR(cdecl_empty_name,sema_tcd,none,
"@_cdecl symbol name cannot be empty", ())
ERROR(cdecl_throws,sema_tcd,none,
"raising errors from @_cdecl functions is not supported", ())

ERROR(attr_methods_only,sema_tcd,none,
"only methods can be declared %0", (DeclAttribute))
ERROR(access_control_in_protocol,sema_tcd,none,
Expand Down Expand Up @@ -2344,7 +2352,7 @@ ERROR(objc_name_func_mismatch,sema_objc,none,
(bool, unsigned, bool, unsigned, bool, bool))

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

ERROR(objc_invalid_on_var,sema_objc,none,
"property cannot be %" OBJC_ATTR_SELECT "0 "
Expand Down
11 changes: 9 additions & 2 deletions include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const unsigned char MODULE_DOC_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x07 };

/// Serialized module format major version number.
///
/// Always 0 for Swift 1.0.
/// Always 0 for Swift 1.x and 2.x.
const uint16_t VERSION_MAJOR = 0;

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

using DeclID = Fixnum<31>;
using DeclIDField = BCFixed<31>;
Expand Down Expand Up @@ -1203,6 +1203,13 @@ namespace decls_block {
BCFixed<1>, // implicit flag
BCBlob // _silgen_name
>;

using CDeclDeclAttrLayout = BCRecordLayout<
CDecl_DECL_ATTR,
BCFixed<1>, // implicit flag
BCBlob // _silgen_name
>;


using AlignmentDeclAttrLayout = BCRecordLayout<
Alignment_DECL_ATTR,
Expand Down
5 changes: 4 additions & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1875,7 +1875,10 @@ void AbstractFunctionDecl::setForeignErrorConvention(

Optional<ForeignErrorConvention>
AbstractFunctionDecl::getForeignErrorConvention() const {
if (!isObjC() || !isBodyThrowing()) return None;
if (!isObjC() && !getAttrs().hasAttribute<CDeclAttr>())
return None;
if (!isBodyThrowing())
return None;
auto &conventionsMap = getASTContext().Impl.ForeignErrorConventions;
auto it = conventionsMap.find(this);
if (it == conventionsMap.end()) return None;
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ void DeclAttribute::print(ASTPrinter &Printer,
if (cast<AutoClosureAttr>(this)->isEscaping())
Printer << "(escaping)";
break;

case DAK_CDecl:
Printer << "@_cdecl(\"" << cast<CDeclAttr>(this)->Name << "\")";
break;

case DAK_ObjC: {
if (Options.PrintForSIL && isImplicit())
break;
Expand Down Expand Up @@ -421,6 +426,8 @@ StringRef DeclAttribute::getAttrName() const {
return "_silgen_name";
case DAK_Alignment:
return "_alignment";
case DAK_CDecl:
return "_cdecl";
case DAK_SwiftNativeObjCRuntimeBase:
return "_swift_native_objc_runtime_base";
case DAK_Semantics:
Expand Down
5 changes: 3 additions & 2 deletions lib/AST/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2249,8 +2249,9 @@ struct ASTNodeBase {};
abort();
}

if (AFD->getForeignErrorConvention() && !AFD->isObjC()) {
Out << "foreign error convention on non-@objc function\n";
if (AFD->getForeignErrorConvention()
&& !AFD->isObjC() && !AFD->getAttrs().hasAttribute<CDeclAttr>()) {
Out << "foreign error convention on non-@objc, non-@_cdecl function\n";
AFD->dump(Out);
abort();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/Linking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ void LinkEntity::mangle(raw_ostream &buffer) const {

// entity ::= declaration // other declaration
case Kind::Function:
// As a special case, functions can have external asm names.
// As a special case, functions can have manually mangled names.
if (auto AsmA = getDecl()->getAttrs().getAttribute<SILGenNameAttr>()) {
buffer << AsmA->Name;
return;
Expand Down
12 changes: 10 additions & 2 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
break;
}

case DAK_CDecl:
case DAK_SILGenName: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
Expand Down Expand Up @@ -599,9 +600,16 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
}

if (!DiscardAttribute)
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
if (!DiscardAttribute) {
if (DK == DAK_SILGenName)
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
AttrRange, /*Implicit=*/false));
else if (DK == DAK_CDecl)
Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc,
AttrRange, /*Implicit=*/false));
else
llvm_unreachable("out of sync with switch");
}

break;
}
Expand Down
Loading

0 comments on commit 5a57e57

Please sign in to comment.