Skip to content

Commit 796075a

Browse files
authored
[cxx-interop] Fix spurious ambiguous member lookup (#78132)
Nested calls to importBaseMemberDecl() subvert its cache and compromise its idempotence, causing the semantic checker to spuriously report ambiguous member lookups when multiple ClangRecordMemberLookup requests are made (e.g., because of an unrelated missing member lookup). One such scenario is documented as a test case: test/Interop/Cxx/class/inheritance/inherited-lookup-typechecker.swift fails without this patch because of the expected error from the missing member. Meanwhile, test/Interop/Cxx/class/inheritance/inherited-lookup-executable.swift works because it does not attempt to access a missing member. This patch fixes the issue by only calling importBaseMemberDecl() in the most derived class (where the ClangRecordMemberLookup originated, i.e., not in recursive requests). As a consequence of my patch, synthesized member accessors in the derived class directly invoke the member from the base class where the member is inherited from, rather than incurring an indirection at each level of inheritance. As such, the synthesized symbol names are different (and shorter). I've taken this opportunity to update the relevant tests to // CHECK for more of the mangled symbol, rather than only the synthesized symbol prefix, for more precise testing and slightly better readability. rdar://141069984
1 parent b5cfa44 commit 796075a

File tree

11 files changed

+93
-18
lines changed

11 files changed

+93
-18
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,11 @@ class CXXNamespaceMemberLookup
135135
struct ClangRecordMemberLookupDescriptor final {
136136
NominalTypeDecl *recordDecl;
137137
DeclName name;
138+
bool inherited;
138139

139-
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name)
140-
: recordDecl(recordDecl), name(name) {
140+
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name,
141+
bool inherited = false)
142+
: recordDecl(recordDecl), name(name), inherited(inherited) {
141143
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
142144
}
143145

lib/ClangImporter/ClangImporter.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "clang/Basic/IdentifierTable.h"
6464
#include "clang/Basic/LangStandard.h"
6565
#include "clang/Basic/Module.h"
66+
#include "clang/Basic/Specifiers.h"
6667
#include "clang/Basic/TargetInfo.h"
6768
#include "clang/CAS/CASOptions.h"
6869
#include "clang/CAS/IncludeTree.h"
@@ -6147,6 +6148,7 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
61476148
Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const {
61486149
NominalTypeDecl *recordDecl = desc.recordDecl;
61496150
DeclName name = desc.name;
6151+
bool inherited = desc.inherited;
61506152

61516153
auto &ctx = recordDecl->getASTContext();
61526154
auto allResults = evaluateOrDefault(
@@ -6200,9 +6202,11 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
62006202
continue;
62016203

62026204
// Add Clang members that are imported lazily.
6203-
auto baseResults = evaluateOrDefault(
6204-
ctx.evaluator,
6205-
ClangRecordMemberLookup({cast<NominalTypeDecl>(import), name}), {});
6205+
auto baseResults =
6206+
evaluateOrDefault(ctx.evaluator,
6207+
ClangRecordMemberLookup(
6208+
{cast<NominalTypeDecl>(import), name, true}),
6209+
{});
62066210
// Add members that are synthesized eagerly, such as subscripts.
62076211
for (auto member :
62086212
cast<NominalTypeDecl>(import)->getCurrentMembersWithoutLoading()) {
@@ -6221,6 +6225,21 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
62216225
// as that would cause an ambiguous lookup.
62226226
if (foundNameArities.count(getArity(foundInBase)))
62236227
continue;
6228+
6229+
// Do not importBaseMemberDecl() if this is a recursive lookup into
6230+
// some class's superclass. importBaseMemberDecl() caches synthesized
6231+
// members, which does not work if we call it on its result, e.g.:
6232+
//
6233+
// importBaseMemberDecl(importBaseMemberDecl(foundInBase,
6234+
// recorDecl), recordDecl)
6235+
//
6236+
// Instead, we simply pass on the imported decl (foundInBase) as is,
6237+
// so that only the top-most request calls importBaseMemberDecl().
6238+
if (inherited) {
6239+
result.push_back(foundInBase);
6240+
continue;
6241+
}
6242+
62246243
if (auto newDecl = clangModuleLoader->importBaseMemberDecl(
62256244
foundInBase, recordDecl)) {
62266245
result.push_back(newDecl);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
struct Base1 {
4+
int methodBase(void) const { return 1; }
5+
};
6+
7+
struct IBase1 : Base1 {
8+
int methodIBase(void) const { return 11; }
9+
};
10+
11+
struct IIBase1 : IBase1 {
12+
int methodIIBase(void) const { return 111; }
13+
};

test/Interop/Cxx/class/inheritance/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ module FunctionsObjC {
4343
requires cplusplus
4444
requires objc
4545
}
46+
47+
module InheritedLookup {
48+
header "inherited-lookup.h"
49+
requires cplusplus
50+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ func testGetX() -> CInt {
99

1010
let _ = testGetX()
1111

12-
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]])
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{(.*)(30CopyTrackedDerivedDerivedClass33__synthesizedBaseGetterAccessor_x|__synthesizedBaseGetterAccessor_x@CopyTrackedDerivedDerivedClass)(.*)}}(ptr {{.*}} %[[THIS_PTR:.*]])
1313
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i{{32|64}} 4
14-
// CHECK: call{{.*}} i32 @{{.*}}__synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[ADD_PTR]])
14+
// CHECK: %[[X:.*]] = getelementptr inbounds %class.CopyTrackedBaseClass, ptr %[[ADD_PTR]], i32 0, i32 0
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -cxx-interoperability-mode=default)
2+
//
3+
// REQUIRES: executable_test
4+
import InheritedLookup
5+
import StdlibUnittest
6+
7+
var InheritedMemberTestSuite = TestSuite("Test if inherited lookup works")
8+
9+
InheritedMemberTestSuite.test("IIBase1::method() resolves to grandparent") {
10+
let iibase1 = IIBase1()
11+
expectEqual(iibase1.methodBase(), 1)
12+
expectEqual(iibase1.methodIBase(), 11)
13+
expectEqual(iibase1.methodIIBase(), 111)
14+
}
15+
16+
runAllTests()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift -verify-ignore-unknown -I %S/Inputs -cxx-interoperability-mode=default
2+
import InheritedLookup
3+
4+
extension IIBase1 {
5+
func ext() {
6+
// NOTE: we deliberately look up a missing member above because doing so
7+
// forces multiple ClangRecordMemberLookup requests, which should be
8+
// idempotent (though this hasn't always been the case, because bugs).
9+
missing() // expected-error {{cannot find 'missing' in scope}}
10+
11+
// For instance, a non-idempotent ClangRecordMemberLookup would cause
12+
// the following to appear ambiguous:
13+
methodBase()
14+
methodIBase()
15+
methodIIBase()
16+
}
17+
}
18+
19+
func f(v: IIBase1) {
20+
v.missing() // expected-error {{'IIBase1' has no member 'missing'}}
21+
v.methodBase()
22+
v.methodIBase()
23+
v.methodIIBase()
24+
}

test/Interop/Cxx/class/inheritance/subscript-irgen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ func testGetX() -> CInt {
99

1010
let _ = testGetX()
1111

12-
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]], i32 {{.*}})
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{(.*)(30CopyTrackedDerivedDerivedClass39__synthesizedBaseCall_operatorSubscript|__synthesizedBaseCall_operatorSubscript@CopyTrackedDerivedDerivedClass)(.*)}}(ptr {{.*}} %[[THIS_PTR:.*]], i32 {{.*}})
1313
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i{{32|64}} 4
14-
// CHECK: call{{.*}} i32 @{{.*}}__synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[ADD_PTR]], i32 {{.*}})
14+
// CHECK: %[[CALL:.*]] = call {{.*}} i32 @{{.*}}CopyTrackedBaseClass{{.*}}(ptr {{.*}} %[[ADD_PTR]], i32 {{.*}})

