Skip to content

Commit 79227e7

Browse files
committed
[cxx-interop] Fix ambiguous methods in long chains of inheritance
1 parent 54f0bf2 commit 79227e7

File tree

5 files changed

+43
-1
lines changed

5 files changed

+43
-1
lines changed

include/swift/AST/ClangModuleLoader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ class ClangModuleLoader : public ModuleLoader {
216216
DeclContext *newContext,
217217
ClangInheritanceInfo inheritance) = 0;
218218

219+
/// Checks if \param decl is the original method or a clone from a base class
220+
virtual bool isClonedMemberDecl(ValueDecl *decl) = 0;
221+
219222
/// Emits diagnostics for any declarations named name
220223
/// whose direct declaration context is a TU.
221224
virtual void diagnoseTopLevelValue(const DeclName &name) = 0;

include/swift/ClangImporter/ClangImporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ class ClangImporter final : public ClangModuleLoader {
657657
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
658658
ClangInheritanceInfo inheritance) override;
659659

660+
bool isClonedMemberDecl(ValueDecl *decl) override;
661+
660662
/// Emits diagnostics for any declarations named name
661663
/// whose direct declaration context is a TU.
662664
void diagnoseTopLevelValue(const DeclName &name) override;

lib/ClangImporter/ClangImporter.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6311,7 +6311,8 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
63116311
cast<NominalTypeDecl>(recordDecl)->getCurrentMembersWithoutLoading()) {
63126312
auto namedMember = dyn_cast<ValueDecl>(member);
63136313
if (!namedMember || !namedMember->hasName() ||
6314-
namedMember->getName().getBaseName() != name)
6314+
namedMember->getName().getBaseName() != name ||
6315+
clangModuleLoader->isClonedMemberDecl(namedMember))
63156316
continue;
63166317

63176318
auto *imported = clangModuleLoader->importBaseMemberDecl(
@@ -7664,11 +7665,24 @@ ValueDecl *ClangImporter::Implementation::importBaseMemberDecl(
76647665
if (known == clonedBaseMembers.end()) {
76657666
ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance);
76667667
known = clonedBaseMembers.insert({key, cloned}).first;
7668+
clonedMembers.insert(cloned);
76677669
}
76687670

76697671
return known->second;
76707672
}
76717673

7674+
bool ClangImporter::Implementation::isClonedMemberDecl(ValueDecl *decl) {
7675+
// If this is a cloned decl, we don't want to reclone it
7676+
// Otherwise, we may end up with multiple copies of the same method
7677+
if (!decl->hasClangNode()) {
7678+
// Skip decls with a clang node as those will never be a clone
7679+
auto result = clonedMembers.find(decl);
7680+
return result != clonedMembers.end();
7681+
}
7682+
7683+
return false;
7684+
}
7685+
76727686
size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity(
76737687
const ValueDecl *valueDecl) {
76747688
if (auto *func = dyn_cast<FuncDecl>(valueDecl)) {
@@ -7685,6 +7699,10 @@ ClangImporter::importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
76857699
return Impl.importBaseMemberDecl(decl, newContext, inheritance);
76867700
}
76877701

7702+
bool ClangImporter::isClonedMemberDecl(ValueDecl *decl) {
7703+
return Impl.isClonedMemberDecl(decl);
7704+
}
7705+
76887706
void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
76897707
Impl.diagnoseTopLevelValue(name);
76907708
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
692692
llvm::DenseMap<std::pair<ValueDecl *, DeclContext *>, ValueDecl *>
693693
clonedBaseMembers;
694694

695+
// Store all methods that result from cloning a base member
696+
llvm::DenseSet<ValueDecl *> clonedMembers;
697+
695698
public:
696699
llvm::DenseMap<const clang::ParmVarDecl*, FuncDecl*> defaultArgGenerators;
697700

@@ -700,6 +703,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
700703
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
701704
ClangInheritanceInfo inheritance);
702705

706+
bool isClonedMemberDecl(ValueDecl *decl);
707+
703708
static size_t getImportedBaseMemberDeclArity(const ValueDecl *valueDecl);
704709

705710
// Cache for already-specialized function templates and any thunks they may

test/Interop/Cxx/class/inheritance/inherited-lookup-executable.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ import StdlibUnittest
66

77
var InheritedMemberTestSuite = TestSuite("Test if inherited lookup works")
88

9+
// The compiler handles
10+
// - let iiiOne = IIIOne()
11+
// - struct Foo { let iiiOne : IIIOne }
12+
// differently and because of that, by introducing the struct HasOne in this test,
13+
// the function importBaseMemberDecl is called twice for each pair of parent-child structs.
14+
// The second time, all the structs have a clone version of the method,
15+
// which was triggering a "use of ambiguous method" error.
16+
struct HasOne {
17+
let one: One
18+
let iOne: IOne
19+
let iiOne: IIOne
20+
let iiiOne: IIIOne
21+
}
22+
923
InheritedMemberTestSuite.test("Regular methods resolve to base classes") {
1024
// No inheritance (sanity check)
1125
let one = One()

0 commit comments

Comments
 (0)