Skip to content

Commit 41dc466

Browse files
authored
Merge pull request #68846 from hyp/eng/base-member-cxx-synthesized-accessor
[cxx-interop] Use a synthesized C++ method when invoking a base metho…
2 parents bffd78f + f7ce9aa commit 41dc466

27 files changed

+1167
-66
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ ERROR(move_only_requires_move_only,none,
247247
"use of noncopyable C++ type '%0' requires -enable-experimental-move-only",
248248
(StringRef))
249249

250+
ERROR(failed_base_method_call_synthesis,none,
251+
"failed to synthesize call to the base method %0 of type %0",
252+
(ValueDecl *, ValueDecl *))
253+
250254
NOTE(unsupported_builtin_type, none, "built-in type '%0' not supported", (StringRef))
251255
NOTE(record_field_not_imported, none, "field %0 unavailable (cannot import)", (const clang::NamedDecl*))
252256
NOTE(invoked_func_not_imported, none, "function %0 unavailable (cannot import)", (const clang::NamedDecl*))

lib/ClangImporter/ClangImporter.cpp

Lines changed: 454 additions & 61 deletions
Large diffs are not rendered by default.

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3629,6 +3629,10 @@ class CXXMethodConventions : public CFunctionTypeConventions {
36293629
// possible to make it easy for LLVM to optimize away the thunk.
36303630
return ResultConvention::Indirect;
36313631
}
3632+
if (TheDecl->hasAttr<clang::CFReturnsRetainedAttr>() &&
3633+
resultTL.getLoweredType().isForeignReferenceType()) {
3634+
return ResultConvention::Owned;
3635+
}
36323636
return CFunctionTypeConventions::getResult(resultTL);
36333637
}
36343638
static bool classof(const Conventions *C) {

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,37 @@ struct ClassTemplate {
6666
};
6767

6868
struct DerivedFromClassTemplate : ClassTemplate<int> {};
69+
70+
int &getCopyCounter() {
71+
static int copyCounter = 0;
72+
return copyCounter;
73+
}
74+
75+
class CopyTrackedBaseClass {
76+
public:
77+
CopyTrackedBaseClass(int x) : x(x) {}
78+
CopyTrackedBaseClass(const CopyTrackedBaseClass &other) : x(other.x) {
79+
++getCopyCounter();
80+
}
81+
82+
int x;
83+
};
84+
85+
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
86+
public:
87+
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
88+
};
89+
90+
class NonEmptyBase {
91+
public:
92+
int getY() const {
93+
return y;
94+
}
95+
private:
96+
int y = 11;
97+
};
98+
99+
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
100+
public:
101+
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
102+
};

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,48 @@ struct DerivedFromEmptyBaseClass : EmptyBaseClass {
110110
int a = 42;
111111
int b = 42;
112112
};
113+
114+
int &getCopyCounter() {
115+
static int copyCounter = 0;
116+
return copyCounter;
117+
}
118+
119+
class CopyTrackedBaseClass {
120+
public:
121+
CopyTrackedBaseClass(int x) : x(x) {}
122+
CopyTrackedBaseClass(const CopyTrackedBaseClass &other) : x(other.x) {
123+
++getCopyCounter();
124+
}
125+
126+
int getX() const {
127+
return x;
128+
}
129+
int getXMut() {
130+
return x;
131+
}
132+
private:
133+
int x;
134+
};
135+
136+
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
137+
public:
138+
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
139+
140+
int getDerivedX() const {
141+
return getX();
142+
}
143+
};
144+
145+
class NonEmptyBase {
146+
public:
147+
int getY() const {
148+
return y;
149+
}
150+
private:
151+
int y = 11;
152+
};
153+
154+
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
155+
public:
156+
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
157+
};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ module SubTypes {
1515
header "sub-types.h"
1616
}
1717