test/Interop/Cxx/class/move-only/inherited-field-access-irgen.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@ func testSetX(_ x: CInt) {
1717

1818
testSetX(2)
1919

20-
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}}
21-
22-
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseCall___synthesizedBaseSetterAccessor{{.*}}
23-
24-
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseGetterAccessor{{.*}}
20+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{(.*)(31NonCopyableHolderDerivedDerived*33__synthesizedBaseGetterAccessor_x|__synthesizedBaseGetterAccessor_x@NonCopyableHolderDerivedDerived)(.*)}}
2521
// CHECK: %[[VPTR:.*]] = getelementptr inbounds %struct.NonCopyableHolder
2622
// CHECK: ret ptr %[[VPTR]]
2723

28-
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseSetterAccessor{{.*}}
24+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{(.*)(31NonCopyableHolderDerivedDerived33__synthesizedBaseSetterAccessor_x|__synthesizedBaseSetterAccessor_x@NonCopyableHolderDerivedDerived)(.*)}}
2925
// CHECK: %[[VPTRS:.*]] = getelementptr inbounds %struct.NonCopyableHolder
3026
// CHECK: ret ptr %[[VPTRS]]

test/Interop/Cxx/class/move-only/inherited-field-access-silgen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ testSetX(2)
2626

2727
// CHECK: sil shared [transparent] @$sSo024NonCopyableHolderDerivedD0V1xSo0aB0Vvlu : $@convention(method) (@{{(in_)?}}guaranteed NonCopyableHolderDerivedDerived) -> UnsafePointer<NonCopyable>
2828
// CHECK: {{.*}}(%[[SELF_VAL:.*]] : ${{(\*)?}}NonCopyableHolderDerivedDerived):
29-
// CHECK: function_ref @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}} : $@convention(cxx_method) (@in_guaranteed NonCopyableHolderDerivedDerived) -> UnsafePointer<NonCopyable>
29+
// CHECK: function_ref @{{(.*)(31NonCopyableHolderDerivedDerived33__synthesizedBaseGetterAccessor_x|__synthesizedBaseGetterAccessor_x@NonCopyableHolderDerivedDerived)(.*)}} : $@convention(cxx_method) (@in_guaranteed NonCopyableHolderDerivedDerived) -> UnsafePointer<NonCopyable>
3030
// CHECK-NEXT: apply %{{.*}}
3131

