Skip to content

Commit b62171a

Browse files
[c-interop] Relax C name validation as warning
And allow expert users to use potentially invalid symbols by specifying it explicitly.
1 parent 7767f6d commit b62171a

File tree

7 files changed

+49
-20
lines changed

7 files changed

+49
-20
lines changed

include/swift/AST/Attr.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,18 +2342,22 @@ class ExposeAttr : public DeclAttribute {
23422342
/// Define the `@_extern` attribute, used to import external declarations in
23432343
/// the specified way to interoperate with Swift.
23442344
class ExternAttr : public DeclAttribute {
2345+
SourceLoc LParenLoc, RParenLoc;
2346+
23452347
public:
2346-
ExternAttr(llvm::Optional<StringRef> ModuleName, llvm::Optional<StringRef> Name,
2347-
SourceLoc AtLoc, SourceRange Range, ExternKind Kind, bool Implicit)
2348-
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit),
2349-
ModuleName(ModuleName), Name(Name) {
2348+
ExternAttr(llvm::Optional<StringRef> ModuleName,
2349+
llvm::Optional<StringRef> Name, SourceLoc AtLoc,
2350+
SourceLoc LParenLoc, SourceLoc RParenLoc, SourceRange Range,
2351+
ExternKind Kind, bool Implicit)
2352+
: DeclAttribute(DAK_Extern, AtLoc, Range, Implicit), LParenLoc(LParenLoc),
2353+
RParenLoc(RParenLoc), ModuleName(ModuleName), Name(Name) {
23502354
Bits.ExternAttr.kind = static_cast<unsigned>(Kind);
23512355
}
23522356

2353-
ExternAttr(llvm::Optional<StringRef> ModuleName, llvm::Optional<StringRef> Name,
2354-
ExternKind Kind, bool Implicit)
2355-
: ExternAttr(ModuleName, Name, SourceLoc(), SourceRange(), Kind,
2356-
Implicit) {}
2357+
ExternAttr(llvm::Optional<StringRef> ModuleName,
2358+
llvm::Optional<StringRef> Name, ExternKind Kind, bool Implicit)
2359+
: ExternAttr(ModuleName, Name, SourceLoc(), SourceLoc(), SourceLoc(),
2360+
SourceRange(), Kind, Implicit) {}
23572361

23582362
/// The module name to import the named declaration in it
23592363
/// Used for Wasm import declaration.
@@ -2363,6 +2367,9 @@ class ExternAttr : public DeclAttribute {
23632367
/// std::nullopt if the declaration name is not specified with @_extern(c)
23642368
const llvm::Optional<StringRef> Name;
23652369

2370+
SourceLoc getLParenLoc() const { return LParenLoc; }
2371+
SourceLoc getRParenLoc() const { return RParenLoc; }
2372+
23662373
/// Returns the kind of extern.
23672374
ExternKind getExternKind() const {
23682375
return static_cast<ExternKind>(Bits.ExternAttr.kind);

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,8 +1878,10 @@ ERROR(extern_empty_c_name,none,
18781878
"expected non-empty C name in @_extern attribute", ())
18791879
ERROR(extern_only_non_other_attr,none,
18801880
"@_extern attribute cannot be applied to an '@%0' declaration", (StringRef))
1881-
ERROR(extern_c_invalid_name, none,
1882-
"@_extern attribute has invalid C name '%0'", (StringRef))
1881+
WARNING(extern_c_maybe_invalid_name, none,
1882+
"C name '%0' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning",
1883+
(StringRef))
1884+
18831885
ERROR(c_func_variadic, none,
18841886
"cannot declare variadic argument %0 in %kind1",
18851887
(DeclName, const ValueDecl *))

lib/Parse/ParseDecl.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,7 @@ Parser::parseDifferentiableAttribute(SourceLoc atLoc, SourceLoc loc) {
14341434
bool Parser::parseExternAttribute(DeclAttributes &Attributes,
14351435
bool &DiscardAttribute, StringRef AttrName,
14361436
SourceLoc AtLoc, SourceLoc Loc) {
1437+
SourceLoc lParenLoc = Tok.getLoc(), rParenLoc;
14371438

14381439
// Parse @_extern(<language>, ...)
14391440
if (!consumeIf(tok::l_paren)) {
@@ -1512,6 +1513,7 @@ bool Parser::parseExternAttribute(DeclAttributes &Attributes,
15121513
}
15131514
}
15141515

1516+
rParenLoc = Tok.getLoc();
15151517
if (!consumeIf(tok::r_paren)) {
15161518
diagnose(Loc, diag::attr_expected_rparen, AttrName,
15171519
DeclAttribute::isDeclModifier(DAK_Extern));
@@ -1527,9 +1529,10 @@ bool Parser::parseExternAttribute(DeclAttributes &Attributes,
15271529
}
15281530

15291531
if (!DiscardAttribute) {
1530-
Attributes.add(new (Context) ExternAttr(importModuleName, importName, AtLoc,
1531-
AttrRange, kind,
1532-
/*Implicit=*/false));
1532+
Attributes.add(new (Context)
1533+
ExternAttr(importModuleName, importName, AtLoc,
1534+
lParenLoc, rParenLoc, AttrRange, kind,
1535+
/*Implicit=*/false));
15331536
}
15341537
return false;
15351538
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,11 +2134,14 @@ void AttributeChecker::visitExternAttr(ExternAttr *attr) {
21342134
StringRef cName = attr->getCName(FD);
21352135
if (cName.empty()) {
21362136
diagnose(attr->getLocation(), diag::extern_empty_c_name);
2137-
} else if (!clang::isValidAsciiIdentifier(cName)) {
2138-
// Conservatively ban non ASCII identifiers. The C standard allows
2137+
} else if (!attr->Name.has_value() && !clang::isValidAsciiIdentifier(cName)) {
2138+
// Warn non ASCII identifiers if it's *implicitly* specified. The C standard allows
21392139
// Universal Character Names in identifiers, but clang doesn't provide
2140-
// an easy way to validate them, so we don't allow them for now.
2141-
diagnose(attr->getLocation(), diag::extern_c_invalid_name, cName);
2140+
// an easy way to validate them, so we warn identifers that is potentially
2141+
// invalid. If it's explicitly specified, we assume the user knows what
2142+
// they are doing, and don't warn.
2143+
diagnose(attr->getLocation(), diag::extern_c_maybe_invalid_name, cName)
2144+
.fixItInsert(attr->getRParenLoc(), (", \"" + cName + "\"").str());
21422145
}
21432146

21442147
// Ensure the decl has C compatible interface. Otherwise it produces diagnostics.

test/attr/attr_extern.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,16 @@ func underscoredValid()
4949
@_extern(c, "") // expected-error {{expected non-empty C name in @_extern attribute}}
5050
func emptyCName()
5151
52-
@_extern(c, "0start_with_digit") // expected-error {{@_extern attribute has invalid C name '0start_with_digit'}}
53-
func digitPrefixed()
52+
// Allow specifying any identifier explicitly
53+
@_extern(c, "0start_with_digit")
54+
func explicitDigitPrefixed()
5455
55-
@_extern(c) // expected-error {{@_extern attribute has invalid C name '+'}}
56+
@_extern(c) // expected-warning {{C name '+' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning}}
5657
func +(a: Int, b: Bool) -> Bool
5758
59+
@_extern(c) // expected-warning {{C name '🥸_implicitInvalid' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning}}
60+
func 🥸_implicitInvalid()
61+
5862
@_extern(c)
5963
func omitCName()
6064

test/attr/attr_extern_fixit.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-typecheck-verify-swift -emit-fixits-path %t.remap -fixit-all -diagnostics-editor-mode
2+
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
3+
4+
@_extern(c) // expected-warning {{C name '+' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning}}
5+
func +(a: Int, b: Bool) -> Bool
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %target-typecheck-verify-swift -emit-fixits-path %t.remap -fixit-all -diagnostics-editor-mode
2+
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
3+
4+
@_extern(c, "+") // expected-warning {{C name '+' may be invalid; explicitly specify the name in @_extern(c) to suppress this warning}}
5+
func +(a: Int, b: Bool) -> Bool

0 commit comments

Comments
 (0)