Skip to content

Commit fb59293

Browse files
[Parse] Add a cType key to the convention attribute.
For example, one may write a calling convention like convention(c, cType: "void *(void)") for a procedure that takes no arguments.
1 parent ea56a63 commit fb59293

File tree

9 files changed

+163
-73
lines changed

9 files changed

+163
-73
lines changed

include/swift/AST/Attr.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,24 @@ class TypeAttributes {
6464
/// AtLoc - This is the location of the first '@' in the attribute specifier.
6565
/// If this is an empty attribute specifier, then this will be an invalid loc.
6666
SourceLoc AtLoc;
67-
Optional<StringRef> convention = None;
68-
Optional<StringRef> conventionWitnessMethodProtocol = None;
67+
68+
struct Convention {
69+
StringRef Name = {};
70+
StringRef WitnessMethodProtocol = {};
71+
StringRef ClangType = {};
72+
// Carry the source location for diagnostics.
73+
SourceLoc ClangTypeLoc = {};
74+
75+
/// Convenience factory function to create a Swift convention.
76+
///
77+
/// Don't use this function if you are creating a C convention as you
78+
/// probably need a ClangType field as well.
79+
static Convention makeSwiftConvention(StringRef name) {
80+
return {name, "", "", {}};
81+
}
82+
};
83+
84+
Optional<Convention> ConventionArguments;
6985

7086
// Indicates whether the type's '@differentiable' attribute has a 'linear'
7187
// argument.
@@ -134,8 +150,14 @@ class TypeAttributes {
134150
return true;
135151
}
136152

137-
bool hasConvention() const { return convention.hasValue(); }
138-
StringRef getConvention() const { return *convention; }
153+
bool hasConvention() const { return ConventionArguments.hasValue(); }
154+
155+
/// Returns the primary calling convention string.
156+
///
157+
/// Note: For C conventions, this may not represent the full convention.
158+
StringRef getConventionName() const {
159+
return ConventionArguments.getValue().Name;
160+
}
139161

140162
bool hasOwnership() const {
141163
return getOwnership() != ReferenceOwnership::Strong;

include/swift/AST/DiagnosticsParse.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,13 @@ ERROR(convention_attribute_expected_name,none,
14041404
"expected convention name identifier in 'convention' attribute", ())
14051405
ERROR(convention_attribute_expected_rparen,none,
14061406
"expected ')' after convention name for 'convention' attribute", ())
1407+
ERROR(convention_attribute_ctype_expected_label,none,
1408+
"expected 'cType' label in 'convention' attribute", ())
1409+
ERROR(convention_attribute_ctype_expected_colon,none,
1410+
"expected ':' after 'cType' for 'convention' attribute", ())
1411+
ERROR(convention_attribute_ctype_expected_string,none,
1412+
"expected string literal containing clang type for 'cType' in "
1413+
"'convention' attribute", ())
14071414
ERROR(convention_attribute_witness_method_expected_colon,none,
14081415
"expected ':' after 'witness_method' for 'convention' attribute", ())
14091416
ERROR(convention_attribute_witness_method_expected_protocol,none,

include/swift/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,10 @@ class Parser {
10171017
bool parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier,
10181018
SourceLoc &SpecifierLoc,
10191019
TypeAttributes &Attributes);
1020+
1021+
bool parseConventionAttributeInternal(bool justChecking,
1022+
TypeAttributes::Convention &convention);
1023+
10201024
bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
10211025
bool justChecking = false);
10221026

lib/AST/TypeRepr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,11 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer,
312312
if (hasAttr(TAK_thick))
313313
Printer.printSimpleAttr("@thick") << " ";
314314

315-
if (hasAttr(TAK_convention) && Attrs.convention.hasValue()) {
315+
if (hasAttr(TAK_convention) && Attrs.hasConvention()) {
316+
// TODO: (Varun) Print clang type here!
316317
Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute);
317318
Printer.printAttrName("@convention");
318-
Printer << "(" << Attrs.convention.getValue() << ")";
319+
Printer << "(" << Attrs.getConvention() << ")";
319320
Printer.printStructurePost(PrintStructureKind::BuiltinAttribute);
320321
Printer << " ";
321322
}

lib/Parse/ParseDecl.cpp

Lines changed: 92 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,91 @@ static bool parseDifferentiableAttributeArgument(Parser &P,
22522252
return false;
22532253
}
22542254

2255+
/// Parse the inside of a convention attribute '(...)'.
2256+
///
2257+
/// The '@convention' prefix should've been parsed by the caller.
2258+
/// See `Parser::parseTypeAttribute` for the justChecking argument.
2259+
///
2260+
/// Returns true if there was an error.
2261+
bool Parser::parseConventionAttributeInternal(
2262+
bool justChecking, TypeAttributes::Convention &convention) {
2263+
SourceLoc LPLoc;
2264+
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
2265+
if (!justChecking)
2266+
diagnose(Tok, diag::convention_attribute_expected_lparen);
2267+
return true;
2268+
}
2269+
2270+
if (Tok.isNot(tok::identifier)) {
2271+
if (!justChecking)
2272+
diagnose(Tok, diag::convention_attribute_expected_name);
2273+
return true;
2274+
}
2275+
2276+
convention.Name = Tok.getText();
2277+
consumeToken(tok::identifier);
2278+
2279+
// Consume extra (optional) ', cType: " blah blah "'
2280+
if (consumeIf(tok::comma)) {
2281+
if (Tok.isNot(tok::identifier)) {
2282+
if (!justChecking)
2283+
diagnose(Tok, diag::convention_attribute_ctype_expected_label);
2284+
return true;
2285+
}
2286+
auto cTypeLabel = Tok.getText();
2287+
consumeToken(tok::identifier);
2288+
if (cTypeLabel != "cType") {
2289+
if (!justChecking)
2290+
diagnose(Tok, diag::convention_attribute_ctype_expected_label);
2291+
return true;
2292+
}
2293+
if (!consumeIf(tok::colon)) {
2294+
if (!justChecking)
2295+
diagnose(Tok, diag::convention_attribute_ctype_expected_colon);
2296+
return true;
2297+
}
2298+
if (Tok.isNot(tok::string_literal)) {
2299+
if (!justChecking)
2300+
diagnose(Tok, diag::convention_attribute_ctype_expected_string);
2301+
return true;
2302+
}
2303+
if (auto ty = getStringLiteralIfNotInterpolated(Tok.getLoc(), "(C type)")) {
2304+
convention.ClangType = ty.getValue();
2305+
convention.ClangTypeLoc = Tok.getLoc();
2306+
}
2307+
consumeToken(tok::string_literal);
2308+
}
2309+
2310+
if (convention.Name == "witness_method") {
2311+
if (!consumeIf(tok::colon)) {
2312+
if (!justChecking)
2313+
diagnose(Tok,
2314+
diag::convention_attribute_witness_method_expected_colon);
2315+
return true;
2316+
}
2317+
if (Tok.isNot(tok::identifier)) {
2318+
if (!justChecking)
2319+
diagnose(Tok,
2320+
diag::convention_attribute_witness_method_expected_protocol);
2321+
return true;
2322+
}
2323+
2324+
convention.WitnessMethodProtocol = Tok.getText();
2325+
consumeToken(tok::identifier);
2326+
}
2327+
2328+
// Parse the ')'. We can't use parseMatchingToken if we're in
2329+
// just-checking mode.
2330+
if (justChecking && Tok.isNot(tok::r_paren))
2331+
return true;
2332+
2333+
SourceLoc RPLoc;
2334+
parseMatchingToken(tok::r_paren, RPLoc,
2335+
diag::convention_attribute_expected_rparen,
2336+
LPLoc);
2337+
return false;
2338+
}
2339+
22552340
/// \verbatim
22562341
/// attribute-type:
22572342
/// 'noreturn'
@@ -2325,57 +2410,17 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
23252410
StringRef Text = Tok.getText();
23262411
consumeToken();
23272412

2328-
StringRef conventionName;
2329-
StringRef witnessMethodProtocol;
2330-
2413+
TypeAttributes::Convention convention;
23312414
if (attr == TAK_convention) {
2332-
SourceLoc LPLoc;
2333-
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
2334-
if (!justChecking)
2335-
diagnose(Tok, diag::convention_attribute_expected_lparen);
2336-
return true;
2337-
}
2338-
2339-
if (Tok.isNot(tok::identifier)) {
2340-
if (!justChecking)
2341-
diagnose(Tok, diag::convention_attribute_expected_name);
2415+
bool failedToParse =
2416+
parseConventionAttributeInternal(justChecking, convention);
2417+
if (failedToParse) {
2418+
if (Tok.is(tok::r_paren))
2419+
consumeToken();
23422420
return true;
23432421
}
2344-
2345-
conventionName = Tok.getText();
2346-
consumeToken(tok::identifier);
2347-
2348-
if (conventionName == "witness_method") {
2349-
if (Tok.isNot(tok::colon)) {
2350-
if (!justChecking)
2351-
diagnose(Tok,
2352-
diag::convention_attribute_witness_method_expected_colon);
2353-
return true;
2354-
}
2355-
consumeToken(tok::colon);
2356-
if (Tok.isNot(tok::identifier)) {
2357-
if (!justChecking)
2358-
diagnose(Tok,
2359-
diag::convention_attribute_witness_method_expected_protocol);
2360-
return true;
2361-
}
2362-
2363-
witnessMethodProtocol = Tok.getText();
2364-
consumeToken(tok::identifier);
2365-
}
2366-
2367-
// Parse the ')'. We can't use parseMatchingToken if we're in
2368-
// just-checking mode.
2369-
if (justChecking && Tok.isNot(tok::r_paren))
2370-
return true;
2371-
2372-
SourceLoc RPLoc;
2373-
parseMatchingToken(tok::r_paren, RPLoc,
2374-
diag::convention_attribute_expected_rparen,
2375-
LPLoc);
23762422
}
23772423

2378-
23792424
// In just-checking mode, we only need to consume the tokens, and we don't
23802425
// want to do any other analysis.
23812426
if (justChecking)
@@ -2474,8 +2519,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc,
24742519

24752520
// Convention attribute.
24762521
case TAK_convention:
2477-
Attributes.convention = conventionName;
2478-
Attributes.conventionWitnessMethodProtocol = witnessMethodProtocol;
2522+
Attributes.ConventionArguments = convention;
24792523
break;
24802524

24812525
case TAK__opaqueReturnTypeOf: {

lib/ParseSIL/ParseSIL.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,8 @@ bool SILParser::parseSILType(SILType &Result,
12721272
if (IsFuncDecl && !attrs.has(TAK_convention)) {
12731273
// Use a random location.
12741274
attrs.setAttr(TAK_convention, P.PreviousLoc);
1275-
attrs.convention = "thin";
1275+
attrs.ConventionArguments =
1276+
TypeAttributes::Convention::makeSwiftConvention("thin");
12761277
}
12771278

12781279
ParserResult<TypeRepr> TyR = P.parseType(diag::expected_sil_type,

lib/Sema/TypeCheckType.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,7 +2165,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
21652165
if (!attrs.hasConvention()) {
21662166
rep = SILFunctionType::Representation::Thick;
21672167
} else {
2168-
auto convention = attrs.getConvention();
2168+
auto convention = attrs.getConventionName();
21692169
// SIL exposes a greater number of conventions than Swift source.
21702170
auto parsedRep =
21712171
llvm::StringSwitch<Optional<SILFunctionType::Representation>>(
@@ -2182,14 +2182,15 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
21822182
.Default(None);
21832183
if (!parsedRep) {
21842184
diagnose(attrs.getLoc(TAK_convention),
2185-
diag::unsupported_sil_convention, attrs.getConvention());
2185+
diag::unsupported_sil_convention, attrs.getConventionName());
21862186
rep = SILFunctionType::Representation::Thin;
21872187
} else {
21882188
rep = *parsedRep;
21892189
}
21902190

21912191
if (rep == SILFunctionType::Representation::WitnessMethod) {
2192-
auto protocolName = *attrs.conventionWitnessMethodProtocol;
2192+
auto protocolName =
2193+
attrs.ConventionArguments.getValue().WitnessMethodProtocol;
21932194
witnessMethodProtocol = new (Context) SimpleIdentTypeRepr(
21942195
SourceLoc(), Context.getIdentifier(protocolName));
21952196
}
@@ -2220,15 +2221,15 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
22202221
if (attrs.hasConvention()) {
22212222
auto parsedRep =
22222223
llvm::StringSwitch<Optional<FunctionType::Representation>>(
2223-
attrs.getConvention())
2224+
attrs.getConventionName())
22242225
.Case("swift", FunctionType::Representation::Swift)
22252226
.Case("block", FunctionType::Representation::Block)
22262227
.Case("thin", FunctionType::Representation::Thin)
22272228
.Case("c", FunctionType::Representation::CFunctionPointer)
22282229
.Default(None);
22292230
if (!parsedRep) {
22302231
diagnose(attrs.getLoc(TAK_convention), diag::unsupported_convention,
2231-
attrs.getConvention());
2232+
attrs.getConventionName());
22322233
rep = FunctionType::Representation::Swift;
22332234
} else {
22342235
rep = *parsedRep;
@@ -2239,7 +2240,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
22392240
rep == FunctionType::Representation::Block) {
22402241
diagnose(attrs.getLoc(TAK_convention),
22412242
diag::invalid_autoclosure_and_convention_attributes,
2242-
attrs.getConvention());
2243+
attrs.getConventionName());
22432244
attrs.clearAttribute(TAK_convention);
22442245
}
22452246
}
@@ -2355,7 +2356,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
23552356
// Remove the function attributes from the set so that we don't diagnose.
23562357
for (auto i : FunctionAttrs)
23572358
attrs.clearAttribute(i);
2358-
attrs.convention = None;
2359+
attrs.ConventionArguments = None;
23592360
}
23602361

23612362
// In SIL, handle @opened (n), which creates an existential archetype.

test/Parse/c_function_pointers.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ if true {
4747
func genericFunc<T>(_ t: T) -> T { return t }
4848

4949
let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be formed from a reference to a generic function}}
50+
51+
func ct1() -> () { print("") }
52+
53+
let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1
54+
let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}}
55+
let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}}
56+
let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}}
57+
let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}}

0 commit comments

Comments
 (0)