3232
// CHECK: sil shared [transparent] @$sSo024NonCopyableHolderDerivedD0V1xSo0aB0Vvau : $@convention(method) (@inout NonCopyableHolderDerivedDerived) -> UnsafeMutablePointer<NonCopyable>
33-
// CHECK: function_ref @{{.*}}__synthesizedBaseCall___synthesizedBaseSetterAccessor{{.*}} : $@convention(cxx_method) (@inout NonCopyableHolderDerivedDerived) -> UnsafeMutablePointer<NonCopyable>
33+
// CHECK: function_ref @{{(.*)(31NonCopyableHolderDerivedDerived33__synthesizedBaseSetterAccessor_x|__synthesizedBaseSetterAccessor_x@NonCopyableHolderDerivedDerived)(.*)}} : $@convention(cxx_method) (@inout NonCopyableHolderDerivedDerived) -> UnsafeMutablePointer<NonCopyable>

test/Interop/Cxx/foreign-reference/function-inheritance-irgen.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ func testGetX() -> CInt {
1010
let _ = testGetX()
1111

1212

13-
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseCall_{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]])
13+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{(.*)(30CopyTrackedDerivedDerivedClass26__synthesizedBaseCall_getX|__synthesizedBaseCall_getX@CopyTrackedDerivedDerivedClass)(.*)}}(ptr {{.*}} %[[THIS_PTR:.*]])
1414
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i{{32|64}} 4
1515
// CHECK: call{{.*}} i32 @{{.*}}(ptr {{.*}} %[[ADD_PTR]])

0 commit comments

Comments
 (0)