Parser: Accept @cdecl with an indentifier for the C name

Begin accepting the attribute in the form of `@cdecl(cName)`, using an
identifier instead of a string.

For ease of landing this change we still accept the string form. We
should stop accepting it before making this feature available in
production.
This commit is contained in:
Alexis Laferrière
2025-05-16 15:33:20 -07:00
parent b8b13d8086
commit 2601ff44d4
4 changed files with 61 additions and 4 deletions

View File

@@ -1532,6 +1532,9 @@ ERROR(attr_expected_comma,none,
ERROR(attr_expected_string_literal,none,
"expected string literal in '%0' attribute", (StringRef))
ERROR(attr_expected_cname,none,
"expected C identifier in '%0' attribute", (StringRef))
ERROR(attr_expected_option_such_as,none,
"expected '%0' option such as '%1'", (StringRef, StringRef))

View File

@@ -3014,7 +3014,45 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
break;
}
case DeclAttrKind::CDecl:
case DeclAttrKind::CDecl: {
if (!AttrName.starts_with("_") &&
// Backwards support for @cdecl("stringId"). Remove before enabling in
// production so we accept only the identifier format.
lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
return Tok.isNot(tok::string_literal);
})) {
std::optional<StringRef> CName;
if (consumeIfAttributeLParen()) {
// Custom C name.
if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::attr_expected_cname, AttrName);
return makeParserSuccess();
}
CName = Tok.getText();
consumeToken(tok::identifier);
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return makeParserSuccess();
}
} else {
AttrRange = SourceRange(Loc);
}
Attributes.add(new (Context) CDeclAttr(CName.value_or(StringRef()), AtLoc,
AttrRange, /*Implicit=*/false,
/*isUnderscored*/false));
break;
}
// Leave legacy @_cdecls to the logic expecting a string.
LLVM_FALLTHROUGH;
}
case DeclAttrKind::Expose:
case DeclAttrKind::SILGenName: {
if (!consumeIfAttributeLParen()) {

View File

@@ -28,8 +28,8 @@
// CHECK: #endif
/// My documentation
@cdecl("simple")
func a_simple(x: Int, bar y: Int) -> Int { return x }
@cdecl(simple)
func a0_simple(x: Int, bar y: Int) -> Int { return x }
// CHECK-LABEL: // My documentation
// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t simple(ptrdiff_t x, ptrdiff_t y) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;

View File

@@ -6,7 +6,23 @@
// REQUIRES: swift_feature_CDecl
@cdecl("cdecl_foo") func foo(x: Int) -> Int { return x }
@cdecl(cdecl_foo) func foo(x: Int) -> Int { return x }
@cdecl(not an identifier) func invalidName() {}
// expected-error @-1 {{expected ')' in 'cdecl' attribute}}
// expected-error @-2 {{expected declaration}}
@cdecl() func emptyParen() {}
// expected-error @-1 {{expected C identifier in 'cdecl' attribute}}
// expected-error @-2 {{expected declaration}}
@cdecl(42) func aNumber() {}
// expected-error @-1 {{expected C identifier in 'cdecl' attribute}}
// expected-error @-2 {{expected declaration}}
@cdecl(a:selector:) func selectordName() {}
// expected-error @-1 {{expected ')' in 'cdecl' attribute}}
// expected-error @-2 {{expected declaration}}
@cdecl("") // expected-error{{@cdecl symbol name cannot be empty}}
func emptyName(x: Int) -> Int { return x }