Skip to content

Commit

Permalink
Add a '@convention(xxx)' attribute for specifying function conventions.
Browse files Browse the repository at this point in the history
This is new attribute we're using to coalesce @thin, @objc_block, and @cc, and to extend to new uses like C function pointer types. Parse the new attribute, but preserve support for the old attributes, and print with the old attributes for now to separate out test changes. Migration fixits and test updates to come. I did take the opportunity here to kill off the '@cc(cdecl)' hack for AST-level function pointer types, which are now only spelt with @convention(c).

Swift SVN r27247
  • Loading branch information
jckarter committed Apr 13, 2015
1 parent aa5fcab commit b03795e
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 76 deletions.
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#endif

// Type attributes
//TYPE_ATTR(convention)
TYPE_ATTR(convention)
TYPE_ATTR(noreturn)

// SIL-specific attributes
Expand Down
11 changes: 7 additions & 4 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ class TypeAttributes {
/// AtLoc - This is the location of the first '@' in the attribute specifier.
/// If this is an empty attribute specifier, then this will be an invalid loc.
SourceLoc AtLoc;
// TODO: Replace "cc" attribute with proper convention attribute.
Optional<StringRef> cc = None;
Optional<StringRef> deprecatedCC = None;
Optional<StringRef> convention = None;

// For an opened existential type, the known ID.
Optional<UUID> OpenedID;
Expand Down Expand Up @@ -249,8 +249,11 @@ class TypeAttributes {
return true;
}

bool hasCC() const { return cc.hasValue(); }
StringRef getAbstractCC() const { return *cc; }
bool hasDeprecatedCC() const { return deprecatedCC.hasValue(); }
StringRef getDeprecatedCC() const { return *deprecatedCC; }

bool hasConvention() const { return convention.hasValue(); }
StringRef getConvention() const { return *convention; }

bool hasOwnership() const { return getOwnership() != Ownership::Strong; }
Ownership getOwnership() const {
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,14 @@ ERROR(cc_attribute_expected_name,attribute_parsing,none,
ERROR(cc_attribute_expected_rparen,attribute_parsing,none,
"expected ')' after calling convention name for 'cc' attribute", ())

// convention
ERROR(convention_attribute_expected_lparen,attribute_parsing,none,
"expected '(' after 'convention' attribute", ())
ERROR(convention_attribute_expected_name,attribute_parsing,none,
"expected convention name identifier in 'convention' attribute", ())
ERROR(convention_attribute_expected_rparen,attribute_parsing,none,
"expected ')' after convention name for 'convention' attribute", ())

// objc
ERROR(attr_objc_missing_colon,attribute_parsing,none,
"missing ':' after selector piece in @objc attribute", ())
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,12 @@ ERROR(objc_block_cannot_be_thin,attribute_parsing,none,
"@objc_block function type cannot be @thin", ())
ERROR(attribute_not_supported,attribute_parsing,none,
"this attribute is not supported", ())
ERROR(convention_with_deprecated_representation_attribute,attribute_parsing,none,
"@convention attribute cannot be used with deprecated @%0 attribute",
(StringRef))
ERROR(unsupported_convention,type_parsing,none,
"convention '%0' not supported", (StringRef))


// SIL
ERROR(sil_local_storage_nested, decl_parsing,none,
Expand All @@ -1790,6 +1796,8 @@ ERROR(sil_function_multiple_error_results,type_parsing,PointsToFirstBadToken,
"SIL function types cannot have multiple @error results", ())
ERROR(unsupported_cc_representation_combo,type_parsing,none,
"cc unsupported with this sil representation", ())
ERROR(unsupported_sil_convention,type_parsing,none,
"convention '%0' not supported in SIL", (StringRef))

// SIL Metatypes
ERROR(sil_metatype_without_repr,type_parsing,none,
Expand Down
3 changes: 1 addition & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2405,8 +2405,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
Printer << "@objc_block ";
break;
case SILFunctionType::Representation::CFunctionPointer:
//Printer << "@convention(c) ";
Printer << "@cc(cdecl) ";
Printer << "@convention(c) ";
break;
case SILFunctionType::Representation::Method:
//Printer << "@convention(method) ";
Expand Down
7 changes: 5 additions & 2 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,11 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer) const {
if (Attrs.has(TAK_objc_block)) Printer << "@objc_block ";
if (Attrs.has(TAK_thin)) Printer << "@thin ";
if (Attrs.has(TAK_thick)) Printer << "@thick ";
if (Attrs.cc.hasValue()) {
Printer << "@cc(" << Attrs.cc.getValue() << ")";
if (Attrs.deprecatedCC.hasValue()) {
Printer << "@cc(" << Attrs.deprecatedCC.getValue() << ")";
}
if (Attrs.convention.hasValue()) {
Printer << "@convention(" << Attrs.convention.getValue() << ")";
}
}

Expand Down
39 changes: 37 additions & 2 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
break;
}

// 'cc' attribute.
// Deprecated 'cc' attribute.
case TAK_cc: {
// Parse the cc name in parens.
SourceLoc beginLoc = Tok.getLoc(), nameLoc, endLoc;
Expand Down Expand Up @@ -1244,7 +1244,42 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
if (justChecking) return false;

if (!name.empty())
Attributes.cc = name;
Attributes.deprecatedCC = name;
return false;
}

// Convention attribute.
case TAK_convention: {
// Parse the convention name in parens.
SourceLoc beginLoc = Tok.getLoc(), nameLoc, endLoc;
StringRef name;
if (consumeIfNotAtStartOfLine(tok::l_paren)) {
if (Tok.is(tok::identifier)) {
nameLoc = Tok.getLoc();
name = Tok.getText();
consumeToken();
} else if (!justChecking) {
diagnose(Tok, diag::convention_attribute_expected_name);
}

// Parse the ')'. We can't use parseMatchingToken if we're in
// just-checking mode.
if (!justChecking) {
parseMatchingToken(tok::r_paren, endLoc,
diag::convention_attribute_expected_rparen,
beginLoc);
} else if (!consumeIf(tok::r_paren)) {
return true;
}
} else if (!justChecking) {
diagnose(Tok, diag::convention_attribute_expected_lparen);
}

// Don't validate the CC in just-checking mode.
if (justChecking) return false;

if (!name.empty())
Attributes.convention = name;
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Parse/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ bool SILParser::parseSILType(SILType &Result, GenericParamList *&GenericParams,

// Global functions are implicitly @thin.
if (IsFuncDecl && !(attrs.has(TAK_thick) || attrs.has(TAK_thin) ||
attrs.has(TAK_objc_block))) {
attrs.has(TAK_objc_block) || attrs.has(TAK_convention))) {
// Use a random location.
attrs.setAttr(TAK_thin, P.PreviousLoc);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/SILPasses/Utils/Devirtualize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ SILInstruction *swift::tryDevirtualizeApply(ApplyInst *AI) {
///
/// \code
/// %XX = alloc_ref $Foo
/// %YY = class_method %XX : $Foo, #Foo.get!1 : $@cc(method) @thin ...
/// %YY = class_method %XX : $Foo, #Foo.get!1 : $@convention(method)...
/// \endcode
///
/// or
Expand Down
157 changes: 115 additions & 42 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/Basic/SourceManager.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
using namespace swift;

Expand Down Expand Up @@ -1277,7 +1278,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
// Pass down the variable function type attributes to the
// function-type creator.
static const TypeAttrKind FunctionAttrs[] = {
TAK_objc_block, TAK_cc, TAK_thin, TAK_noreturn,
TAK_objc_block, TAK_cc, TAK_convention, TAK_thin, TAK_noreturn,
TAK_callee_owned, TAK_callee_guaranteed, TAK_noescape
};

Expand All @@ -1303,10 +1304,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
for (auto silOnlyAttr : {TAK_thin, TAK_thick}) {
checkUnsupportedAttr(silOnlyAttr);
}
// TODO: Pick a real syntax for C function pointers.
// For now admit @cc(cdecl) as a syntax for C function pointers.
if (!Context.LangOpts.EnableCFunctionPointers)
checkUnsupportedAttr(TAK_cc);
checkUnsupportedAttr(TAK_cc);
}

bool hasFunctionAttr = false;
Expand Down Expand Up @@ -1347,52 +1345,127 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
// TODO: Coalesce the representation attributes into a single 'convention'
// attribute.
SILFunctionType::Representation rep;
if (thin)
rep = SILFunctionType::Representation::Thin;
else if (block)
rep = SILFunctionType::Representation::Block;
else
rep = SILFunctionType::Representation::Thick;

if (attrs.hasCC()) {
if (attrs.getAbstractCC() == "cdecl") {
if (rep == SILFunctionType::Representation::Thin)
rep = SILFunctionType::Representation::CFunctionPointer;
else if (rep == SILFunctionType::Representation::Block)
/* OK */;
else
TC.diagnose(attrs.getLoc(TAK_cc),
diag::unsupported_cc_representation_combo);
} else if (attrs.getAbstractCC() == "method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::Method;
} else if (attrs.getAbstractCC() == "objc_method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::ObjCMethod;
} else if (attrs.getAbstractCC() == "witness_method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::WitnessMethod;

if (attrs.hasConvention()) {
// SIL exposes a greater number of conventions than Swift source.
auto parsedRep =
llvm::StringSwitch<Optional<SILFunctionType::Representation>>
(attrs.getConvention())
.Case("thick", SILFunctionType::Representation::Thick)
.Case("block", SILFunctionType::Representation::Block)
.Case("thin", SILFunctionType::Representation::Thin)
.Case("c", SILFunctionType::Representation::CFunctionPointer)
.Case("method", SILFunctionType::Representation::Method)
.Case("objc_method", SILFunctionType::Representation::ObjCMethod)
.Case("witness_method", SILFunctionType::Representation::WitnessMethod)
.Default(None);
if (!parsedRep) {
TC.diagnose(attrs.getLoc(TAK_convention),
diag::unsupported_sil_convention, attrs.getConvention());
rep = SILFunctionType::Representation::Thin;
} else {
rep = *parsedRep;
}

// Don't allow both @convention and the old representation attrs.
if (attrs.has(TAK_thin)) {
TC.diagnose(attrs.getLoc(TAK_thin),
diag::convention_with_deprecated_representation_attribute,
"thin");
}
if (attrs.has(TAK_objc_block)) {
TC.diagnose(attrs.getLoc(TAK_objc_block),
diag::convention_with_deprecated_representation_attribute,
"objc_block");
}
if (attrs.has(TAK_cc)) {
TC.diagnose(attrs.getLoc(TAK_cc),
diag::unsupported_cc_representation_combo);
diag::convention_with_deprecated_representation_attribute,
"cc");
}
} else {
// Handle the old attributes.
// TODO: Warning and fixit to migrate.
if (thin)
rep = SILFunctionType::Representation::Thin;
else if (block)
rep = SILFunctionType::Representation::Block;
else
rep = SILFunctionType::Representation::Thick;

if (attrs.hasDeprecatedCC()) {
if (attrs.getDeprecatedCC() == "cdecl") {
if (rep == SILFunctionType::Representation::Thin)
rep = SILFunctionType::Representation::CFunctionPointer;
else if (rep == SILFunctionType::Representation::Block)
/* OK */;
else
TC.diagnose(attrs.getLoc(TAK_cc),
diag::unsupported_cc_representation_combo);
} else if (attrs.getDeprecatedCC() == "method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::Method;
} else if (attrs.getDeprecatedCC() == "objc_method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::ObjCMethod;
} else if (attrs.getDeprecatedCC() == "witness_method"
&& rep == SILFunctionType::Representation::Thin) {
rep = SILFunctionType::Representation::WitnessMethod;
} else {
TC.diagnose(attrs.getLoc(TAK_cc),
diag::unsupported_cc_representation_combo);
}
}
}

// Resolve the function type directly with these attributes.
SILFunctionType::ExtInfo extInfo(rep,
attrs.has(TAK_noreturn));

ty = resolveSILFunctionType(fnRepr, options, extInfo, calleeConvention);
} else {
if (attrs.hasDeprecatedCC())
TC.diagnose(attrs.getLoc(TAK_cc), diag::attribute_not_supported);

FunctionType::Representation rep;
if (thin)
rep = FunctionType::Representation::Thin;
else if (block)
rep = FunctionType::Representation::Block;
else if (attrs.hasCC() && attrs.getAbstractCC() == "cdecl")
rep = FunctionType::Representation::CFunctionPointer;
else
rep = FunctionType::Representation::Swift;
if (attrs.hasConvention()) {
auto parsedRep =
llvm::StringSwitch<Optional<FunctionType::Representation>>
(attrs.getConvention())
.Case("swift", FunctionType::Representation::Swift)
.Case("block", FunctionType::Representation::Block)
.Case("thin", FunctionType::Representation::Thin)
.Case("c", FunctionType::Representation::CFunctionPointer)
.Default(None);
if (!parsedRep) {
TC.diagnose(attrs.getLoc(TAK_convention),
diag::unsupported_convention, attrs.getConvention());
rep = FunctionType::Representation::Swift;
} else {
rep = *parsedRep;
}

// Don't allow both @convention and the old representation attrs.
if (attrs.has(TAK_thin)) {
TC.diagnose(attrs.getLoc(TAK_thin),
diag::convention_with_deprecated_representation_attribute,
"thin");
}
if (attrs.has(TAK_objc_block)) {
TC.diagnose(attrs.getLoc(TAK_objc_block),
diag::convention_with_deprecated_representation_attribute,
"objc_block");
}
} else {
// Handle the old attributes.
// TODO: Warning and fixit to migrate.
if (thin)
rep = FunctionType::Representation::Thin;
else if (block)
rep = FunctionType::Representation::Block;
else
rep = FunctionType::Representation::Swift;
}

// Resolve the function type directly with these attributes.
FunctionType::ExtInfo extInfo(rep,
Expand All @@ -1406,8 +1479,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,

for (auto i : FunctionAttrs)
attrs.clearAttribute(i);
attrs.cc = None;

attrs.deprecatedCC = None;
attrs.convention = None;
} else if (hasFunctionAttr) {
for (auto i : FunctionAttrs) {
if (attrs.has(i)) {
Expand Down
8 changes: 4 additions & 4 deletions test/ClangModules/ctypes_parse_function_pointers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ func testFunctionPointers() {
let fp = getFunctionPointer()
useFunctionPointer(fp)

let explicitFP: @cc(cdecl) (CInt) -> CInt = fp
let explicitFP: @convention(c) (CInt) -> CInt = fp

let wrapper: FunctionPointerWrapper = FunctionPointerWrapper(a: nil, b: nil)
let wrapper2 = FunctionPointerWrapper(a: fp, b: fp)
useFunctionPointer(wrapper.a)
let _: @cc(cdecl) (CInt) -> CInt = wrapper.b
let _: @convention(c) (CInt) -> CInt = wrapper.b

var anotherFP: @cc(cdecl) (CInt, CLong, UnsafeMutablePointer<Void>) -> Void
var anotherFP: @convention(c) (CInt, CLong, UnsafeMutablePointer<Void>) -> Void
= getFunctionPointer2()

useFunctionPointer2(anotherFP)
anotherFP = fp // expected-error {{cannot assign a value of type 'fptr!' to a value of type '@cc(cdecl) (CInt, CLong, UnsafeMutablePointer<Void>) -> Void'}}
anotherFP = fp // expected-error {{cannot assign a value of type 'fptr!' to a value of type '@convention(c) (CInt, CLong, UnsafeMutablePointer<Void>) -> Void'}}
}

Loading

0 comments on commit b03795e

Please sign in to comment.