Skip to content

[Clang][Sema] Allow qualified type names in swift_name attribute #145947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2025

Conversation

egorzhdan
Copy link
Contributor

This allows adding __attribute__((swift_name("MyNamespace.MyType.my_method()"))) on standalone C++ functions to make Swift import them as member functions of a type that is nested in a C++ namespace.

rdar://138934888

This allows adding `__attribute__((swift_name("MyNamespace.MyType.my_method()")))` on standalone C++ functions to make Swift import them as member functions of a type that is nested in a C++ namespace.

rdar://138934888
@egorzhdan egorzhdan requested a review from Xazax-hun June 26, 2025 17:57
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jun 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 26, 2025

@llvm/pr-subscribers-clang

Author: Egor Zhdan (egorzhdan)

Changes

This allows adding __attribute__((swift_name("MyNamespace.MyType.my_method()"))) on standalone C++ functions to make Swift import them as member functions of a type that is nested in a C++ namespace.

rdar://138934888


Full diff: https://github.com/llvm/llvm-project/pull/145947.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaSwift.cpp (+13-4)
  • (modified) clang/test/SemaObjCXX/attr-swift_name-cxx.mm (+36)
diff --git a/clang/lib/Sema/SemaSwift.cpp b/clang/lib/Sema/SemaSwift.cpp
index 4aae855a24b8f..4000beff7dc49 100644
--- a/clang/lib/Sema/SemaSwift.cpp
+++ b/clang/lib/Sema/SemaSwift.cpp
@@ -72,6 +72,15 @@ static bool isValidSwiftErrorResultType(QualType Ty) {
   return isValidSwiftContextType(Ty);
 }
 
+static bool isValidSwiftContextName(StringRef ContextName) {
+  // ContextName might be qualified, e.g. 'MyNamespace.MyStruct'.
+  SmallVector<StringRef, 1> ContextNameComponents;
+  ContextName.split(ContextNameComponents, '.');
+  return all_of(ContextNameComponents, [&](StringRef Component) {
+    return isValidAsciiIdentifier(Component);
+  });
+}
+
 void SemaSwift::handleAttrAttr(Decl *D, const ParsedAttr &AL) {
   if (AL.isInvalid() || AL.isUsedAsTypeAttr())
     return;
@@ -356,11 +365,11 @@ static bool validateSwiftFunctionName(Sema &S, const ParsedAttr &AL,
 
   // Split at the first '.', if it exists, which separates the context name
   // from the base name.
-  std::tie(ContextName, BaseName) = BaseName.split('.');
+  std::tie(ContextName, BaseName) = BaseName.rsplit('.');
   if (BaseName.empty()) {
     BaseName = ContextName;
     ContextName = StringRef();
-  } else if (ContextName.empty() || !isValidAsciiIdentifier(ContextName)) {
+  } else if (ContextName.empty() || !isValidSwiftContextName(ContextName)) {
     S.Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
         << AL << /*context*/ 1;
     return false;
@@ -584,11 +593,11 @@ bool SemaSwift::DiagnoseName(Decl *D, StringRef Name, SourceLocation Loc,
              !IsAsync) {
     StringRef ContextName, BaseName;
 
-    std::tie(ContextName, BaseName) = Name.split('.');
+    std::tie(ContextName, BaseName) = Name.rsplit('.');
     if (BaseName.empty()) {
       BaseName = ContextName;
       ContextName = StringRef();
-    } else if (!isValidAsciiIdentifier(ContextName)) {
+    } else if (!isValidSwiftContextName(ContextName)) {
       Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
           << AL << /*context*/ 1;
       return false;
diff --git a/clang/test/SemaObjCXX/attr-swift_name-cxx.mm b/clang/test/SemaObjCXX/attr-swift_name-cxx.mm
index 658dbfff185ca..9e83c63ecf2f8 100644
--- a/clang/test/SemaObjCXX/attr-swift_name-cxx.mm
+++ b/clang/test/SemaObjCXX/attr-swift_name-cxx.mm
@@ -1,7 +1,43 @@
 // RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
 
+#define SWIFT_NAME(name) __attribute__((swift_name(name)))
 #define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name)))
 
+namespace MyNS {
+struct NestedStruct {};
+}
+
+void nestedStruct_method(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.method(self:)");
+void nestedStruct_methodConstRef(const MyNS::NestedStruct&) SWIFT_NAME("MyNS.NestedStruct.methodConstRef(self:)");
+void nestedStruct_invalidContext1(MyNS::NestedStruct) SWIFT_NAME(".MyNS.NestedStruct.invalidContext1(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+void nestedStruct_invalidContext2(MyNS::NestedStruct) SWIFT_NAME("MyNS::NestedStruct.invalidContext2(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+void nestedStruct_invalidContext3(MyNS::NestedStruct) SWIFT_NAME("::MyNS::NestedStruct.invalidContext3(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+void nestedStruct_invalidContext4(MyNS::NestedStruct) SWIFT_NAME("MyNS..NestedStruct.invalidContext4(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+void nestedStruct_invalidContext5(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.invalidContext5.(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}
+void nestedStruct_invalidContext6(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct::invalidContext6(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}
+
+namespace MyNS {
+namespace MyDeepNS {
+struct DeepNestedStruct {};
+}
+}
+
+void deepNestedStruct_method(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.method(self:)");
+void deepNestedStruct_methodConstRef(const MyNS::MyDeepNS::DeepNestedStruct&) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.methodConstRef(self:)");
+void deepNestedStruct_invalidContext(const MyNS::MyDeepNS::DeepNestedStruct&) SWIFT_NAME("MyNS::MyDeepNS::DeepNestedStruct.methodConstRef(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+
+typedef MyNS::MyDeepNS::DeepNestedStruct DeepNestedStructTypedef;
+
+void deepNestedStructTypedef_method(DeepNestedStructTypedef) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");
+void deepNestedStructTypedef_methodQualName(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");
+
+struct TopLevelStruct {
+  struct StructInStruct {};
+};
+
+void structInStruct_method(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct.StructInStruct.method(self:)");
+void structInStruct_invalidContext(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct::StructInStruct.method(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
+
 typedef int (^CallbackTy)(void);
 
 class CXXClass {

@egorzhdan egorzhdan merged commit d8ca77e into main Jun 27, 2025
10 checks passed
@egorzhdan egorzhdan deleted the users/egorzhdan/allow-qual-swift-name branch June 27, 2025 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants