Skip to content

[cxx-interop] Implement operator[] for value return types (SR-14351). #36688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 33 additions & 17 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7579,25 +7579,35 @@ synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
inoutSelfExpr,
keyRefExpr);

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

SubstitutionMap subMap =
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
{ elementTy }, { });
auto pointeePropertyRefExpr =
new (ctx) MemberRefExpr(getterImplCallExpr,
SourceLoc(),
ConcreteDeclRef(pointeePropertyDecl, subMap),
DeclNameLoc(),
/*implicit=*/ true);
pointeePropertyRefExpr->setType(elementTy);
// The following check returns true if the subscript operator returns a C++
// reference type. This check actually checks to see if the type is a pointer
// type, but this does not apply to C pointers because they are Optional types
// when imported. TODO: Use a more obvious check here.
if (getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
// Retrieve the corresponding `.pointee` declaration.
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);

// Handle operator[] that returns a reference type.
SubstitutionMap subMap =
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
{ elementTy }, { });
auto pointeePropertyRefExpr =
new (ctx) MemberRefExpr(getterImplCallExpr,
SourceLoc(),
ConcreteDeclRef(pointeePropertyDecl, subMap),
DeclNameLoc(),
/*implicit=*/ true);
pointeePropertyRefExpr->setType(elementTy);
propertyExpr = pointeePropertyRefExpr;
}

auto returnStmt = new (ctx) ReturnStmt(SourceLoc(),
pointeePropertyRefExpr,
propertyExpr,
/*implicit=*/ true);

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

// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
const auto rawElementTy = getterImpl->getResultInterfaceType();
// Unwrap `T`.
const auto elementTy = rawElementTy->getAnyPointerElementType();
// Unwrap `T`. Use rawElementTy for return by value.
const auto elementTy = rawElementTy->getAnyPointerElementType() ?
rawElementTy->getAnyPointerElementType() :
rawElementTy;

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

makeComputed(subscript, getterDecl, setterDecl);

// Implicitly unwrap Optional types for T *operator[].
Impl.recordImplicitUnwrapForDecl(subscript,
getterImpl->isImplicitlyUnwrappedOptional());

return subscript;
}

