Skip to content

Commit 68ff55a

Browse files
authored
Merge pull request #36688 from plotfi/cxx-operator-subscript
[cxx-interop] Implement operator[] for value return types (SR-14351).
2 parents b00d057 + 864b3a4 commit 68ff55a

11 files changed

+469
-35
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7579,25 +7579,35 @@ synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
75797579
inoutSelfExpr,
75807580
keyRefExpr);
75817581

7582-
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
7583-
// Retrieve the corresponding `.pointee` declaration.
7582+
// This default handles C++'s operator[] that returns a value type.
7583+
Expr *propertyExpr = getterImplCallExpr;
75847584
PointerTypeKind ptrKind;
7585-
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind);
7586-
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
75877585

7588-
SubstitutionMap subMap =
7589-
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
7590-
{ elementTy }, { });
7591-
auto pointeePropertyRefExpr =
7592-
new (ctx) MemberRefExpr(getterImplCallExpr,
7593-
SourceLoc(),
7594-
ConcreteDeclRef(pointeePropertyDecl, subMap),
7595-
DeclNameLoc(),
7596-
/*implicit=*/ true);
7597-
pointeePropertyRefExpr->setType(elementTy);
7586+
// The following check returns true if the subscript operator returns a C++
7587+
// reference type. This check actually checks to see if the type is a pointer
7588+
// type, but this does not apply to C pointers because they are Optional types
7589+
// when imported. TODO: Use a more obvious check here.
7590+
if (getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
7591+
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
7592+
// Retrieve the corresponding `.pointee` declaration.
7593+
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
7594+
7595+
// Handle operator[] that returns a reference type.
7596+
SubstitutionMap subMap =
7597+
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
7598+
{ elementTy }, { });
7599+
auto pointeePropertyRefExpr =
7600+
new (ctx) MemberRefExpr(getterImplCallExpr,
7601+
SourceLoc(),
7602+
ConcreteDeclRef(pointeePropertyDecl, subMap),
7603+
DeclNameLoc(),
7604+
/*implicit=*/ true);
7605+
pointeePropertyRefExpr->setType(elementTy);
7606+
propertyExpr = pointeePropertyRefExpr;
7607+
}
75987608

75997609
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(),
7600-
pointeePropertyRefExpr,
7610+
propertyExpr,
76017611
/*implicit=*/ true);
76027612

76037613
auto body = BraceStmt::create(ctx, SourceLoc(), { returnStmt }, SourceLoc(),
@@ -7657,8 +7667,10 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
76577667

76587668
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
76597669
const auto rawElementTy = getterImpl->getResultInterfaceType();
7660-
// Unwrap `T`.
7661-
const auto elementTy = rawElementTy->getAnyPointerElementType();
7670+
// Unwrap `T`. Use rawElementTy for return by value.
7671+
const auto elementTy = rawElementTy->getAnyPointerElementType() ?
7672+
rawElementTy->getAnyPointerElementType() :
7673+
rawElementTy;
76627674

76637675
auto &ctx = Impl.SwiftContext;
76647676
auto bodyParams = getterImpl->getParameters();
@@ -7738,6 +7750,10 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
77387750

77397751
makeComputed(subscript, getterDecl, setterDecl);
77407752

7753+
// Implicitly unwrap Optional types for T *operator[].
7754+
Impl.recordImplicitUnwrapForDecl(subscript,
7755+
getterImpl->isImplicitlyUnwrappedOptional());
7756+
77417757
return subscript;
77427758
}
77437759

