Skip to content

Commit c7345d0

Browse files
authored
Merge pull request #69790 from hyp/eng/move-only-is-back
[cxx-interop] enable support for move-only types
2 parents 290d309 + 623d3d2 commit c7345d0

22 files changed

+987
-75
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 166 additions & 42 deletions
Large diffs are not rendered by default.

lib/ClangImporter/ImportDecl.cpp

Lines changed: 26 additions & 10 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
}
@@ -2183,17 +2193,23 @@ namespace {
21832193
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result;
21842194

21852195
if (recordHasMoveOnlySemantics(decl)) {
2186-
if (!Impl.SwiftContext.LangOpts.hasFeature(Feature::MoveOnly)) {
2196+
if (Impl.isCxxInteropCompatVersionAtLeast(
2197+
version::getUpcomingCxxInteropCompatVersion())) {
2198+
if (decl->isInStdNamespace() && decl->getName() == "promise") {
2199+
// Do not import std::promise.
2200+
return nullptr;
2201+
}
2202+
result->getAttrs().add(new (Impl.SwiftContext)
2203+
MoveOnlyAttr(/*Implicit=*/true));
2204+
} else {
21872205
Impl.addImportDiagnostic(
2188-
decl, Diagnostic(
2189-
diag::move_only_requires_move_only,
2190-
Impl.SwiftContext.AllocateCopy(decl->getNameAsString())),
2206+
decl,
2207+
Diagnostic(
2208+
diag::move_only_requires_move_only,
2209+
Impl.SwiftContext.AllocateCopy(decl->getNameAsString())),
21912210
decl->getLocation());
21922211
return nullptr;
21932212
}
2194-
2195-
result->getAttrs().add(new (Impl.SwiftContext)
2196-
MoveOnlyAttr(/*Implicit=*/true));
21972213
}
21982214

21992215
// FIXME: Figure out what to do with superclasses in C++. One possible
@@ -3964,7 +3980,7 @@ namespace {
39643980
auto loc = Impl.importSourceLoc(decl->getLocation());
39653981
auto dc = Impl.importDeclContextOf(
39663982
decl, importedName.getEffectiveContext());
3967-
3983+
39683984
SmallVector<GenericTypeParamDecl *, 4> genericParams;
39693985
for (auto &param : *decl->getTemplateParameters()) {
39703986
auto genericParamDecl =

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,12 +1496,36 @@ AccessorDecl *SwiftDeclSynthesizer::buildSubscriptSetterDecl(
14961496

14971497
// MARK: C++ subscripts
14981498

1499+
Expr *SwiftDeclSynthesizer::synthesizeReturnReinterpretCast(ASTContext &ctx,
1500+
Type givenType,
1501+
Type exprType,
1502+
Expr *baseExpr) {
1503+
auto reinterpretCast = cast<FuncDecl>(
1504+
getBuiltinValueDecl(ctx, ctx.getIdentifier("reinterpretCast")));
1505+
SubstitutionMap subMap = SubstitutionMap::get(
1506+
reinterpretCast->getGenericSignature(), {givenType, exprType}, {});
1507+
ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap);
1508+
auto reinterpretCastRef =
1509+
new (ctx) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true);
1510+
FunctionType::ExtInfo info;
1511+
reinterpretCastRef->setType(
1512+
FunctionType::get({FunctionType::Param(givenType)}, exprType, info));
1513+
1514+
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {baseExpr});
1515+
auto reinterpreted =
1516+
CallExpr::createImplicit(ctx, reinterpretCastRef, argList);
1517+
reinterpreted->setType(exprType);
1518+
reinterpreted->setThrows(nullptr);
1519+
return reinterpreted;
1520+
}
1521+
14991522
/// Synthesizer callback for a subscript getter or a getter for a
15001523
/// dereference property (`var pointee`). If the getter's implementation returns
15011524
/// an UnsafePointer or UnsafeMutablePointer, it unwraps the pointer and returns
15021525
/// the underlying value.
15031526
static std::pair<BraceStmt *, bool>
1504-
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
1527+
synthesizeUnwrappingGetterOrAddressGetterBody(AbstractFunctionDecl *afd,
1528+
void *context, bool isAddress) {
15051529
auto getterDecl = cast<AccessorDecl>(afd);
15061530
auto getterImpl = static_cast<FuncDecl *>(context);
15071531

@@ -1525,7 +1549,8 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
15251549
// reference type. This check actually checks to see if the type is a pointer
15261550
// type, but this does not apply to C pointers because they are Optional types
15271551
// when imported. TODO: Use a more obvious check here.
1528-
if (getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
1552+
if (!isAddress &&
1553+
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
15291554
// `getterImpl` can return either UnsafePointer or UnsafeMutablePointer.
15301555
// Retrieve the corresponding `.pointee` declaration.
15311556
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
@@ -1540,6 +1565,11 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
15401565
pointeePropertyRefExpr->setType(elementTy);
15411566
propertyExpr = pointeePropertyRefExpr;
15421567
}
1568+
// Cast an 'address' result from a mutable pointer if needed.
1569+
if (isAddress &&
1570+
getterImpl->getResultInterfaceType()->isUnsafeMutablePointer())
1571+
propertyExpr = SwiftDeclSynthesizer::synthesizeReturnReinterpretCast(
1572+
ctx, getterImpl->getResultInterfaceType(), elementTy, propertyExpr);
15431573

15441574
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), propertyExpr,
15451575
/*implicit*/ true);
@@ -1549,6 +1579,19 @@ synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
15491579
return {body, /*isTypeChecked*/ true};
15501580
}
15511581

1582+
static std::pair<BraceStmt *, bool>
1583+
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
1584+
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
1585+
/*isAddress=*/false);
1586+
}
1587+
1588+
static std::pair<BraceStmt *, bool>
1589+
synthesizeUnwrappingAddressGetterBody(AbstractFunctionDecl *afd,
1590+
void *context) {
1591+
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
1592+
/*isAddress=*/true);
1593+
}
1594+
15521595
/// Synthesizer callback for a subscript setter or a setter for a dereference
15531596
/// property (`var pointee`).
15541597
static std::pair<BraceStmt *, bool>
@@ -1596,6 +1639,26 @@ synthesizeUnwrappingSetterBody(AbstractFunctionDecl *afd, void *context) {
15961639
return {body, /*isTypeChecked*/ true};
15971640
}
15981641

