Skip to content

Commit 81a0f98

Browse files
authored
Merge pull request #82194 from xymus/cdecl-parser
Parser: Accept `@cdecl` with an optional identifier for a custom C name
2 parents 9bfb680 + 7316318 commit 81a0f98

File tree

12 files changed

+167
-24
lines changed

12 files changed

+167
-24
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,11 +937,12 @@ BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed(
937937
BridgedSourceRange cRange, swift::PlatformKind platform,
938938
BridgedVersionTuple cVersion);
939939

940-
SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:)")
940+
SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:underscored:)")
941941
BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
942942
BridgedSourceLoc cAtLoc,
943943
BridgedSourceRange cRange,
944-
BridgedStringRef cName);
944+
BridgedStringRef cName,
945+
bool underscored);
945946

946947
SWIFT_NAME(
947948
"BridgedCustomAttr.createParsed(_:atLoc:type:initContext:argumentList:)")

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,9 @@ ERROR(attr_expected_comma,none,
15321532
ERROR(attr_expected_string_literal,none,
15331533
"expected string literal in '%0' attribute", (StringRef))
15341534

1535+
ERROR(attr_expected_cname,none,
1536+
"expected C identifier in '%0' attribute", (StringRef))
1537+
15351538
ERROR(attr_expected_option_such_as,none,
15361539
"expected '%0' option such as '%1'", (StringRef, StringRef))
15371540

lib/AST/Bridging/DeclAttributeBridging.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,11 @@ BridgedBackDeployedAttr BridgedBackDeployedAttr_createParsed(
204204
BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
205205
BridgedSourceLoc cAtLoc,
206206
BridgedSourceRange cRange,
207-
BridgedStringRef cName) {
207+
BridgedStringRef cName,
208+
bool underscored) {
208209
return new (cContext.unbridged())
209210
CDeclAttr(cName.unbridged(), cAtLoc.unbridged(), cRange.unbridged(),
210-
/*Implicit=*/false, /*Underscored*/true);
211+
/*Implicit=*/false, /*Underscored*/underscored);
211212
}
212213