lib/ClangImporter/ImportName.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,13 +1745,15 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
17451745
break;
17461746
case clang::OverloadedOperatorKind::OO_Subscript: {
17471747
auto returnType = functionDecl->getReturnType();
1748-
if (!returnType->isReferenceType()) {
1749-
// TODO: support non-reference return types (SR-14351)
1750-
return ImportedName();
1751-
}
1752-
if (returnType->getPointeeType().isConstQualified()) {
1748+
if ((!returnType->isReferenceType() && !returnType->isAnyPointerType()) ||
1749+
returnType->getPointeeType().isConstQualified()) {
1750+
// If we are handling a non-reference return type, treat it as a getter
1751+
// so that we do not SILGen the value type operator[] as an rvalue.
17531752
baseName = "__operatorSubscriptConst";
17541753
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
1754+
} else if (returnType->isAnyPointerType()) {
1755+
baseName = "__operatorSubscript";
1756+
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
17551757
} else {
17561758
baseName = "__operatorSubscript";
17571759
result.info.accessorKind = ImportedAccessorKind::SubscriptSetter;

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

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,106 @@ struct TemplatedSubscriptArray {
137137
}
138138
};
139139

140-
struct NonReferenceReadIntArray {
140+
struct IntArrayByVal {
141+
// For testing purposes.
142+
void setValueAtIndex(int value, unsigned i) { values[i] = value; }
143+
int operator[](int x) const { return values[x]; }
141144
private:
142145
int values[3] = { 1, 2, 3 };
146+
};
143147

144-
public:
145-
int operator[](int x) const {
146-
return values[x];
147-
}
148+
struct NonTrivialIntArrayByVal {
149+
NonTrivialIntArrayByVal(int first) { values[0] = first; }
150+
NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {}
151+
int operator[](int x) const { return values[x]; }
152+
153+
// For testing purposes.
154+
void setValueAtIndex(int value, unsigned i) { values[i] = value; }
155+
156+
private:
157+
int values[5] = { 1, 2, 3, 4, 5 };
158+
};
159+
160+
struct DifferentTypesArrayByVal {
161+
int operator[](int x) { return values[x]; }
162+
bool operator[](bool x) { return boolValues[x]; }
163+
double operator[](double x) const { return doubleValues[x == 0.0]; }
164+
165+
private:
166+
int values[3] = { 1, 2, 3 };
167+
double doubleValues[2] = { 1.5, 2.5 };
168+
bool boolValues[2] = { true, false };
169+
};
170+
171+
template<class T> struct TemplatedArrayByVal {
172+
T ts[];
173+
T operator[](int i) { return ts[i]; }
174+
};
175+
typedef TemplatedArrayByVal<double> TemplatedDoubleArrayByVal;
176+
177+
struct TemplatedSubscriptArrayByVal {
178+
int *ptr;
179+
template<class T> T operator[](T i) { return ptr[i]; }
180+
};
181+
182+
struct NonTrivial {
183+
char *Str;
184+
long long a;
185+
short b;
186+
long long c;
187+
short d;
188+
long long e;
189+
int f;
190+
NonTrivial() {
191+
Str = (char*)"Non-Trivial";
192+
a = 1;
193+
b = 2;
194+
c = 3;
195+
d = 4;
196+
e = 5;
197+
f = 6;
198+
}
199+
~NonTrivial() { Str = nullptr; }
200+
};
201+
202+
struct NonTrivialArrayByVal {
203+
NonTrivial operator[](int x) { return S; }
204+
private:
205+
NonTrivial S;
206+
};
207+
208+
struct PtrByVal {
209+
int *operator[](int x) { return &a; }
210+
private:
211+
int a = 64;
212+
};
213+
214+
struct RefToPtr {
215+
RefToPtr() { b = &a; }
216+
int *&operator[](int x) { return b; }
217+
private:
218+
int a = 64;
219+
int *b = nullptr;
220+
};
221+
222+
struct PtrToPtr {
223+
PtrToPtr() { b = &a; }
224+
int **operator[](int x) { return &b; }
225+
private:
226+
int a = 64;
227+
int *b = nullptr;
228+
};
229+
230+
struct ConstOpPtrByVal {
231+
const int *operator[](int x) const { return &a; }
232+
private:
233+
int a = 64;
234+
};
235+
236+
struct ConstPtrByVal {
237+
const int *operator[](int x) { return &a; }
238+
private:
239+
int a = 64;
148240
};
149241

150242
#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 NonTrivialIntArrayByVal::operator[](int x) {
40+
return values[x];
41+
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,16 @@ struct ReadWriteIntArray {
2929
int &operator[](int x);
3030
};
3131

32-
#endif
32+
struct NonTrivialIntArrayByVal {
33+
NonTrivialIntArrayByVal(int first) { values[0] = first; }
34+
NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {}
35+
int operator[](int x);
36+
37+
// For testing purposes.
38+
void setValueAtIndex(int value, unsigned i) { values[i] = value; }
39+
40+
private:
41+
int values[5] = { 1, 2, 3, 4, 5 };
42+
};
43+
44+
#endif

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import MemberInline
88
public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs }
99

1010
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrappermiES_|"\?\?GLoadableIntWrapper@@QEAA\?AU0@U0@@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval\(.*\) align 4}} {{%[0-9]+}})
11-
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %rhs.coerce|\[1 x i32\] %rhs.coerce|i64 %rhs.coerce|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}})
11+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %.*.coerce|\[1 x i32\] %.*.coerce|i64 %.*.coerce|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %.*}})
1212

