[cxx-interop] Allow import-as-member for types in namespaces

This adds support for `swift_name` attribute being used with C++ types that are declared within namespaces, e.g.
```
__attribute__((swift_name("MyNamespace.MyType.my_method()")))
```

Previously import-as-member would only accept a top-level unqualified type name.

rdar://138934888
This commit is contained in:
Egor Zhdan
2025-06-25 19:31:09 +01:00
parent 17cbe28e9d
commit e95f6a3ce9
8 changed files with 127 additions and 15 deletions

View File

@@ -219,7 +219,7 @@ bool SwiftLookupTable::contextRequiresName(ContextKind kind) {
/// Try to translate the given Clang declaration into a context.
static std::optional<SwiftLookupTable::StoredContext>
translateDeclToContext(clang::NamedDecl *decl) {
translateDeclToContext(const clang::NamedDecl *decl) {
// Tag declaration.
if (auto tag = dyn_cast<clang::TagDecl>(decl)) {
if (tag->getIdentifier())
@@ -324,22 +324,46 @@ SwiftLookupTable::translateContext(EffectiveClangContext context) {
/// Lookup an unresolved context name and resolve it to a Clang
/// declaration context or typedef name.
clang::NamedDecl *SwiftLookupTable::resolveContext(StringRef unresolvedName) {
const clang::NamedDecl *
SwiftLookupTable::resolveContext(StringRef unresolvedName) {
SmallVector<StringRef, 1> nameComponents;
unresolvedName.split(nameComponents, '.');
EffectiveClangContext parentContext;
// Look for a context with the given Swift name.
for (auto entry :
lookup(SerializedSwiftName(unresolvedName),
std::make_pair(ContextKind::TranslationUnit, StringRef()))) {
if (auto decl = entry.dyn_cast<clang::NamedDecl *>()) {
if (isa<clang::TagDecl>(decl) ||
isa<clang::ObjCInterfaceDecl>(decl) ||
isa<clang::TypedefNameDecl>(decl))
return decl;
for (auto nameComponent : nameComponents) {
auto entries =
parentContext
? lookup(SerializedSwiftName(nameComponent), parentContext)
: lookup(SerializedSwiftName(nameComponent),
std::make_pair(ContextKind::TranslationUnit, StringRef()));
bool entryFound = false;
for (auto entry : entries) {
if (auto decl = entry.dyn_cast<clang::NamedDecl *>()) {
if (isa<clang::TagDecl>(decl) ||
isa<clang::ObjCInterfaceDecl>(decl) ||
isa<clang::NamespaceDecl>(decl)) {
entryFound = true;
parentContext = EffectiveClangContext(cast<clang::DeclContext>(decl));
break;
}
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(decl)) {
entryFound = true;
parentContext = EffectiveClangContext(typedefDecl);
break;
}
}
}
// If we could not resolve this component of the qualified name, bail.
if (!entryFound)
return nullptr;
}
// FIXME: Search imported modules to resolve the context.
return nullptr;
return parentContext.getAsDeclContext()
? cast<clang::NamedDecl>(parentContext.getAsDeclContext())
: parentContext.getTypedefName();
}
void SwiftLookupTable::addCategory(clang::ObjCCategoryDecl *category) {

View File

@@ -593,7 +593,7 @@ private:
public:
/// Lookup an unresolved context name and resolve it to a Clang
/// named declaration.
clang::NamedDecl *resolveContext(StringRef unresolvedName);
const clang::NamedDecl *resolveContext(StringRef unresolvedName);
/// Lookup the set of entities with the given base name.
///

View File

@@ -13,4 +13,4 @@ Functions:
- Name: ZXSpectrumSetMisnamedRegister
SwiftName: 'setter:ZXSpectrum.misnamedRegister(self:newValue:)'
- Name: ZXSpectrumHelperReset
SwiftName: 'ZXSpectrum.Helper.reset()'
SwiftName: 'ZXSpectrum::Helper.reset()'

View File

@@ -0,0 +1,26 @@
#define SWIFT_NAME(name) __attribute__((swift_name(name)))
namespace MyNS {
struct NestedStruct {
int value = 123;
};
}
int nestedStruct_method(MyNS::NestedStruct p) SWIFT_NAME("MyNS.NestedStruct.method(self:)") { return p.value; }
int nestedStruct_methodConstRef(const MyNS::NestedStruct &p) SWIFT_NAME("MyNS.NestedStruct.methodConstRef(self:)") { return p.value + 1; }
namespace MyNS {
namespace MyDeepNS {
struct DeepNestedStruct {
int value = 456;
};
}
}
int deepNestedStruct_method(MyNS::MyDeepNS::DeepNestedStruct p) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.method(self:)") { return p.value; }
int deepNestedStruct_methodConstRef(const MyNS::MyDeepNS::DeepNestedStruct &p) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.methodConstRef(self:)") { return p.value + 2; }
typedef MyNS::MyDeepNS::DeepNestedStruct DeepNestedStructTypedef;
int deepNestedStructTypedef_method(DeepNestedStructTypedef p) SWIFT_NAME("DeepNestedStructTypedef.methodTypedef(self:)") { return p.value + 3; }
int deepNestedStructTypedef_methodQualName(MyNS::MyDeepNS::DeepNestedStruct p) SWIFT_NAME("DeepNestedStructTypedef.methodTypedefQualName(self:)") { return p.value + 4; }

View File

@@ -60,6 +60,12 @@ module Enums {
requires cplusplus
}
module ImportAsMember {
header "import-as-member.h"
export *
requires cplusplus
}
module MembersDirect {
header "members-direct.h"
requires cplusplus

View File

@@ -0,0 +1,13 @@
// RUN: %target-swift-ide-test -print-module -module-to-print=ImportAsMember -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
// CHECK: extension MyNS.NestedStruct {
// CHECK-NEXT: func method() -> Int32
// CHECK-NEXT: func methodConstRef() -> Int32
// CHECK-NEXT: }
// CHECK: extension MyNS.MyDeepNS.DeepNestedStruct {
// CHECK-NEXT: func method() -> Int32
// CHECK-NEXT: func methodConstRef() -> Int32
// CHECK-NEXT: func methodTypedef() -> Int32
// CHECK-NEXT: func methodTypedefQualName() -> Int32
// CHECK-NEXT: }

View File

@@ -0,0 +1,19 @@
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=upcoming-swift
import ImportAsMember
func takesNestedStruct(_ s: MyNS.NestedStruct) {
_ = s.method()
_ = s.methodConstRef()
_ = nestedStruct_method(s) // expected-error {{'nestedStruct_method' has been replaced by instance method 'MyNS.NestedStruct.method()'}}
}
func takesDeepNestedStruct(_ s: MyNS.MyDeepNS.DeepNestedStruct) {
_ = s.method()
_ = s.methodConstRef()
_ = s.methodTypedef()
_ = s.methodTypedefQualName()
_ = deepNestedStruct_method(s) // expected-error {{'deepNestedStruct_method' has been replaced by instance method 'MyNS.MyDeepNS.DeepNestedStruct.method()'}}
}

View File

@@ -0,0 +1,24 @@
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift)
// REQUIRES: executable_test
import StdlibUnittest
import ImportAsMember
var NamespacesTestSuite = TestSuite("Import as member of namespace")
NamespacesTestSuite.test("Struct in a namespace") {
let s = MyNS.NestedStruct()
expectEqual(123, s.method())
expectEqual(124, s.methodConstRef())
}
NamespacesTestSuite.test("Struct in a deep namespace") {
let s = MyNS.MyDeepNS.DeepNestedStruct()
expectEqual(456, s.method())
expectEqual(458, s.methodConstRef())
expectEqual(459, s.methodTypedef())
expectEqual(460, s.methodTypedefQualName())
}
runAllTests()