1642+
static std::pair<BraceStmt *, bool>
1643+
synthesizeUnwrappingAddressSetterBody(AbstractFunctionDecl *afd,
1644+
void *context) {
1645+
auto setterDecl = cast<AccessorDecl>(afd);
1646+
auto setterImpl = static_cast<FuncDecl *>(context);
1647+
1648+
ASTContext &ctx = setterDecl->getASTContext();
1649+
1650+
auto selfArg = createSelfArg(setterDecl);
1651+
auto *setterImplCallExpr =
1652+
createAccessorImplCallExpr(setterImpl, selfArg, nullptr);
1653+
1654+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), setterImplCallExpr,
1655+
/*implicit*/ true);
1656+
1657+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
1658+
/*implicit*/ true);
1659+
return {body, /*isTypeChecked*/ true};
1660+
}
1661+
15991662
SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
16001663
FuncDecl *setter) {
16011664
assert((getter || setter) &&
@@ -1685,7 +1748,6 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
16851748
FuncDecl *setter) {
16861749
assert((getter || setter) &&
16871750
"getter or setter required to generate a pointee property");
1688-
16891751
auto &ctx = ImporterImpl.SwiftContext;
16901752
FuncDecl *getterImpl = getter ? getter : setter;
16911753
FuncDecl *setterImpl = setter;
@@ -1697,6 +1759,11 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
16971759
const auto elementTy = rawElementTy->getAnyPointerElementType()
16981760
? rawElementTy->getAnyPointerElementType()
16991761
: rawElementTy;
1762+
// Use 'address' or 'mutableAddress' accessors for non-copyable
1763+
// types that are returned indirectly.
1764+
bool isImplicit = !elementTy->isNoncopyable(dc);
1765+
bool useAddress =
1766+
rawElementTy->getAnyPointerElementType() && elementTy->isNoncopyable(dc);
17001767

17011768
auto result = new (ctx)
17021769
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
@@ -1705,16 +1772,21 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
17051772
result->setAccess(AccessLevel::Public);
17061773

17071774
AccessorDecl *getterDecl = AccessorDecl::create(
1708-
ctx, getterImpl->getLoc(), getterImpl->getLoc(), AccessorKind::Get,
1709-
result, SourceLoc(), StaticSpellingKind::None,
1775+
ctx, getterImpl->getLoc(), getterImpl->getLoc(),
1776+
useAddress ? AccessorKind::Address : AccessorKind::Get, result,
1777+
SourceLoc(), StaticSpellingKind::None,
17101778
/*async*/ false, SourceLoc(),
17111779
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
1712-
ParameterList::createEmpty(ctx), elementTy, dc);
1780+
ParameterList::createEmpty(ctx),
1781+
useAddress ? elementTy->wrapInPointer(PTK_UnsafePointer) : elementTy, dc);
17131782
getterDecl->setAccess(AccessLevel::Public);
1714-
getterDecl->setImplicit();
1783+
if (isImplicit)
1784+
getterDecl->setImplicit();
17151785
getterDecl->setIsDynamic(false);
17161786
getterDecl->setIsTransparent(true);
1717-
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody,
1787+
getterDecl->setBodySynthesizer(useAddress
1788+
? synthesizeUnwrappingAddressGetterBody
1789+
: synthesizeUnwrappingGetterBody,
17181790
getterImpl);
17191791

17201792
if (getterImpl->isMutating()) {
@@ -1733,20 +1805,29 @@ SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
17331805
paramVarDecl->setSpecifier(ParamSpecifier::Default);
17341806
paramVarDecl->setInterfaceType(elementTy);
17351807

1736-
auto setterParamList =
1737-
ParameterList::create(ctx, {paramVarDecl});
1808+
auto setterParamList = useAddress
1809+
? ParameterList::create(ctx, {})
1810+
: ParameterList::create(ctx, {paramVarDecl});
17381811

17391812
setterDecl = AccessorDecl::create(
1740-
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
1741-
result, SourceLoc(), StaticSpellingKind::None,
1813+
ctx, setterImpl->getLoc(), setterImpl->getLoc(),
1814+
useAddress ? AccessorKind::MutableAddress : AccessorKind::Set, result,
1815+
SourceLoc(), StaticSpellingKind::None,
17421816
/*async*/ false, SourceLoc(),
17431817
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
1744-
setterParamList, TupleType::getEmpty(ctx), dc);
1818+
setterParamList,
1819+
useAddress ? elementTy->wrapInPointer(PTK_UnsafeMutablePointer)
1820+
: TupleType::getEmpty(ctx),
1821+
dc);
17451822
setterDecl->setAccess(AccessLevel::Public);
1746-
setterDecl->setImplicit();
1823+
if (isImplicit)
1824+
setterDecl->setImplicit();
17471825
setterDecl->setIsDynamic(false);
17481826
setterDecl->setIsTransparent(true);
1749-
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);
1827+
setterDecl->setBodySynthesizer(useAddress
1828+
? synthesizeUnwrappingAddressSetterBody
1829+
: synthesizeUnwrappingSetterBody,
1830+
setterImpl);
17501831

