Skip to content

Commit ab1938a

Browse files
committed
[cxx-interop] Implement operator[] for value return types (SR-14351).
This builds on top of the work of Egor Zhdan. It implements `T operator[]` and does so largely by taking a path very much like the `const T &operator[]` path.
1 parent ad3568e commit ab1938a

11 files changed

+255
-18
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7488,19 +7488,25 @@ synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
74887488
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind);
74897489
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
74907490

7491-
SubstitutionMap subMap =
7492-
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
7493-
{ elementTy }, { });
7494-
auto pointeePropertyRefExpr =
7495-
new (ctx) MemberRefExpr(getterImplCallExpr,
7496-
SourceLoc(),
7497-
ConcreteDeclRef(pointeePropertyDecl, subMap),
7498-
DeclNameLoc(),
7499-
/*implicit=*/ true);
7500-
pointeePropertyRefExpr->setType(elementTy);
7491+
// This default handles C++'s operator[] that returns a value type.
7492+
Expr *PropertyExpr = getterImplCallExpr;
7493+
if (pointeePropertyDecl != nullptr) {
7494+
// Handle operator[] that returns a reference type.
7495+
SubstitutionMap subMap =
7496+
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
7497+
{ elementTy }, { });
7498+
auto pointeePropertyRefExpr =
7499+
new (ctx) MemberRefExpr(getterImplCallExpr,
7500+
SourceLoc(),
7501+
ConcreteDeclRef(pointeePropertyDecl, subMap),
7502+
DeclNameLoc(),
7503+
/*implicit=*/ true);
7504+
pointeePropertyRefExpr->setType(elementTy);
7505+
PropertyExpr = pointeePropertyRefExpr;
7506+
}
75017507

75027508
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(),
7503-
pointeePropertyRefExpr,
7509+
PropertyExpr,
75047510
/*implicit=*/ true);
75057511

