Skip to content

Commit 40a7e68

Browse files
committed
[cxx-interop] Import iterator dereference operators
C++ iterator dereference operator is mapped to a Swift computed property called `pointee`. For example: ```cpp struct ConstIterator { // ... const int &operator*() const { /* ... */ } }; ``` is imported as ```swift struct ConstIterator { var pointee: Int32 { get } @available(*, unavailable, message: "use .pointee property") func __operatorStar() -> UnsafePointer<Int32> } ```
1 parent a00c0b4 commit 40a7e68

File tree

4 files changed

+129
-9
lines changed

4 files changed

+129
-9
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,12 +3595,24 @@ namespace {
35953595
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(m)) {
35963596
auto cxxOperatorKind = cxxMethod->getOverloadedOperator();
35973597

3598+
if (cxxOperatorKind == clang::OO_Star && cxxMethod->param_empty()) {
3599+
// This is a dereference operator. We synthesize a computed
3600+
// property called `pointee` for it.
3601+
VarDecl *pointeeProperty = makeDereferencedPointeeProperty(MD);
3602+
result->addMember(pointeeProperty);
3603+
3604+
Impl.markUnavailable(MD, "use .pointee property");
3605+
MD->overwriteAccess(AccessLevel::Private);
3606+
}
35983607
// Check if this method _is_ an overloaded operator but is not a
3599-
// call / subscript. Those 2 operators do not need static versions
3600-
if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_None &&
3601-
cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call &&
3602-
cxxOperatorKind !=
3603-
clang::OverloadedOperatorKind::OO_Subscript) {
3608+
// call / subscript / dereference. Those 3 operators do not need
3609+
// static versions.
3610+
else if (cxxOperatorKind !=
3611+
clang::OverloadedOperatorKind::OO_None &&
3612+
cxxOperatorKind !=
3613+
clang::OverloadedOperatorKind::OO_Call &&
3614+
cxxOperatorKind !=
3615+
clang::OverloadedOperatorKind::OO_Subscript) {
36043616

36053617
auto opFuncDecl = makeOperator(MD, cxxMethod);
36063618

@@ -5410,6 +5422,11 @@ namespace {
54105422
/// \param setter function returning `UnsafeMutablePointer<T>`
54115423
/// \return subscript declaration
54125424
SubscriptDecl *makeSubscript(FuncDecl *getter, FuncDecl *setter);
5425+
5426+
/// Given an imported C++ dereference operator (`operator*()`), create a
5427+
/// `pointee` computed property.
5428+
VarDecl *makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc);
5429+
54135430
FuncDecl *makeOperator(FuncDecl *operatorMethod,
54145431
clang::CXXMethodDecl *clangOperator);
54155432

@@ -7833,16 +7850,21 @@ SwiftDeclConverter::importAccessor(const clang::ObjCMethodDecl *clangAccessor,
78337850
return accessor;
78347851
}
78357852

7836-
/// Synthesizer callback for a subscript getter.
7853+
/// Synthesizer callback for a subscript getter or a getter for a
7854+
/// dereference property (`var pointee`). If the getter's implementation returns
7855+
/// an UnsafePointer or UnsafeMutablePointer, it unwraps the pointer and returns
7856+
/// the underlying value.
78377857
static std::pair<BraceStmt *, bool>
7838-
synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
7858+
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
78397859
auto getterDecl = cast<AccessorDecl>(afd);
78407860
auto getterImpl = static_cast<FuncDecl *>(context);
78417861

78427862
ASTContext &ctx = getterDecl->getASTContext();
78437863

78447864
Expr *selfExpr = createSelfExpr(getterDecl);
7845-
DeclRefExpr *keyRefExpr = createParamRefExpr(getterDecl, 0);
7865+
DeclRefExpr *keyRefExpr = getterDecl->getParameters()->size() == 0
7866+
? nullptr
7867+
: createParamRefExpr(getterDecl, 0);
78467868

78477869
Type elementTy = getterDecl->getResultInterfaceType();
78487870

@@ -8048,7 +8070,7 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
80488070
getterDecl->setImplicit();
80498071
getterDecl->setIsDynamic(false);
80508072
getterDecl->setIsTransparent(true);
8051-
getterDecl->setBodySynthesizer(synthesizeSubscriptGetterBody, getterImpl);
8073+
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody, getterImpl);
80528074

80538075
if (getterImpl->isMutating()) {
80548076
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
@@ -8101,6 +8123,47 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
81018123
return subscript;
81028124
}
81038125

8126+
VarDecl *
8127+
SwiftDeclConverter::makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc) {
8128+
auto &ctx = Impl.SwiftContext;
8129+
auto dc = dereferenceFunc->getDeclContext();
8130+
8131+
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
8132+
const auto rawElementTy = dereferenceFunc->getResultInterfaceType();
8133+
// Unwrap `T`. Use rawElementTy for return by value.
8134+
const auto elementTy = rawElementTy->getAnyPointerElementType()
8135+
? rawElementTy->getAnyPointerElementType()
8136+
: rawElementTy;
8137+
8138+
auto result = new (ctx)
8139+
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
8140+
dereferenceFunc->getStartLoc(), ctx.getIdentifier("pointee"), dc);
8141+
result->setInterfaceType(elementTy);
8142+
result->setAccess(AccessLevel::Public);
8143+
result->setImplInfo(StorageImplInfo::getImmutableComputed());
8144+
8145+
AccessorDecl *getterDecl = AccessorDecl::create(
8146+
ctx, dereferenceFunc->getLoc(), dereferenceFunc->getLoc(),
8147+
AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None,
8148+
/*async*/ false, SourceLoc(),
8149+
/*throws*/ false, SourceLoc(), nullptr, ParameterList::createEmpty(ctx),
8150+
elementTy, dc);
8151+
getterDecl->setAccess(AccessLevel::Public);
8152+
getterDecl->setImplicit();
8153+
getterDecl->setIsDynamic(false);
8154+
getterDecl->setIsTransparent(true);
8155+
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody,
8156+
dereferenceFunc);
8157+
8158+
if (dereferenceFunc->isMutating()) {
8159+
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
8160+
result->setIsGetterMutating(true);
8161+
}
8162+
8163+
makeComputed(result, getterDecl, /*setter*/ nullptr);
8164+
return result;
8165+
}
8166+
81048167
static std::pair<BraceStmt *, bool>
81058168
synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) {
81068169
ASTContext &ctx = afd->getASTContext();

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,25 @@ struct DerivedFromReadWriteIntArray : ReadWriteIntArray {};
274274

275275
struct DerivedFromNonTrivialArrayByVal : NonTrivialArrayByVal {};
276276

277+
struct Iterator {
278+
private:
279+
int value = 123;
280+
public:
281+
int &operator*() { return value; }
282+
};
283+
284+
struct ConstIterator {
285+
private:
286+
int value = 234;
287+
public:
288+
const int &operator*() const { return value; }
289+
};
290+
291+
struct ConstIteratorByVal {
292+
private:
293+
int value = 456;
294+
public:
295+
int operator*() const { return value; }
296+
};
297+
277298
#endif

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,21 @@
190190
// CHECK: subscript(x: Int32) -> NonTrivial { get }
191191
// CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> NonTrivial
192192
// CHECK: }
193+
194+
// CHECK: struct Iterator {
195+
// CHECK: var pointee: Int32 { mutating get }
196+
// CHECK: @available(*, unavailable, message: "use .pointee property")
197+
// CHECK: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
198+
// CHECK: }
199+
200+
// CHECK: struct ConstIterator {
201+
// CHECK: var pointee: Int32 { get }
202+
// CHECK: @available(*, unavailable, message: "use .pointee property")
203+
// CHECK: func __operatorStar() -> UnsafePointer<Int32>
204+
// CHECK: }
205+
206+
// CHECK: struct ConstIteratorByVal {
207+
// CHECK: var pointee: Int32 { get }
208+
// CHECK: @available(*, unavailable, message: "use .pointee property")
209+
// CHECK: func __operatorStar() -> Int32
210+
// CHECK: }

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,22 @@ OperatorsTestSuite.test("PtrToPtr.subscript (inline)") {
240240
// expectEqual(23, arr[0])
241241
//}
242242

243+
OperatorsTestSuite.test("Iterator.pointee") {
244+
var iter = Iterator()
245+
let res = iter.pointee
246+
expectEqual(123, res)
247+
}
248+
249+
OperatorsTestSuite.test("ConstIterator.pointee") {
250+
let iter = ConstIterator()
251+
let res = iter.pointee
252+
expectEqual(234, res)
253+
}
254+
255+
OperatorsTestSuite.test("ConstIteratorByVal.pointee") {
256+
let iter = ConstIteratorByVal()
257+
let res = iter.pointee
258+
expectEqual(456, res)
259+
}
260+
243261
runAllTests()

0 commit comments

Comments
 (0)