1313
public func call(_ wrapper: inout LoadableIntWrapper, _ arg: Int32) -> Int32 { wrapper(arg) }
1414

1515
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZN18LoadableIntWrapperclEi|"\?\?GLoadableIntWrapper@@QEAAHH@Z")]](%struct.LoadableIntWrapper* {{%[0-9]+}}, {{i32|\[1 x i32\]|i64|%struct.LoadableIntWrapper\* byval\(.*\) align 4}} {{%[0-9]+}})
16-
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %rhs}})
16+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %.*}})
1717

1818
public func call(_ wrapper: inout AddressOnlyIntWrapper) -> Int32 { wrapper() }
1919

@@ -23,7 +23,7 @@ public func call(_ wrapper: inout AddressOnlyIntWrapper) -> Int32 { wrapper() }
2323
public func index(_ arr: inout ReadOnlyIntArray, _ arg: Int32) -> Int32 { arr[arg] }
2424

2525
// CHECK: call [[RES:i32|i64]]* [[NAME:@(_ZNK16ReadOnlyIntArrayixEi|"\?\?AReadOnlyIntArray@@QEBAAEBHH@Z")]](%struct.ReadOnlyIntArray* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
26-
// CHECK: define linkonce_odr nonnull align 4 dereferenceable(4) [[RES]]* [[NAME]](%struct.ReadOnlyIntArray* nonnull dereferenceable(20) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.ReadOnlyIntArray\* byval\(%struct.ReadOnlyIntArray\) align 2 %rhs}})
26+
// CHECK: define linkonce_odr nonnull align 4 dereferenceable(4) [[RES]]* [[NAME]](%struct.ReadOnlyIntArray* nonnull dereferenceable(20) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.ReadOnlyIntArray\* byval\(%struct.ReadOnlyIntArray\) align 2 %.*}})
2727
// CHECK: [[THIS:%.*]] = load %struct.ReadOnlyIntArray*, %struct.ReadOnlyIntArray**
2828
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadOnlyIntArray, %struct.ReadOnlyIntArray* [[THIS]]
2929
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
@@ -32,8 +32,18 @@ public func index(_ arr: inout ReadOnlyIntArray, _ arg: Int32) -> Int32 { arr[ar
3232
public func index(_ arr: inout ReadWriteIntArray, _ arg: Int32, _ val: Int32) { arr[arg] = val }
3333

3434
// CHECK: call [[RES:i32|i64]]* [[NAME:@(_ZN17ReadWriteIntArrayixEi|"\?\?AReadWriteIntArray@@QEAAAEAHH@Z")]](%struct.ReadWriteIntArray* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
35-
// CHECK: define linkonce_odr nonnull align 4 dereferenceable(4) [[RES]]* [[NAME]](%struct.ReadWriteIntArray* nonnull dereferenceable(20) {{.*}}, {{i32 %x|\[1 x i32\] %x|i64 %x|%struct.ReadWriteIntArray\* byval\(%struct.ReadWriteIntArray\) align 2 %rhs}})
35+
// CHECK: define linkonce_odr nonnull align 4 dereferenceable(4) [[RES]]* [[NAME]](%struct.ReadWriteIntArray* nonnull dereferenceable(20) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.ReadWriteIntArray\* byval\(%struct.ReadWriteIntArray\) align 2 %.*}})
3636
// CHECK: [[THIS:%.*]] = load %struct.ReadWriteIntArray*, %struct.ReadWriteIntArray**
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 NonTrivialIntArrayByVal, _ arg: Int32) -> Int32 { arr[arg] }
42+
43+
// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZNK23NonTrivialIntArrayByValixEi|"\?\?ANonTrivialIntArrayByVal@@QEBAAEBHH@Z")]](%struct.NonTrivialIntArrayByVal* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
44+
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.NonTrivialIntArrayByVal* nonnull dereferenceable(20) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.NonTrivialIntArrayByVal\* byval\(%struct.NonTrivialIntArrayByVal\) align 2 %.*}})
45+
// CHECK: [[THIS:%.*]] = load %struct.NonTrivialIntArrayByVal*, %struct.NonTrivialIntArrayByVal**
46+
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.NonTrivialIntArrayByVal, %struct.NonTrivialIntArrayByVal* [[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]]

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

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,77 @@
9191
// CHECK: }
9292