75067512
auto body = BraceStmt::create(ctx, SourceLoc(), { returnStmt }, SourceLoc(),
@@ -7560,8 +7566,10 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
75607566

75617567
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
75627568
const auto rawElementTy = getterImpl->getResultInterfaceType();
7563-
// Unwrap `T`.
7564-
const auto elementTy = rawElementTy->getAnyPointerElementType();
7569+
// Unwrap `T`. User rawElementTy for return by value.
7570+
const auto elementTy = rawElementTy->getAnyPointerElementType() ?
7571+
rawElementTy->getAnyPointerElementType() :
7572+
rawElementTy;
75657573

75667574
auto &ctx = Impl.SwiftContext;
75677575
auto bodyParams = getterImpl->getParameters();

lib/ClangImporter/ImportName.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,11 +1741,10 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17411741
break;
17421742
case clang::OverloadedOperatorKind::OO_Subscript: {
17431743
auto returnType = functionDecl->getReturnType();
1744-
if (!returnType->isReferenceType()) {
1745-
// TODO: support non-reference return types (SR-14351)
1746-
return ImportedName();
1747-
}
1748-
if (returnType->getPointeeType().isConstQualified()) {
1744+
if (!returnType->isReferenceType() ||
1745+
returnType->getPointeeType().isConstQualified()) {
1746+
// If we are handling a non-reference return type, treat it as a getter
1747+
// so that we do not SILGen the value type operator[] as an rvalue.
17491748
baseName = "__operatorSubscriptConst";
17501749
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
17511750
} else {

test/Interop/Cxx/operators/Inputs/member-inline.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,56 @@ struct NonReferenceReadIntArray {
147147
}
148148
};
149149

150+
struct ReadWriteIntArrayByVal {
151+
int operator[](int x) { return values[x]; }
152+
struct NestedIntArray {
153+
int operator[](int x) const { return values[x]; }
154+
155+
private:
156+
int values[5] = { 1, 2, 3, 4, 5 };
157+
};
158+
159+
private:
160+
int values[5] = { 1, 2, 3, 4, 5 };
161+
162+
};
163+
164+
struct ReadOnlyIntArrayByVal {
165+
ReadOnlyIntArrayByVal(int first) { values[0] = first; }
166+
ReadOnlyIntArrayByVal(const ReadOnlyIntArray &other) {}
167+
int operator[](int x) const { return values[x]; }
168+
169+
private:
170+
int values[5] = { 1, 2, 3, 4, 5 };
171+
};
172+
173+
struct WriteOnlyIntArrayByVal {
174+
int operator[](int x) { return values[x]; }
175+
176+
private:
177+
int values[5] = { 1, 2, 3, 4, 5 };
178+
};
179+
180+
struct DifferentTypesArrayByVal {
181+
int operator[](int x) { return values[x]; }
182+
bool operator[](bool x) { return boolValues[x]; }
183+
double operator[](double x) const { return doubleValues[x == 0.0]; }
184+
185+
private:
186+
int values[3] = { 1, 2, 3 };
187+
double doubleValues[2] = { 1.5, 2.5 };
188+
bool boolValues[2] = { true, false };
189+
};
190+
191+
template<class T> struct TemplatedArrayByVal {
192+
T ts[];
193+
T operator[](int i) { return ts[i]; }
194+
};
195+
typedef TemplatedArrayByVal<double> TemplatedDoubleArrayByVal;
196+
197+
struct TemplatedSubscriptArrayByVal {
198+
int *ptr;
199+
template<class T> T operator[](T i) { return ptr[i]; }
200+
};
201+
150202
#endif

test/Interop/Cxx/operators/Inputs/member-out-of-line.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ const int& ReadWriteIntArray::operator[](int x) const {
3535
int& ReadWriteIntArray::operator[](int x) {
3636
return values[x];
3737
}
38+
39+
int ReadWriteIntArrayByVal::operator[](int x) {
40+
return values[x];
41+
}

test/Interop/Cxx/operators/Inputs/member-out-of-line.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@ struct ReadWriteIntArray {
2929
int &operator[](int x);
3030
};
3131

32+
struct ReadWriteIntArrayByVal {
33+
private:
34+
int values[5] = { 1, 2, 3, 4, 5 };
35+
36+
public:
37+
int operator[](int x);
38+
};
39+
3240
#endif

test/Interop/Cxx/operators/member-inline-irgen.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,23 @@ public func index(_ arr: inout ReadWriteIntArray, _ arg: Int32, _ val: Int32) {
3737
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadWriteIntArray, %struct.ReadWriteIntArray* [[THIS]]
3838
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
3939
// CHECK: ret {{i32|i64}}* [[VALUE]]
40+
41+
public func index(_ arr: inout ReadOnlyIntArrayByVal, _ arg: Int32) -> Int32 { arr[arg] }
42+
43+
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZNK21ReadOnlyIntArrayByValixEi|"\?\?AReadOnlyIntArrayByVal@@QEBAAEBHH@Z")]](%struct.ReadOnlyIntArrayByVal* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
44+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.ReadOnlyIntArrayByVal* nonnull dereferenceable(20) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.ReadOnlyIntArrayByVal\* byval\(%struct.ReadOnlyIntArrayByVal\) align 2 %rhs}})
45+
// CHECK: [[THIS:%.*]] = load %struct.ReadOnlyIntArrayByVal*, %struct.ReadOnlyIntArrayByVal**
46+
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadOnlyIntArrayByVal, %struct.ReadOnlyIntArrayByVal* [[THIS]]
47+
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
48+
// CHECK: [[VALUE2:%.*]] = load {{i32|i64}}, {{i32|i64}}* [[VALUE]]
49+
// CHECK: ret {{i32|i64}} [[VALUE2]]
50+
51+
public func index(_ arr: inout ReadWriteIntArrayByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg] }
52+
53+
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN22ReadWriteIntArrayByValixEi|"\?\?AReadWriteIntArrayByVal@@QEAAAEAHH@Z")]](%struct.ReadWriteIntArrayByVal* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
54+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.ReadWriteIntArrayByVal* nonnull dereferenceable(20) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.ReadWriteIntArrayByVal\* byval\(%struct.ReadWriteIntArrayByVal\) align 2 %rhs}})
55+
// CHECK: [[THIS:%.*]] = load %struct.ReadWriteIntArrayByVal*, %struct.ReadWriteIntArrayByVal**
56+
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadWriteIntArrayByVal, %struct.ReadWriteIntArrayByVal* [[THIS]]
57+
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
58+
// CHECK: [[VALUE2:%.*]] = load {{i32|i64}}, {{i32|i64}}* [[VALUE]]
59+
// CHECK: ret {{i32|i64}} [[VALUE2]]

