Skip to content

Commit aff6a6b

Browse files
committed
[cxx-interop] add support for 'address' pointee for non-copyable C++ types
1 parent 8a2d066 commit aff6a6b

File tree

6 files changed

+161
-14
lines changed

6 files changed

+161
-14
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,22 @@ void ClangImporter::Implementation::makeComputed(AbstractStorageDecl *storage,
125125
AccessorDecl *getter,
126126
AccessorDecl *setter) {
127127
assert(getter);
128+
// The synthesized computed property can either use a `get` or an
129+
// `unsafeAddress` accessor.
130+
auto isAddress = getter->getAccessorKind() == AccessorKind::Address;
128131
storage->getASTContext().evaluator.cacheOutput(HasStorageRequest{storage}, false);
129132
if (setter) {
130-
storage->setImplInfo(StorageImplInfo::getMutableComputed());
133+
if (isAddress)
134+
assert(setter->getAccessorKind() == AccessorKind::MutableAddress);
135+
storage->setImplInfo(
136+
isAddress ? StorageImplInfo(ReadImplKind::Address,
137+
WriteImplKind::MutableAddress,
138+
ReadWriteImplKind::MutableAddress)
139+
: StorageImplInfo::getMutableComputed());
131140
storage->setAccessors(SourceLoc(), {getter, setter}, SourceLoc());
132141
} else {
133-
storage->setImplInfo(StorageImplInfo::getImmutableComputed());
142+
storage->setImplInfo(isAddress ? StorageImplInfo(ReadImplKind::Address)
143+
: StorageImplInfo::getImmutableComputed());
134144
storage->setAccessors(SourceLoc(), {getter}, SourceLoc());
135145
}
136146
}

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,7 +1501,8 @@ AccessorDecl *SwiftDeclSynthesizer::buildSubscriptSetterDecl(
15011501
/// an UnsafePointer or UnsafeMutablePointer, it unwraps the pointer and returns
15021502
/// the underlying value.
15031503
static std::pair<BraceStmt *, bool>
1504-
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
1504+
synthesizeUnwrappingGetterOrAddressGetterBody(AbstractFunctionDecl *afd,
1505+
void *context, bool isAddress) {
15051506
auto getterDecl = cast<AccessorDecl>(afd);
15061507
auto getterImpl = static_cast<FuncDecl *>(context);
15071508

@@ -1525,7 +1526,8 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
15251526
// reference type. This check actually checks to see if the type is a pointer
15261527
// type, but this does not apply to C pointers because they are Optional types
15271528
// when imported. TODO: Use a more obvious check here.
1528-
if (getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
1529+
if (!isAddress &&
1530+
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
15291531
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
15301532
// Retrieve the corresponding `.pointee` declaration.
15311533
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
@@ -1549,6 +1551,19 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
15491551
return {body, /*isTypeChecked*/ true};
15501552
}
15511553

1554+
static std::pair<BraceStmt *, bool>
1555+
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
1556+
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
1557+
/*isAddress=*/false);
1558+
}
1559+
1560+
static std::pair<BraceStmt *, bool>
1561+
synthesizeUnwrappingAddressGetterBody(AbstractFunctionDecl *afd,
1562+
void *context) {
1563+
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
1564+
/*isAddress=*/true);
1565+
}
1566+
15521567
/// Synthesizer callback for a subscript setter or a setter for a dereference
15531568
/// property (`var pointee`).
15541569
static std::pair<BraceStmt *, bool>
@@ -1685,7 +1700,6 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
16851700
FuncDecl *setter) {
16861701
assert((getter || setter) &&
16871702
"getter or setter required to generate a pointee property");
1688-
16891703
auto &ctx = ImporterImpl.SwiftContext;
16901704
FuncDecl *getterImpl = getter ? getter : setter;
16911705
FuncDecl *setterImpl = setter;
@@ -1697,6 +1711,9 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
16971711
const auto elementTy = rawElementTy->getAnyPointerElementType()
16981712
? rawElementTy->getAnyPointerElementType()
16991713
: rawElementTy;
1714+
// Use 'address' or 'mutableAddress' accessors for non-copyable
1715+
// types.
1716+
bool useAddress = elementTy->isNoncopyable(dc);
17001717

17011718
auto result = new (ctx)
17021719
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
@@ -1705,16 +1722,21 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
17051722
result->setAccess(AccessLevel::Public);
17061723

17071724
AccessorDecl *getterDecl = AccessorDecl::create(
1708-
ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get,
1709-
result, SourceLoc(), StaticSpellingKind::None,
1725+
ctx, getterImpl->getLoc(), getterImpl->getLoc(),
1726+
useAddress ? AccessorKind::Address : AccessorKind::Get, result,
1727+
SourceLoc(), StaticSpellingKind::None,
17101728
/*async*/ false, SourceLoc(),
17111729
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
1712-
ParameterList::createEmpty(ctx), elementTy, dc);
1730+
ParameterList::createEmpty(ctx),
1731+
useAddress ? elementTy->wrapInPointer(PTK_UnsafePointer) : elementTy, dc);
17131732
getterDecl->setAccess(AccessLevel::Public);
1714-
getterDecl->setImplicit();
1733+
if (!useAddress)
1734+
getterDecl->setImplicit();
17151735
getterDecl->setIsDynamic(false);
17161736
getterDecl->setIsTransparent(true);
1717-
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody,
1737+
getterDecl->setBodySynthesizer(useAddress
1738+
? synthesizeUnwrappingAddressGetterBody
1739+
: synthesizeUnwrappingGetterBody,
17181740
getterImpl);
17191741