9393

94-
// Non-reference subscript operators are not currently imported (SR-14351)
95-
// so just make sure we don't crash.
96-
// CHECK: struct NonReferenceReadIntArray {
94+
// CHECK: struct IntArrayByVal {
95+
// CHECK: @available(*, unavailable, message: "use subscript")
96+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
97+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
98+
// CHECK: }
99+
100+
// CHECK: struct NonTrivialIntArrayByVal {
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: struct DifferentTypesArrayByVal {
107+
// CHECK: @available(*, unavailable, message: "use subscript")
108+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
109+
110+
// CHECK: @available(*, unavailable, message: "use subscript")
111+
// CHECK: mutating func __operatorSubscriptConst(_ x: Bool) -> Bool
112+
113+
// CHECK: @available(*, unavailable, message: "use subscript")
114+
// CHECK: mutating func __operatorSubscriptConst(_ x: Double) -> Double
115+
116+
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
117+
// CHECK: subscript(x: Bool) -> Bool { mutating get }
118+
// CHECK: subscript(x: Double) -> Double { mutating get }
119+
// CHECK: }
120+
121+
122+
// CHECK: struct TemplatedArrayByVal<T> {
97123
// CHECK: }
124+
// CHECK: struct __CxxTemplateInst19TemplatedArrayByValIdE {
125+
// CHECK: @available(*, unavailable, message: "use subscript")
126+
// CHECK: mutating func __operatorSubscriptConst(_ i: Int32) -> Double
127+
// CHECK: subscript(i: Int32) -> Double { mutating get }
128+
// CHECK: }
129+
// CHECK: typealias TemplatedDoubleArrayByVal = __CxxTemplateInst19TemplatedArrayByValIdE
130+
131+
132+
// CHECK: struct TemplatedSubscriptArrayByVal {
133+
// CHECK: @available(*, unavailable, message: "use subscript")
134+
// CHECK: mutating func __operatorSubscriptConst<T>(_ i: T) -> T
135+
// CHECK: subscript(i: T) -> T { mutating get }
136+
// CHECK: }
137+
138+
// CHECK: struct NonTrivialArrayByVal {
139+
// CHECK: @available(*, unavailable, message: "use subscript")
140+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> NonTrivial
141+
// CHECK: subscript(x: Int32) -> NonTrivial { mutating get }
142+
// CHECK: }
143+
// CHECK: struct PtrByVal {
144+
// CHECK: @available(*, unavailable, message: "use subscript")
145+
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<Int32>!
146+
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<Int32>! { mutating get }
147+
// CHECK: }
148+
// CHECK: struct RefToPtr {
149+
// CHECK: @available(*, unavailable, message: "use subscript")
150+
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>
151+
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<Int32>? { mutating get set }
152+
// CHECK: }
153+
// CHECK: struct PtrToPtr {
154+
// CHECK: @available(*, unavailable, message: "use subscript")
155+
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>!
156+
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>! { mutating get }
157+
// CHECK: }
158+
// CHECK: struct ConstOpPtrByVal {
159+
// CHECK: @available(*, unavailable, message: "use subscript")
160+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>!
161+
// CHECK: subscript(x: Int32) -> UnsafePointer<Int32>! { mutating get }
162+
// CHECK: }
163+
// CHECK: struct ConstPtrByVal {
164+
// CHECK: @available(*, unavailable, message: "use subscript")
165+
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>!
166+
// CHECK: subscript(x: Int32) -> UnsafePointer<Int32>! { mutating get }
167+
// CHECK: }

0 commit comments

Comments
 (0)