test/Interop/Cxx/operators/member-inline-module-interface.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,58 @@
9595
// so just make sure we don't crash.
9696
// CHECK: struct NonReferenceReadIntArray {
9797
// CHECK: }
98+
99+
// CHECK: struct ReadWriteIntArrayByVal {
100+
// CHECK: struct NestedIntArray {
101+
// CHECK: @available(*, unavailable, message: "use subscript")
102+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
103+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
104+
// CHECK: }
105+
106+
// CHECK: @available(*, unavailable, message: "use subscript")
107+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
108+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
109+
// CHECK: }
110+
111+
112+
// CHECK: struct ReadOnlyIntArrayByVal {
113+
// CHECK: @available(*, unavailable, message: "use subscript")
114+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
115+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
116+
// CHECK: }
117+
118+
// CHECK: struct WriteOnlyIntArrayByVal {
119+
// CHECK: @available(*, unavailable, message: "use subscript")
120+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
121+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
122+
// CHECK: }
123+
124+
125+
// CHECK: struct DifferentTypesArrayByVal {
126+
// CHECK: @available(*, unavailable, message: "use subscript")
127+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
128+
129+
// CHECK: @available(*, unavailable, message: "use subscript")
130+
// CHECK: mutating func __operatorSubscriptConst(_ x: Bool) -> Bool
131+
132+
// CHECK: @available(*, unavailable, message: "use subscript")
133+
// CHECK: mutating func __operatorSubscriptConst(_ x: Double) -> Double
134+
135+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
136+
// CHECK: subscript(x: Bool) -> Bool { mutating get }
137+
// CHECK: subscript(x: Double) -> Double { mutating get }
138+
// CHECK: }
139+
140+
141+
// CHECK: struct TemplatedArrayByVal<T> {
142+
// CHECK: }
143+
// CHECK: struct __CxxTemplateInst19TemplatedArrayByValIdE {
144+
// CHECK: @available(*, unavailable, message: "use subscript")
145+
// CHECK: mutating func __operatorSubscriptConst(_ i: Int32) -> Double
146+
// CHECK: subscript(i: Int32) -> Double { mutating get }
147+
// CHECK: }
148+
// CHECK: typealias TemplatedDoubleArrayByVal = __CxxTemplateInst19TemplatedArrayByValIdE
149+
150+
151+
// CHECK: struct TemplatedSubscriptArrayByVal {
152+
// CHECK: }

