Skip to content

Commit 85b5af7

Browse files
authored
Merge pull request #67178 from hyp/eng/base-class-members-ambig
[cxx-interop] Do not add base class members that cause lookup ambigui…
2 parents b1f36d2 + a103325 commit 85b5af7

10 files changed

+106
-2
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5167,6 +5167,15 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
51675167
// If this is a C++ record, look through any base classes.
51685168
if (auto cxxRecord =
51695169
dyn_cast<clang::CXXRecordDecl>(recordDecl->getClangDecl())) {
5170+
// Capture the arity of already found members in the
5171+
// current record, to avoid adding ambiguous members
5172+
// from base classes.
5173+
const auto getArity =
5174+
ClangImporter::Implementation::getImportedBaseMemberDeclArity;
5175+
llvm::SmallSet<size_t, 4> foundNameArities;
5176+
for (const auto *valueDecl : result)
5177+
foundNameArities.insert(getArity(valueDecl));
5178+
51705179
for (auto base : cxxRecord->bases()) {
51715180
if (base.getAccessSpecifier() != clang::AccessSpecifier::AS_public)
51725181
continue;
@@ -5202,6 +5211,10 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
52025211
}
52035212
}
52045213
for (auto foundInBase : baseResults) {
5214+
// Do not add duplicate entry with the same arity,
5215+
// as that would cause an ambiguous lookup.
5216+
if (foundNameArities.count(getArity(foundInBase)))
5217+
continue;
52055218
if (auto newDecl = clangModuleLoader->importBaseMemberDecl(
52065219
foundInBase, recordDecl)) {
52075220
result.push_back(newDecl);
@@ -6375,6 +6388,16 @@ ValueDecl *ClangImporter::Implementation::importBaseMemberDecl(
63756388
return known->second;
63766389
}
63776390

6391+
size_t ClangImporter::Implementation::getImportedBaseMemberDeclArity(
6392+
const ValueDecl *valueDecl) {
6393+
if (auto *func = dyn_cast<FuncDecl>(valueDecl)) {
6394+
if (auto *params = func->getParameters()) {
6395+
return params->size();
6396+
}
6397+
}
6398+
return 0;
6399+
}
6400+
63786401
ValueDecl *ClangImporter::importBaseMemberDecl(ValueDecl *decl,
63796402
DeclContext *newContext) {
63806403
return Impl.importBaseMemberDecl(decl, newContext);

lib/ClangImporter/ImportDecl.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8890,6 +8890,23 @@ void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
88908890
for (auto member: members) {
88918891
// This means we found a member in a C++ record's base class.
88928892
if (swiftDecl->getClangDecl() != clangRecord) {
8893+
// Do not clone the base member into the derived class
8894+
// when the derived class already has a member of such
8895+
// name and arity.
8896+
auto memberArity =
8897+
getImportedBaseMemberDeclArity(cast<ValueDecl>(member));
8898+
bool shouldAddBaseMember = true;
8899+
for (const auto *currentMember : swiftDecl->getMembers()) {
8900+
auto vd = dyn_cast<ValueDecl>(currentMember);
8901+
if (vd->getName() == cast<ValueDecl>(member)->getName()) {
8902+
if (memberArity == getImportedBaseMemberDeclArity(vd)) {
8903+
shouldAddBaseMember = false;
8904+
break;
8905+
}
8906+
}
8907+
}
8908+
if (!shouldAddBaseMember)
8909+
continue;
88938910
// So we need to clone the member into the derived class.
88948911
if (auto newDecl = importBaseMemberDecl(cast<ValueDecl>(member), swiftDecl)) {
88958912
swiftDecl->addMember(newDecl);

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
650650
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext);
651651

652652
public:
653+
static size_t getImportedBaseMemberDeclArity(const ValueDecl *valueDecl);
654+
653655
// Cache for already-specialized function templates and any thunks they may
654656
// have.
655657
llvm::DenseMap<clang::FunctionDecl *, ValueDecl *>

test/Interop/Cxx/class/inheritance/Inputs/fields.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ struct HasThreeFields {
44
int c = 3;
55
};
66

7+
struct DerivedWithSameField : HasThreeFields {
8+
int a = 2;
9+
};
10+
711
struct DerivedWithOneField : HasThreeFields {
812
int d = 4;
913
};
@@ -59,4 +63,4 @@ struct ClassTemplate {
5963
T value;
6064
};
6165

62-
struct DerivedFromClassTemplate : ClassTemplate<int> {};
66+
struct DerivedFromClassTemplate : ClassTemplate<int> {};

test/Interop/Cxx/class/inheritance/Inputs/functions.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ struct Base {
5050
}
5151

5252
void pure() const __attribute__((pure)) {}
53+
54+
inline int sameMethodNameSameSignature() const {
55+
return 42;
56+
}
57+
58+
inline int sameMethodDifferentSignature() const {
59+
return 18;
60+
}
5361
};
5462

5563
struct OtherBase {
@@ -65,6 +73,14 @@ struct Derived : Base, OtherBase {
6573
__attribute__((swift_attr("import_unsafe"))) {
6674
return "Derived::inDerived";
6775
}
76+
77+
inline int sameMethodNameSameSignature() const {
78+
return 21;
79+
}
80+
81+
inline int sameMethodDifferentSignature(int x) const {
82+
return x + 1;
83+
}
6884
};
6985

7086
struct DerivedFromDerived : Derived {
@@ -92,4 +108,4 @@ struct EmptyBaseClass {
92108
struct DerivedFromEmptyBaseClass : EmptyBaseClass {
93109
int a = 42;
94110
int b = 42;
95-
};
111+
};

test/Interop/Cxx/class/inheritance/fields-module-interface.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
// CHECK-NEXT: var c: Int32
99
// CHECK-NEXT: }
1010

11+
// CHECK-NEXT: struct DerivedWithSameField {
12+
// CHECK-NEXT: init()
13+
// CHECK-NEXT: var a: Int32
14+
// CHECK-NEXT: var b: Int32
15+
// CHECK-NEXT: var c: Int32
16+
// CHECK-NEXT: }
17+
1118
// CHECK-NEXT: struct DerivedWithOneField {
1219
// CHECK-NEXT: init()
1320
// CHECK-NEXT: var d: Int32

test/Interop/Cxx/class/inheritance/fields.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,10 @@ FieldsTestSuite.test("Derived from class template") {
7979
expectEqual(derived.value, 42)
8080
}
8181

82+
FieldsTestSuite.test("Same field from derived") {
83+
var derived = DerivedWithSameField()
84+
derived.a = 42
85+
expectEqual(derived.a, 42)
86+
}
87+
8288
runAllTests()

test/Interop/Cxx/class/inheritance/functions-module-interface.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "swiftRenamed(input:)")
3030
// CHECK-NEXT: mutating func renamed(_ i: Int32) -> Int32
3131
// CHECK-NEXT: @_effects(readonly) func pure()
32+
// CHECK-NEXT: @discardableResult
33+
// CHECK-NEXT: func sameMethodNameSameSignature() -> Int32
34+
// CHECK-NEXT: @discardableResult
35+
// CHECK-NEXT: func sameMethodDifferentSignature() -> Int32
3236
// CHECK-NEXT: }
3337

3438
// CHECK-NEXT: struct OtherBase {
@@ -42,6 +46,10 @@
4246
// CHECK-NEXT: @discardableResult
4347
// CHECK-NEXT: func inDerived() -> UnsafePointer<CChar>!
4448
// CHECK-NEXT: @discardableResult
49+
// CHECK-NEXT: func sameMethodNameSameSignature() -> Int32
50+
// CHECK-NEXT: @discardableResult
51+
// CHECK-NEXT: func sameMethodDifferentSignature(_ x: Int32) -> Int32
52+
// CHECK-NEXT: @discardableResult
4553
// CHECK-NEXT: mutating func mutatingInBase() -> UnsafePointer<CChar>?
4654
// CHECK-NEXT: @discardableResult
4755
// CHECK-NEXT: func constInBase() -> UnsafePointer<CChar>?
@@ -57,6 +65,8 @@
5765
// CHECK-NEXT: mutating func renamed(_ i: Int32) -> Int32
5866
// CHECK-NEXT: @_effects(readonly) func pure()
5967
// CHECK-NEXT: @discardableResult
68+
// CHECK-NEXT: func sameMethodDifferentSignature() -> Int32
69+
// CHECK-NEXT: @discardableResult
6070
// CHECK-NEXT: func inOtherBase() -> UnsafePointer<CChar>?
6171
// CHECK-NEXT: }
6272

@@ -67,6 +77,10 @@
6777
// CHECK-NEXT: @discardableResult
6878
// CHECK-NEXT: func inDerived() -> UnsafePointer<CChar>?
6979
// CHECK-NEXT: @discardableResult
80+
// CHECK-NEXT: func sameMethodNameSameSignature() -> Int32
81+
// CHECK-NEXT: @discardableResult
82+
// CHECK-NEXT: func sameMethodDifferentSignature(_ x: Int32) -> Int32
83+
// CHECK-NEXT: @discardableResult
7084
// CHECK-NEXT: mutating func mutatingInBase() -> UnsafePointer<CChar>?
7185
// CHECK-NEXT: @discardableResult
7286
// CHECK-NEXT: func constInBase() -> UnsafePointer<CChar>?
@@ -82,6 +96,8 @@
8296
// CHECK-NEXT: mutating func renamed(_ i: Int32) -> Int32
8397
// CHECK-NEXT: @_effects(readonly) func pure()
8498
// CHECK-NEXT: @discardableResult
99+
// CHECK-NEXT: func sameMethodDifferentSignature() -> Int32
100+
// CHECK-NEXT: @discardableResult
85101
// CHECK-NEXT: func inOtherBase() -> UnsafePointer<CChar>?
86102
// CHECK-NEXT: }
87103

test/Interop/Cxx/class/inheritance/functions-typechecker.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ extension Base {
1111
}
1212

1313
Derived().swiftFunc() // expected-error {{value of type 'Derived' has no member 'swiftFunc'}}
14+
15+
// ok, this calls the derived method.
16+
Derived().sameMethodNameSameSignature()
17+
Derived().sameMethodDifferentSignature(1)
18+
// ok, this is the base class method.
19+
Derived().sameMethodDifferentSignature()

test/Interop/Cxx/class/inheritance/functions.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ FunctionsTestSuite.test("Other base member from derived") {
3838
expectEqual(String(cString: derived.inOtherBase()!), "OtherBase::inOtherBase")
3939
}
4040

41+
FunctionsTestSuite.test("Unambiguous members from derived") {
42+
let derived = Derived()
43+
expectEqual(derived.sameMethodNameSameSignature(), 21)
44+
expectEqual(derived.sameMethodDifferentSignature(1), 2)
45+
expectEqual(derived.sameMethodDifferentSignature(), 18)
46+
}
47+
4148
FunctionsTestSuite.test("Basic methods from derived * 2") {
4249
let dd = DerivedFromDerived()
4350
expectEqual(String(cString: dd.constInBase()!), "Base::constInBase")

0 commit comments

Comments
 (0)