Expand Down
12 changes: 7 additions & 5 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1745,13 +1745,15 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
break;
case clang::OverloadedOperatorKind::OO_Subscript: {
auto returnType = functionDecl->getReturnType();
if (!returnType->isReferenceType()) {
// TODO: support non-reference return types (SR-14351)
return ImportedName();
}
if (returnType->getPointeeType().isConstQualified()) {
if ((!returnType->isReferenceType() && !returnType->isAnyPointerType()) ||
returnType->getPointeeType().isConstQualified()) {
// If we are handling a non-reference return type, treat it as a getter
// so that we do not SILGen the value type operator[] as an rvalue.
baseName = "__operatorSubscriptConst";
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
} else if (returnType->isAnyPointerType()) {
baseName = "__operatorSubscript";
result.info.accessorKind = ImportedAccessorKind::SubscriptGetter;
} else {
baseName = "__operatorSubscript";
result.info.accessorKind = ImportedAccessorKind::SubscriptSetter;
Expand Down
102 changes: 97 additions & 5 deletions test/Interop/Cxx/operators/Inputs/member-inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,106 @@ struct TemplatedSubscriptArray {
}
};

struct NonReferenceReadIntArray {
struct IntArrayByVal {
// For testing purposes.
void setValueAtIndex(int value, unsigned i) { values[i] = value; }
int operator[](int x) const { return values[x]; }
private:
int values[3] = { 1, 2, 3 };
};

public:
int operator[](int x) const {
return values[x];
}
struct NonTrivialIntArrayByVal {
NonTrivialIntArrayByVal(int first) { values[0] = first; }
NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {}
int operator[](int x) const { return values[x]; }

// For testing purposes.
void setValueAtIndex(int value, unsigned i) { values[i] = value; }

private:
int values[5] = { 1, 2, 3, 4, 5 };
};

struct DifferentTypesArrayByVal {
int operator[](int x) { return values[x]; }
bool operator[](bool x) { return boolValues[x]; }
double operator[](double x) const { return doubleValues[x == 0.0]; }

private:
int values[3] = { 1, 2, 3 };
double doubleValues[2] = { 1.5, 2.5 };
bool boolValues[2] = { true, false };
};

template<class T> struct TemplatedArrayByVal {
T ts[];
T operator[](int i) { return ts[i]; }
};
typedef TemplatedArrayByVal<double> TemplatedDoubleArrayByVal;

struct TemplatedSubscriptArrayByVal {
int *ptr;
template<class T> T operator[](T i) { return ptr[i]; }
};

struct NonTrivial {
char *Str;
long long a;
short b;
long long c;
short d;
long long e;
int f;
NonTrivial() {
Str = (char*)"Non-Trivial";
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
f = 6;
}
~NonTrivial() { Str = nullptr; }
};

struct NonTrivialArrayByVal {
NonTrivial operator[](int x) { return S; }
private:
NonTrivial S;
};

struct PtrByVal {
int *operator[](int x) { return &a; }
private:
int a = 64;
};

struct RefToPtr {
RefToPtr() { b = &a; }
int *&operator[](int x) { return b; }
private:
int a = 64;
int *b = nullptr;
};

struct PtrToPtr {
PtrToPtr() { b = &a; }
int **operator[](int x) { return &b; }
private:
int a = 64;
int *b = nullptr;
};

struct ConstOpPtrByVal {
const int *operator[](int x) const { return &a; }
private:
int a = 64;
};

struct ConstPtrByVal {
const int *operator[](int x) { return &a; }
private:
int a = 64;
};

#endif
4 changes: 4 additions & 0 deletions test/Interop/Cxx/operators/Inputs/member-out-of-line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ const int& ReadWriteIntArray::operator[](int x) const {
int& ReadWriteIntArray::operator[](int x) {
return values[x];
}

int NonTrivialIntArrayByVal::operator[](int x) {
return values[x];
}
14 changes: 13 additions & 1 deletion test/Interop/Cxx/operators/Inputs/member-out-of-line.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ struct ReadWriteIntArray {
int &operator[](int x);
};

#endif
struct NonTrivialIntArrayByVal {
NonTrivialIntArrayByVal(int first) { values[0] = first; }
NonTrivialIntArrayByVal(const NonTrivialIntArrayByVal &other) {}
int operator[](int x);

// For testing purposes.
void setValueAtIndex(int value, unsigned i) { values[i] = value; }

private:
int values[5] = { 1, 2, 3, 4, 5 };
};

#endif
18 changes: 14 additions & 4 deletions test/Interop/Cxx/operators/member-inline-irgen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import MemberInline
public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> LoadableIntWrapper { lhs - rhs }

// 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]+}})
// 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}})
// 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 %.*}})

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

// 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]+}})
// 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}})
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.LoadableIntWrapper* nonnull dereferenceable(4) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.LoadableIntWrapper\* byval\(%struct.LoadableIntWrapper\) align 4 %.*}})

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

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

// CHECK: call [[RES:i32|i64]]* [[NAME:@(_ZNK16ReadOnlyIntArrayixEi|"\?\?AReadOnlyIntArray@@QEBAAEBHH@Z")]](%struct.ReadOnlyIntArray* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
// 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}})
// 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 %.*}})
// CHECK: [[THIS:%.*]] = load %struct.ReadOnlyIntArray*, %struct.ReadOnlyIntArray**
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadOnlyIntArray, %struct.ReadOnlyIntArray* [[THIS]]
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
Expand All @@ -32,8 +32,18 @@ public func index(_ arr: inout ReadOnlyIntArray, _ arg: Int32) -> Int32 { arr[ar
public func index(_ arr: inout ReadWriteIntArray, _ arg: Int32, _ val: Int32) { arr[arg] = val }

// CHECK: call [[RES:i32|i64]]* [[NAME:@(_ZN17ReadWriteIntArrayixEi|"\?\?AReadWriteIntArray@@QEAAAEAHH@Z")]](%struct.ReadWriteIntArray* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
// 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}})
// 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 %.*}})
// CHECK: [[THIS:%.*]] = load %struct.ReadWriteIntArray*, %struct.ReadWriteIntArray**
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.ReadWriteIntArray, %struct.ReadWriteIntArray* [[THIS]]
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
// CHECK: ret {{i32|i64}}* [[VALUE]]

public func index(_ arr: inout NonTrivialIntArrayByVal, _ arg: Int32) -> Int32 { arr[arg] }

// CHECK: call [[RES:i32|i64]] [[NAME:@(_ZNK23NonTrivialIntArrayByValixEi|"\?\?ANonTrivialIntArrayByVal@@QEBAAEBHH@Z")]](%struct.NonTrivialIntArrayByVal* {{%[0-9]+}}, {{i32|i64}} {{%[0-9]+}})
// CHECK: define linkonce_odr [[RES]] [[NAME]](%struct.NonTrivialIntArrayByVal* nonnull dereferenceable(20) {{.*}}, {{i32 %.*|\[1 x i32\] %.*|i64 %.*|%struct.NonTrivialIntArrayByVal\* byval\(%struct.NonTrivialIntArrayByVal\) align 2 %.*}})
// CHECK: [[THIS:%.*]] = load %struct.NonTrivialIntArrayByVal*, %struct.NonTrivialIntArrayByVal**
// CHECK: [[VALUES:%.*]] = getelementptr inbounds %struct.NonTrivialIntArrayByVal, %struct.NonTrivialIntArrayByVal* [[THIS]]
// CHECK: [[VALUE:%.*]] = getelementptr inbounds [5 x {{i32|i64}}], [5 x {{i32|i64}}]* [[VALUES]]
// CHECK: [[VALUE2:%.*]] = load {{i32|i64}}, {{i32|i64}}* [[VALUE]]
// CHECK: ret {{i32|i64}} [[VALUE2]]
76 changes: 73 additions & 3 deletions test/Interop/Cxx/operators/member-inline-module-interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,77 @@
// CHECK: }


// Non-reference subscript operators are not currently imported (SR-14351)
// so just make sure we don't crash.
// CHECK: struct NonReferenceReadIntArray {
// CHECK: struct IntArrayByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
// CHECK: }

// CHECK: struct NonTrivialIntArrayByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32
// CHECK: subscript(x: Int32) -> Int32 { mutating get }
// CHECK: }

// CHECK: struct DifferentTypesArrayByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> Int32

// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Bool) -> Bool

// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Double) -> Double

// CHECK: subscript(x: Int32) -> Int32 { mutating get }
// CHECK: subscript(x: Bool) -> Bool { mutating get }
// CHECK: subscript(x: Double) -> Double { mutating get }
// CHECK: }


// CHECK: struct TemplatedArrayByVal<T> {
// CHECK: }
// CHECK: struct __CxxTemplateInst19TemplatedArrayByValIdE {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ i: Int32) -> Double
// CHECK: subscript(i: Int32) -> Double { mutating get }
// CHECK: }
// CHECK: typealias TemplatedDoubleArrayByVal = __CxxTemplateInst19TemplatedArrayByValIdE


// CHECK: struct TemplatedSubscriptArrayByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst<T>(_ i: T) -> T
// CHECK: subscript(i: T) -> T { mutating get }
// CHECK: }

// CHECK: struct NonTrivialArrayByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> NonTrivial
// CHECK: subscript(x: Int32) -> NonTrivial { mutating get }
// CHECK: }
// CHECK: struct PtrByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<Int32>!
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<Int32>! { mutating get }
// CHECK: }
// CHECK: struct RefToPtr {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<Int32>? { mutating get set }
// CHECK: }
// CHECK: struct PtrToPtr {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscript(_ x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>!
// CHECK: subscript(x: Int32) -> UnsafeMutablePointer<UnsafeMutablePointer<Int32>?>! { mutating get }
// CHECK: }
// CHECK: struct ConstOpPtrByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>!
// CHECK: subscript(x: Int32) -> UnsafePointer<Int32>! { mutating get }
// CHECK: }
// CHECK: struct ConstPtrByVal {
// CHECK: @available(*, unavailable, message: "use subscript")
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> UnsafePointer<Int32>!
// CHECK: subscript(x: Int32) -> UnsafePointer<Int32>! { mutating get }
// CHECK: }
Loading