Skip to content

[cxx-interop] Import iterator dereference operators #59465

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
Jun 16, 2022
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
81 changes: 72 additions & 9 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3595,12 +3595,24 @@ namespace {
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(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);

Expand Down Expand Up @@ -5410,6 +5422,11 @@ namespace {
/// \param setter function returning `UnsafeMutablePointer<T>`
/// \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);

Expand Down Expand Up @@ -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<BraceStmt *, bool>
synthesizeSubscriptGetterBody(AbstractFunctionDecl *afd, void *context) {
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
auto getterDecl = cast<AccessorDecl>(afd);
auto getterImpl = static_cast<FuncDecl *>(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();

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -8101,6 +8123,47 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) {
return subscript;
}

VarDecl *
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: ImportDecl.cpp is way too big, I'll move the decl synthesis logic into a separate file in another PR.

SwiftDeclConverter::makeDereferencedPointeeProperty(FuncDecl *dereferenceFunc) {
auto &ctx = Impl.SwiftContext;
auto dc = dereferenceFunc->getDeclContext();

// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
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<BraceStmt *, bool>
synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) {
ASTContext &ctx = afd->getASTContext();
Expand Down
21 changes: 21 additions & 0 deletions test/Interop/Cxx/operators/Inputs/member-inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 18 additions & 0 deletions test/Interop/Cxx/operators/member-inline-module-interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Int32>
// CHECK: }

// CHECK: struct ConstIterator {
// CHECK: var pointee: Int32 { get }
// CHECK: @available(*, unavailable, message: "use .pointee property")
// CHECK: func __operatorStar() -> UnsafePointer<Int32>
// CHECK: }

// CHECK: struct ConstIteratorByVal {
// CHECK: var pointee: Int32 { get }
// CHECK: @available(*, unavailable, message: "use .pointee property")
// CHECK: func __operatorStar() -> Int32
// CHECK: }
18 changes: 18 additions & 0 deletions test/Interop/Cxx/operators/member-inline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()