213214
BridgedCustomAttr BridgedCustomAttr_createParsed(

lib/AST/Decl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4574,7 +4574,10 @@ StringRef ValueDecl::getCDeclName() const {
45744574

45754575
// Handle explicit cdecl attributes.
45764576
if (auto cdeclAttr = getAttrs().getAttribute<CDeclAttr>()) {
4577-
return cdeclAttr->Name;
4577+
if (!cdeclAttr->Name.empty())
4578+
return cdeclAttr->Name;
4579+
else
4580+
return getBaseIdentifier().str();
45784581
}
45794582

45804583
return "";

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -544,20 +544,37 @@ extension ASTGenVisitor {
544544
/// E.g.:
545545
/// ```
546546
/// @_cdecl("c_function_name")
547+
/// @cdecl(c_function_name)
548+
/// @cdecl
547549
/// ```
548550
func generateCDeclAttr(attribute node: AttributeSyntax) -> BridgedCDeclAttr? {
549-
self.generateWithLabeledExprListArguments(attribute: node) { args in
550-
guard let name = self.generateConsumingSimpleStringLiteralAttrOption(args: &args) else {
551+
let attrName = node.attributeName.as(IdentifierTypeSyntax.self)?.name.text
552+
let underscored = attrName?.hasPrefix("_") ?? false
553+
554+
var name: BridgedStringRef? = nil
555+
if node.arguments != nil || underscored {
556+
name = self.generateWithLabeledExprListArguments(attribute: node) {
557+
args in
558+
if underscored {
559+
self.generateConsumingSimpleStringLiteralAttrOption(args: &args)
560+
} else {
561+
self.generateConsumingPlainIdentifierAttrOption(args: &args) {
562+
return $0.rawText.bridged
563+
}
564+
}
565+
}
566+
guard name != nil else {
551567
return nil
552568
}
553-
554-
return .createParsed(
555-
self.ctx,
556-
atLoc: self.generateSourceLoc(node.atSign),
557-
range: self.generateAttrSourceRange(node),
558-
name: name
559-
)
560569
}
570+
571+
return .createParsed(
572+
self.ctx,
573+
atLoc: self.generateSourceLoc(node.atSign),
574+
range: self.generateAttrSourceRange(node),
575+
name: name ?? "",
576+
underscored: underscored
577+
)
561578
}
562579

563580
struct GeneratedDerivativeOriginalDecl {

lib/Parse/ParseDecl.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3008,7 +3008,45 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30083008
break;
30093009
}
30103010

3011-
case DeclAttrKind::CDecl:
3011+
case DeclAttrKind::CDecl: {
3012+
if (!AttrName.starts_with("_") &&
3013+
3014+
// Backwards support for @cdecl("stringId"). Remove before enabling in
3015+
// production so we accept only the identifier format.
3016+
lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
3017+
return Tok.isNot(tok::string_literal);
3018+
})) {
3019+
3020+
std::optional<StringRef> CName;
3021+
if (consumeIfAttributeLParen()) {
3022+
// Custom C name.
3023+
if (Tok.isNot(tok::identifier)) {
3024+
diagnose(Loc, diag::attr_expected_cname, AttrName);
3025+
return makeParserSuccess();
3026+
}
3027+
3028+
CName = Tok.getText();
3029+
consumeToken(tok::identifier);
3030+
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
3031+
3032+
if (!consumeIf(tok::r_paren)) {
3033+
diagnose(Loc, diag::attr_expected_rparen, AttrName,
3034+
DeclAttribute::isDeclModifier(DK));
3035+
return makeParserSuccess();
3036+
}
3037+
} else {
3038+
AttrRange = SourceRange(Loc);
3039+
}
3040+
3041+
Attributes.add(new (Context) CDeclAttr(CName.value_or(StringRef()), AtLoc,
3042+
AttrRange, /*Implicit=*/false,
3043+
/*isUnderscored*/false));
3044+
break;
3045+
}
3046+
3047+
// Leave legacy @_cdecls to the logic expecting a string.
3048+
LLVM_FALLTHROUGH;
3049+
}
30123050
case DeclAttrKind::Expose:
30133051
case DeclAttrKind::SILGenName: {
30143052
if (!consumeIfAttributeLParen()) {

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
13071307
if (!clangMangling.empty())
13081308
return clangMangling;
13091309
}
1310-
return CDeclA->Name.str();
1310+
return getDecl()->getCDeclName().str();
13111311
}
13121312

13131313
if (SKind == ASTMangler::SymbolKind::DistributedThunk) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,8 +2387,8 @@ void AttributeChecker::visitCDeclAttr(CDeclAttr *attr) {
23872387
diagnose(attr->getLocation(), diag::cdecl_not_at_top_level,
23882388
attr);
23892389

2390-
// The name must not be empty.
2391-
if (attr->Name.empty())
2390+
// @_cdecl name must not be empty.
2391+
if (attr->Name.empty() && attr->Underscored)
23922392
diagnose(attr->getLocation(), diag::cdecl_empty_name,
23932393
attr);
23942394

test/ASTGen/attrs.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// RUN: -enable-experimental-feature Lifetimes \
66
// RUN: -enable-experimental-feature RawLayout \
77
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
8+
// RUN: -enable-experimental-feature CDecl \
89
// RUN: -enable-experimental-concurrency \
910
// RUN: -enable-experimental-move-only \
1011
// RUN: -enable-experimental-feature ParserASTGen \
@@ -15,6 +16,7 @@
1516
// RUN: -enable-experimental-feature Lifetimes \
1617
// RUN: -enable-experimental-feature RawLayout \
1718
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
19+
// RUN: -enable-experimental-feature CDecl \
1820
// RUN: -enable-experimental-concurrency \
1921
// RUN: -enable-experimental-move-only \
2022
// RUN: | %sanitize-address > %t/cpp-parser.ast
@@ -28,6 +30,7 @@
2830
// RUN: -enable-experimental-feature Lifetimes \
2931
// RUN: -enable-experimental-feature RawLayout \
3032
// RUN: -enable-experimental-feature SymbolLinkageMarkers \
33+
// RUN: -enable-experimental-feature CDecl \
3134
// RUN: -enable-experimental-concurrency \
3235
// RUN: -enable-experimental-move-only
3336

@@ -39,6 +42,7 @@
3942
// REQUIRES: swift_feature_Lifetimes
4043
// REQUIRES: swift_feature_RawLayout
4144
// REQUIRES: swift_feature_SymbolLinkageMarkers
45+
// REQUIRES: swift_feature_CDecl
4246

4347
// rdar://116686158
4448
// UNSUPPORTED: asan
@@ -95,7 +99,9 @@ func fn(_: Int) {}
9599

96100
@_disallowFeatureSuppression(NoncopyableGenerics) public struct LoudlyNC<T: ~Copyable> {}
97101

98-
@_cdecl("c_function_name") func foo(x: Int) {}
102+
@_cdecl("c_function_name") func cdeclUnderscore(x: Int) {}
103+
@cdecl(c_function_name_official) func cdecl(x: Int) {}
104+
@cdecl func cdeclDefault() {}
99105

100106
struct StaticProperties {
101107
dynamic var property: Int { return 1 }

test/PrintAsObjC/cdecl-official.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@
2828
// CHECK: #endif
2929

3030
/// My documentation
31-
@cdecl("simple")
32-
func a_simple(x: Int, bar y: Int) -> Int { return x }
31+
@cdecl(simple)
32+
func a0_simple(x: Int, bar y: Int) -> Int { return x }
3333
// CHECK-LABEL: // My documentation
3434
// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t simple(ptrdiff_t x, ptrdiff_t y) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
3535

36+
@cdecl
37+
func a1_defaultName(x: Int) -> Int { return x }
38+
// CHECK-LABEL: SWIFT_EXTERN ptrdiff_t a1_defaultName(ptrdiff_t x) SWIFT_NOEXCEPT SWIFT_WARN_UNUSED_RESULT;
39+
3640
@cdecl("primitiveTypes")
3741
public func b_primitiveTypes(i: Int, ci: CInt, l: CLong, c: CChar, f: Float, d: Double, b: Bool) {}
3842
// CHECK-LABEL: SWIFT_EXTERN void primitiveTypes(ptrdiff_t i, int ci, long l, char c, float f, double d, bool b) SWIFT_NOEXCEPT;

0 commit comments

Comments
 (0)