test/Interop/Cxx/operators/member-inline-silgen.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,25 @@ public func index(_ arr: inout ReadWriteIntArray, _ arg: Int32, _ val: Int32) {
7373
// CHECK: pointer_to_address [[PTR2]] : $Builtin.RawPointer to [strict] $*Int32
7474
// CHECK: } // end sil function '$sSo17ReadWriteIntArrayVys5Int32VADcis'
7575

76+
public func index(_ arr: inout ReadWriteIntArrayByVal, _ arg: Int32, _ val: Int32) -> Int32 { arr[arg] }
77+
78+
// CHECK: sil @$s4main5indexys5Int32VSo22ReadWriteIntArrayByValVz_A2DtF : $@convention(thin) (@inout ReadWriteIntArrayByVal, Int32, Int32) -> Int32 {
79+
// CHECK: bb0([[ARR:%.*]] : $*ReadWriteIntArrayByVal, [[INDEX:%.*]] : $Int32, [[NEWVALUE:%.*]] : $Int32):
80+
// CHECK: [[ARRACCESS:%.*]] = begin_access [modify] [static] [[ARR]] : $*ReadWriteIntArrayByVal
81+
// CHECK: [[ARRACCESS2:%.*]] = begin_access [modify] [static] [[ARRACCESS]] : $*ReadWriteIntArrayByVal
82+
83+
// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL:@(_ZN22ReadWriteIntArrayByValixEi|\?\?AReadWriteIntArrayByVal@@QEAAAEAHH@Z)]] : $@convention(c) (@inout ReadWriteIntArrayByVal, Int32) -> Int32
84+
// CHECK: [[PTR:%.*]] = apply [[OP]]([[ARRACCESS2]], [[INDEX]]) : $@convention(c) (@inout ReadWriteIntArrayByVal, Int32) -> Int32
85+
// CHECK: } // end sil function '$s4main5indexys5Int32VSo22ReadWriteIntArrayByValVz_A2DtF'
86+
87+
// CHECK: sil shared [transparent] [serializable] @$sSo22ReadWriteIntArrayByValVys5Int32VADcig : $@convention(method) (Int32, @inout ReadWriteIntArrayByVal) -> Int32 {
88+
// CHECK: bb0([[NEWVALUE:%.*]] : $Int32, [[INDEX:%.*]] : $*ReadWriteIntArrayByVal):
89+
// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[INDEX]] : $*ReadWriteIntArrayByVal
90+
// CHECK: [[OP:%.*]] = function_ref [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@inout ReadWriteIntArrayByVal, Int32) -> Int32
91+
// CHECK: [[PTR:%.*]] = apply [[OP]]([[SELFACCESS]], [[NEWVALUE]]) : $@convention(c) (@inout ReadWriteIntArrayByVal, Int32) -> Int32
92+
// CHECK: end_access [[SELFACCESS]] : $*ReadWriteIntArrayByVal
93+
// CHECK: } // end sil function '$sSo22ReadWriteIntArrayByValVys5Int32VADcig
94+
7695
// CHECK: sil [clang ReadOnlyIntArray.__operatorSubscriptConst] [[READCLASSNAME]] : $@convention(c) (@inout ReadOnlyIntArray, Int32) -> UnsafePointer<Int32>
7796
// CHECK: sil [clang ReadWriteIntArray.__operatorSubscript] [[READWRITECLASSNAME]] : $@convention(c) (@inout ReadWriteIntArray, Int32) -> UnsafeMutablePointer<Int32>
97+
// CHECK: sil [clang ReadWriteIntArrayByVal.__operatorSubscriptConst] [[READWRITECLASSNAMEBYVAL]] : $@convention(c) (@inout ReadWriteIntArrayByVal, Int32) -> Int32

test/Interop/Cxx/operators/member-inline-typechecker.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,16 @@ let writeOnlyValue = writeOnlyIntArray[2]
3030
var diffTypesArray = DifferentTypesArray()
3131
let diffTypesResultInt: Int32 = diffTypesArray[0]
3232
let diffTypesResultDouble: Double = diffTypesArray[0.5]
33+
34+
var readWriteIntArrayByVal = ReadWriteIntArrayByVal()
35+
let readWriteValueByVal = readWriteIntArrayByVal[2]
36+
37+
var readOnlyIntArrayByVal = ReadOnlyIntArrayByVal(3)
38+
let readOnlyValueByVal = readOnlyIntArrayByVal[1]
39+
40+
var writeOnlyIntArrayByVal = WriteOnlyIntArrayByVal()
41+
let writeOnlyValueByVal = writeOnlyIntArrayByVal[2]
42+
43+
var diffTypesArrayByVal = DifferentTypesArrayByVal()
44+
let diffTypesResultIntByVal: Int32 = diffTypesArrayByVal[0]
45+
let diffTypesResultDoubleByVal: Double = diffTypesArrayByVal[0.5]

test/Interop/Cxx/operators/member-inline.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,52 @@ OperatorsTestSuite.test("DifferentTypesArray.subscript (inline)") {
9090
expectEqual(1.5.rounded(.up), resultDouble.rounded(.up))
9191
}
9292

93+
94+
OperatorsTestSuite.test("ReadWriteIntArrayByVal.subscript (inline)") {
95+
var arr = ReadWriteIntArrayByVal()
96+
97+
let resultBefore = arr[1]
98+
expectEqual(2, resultBefore)
99+
100+
let resultAfter = arr[1]
101+
expectEqual(2, resultAfter)
102+
}
103+
104+
/*
105+
OperatorsTestSuite.test("ReadOnlyIntArrayByVal.subscript (inline)") {
106+
var arr = ReadOnlyIntArrayByVal(1)
107+
108+
let result0 = arr[0]
109+
let result2 = arr[2]
110+
let result4 = arr[4]
111+
112+
expectEqual(1, result0)
113+
expectEqual(3, result2)
114+
expectEqual(5, result4)
115+
}
116+
117+
OperatorsTestSuite.test("WriteOnlyIntArrayByVal.subscript (inline)") {
118+
var arr = WriteOnlyIntArrayByVal()
119+
120+
let resultBefore = arr[0]
121+
expectEqual(1, resultBefore)
122+
123+
arr[0] = 654
124+
125+
let resultAfter = arr[0]
126+
expectEqual(654, resultAfter)
127+
}
128+
129+
OperatorsTestSuite.test("DifferentTypesArrayByVal.subscript (inline)") {
130+
var arr = DifferentTypesArrayByVal()
131+
132+
let resultInt: Int32 = arr[2]
133+
let resultDouble: Double = arr[0.1]
134+
135+
expectEqual(3, resultInt)
136+
expectEqual(1.5.rounded(.down), resultDouble.rounded(.down))
137+
expectEqual(1.5.rounded(.up), resultDouble.rounded(.up))
138+
}
139+
*/
140+
93141
runAllTests()

test/Interop/Cxx/operators/member-out-of-line.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,14 @@ OperatorsTestSuite.test("ReadWriteIntArray.subscript (out-of-line)") {
5959
expectEqual(234, resultAfter)
6060
}
6161

62+
OperatorsTestSuite.test("ReadWriteIntArrayByVal.subscript (out-of-line)") {
63+
var arr = ReadWriteIntArrayByVal()
64+
65+
let resultBefore = arr[1]
66+
expectEqual(2, resultBefore)
67+
68+
let resultAfter = arr[1]
69+
expectEqual(2, resultAfter)
70+
}
71+
6272
runAllTests()

0 commit comments

Comments
 (0)