17201742
if (getterImpl->isMutating()) {
@@ -1737,13 +1759,18 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
17371759
ParameterList::create(ctx, {paramVarDecl});
17381760

17391761
setterDecl = AccessorDecl::create(
1740-
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
1741-
result, SourceLoc(), StaticSpellingKind::None,
1762+
ctx, setterImpl->getLoc(), setterImpl->getLoc(),
1763+
useAddress ? AccessorKind::MutableAddress : AccessorKind::Set, result,
1764+
SourceLoc(), StaticSpellingKind::None,
17421765
/*async*/ false, SourceLoc(),
17431766
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
1744-
setterParamList, TupleType::getEmpty(ctx), dc);
1767+
setterParamList,
1768+
useAddress ? elementTy->wrapInPointer(PTK_UnsafeMutablePointer)
1769+
: TupleType::getEmpty(ctx),
1770+
dc);
17451771
setterDecl->setAccess(AccessLevel::Public);
1746-
setterDecl->setImplicit();
1772+
if (!useAddress)
1773+
setterDecl->setImplicit();
17471774
setterDecl->setIsDynamic(false);
17481775
setterDecl->setIsTransparent(true);
17491776
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module MoveOnlyCxxOperators {
2+
header "move-only-cxx-value-operators.h"
3+
requires cplusplus
4+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef TEST_INTEROP_CXX_OPERATORS_MOVE_ONLY_OPS_H
2+
#define TEST_INTEROP_CXX_OPERATORS_MOVE_ONLY_OPS_H
3+
4+
#include <memory>
5+
6+
struct Copyable {
7+
int x;
8+
};
9+
10+
struct NonCopyable {
11+
inline NonCopyable(int x) : x(x) {}
12+
inline NonCopyable(const NonCopyable &) = delete;
13+
inline NonCopyable(NonCopyable &&other) : x(other.x) { other.x = 0; }
14+
15+
inline int method(int y) const { return x * y; }
16+
inline int mutMethod(int y) {
17+
x = y;
18+
return y;
19+
}
20+
21+
int x;
22+
};
23+
24+
#define NONCOPYABLE_HOLDER_WRAPPER(Name) \
25+
private: \
26+
NonCopyable x; \
27+
public: \
28+
inline Name(int x) : x(x) {} \
29+
inline Name(const Name &) = delete; \
30+
inline Name(Name &&other) : x(std::move(other.x)) {}
31+
32+
class NonCopyableHolderConstDeref {
33+
NONCOPYABLE_HOLDER_WRAPPER(NonCopyableHolderConstDeref)
34+
35+
inline const NonCopyable & operator *() const { return x; }
36+
};
37+
38+
#endif // TEST_INTEROP_CXX_OPERATORS_MOVE_ONLY_OPS_H
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import MoveOnlyCxxOperators
6+
import StdlibUnittest
7+
8+
var MoveOnlyCxxOperators = TestSuite("Move Only Operators")
9+
10+
func borrowNC(_ x: borrowing NonCopyable) -> CInt {
11+
return x.method(3)
12+
}
13+
14+
func inoutNC(_ x: inout NonCopyable, _ y: CInt) -> CInt {
15+
return x.mutMethod(y)
16+
}
17+
18+
func consumingNC(_ x: consuming NonCopyable) {
19+
// do nothing.
20+
}
21+
22+
MoveOnlyCxxOperators.test("NonCopyableHolderConstDeref pointee borrow") {
23+
let holder = NonCopyableHolderConstDeref(11)
24+
var k = borrowNC(holder.pointee)
25+
expectEqual(k, 33)
26+
k = holder.pointee.method(2)
27+
expectEqual(k, 22)
28+
k = holder.pointee.x
29+
expectEqual(k, 11)
30+
}
31+
32+
runAllTests()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop -DNO_CONSUME
2+
3+
// Note: some errors need full SIL emission right now.
4+
// FIXME: they should be type checker errors.
5+
// RUN: not %target-swift-emit-sil %s -I %S/Inputs -enable-experimental-cxx-interop 2>&1 | %FileCheck %s
6+
7+
import MoveOnlyCxxOperators
8+
9+
func borrowNC(_ x: borrowing NonCopyable) -> CInt {
10+
return x.method(3)
11+
}
12+
13+
func inoutNC(_ x: inout NonCopyable) -> CInt {
14+
return x.mutMethod(5)
15+
}
16+
17+
func consumingNC(_ x: consuming NonCopyable) {
18+
// do nothing.
19+
}
20+
21+
func testNonCopyableHolderConstDerefPointee() {
22+
var holder = NonCopyableHolderConstDeref(11)
23+
_ = borrowNC(holder.pointee) // ok
24+
_ = holder.pointee.method(2)
25+
_ = holder.pointee.x
26+
#if NO_CONSUME
27+
_ = inoutNC(holder.pointee) // expected-error {{cannot pass immutable value as inout argument: 'pointee' is a get-only property}}
28+
holder.pointee.mutMethod(1) // expected-error {{cannot use mutating member on immutable value: 'pointee' is a get-only property}}
29+
holder.pointee.x = 2 // expected-error {{cannot assign to property: 'pointee' is a get-only property}}
30+
#else
31+
consumingNC(holder.pointee) // CHECK: [[@LINE]]:{{.*}}: error:
32+
let consumeVal = holder.pointee // CHECK: [[@LINE]]:{{.*}}: error:
33+
#endif
34+
}
35+
36+
testNonCopyableHolderConstDerefPointee()

0 commit comments

Comments
 (0)