18+
module Subscripts {
19+
header "subscripts.h"
20+
}
21+
1822
module TypeAliases {
1923
header "type-aliases.h"
2024
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
int &getCopyCounter() {
2+
static int copyCounter = 0;
3+
return copyCounter;
4+
}
5+
6+
class CopyTrackedBaseClass {
7+
public:
8+
CopyTrackedBaseClass(int x) : x(x) {}
9+
CopyTrackedBaseClass(const CopyTrackedBaseClass &other) : x(other.x) {
10+
++getCopyCounter();
11+
}
12+
13+
int operator [](int y) const {
14+
return y + x;
15+
}
16+
private:
17+
int x;
18+
};
19+
20+
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
21+
public:
22+
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
23+
};
24+
25+
class NonEmptyBase {
26+
public:
27+
int getY() const {
28+
return y;
29+
}
30+
private:
31+
int y = 11;
32+
};
33+
34+
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
35+
public:
36+
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
37+
};
38+
39+
class SubscriptReturnsRef {
40+
public:
41+
const int &operator [](int y) const {
42+
return x[y];
43+
}
44+
int &operator [](int y) {
45+
return x[y];
46+
}
47+
48+
private:
49+
int x[10] = {0};
50+
};
51+
52+
class DerivedSubscriptReturnsRef: public SubscriptReturnsRef {
53+
public:
54+
inline DerivedSubscriptReturnsRef() : SubscriptReturnsRef() {}
55+
};
56+
57+
class NonConstSubscriptReturnsRef {
58+
public:
59+
int &operator [](int y) {
60+
return x[y];
61+
}
62+
63+
private:
64+
int x[10] = {0};
65+
};
66+
67+
class DerivedNonConstSubscriptReturnsRef: public NonConstSubscriptReturnsRef {
68+
public:
69+
inline DerivedNonConstSubscriptReturnsRef() : NonConstSubscriptReturnsRef() {}
70+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import Fields
4+
5+
func testGetX() -> CInt {
6+
let derivedDerived = CopyTrackedDerivedDerivedClass(42)
7+
return derivedDerived.x
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]])
13+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
14+
// CHECK: call noundef i32 @{{.*}}__synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[ADD_PTR]])
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none | %FileCheck %s
2+
3+
import Fields
4+
5+
func testGetX() -> CInt {
6+
let derived = CopyTrackedDerivedClass(42)
7+
return derived.x
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: sil shared [transparent] @$sSo23CopyTrackedDerivedClassV1xs5Int32Vvg : $@convention(method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
13+
// CHECK: {{.*}}(%[[SELF_VAL:.*]] : $*CopyTrackedDerivedClass):
14+
// CHECK: function_ref @{{.*}}__synthesizedBaseGetterAccessor_{{.*}} : $@convention(cxx_method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
15+
// CHECK-NEXT: apply %{{.*}}(%[[SELF_VAL]])

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,31 @@ FieldsTestSuite.test("Same field from derived") {
8585
expectEqual(derived.a, 42)
8686
}
8787

88+
extension CopyTrackedBaseClass {
89+
var swiftProp: CInt {
90+
return x
91+
}
92+
}
93+
94+
FieldsTestSuite.test("Get field without copying base in the getter accessor") {
95+
let base = CopyTrackedBaseClass(0)
96+
var copyCounter = getCopyCounter().pointee
97+
expectEqual(base.swiftProp, 0)
98+
// Measure copy counter of a regular
99+
// property access for a C++ type to compare
100+
// it to see if any additional copies are
101+
// needed to access the property from the base class.
102+
let expectedCopyCountDiff = getCopyCounter().pointee - copyCounter
103+
104+
let derived = CopyTrackedDerivedClass(234)
105+
copyCounter = getCopyCounter().pointee
106+
expectEqual(derived.x, 234)
107+
expectEqual(copyCounter, getCopyCounter().pointee - expectedCopyCountDiff)
108+
109+
let derivedDerived = CopyTrackedDerivedDerivedClass(-11)
110+
copyCounter = getCopyCounter().pointee
111+
expectEqual(derivedDerived.x, -11)
112+
expectEqual(copyCounter, getCopyCounter().pointee - expectedCopyCountDiff)
113+
}
114+
88115
runAllTests()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import Functions
4+
5+
func testGetX() -> CInt {
6+
let derivedDerived = CopyTrackedDerivedDerivedClass(42)
7+
return derivedDerived.getX()
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: define {{.*}} swiftcc i32 @"$sSo018CopyTrackedDerivedC5ClassV4getXs5Int32VyF"(ptr noalias swiftself dereferenceable(8) %[[SELF_PTR:.*]])
13+
// CHECK: = call i32 @[[SYNTH_METHOD:.*]](ptr %[[SELF_PTR]])
14+
15+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @[[SYNTH_METHOD]](ptr {{.*}} %[[THIS_PTR:.*]])
16+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
17+
// CHECK: call noundef i32 @{{.*}}(ptr {{.*}} %[[ADD_PTR]])
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none | %FileCheck %s
2+
3+
import Functions
4+
5+
func testGetX() -> CInt {
6+
let derived = CopyTrackedDerivedClass(42)
7+
return derived.getX()
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: sil shared @$sSo23CopyTrackedDerivedClassV4getXs5Int32VyF : $@convention(method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
13+
// CHECK: {{.*}}(%[[SELF_VAL:.*]] : $*CopyTrackedDerivedClass):
14+
// CHECK: function_ref @{{.*}}__synthesizedBaseCall_{{.*}} : $@convention(cxx_method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
15+
// CHECK-NEXT: apply %{{.*}}(%[[SELF_VAL]])

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,26 @@ FunctionsTestSuite.test("non-empty derived from empty class") {
8282
expectEqual(derived.b, 42)
8383
}
8484

85+
FunctionsTestSuite.test("base member calls do not require copying") {
86+
let derived = CopyTrackedDerivedClass(42)
87+
var copyCounter = getCopyCounter().pointee
88+
expectEqual(derived.getX(), 42)
89+
expectEqual(copyCounter, getCopyCounter().pointee)
90+
expectEqual(derived.getDerivedX(), 42)
91+
expectEqual(copyCounter, getCopyCounter().pointee)
92+
93+
let derivedDerived = CopyTrackedDerivedDerivedClass(-5)
94+
copyCounter = getCopyCounter().pointee
95+
expectEqual(derivedDerived.getX(), -5)
96+
expectEqual(derivedDerived.getY(), 11)
97+
expectEqual(copyCounter, getCopyCounter().pointee)
98+
}
99+
100+
FunctionsTestSuite.test("mutating base member calls do not require copying") {
101+
var derived = CopyTrackedDerivedClass(42)
102+
var copyCounter = getCopyCounter().pointee
103+
expectEqual(derived.getXMut(), 42)
104+
expectEqual(copyCounter, getCopyCounter().pointee)
105+
}
106+
85107
runAllTests()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import Subscripts
4+
5+
func testGetX() -> CInt {
6+
let derivedDerived = CopyTrackedDerivedDerivedClass(42)
7+
return derivedDerived[0]
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]], i32 {{.*}})
13+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
14+
// CHECK: call noundef i32 @{{.*}}__synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[ADD_PTR]], i32 {{.*}})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none | %FileCheck %s
2+
3+
import Subscripts
4+
5+
func testGetX() -> CInt {
6+
let derived = CopyTrackedDerivedClass(42)
7+
return derived[0]
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: sil shared [transparent] @$sSo23CopyTrackedDerivedClassVys5Int32VADcig : $@convention(method) (Int32, @in_guaranteed CopyTrackedDerivedClass) -> Int32
13+
// CHECK: {{.*}}(%[[INT_VAL:.*]] : $Int32, %[[SELF_VAL:.*]] : $*CopyTrackedDerivedClass):
14+
// CHECK: function_ref @{{.*}}__synthesizedBaseCall_operatorSubscript{{.*}} : $@convention(cxx_method) (Int32, @in_guaranteed CopyTrackedDerivedClass) -> Int32
15+
// CHECK-NEXT: apply %{{.*}}(%[[INT_VAL]], %[[SELF_VAL]])
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import CxxShim
6+
import StdlibUnittest
7+
import Subscripts
8+
9+
var FieldsTestSuite = TestSuite("Getting and setting subscripts in base classes")
10+
11+
FieldsTestSuite.test("Subscript from derived returning ref") {
12+
var derived = DerivedSubscriptReturnsRef()
13+
expectEqual(derived[0], 0)
14+
derived[0] = 42
15+
expectEqual(derived[0], 42)
16+
}
17+
18+
FieldsTestSuite.test("Non-const subscript from derived returning ref") {
19+
var derived = DerivedNonConstSubscriptReturnsRef()
20+
expectEqual(derived[1], 0)
21+
derived[1] = -11
22+
expectEqual(derived[1], -11)
23+
}
24+
25+
runAllTests()

0 commit comments

Comments
 (0)