Skip to content

Commit e910d9b

Browse files
authored
Merge pull request #81709 from swiftlang/susmonteiro/ambiguous-use-of-method
[cxx-interop] Fix ambiguous methods in long chains of inheritance
2 parents d5ed335 + 79227e7 commit e910d9b

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
@@ -6285,7 +6285,8 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
62856285
cast<NominalTypeDecl>(recordDecl)->getCurrentMembersWithoutLoading()) {
62866286
auto namedMember = dyn_cast<ValueDecl>(member);
62876287
if (!namedMember || !namedMember->hasName() ||
6288-
namedMember->getName().getBaseName() != name)
6288+
namedMember->getName().getBaseName() != name ||
6289+
clangModuleLoader->isClonedMemberDecl(namedMember))
62896290
continue;
62906291

62916292
auto *imported = clangModuleLoader->importBaseMemberDecl(
@@ -7638,11 +7639,24 @@ ValueDecl *ClangImporter::Implementation::importBaseMemberDecl(
76387639
if (known == clonedBaseMembers.end()) {
76397640
ValueDecl *cloned = cloneBaseMemberDecl(decl, newContext, inheritance);
76407641
known = clonedBaseMembers.insert({key, cloned}).first;
7642+
clonedMembers.insert(cloned);
76417643
}
76427644

76437645
return known->second;
76447646
}
76457647

7648+
bool ClangImporter::Implementation::isClonedMemberDecl(ValueDecl *decl) {
7649+
// If this is a cloned decl, we don't want to reclone it
7650+
// Otherwise, we may end up with multiple copies of the same method
7651+
if (!decl->hasClangNode()) {
7652+
// Skip decls with a clang node as those will never be a clone
7653+
auto result = clonedMembers.find(decl);
7654+
return result != clonedMembers.end();
7655+
}
7656+
7657+
return false;
7658+
}
7659+
76467660
size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity(
76477661
const ValueDecl *valueDecl) {
76487662
if (auto *func = dyn_cast<FuncDecl>(valueDecl)) {
@@ -7659,6 +7673,10 @@ ClangImporter::importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
76597673
return Impl.importBaseMemberDecl(decl, newContext, inheritance);
76607674
}
76617675

7676+
bool ClangImporter::isClonedMemberDecl(ValueDecl *decl) {
7677+
return Impl.isClonedMemberDecl(decl);
7678+
}
7679+
76627680
void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
76637681
Impl.diagnoseTopLevelValue(name);
76647682
}

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)