17511832
if (setterImpl->isMutating()) {
17521833
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ class SwiftDeclSynthesizer {
6060
VarDecl::Introducer introducer, bool isImplicit,
6161
AccessLevel access, AccessLevel setterAccess);
6262

63+
/// Create a reinterpretCast from the `exprType`, to the `givenType`.
64+
static Expr *synthesizeReturnReinterpretCast(ASTContext &ctx, Type givenType,
65+
Type exprType, Expr *baseExpr);
66+
6367
/// Create a new named constant with the given value.
6468
///
6569
/// \param name The name of the constant.

lib/ClangImporter/bridging

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@
153153
#define SWIFT_UNCHECKED_SENDABLE \
154154
__attribute__((swift_attr("@Sendable")))
155155

156+
/// Specifies that a specific c++ type such class or struct should be imported
157+
/// as a non-copyable Swift value type.
158+
#define SWIFT_NONCOPYABLE \
159+
__attribute__((swift_attr("~Copyable")))
160+
156161
#else // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
157162

158163
// Empty defines for compilers that don't support `attribute(swift_attr)`.
@@ -166,6 +171,7 @@
166171
#define SWIFT_COMPUTED_PROPERTY
167172
#define SWIFT_MUTATING
168173
#define SWIFT_UNCHECKED_SENDABLE
174+
#define SWIFT_NONCOPYABLE
169175

170176
#endif // #if _CXX_INTEROP_HAS_ATTRIBUTE(swift_attr)
171177

test/Interop/Cxx/class/Inputs/type-classification.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,15 @@ struct HasMethodThatReturnsIteratorBox {
251251
IteratorBox getIteratorBox() const;
252252
};
253253

254+
struct __attribute__((swift_attr("~Copyable"))) StructCopyableMovableAnnotatedNonCopyable {
255+
inline StructCopyableMovableAnnotatedNonCopyable() {}
256+
StructCopyableMovableAnnotatedNonCopyable(const StructCopyableMovableAnnotatedNonCopyable &) = default;
257+
StructCopyableMovableAnnotatedNonCopyable(StructCopyableMovableAnnotatedNonCopyable &&) = default;
258+
StructCopyableMovableAnnotatedNonCopyable &
259+
operator=(const StructCopyableMovableAnnotatedNonCopyable &) = default;
260+
StructCopyableMovableAnnotatedNonCopyable &
261+
operator=(StructCopyableMovableAnnotatedNonCopyable &&) = default;
262+
~StructCopyableMovableAnnotatedNonCopyable() = default;
263+
};
264+
254265
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_TYPE_CLASSIFICATION_H
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module MoveOnlyCxxValueType {
2+
header "move-only-cxx-value-type.h"
3+
requires cplusplus
4+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_MOVE_ONLY_VT_H
2+
#define TEST_INTEROP_CXX_CLASS_MOVE_ONLY_VT_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 = -123; }
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+
struct NonCopyableDerived: public NonCopyable {
25+
NonCopyableDerived(int x) : NonCopyable(x) {}
26+
};
27+
28+
struct NonCopyableDerivedDerived: public NonCopyableDerived {
29+
NonCopyableDerivedDerived(int x) : NonCopyableDerived(x) {}
30+
};
31+
32+
struct NonCopyableHolder {
33+
inline NonCopyableHolder(int x) : x(x) {}
34+
inline NonCopyableHolder(const NonCopyableHolder &) = delete;
35+
inline NonCopyableHolder(NonCopyableHolder &&other) : x(std::move(other.x)) {}
36+
37+
inline NonCopyable &returnMutNonCopyableRef() { return x; }
38+
39+
inline const NonCopyable &returnNonCopyableRef() const { return x; }
40+
41+
NonCopyable x;
42+
};
43+
44+
struct NonCopyableHolderDerived: NonCopyableHolder {
45+
inline NonCopyableHolderDerived(int x) : NonCopyableHolder(x) {}
46+
};
47+
48+
struct NonCopyableHolderDerivedDerived: NonCopyableHolderDerived {
49+
inline NonCopyableHolderDerivedDerived(int x) : NonCopyableHolderDerived(x) {}
50+
51+
inline int getActualX() const {
52+
return x.x;
53+
}
54+
};
55+
56+
#endif // TEST_INTEROP_CXX_CLASS_MOVE_ONLY_VT_H
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -cxx-interoperability-mode=upcoming-swift %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import MoveOnlyCxxValueType
4+
5+
func testGetX() -> CInt {
6+
let derived = NonCopyableHolderDerivedDerived(42)
7+
return derived.x.x
8+
}
9+
10+
let _ = testGetX()
11+
12+
func testSetX(_ x: CInt) {
13+
var derived = NonCopyableHolderDerivedDerived(42)
14+
derived.x.x = 2
15+
}
16+
17+
testSetX(2)
18+
19+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}}
20+
21+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseCall___synthesizedBaseSetterAccessor{{.*}}
22+
23+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseGetterAccessor{{.*}}
24+
// CHECK: %[[VPTR:.*]] = getelementptr inbounds %struct.NonCopyableHolder
25+
// CHECK: ret ptr %[[VPTR]]
26+
27+
// CHECK: define {{.*}}linkonce_odr{{.*}} ptr @{{.*}}__synthesizedBaseSetterAccessor{{.*}}
28+
// CHECK: %[[VPTRS:.*]] = getelementptr inbounds %struct.NonCopyableHolder
29+
// CHECK: ret ptr %[[VPTRS]]

0 commit comments

Comments
 (0)