diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index bc337be20c951..460877ecde4dc 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3595,12 +3595,24 @@ namespace { if (auto cxxMethod = dyn_cast(m)) { auto cxxOperatorKind = cxxMethod->getOverloadedOperator(); + if (cxxOperatorKind == clang::OO_Star && cxxMethod->param_empty()) { + // This is a dereference operator. We synthesize a computed + // property called `pointee` for it. + VarDecl *pointeeProperty = makeDereferencedPointeeProperty(MD); + result->addMember(pointeeProperty); + + Impl.markUnavailable(MD, "use .pointee property"); + MD->overwriteAccess(AccessLevel::Private); + } // Check if this method _is_ an overloaded operator but is not a - // call / subscript. Those 2 operators do not need static versions - if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_None && - cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call && - cxxOperatorKind != - clang::OverloadedOperatorKind::OO_Subscript) { + // call / subscript / dereference. Those 3 operators do not need + // static versions. + else if (cxxOperatorKind != + clang::OverloadedOperatorKind::OO_None && + cxxOperatorKind != + clang::OverloadedOperatorKind::OO_Call && + cxxOperatorKind != + clang::OverloadedOperatorKind::OO_Subscript) { auto opFuncDecl = makeOperator(MD, cxxMethod); @@ -5410,6 +5422,11 @@ namespace { /// \param setter function returning `UnsafeMutablePointer` /// \return subscript declaration SubscriptDecl *makeSubscript(FuncDecl *getter, FuncDecl *setter); + + /// Given an imported C++ dereference operator (`operator*()`), create a + /// `pointee` computed property. + VarDecl *makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc); + FuncDecl *makeOperator(FuncDecl *operatorMethod, clang::CXXMethodDecl *clangOperator); @@ -7833,16 +7850,21 @@ SwiftDeclConverter::importAccessor(const clang::ObjCMethodDecl *clangAccessor, return accessor; } -/// Synthesizer callback for a subscript getter. +/// Synthesizer callback for a subscript getter or a getter for a +/// dereference property (`var pointee`). If the getter's implementation returns +/// an UnsafePointer or UnsafeMutablePointer, it unwraps the pointer and returns +/// the underlying value. static std::pair -synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) { +synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) { auto getterDecl = cast(afd); auto getterImpl = static_cast(context); ASTContext &ctx = getterDecl->getASTContext(); Expr *selfExpr = createSelfExpr(getterDecl); - DeclRefExpr *keyRefExpr = createParamRefExpr(getterDecl, 0); + DeclRefExpr *keyRefExpr = getterDecl->getParameters()->size() == 0 + ? nullptr + : createParamRefExpr(getterDecl, 0); Type elementTy = getterDecl->getResultInterfaceType(); @@ -8048,7 +8070,7 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) { getterDecl->setImplicit(); getterDecl->setIsDynamic(false); getterDecl->setIsTransparent(true); - getterDecl->setBodySynthesizer(synthesizeSubscriptGetterBody, getterImpl); + getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody, getterImpl); if (getterImpl->isMutating()) { getterDecl->setSelfAccessKind(SelfAccessKind::Mutating); @@ -8101,6 +8123,47 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) { return subscript; } +VarDecl * +SwiftDeclConverter::makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc) { + auto &ctx = Impl.SwiftContext; + auto dc = dereferenceFunc->getDeclContext(); + + // Get the return type wrapped in `Unsafe(Mutable)Pointer`. + const auto rawElementTy = dereferenceFunc->getResultInterfaceType(); + // Unwrap `T`. Use rawElementTy for return by value. + const auto elementTy = rawElementTy->getAnyPointerElementType() + ? rawElementTy->getAnyPointerElementType() + : rawElementTy; + + auto result = new (ctx) + VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var, + dereferenceFunc->getStartLoc(), ctx.getIdentifier("pointee"), dc); + result->setInterfaceType(elementTy); + result->setAccess(AccessLevel::Public); + result->setImplInfo(StorageImplInfo::getImmutableComputed()); + + AccessorDecl *getterDecl = AccessorDecl::create( + ctx, dereferenceFunc->getLoc(), dereferenceFunc->getLoc(), + AccessorKind::Get, result, SourceLoc(), StaticSpellingKind::None, + /*async*/ false, SourceLoc(), + /*throws*/ false, SourceLoc(), nullptr, ParameterList::createEmpty(ctx), + elementTy, dc); + getterDecl->setAccess(AccessLevel::Public); + getterDecl->setImplicit(); + getterDecl->setIsDynamic(false); + getterDecl->setIsTransparent(true); + getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody, + dereferenceFunc); + + if (dereferenceFunc->isMutating()) { + getterDecl->setSelfAccessKind(SelfAccessKind::Mutating); + result->setIsGetterMutating(true); + } + + makeComputed(result, getterDecl, /*setter*/ nullptr); + return result; +} + static std::pair synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) { ASTContext &ctx = afd->getASTContext(); diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index 68cf3e9be57fb..282c7044ce1e7 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -274,4 +274,25 @@ struct DerivedFromReadWriteIntArray : ReadWriteIntArray {}; struct DerivedFromNonTrivialArrayByVal : NonTrivialArrayByVal {}; +struct Iterator { +private: + int value = 123; +public: + int &operator*() { return value; } +}; + +struct ConstIterator { +private: + int value = 234; +public: + const int &operator*() const { return value; } +}; + +struct ConstIteratorByVal { +private: + int value = 456; +public: + int operator*() const { return value; } +}; + #endif diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index 8927eac237a8a..17f9d0de1b178 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -190,3 +190,21 @@ // CHECK: subscript(x: Int32) -> NonTrivial { get } // CHECK: mutating func __operatorSubscriptConst(_ x: Int32) -> NonTrivial // CHECK: } + +// CHECK: struct Iterator { +// CHECK: var pointee: Int32 { mutating get } +// CHECK: @available(*, unavailable, message: "use .pointee property") +// CHECK: mutating func __operatorStar() -> UnsafeMutablePointer +// CHECK: } + +// CHECK: struct ConstIterator { +// CHECK: var pointee: Int32 { get } +// CHECK: @available(*, unavailable, message: "use .pointee property") +// CHECK: func __operatorStar() -> UnsafePointer +// CHECK: } + +// CHECK: struct ConstIteratorByVal { +// CHECK: var pointee: Int32 { get } +// CHECK: @available(*, unavailable, message: "use .pointee property") +// CHECK: func __operatorStar() -> Int32 +// CHECK: } diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index 6de4df728d794..9a0ceae4603b7 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -240,4 +240,22 @@ OperatorsTestSuite.test("PtrToPtr.subscript (inline)") { // expectEqual(23, arr[0]) //} +OperatorsTestSuite.test("Iterator.pointee") { + var iter = Iterator() + let res = iter.pointee + expectEqual(123, res) +} + +OperatorsTestSuite.test("ConstIterator.pointee") { + let iter = ConstIterator() + let res = iter.pointee + expectEqual(234, res) +} + +OperatorsTestSuite.test("ConstIteratorByVal.pointee") { + let iter = ConstIteratorByVal() + let res = iter.pointee + expectEqual(456, res) +} + runAllTests()