Skip to content

Commit b95b19b

Browse files
committed
[move-only] Ban move only types being stored properties of copyable types.
rdar://101650982
1 parent 9a9cfab commit b95b19b

File tree

7 files changed

+279
-1
lines changed

7 files changed

+279
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6699,5 +6699,24 @@ ERROR(macro_undefined,PointsToFirstBadToken,
66996699
"macro '%0' is undefined; use `-load-plugin-library` to specify dynamic "
67006700
"libraries that contain this macro", (StringRef))
67016701

6702+
//------------------------------------------------------------------------------
6703+
// MARK: Move Only Errors
6704+
//------------------------------------------------------------------------------
6705+
6706+
ERROR(moveonly_copyable_type_that_contains_moveonly_type, none,
6707+
"%0 %1 cannot contain a move-only type without also being move-only",
6708+
(DescriptiveDeclKind, DeclName))
6709+
NOTE(moveonly_copyable_type_that_contains_moveonly_type_location, none,
6710+
"contained move-only %0 '%1.%2'",
6711+
(DescriptiveDeclKind, StringRef, StringRef))
6712+
ERROR(moveonly_cannot_conform_to_protocol, none,
6713+
"Move only type %0 cannot conform yet to any protocols",
6714+
(DeclName))
6715+
ERROR(moveonly_cannot_conform_to_protocol_with_name, none,
6716+
"Move only type %0 cannot conform to protocol %1. Move only types are "
6717+
"unable to conform to protocols yet",
6718+
(DeclName, DeclName))
6719+
67026720
#define UNDEFINE_DIAGNOSTIC_MACROS
67036721
#include "DefineDiagnosticMacros.h"
6722+

include/swift/AST/Types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@ class alignas(1 << TypeAlignInBits) TypeBase
577577

578578
bool isPlaceholder();
579579

580+
/// Returns true if this is a move only type. Returns false if this is a
581+
/// non-move only type or a move only wrapped type.
582+
bool isPureMoveOnly() const;
583+
580584
/// Does the type have outer parenthesis?
581585
bool hasParenSugar() const { return getKind() == TypeKind::Paren; }
582586

lib/AST/Type.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ bool TypeBase::isAny() {
165165
return constraint->isEqual(getASTContext().TheAnyType);
166166
}
167167

168+
bool TypeBase::isPureMoveOnly() const {
169+
if (auto *nom = getCanonicalType()->getNominalOrBoundGenericNominal())
170+
return nom->isMoveOnly();
171+
return false;
172+
}
173+
168174
bool TypeBase::isPlaceholder() {
169175
return is<PlaceholderType>();
170176
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5889,3 +5889,107 @@ bool swift::diagnoseUnhandledThrowsInAsyncContext(DeclContext *dc,
58895889

58905890
return false;
58915891
}
5892+
5893+
//===----------------------------------------------------------------------===//
5894+
// Copyable Type Containing Move Only Type Visitor
5895+
//===----------------------------------------------------------------------===//
5896+
5897+
void swift::diagnoseCopyableTypeContainingMoveOnlyType(
5898+
NominalTypeDecl *copyableNominalType) {
5899+
// If we don't have move only enabled, bail early.
5900+
if (!copyableNominalType->getASTContext().LangOpts.Features.contains(
5901+
Feature::MoveOnly))
5902+
return;
5903+
5904+
// If we already have a move only type, just bail, we have no further work to
5905+
// do.
5906+
if (copyableNominalType->isMoveOnly())
5907+
return;
5908+
5909+
LLVM_DEBUG(llvm::dbgs() << "DiagnoseCopyableType for: "
5910+
<< copyableNominalType->getName() << '\n');
5911+
5912+
auto &DE = copyableNominalType->getASTContext().Diags;
5913+
auto emitError = [&copyableNominalType,
5914+
&DE](PointerUnion<EnumElementDecl *, VarDecl *>
5915+
topFieldToError,
5916+
DeclBaseName parentName, DescriptiveDeclKind fieldKind,
5917+
DeclBaseName fieldName) {
5918+
assert(!topFieldToError.isNull());
5919+
if (auto *eltDecl = topFieldToError.dyn_cast<EnumElementDecl *>()) {
5920+
DE.diagnoseWithNotes(
5921+
copyableNominalType->diagnose(
5922+
diag::moveonly_copyable_type_that_contains_moveonly_type,
5923+
copyableNominalType->getDescriptiveKind(),
5924+
copyableNominalType->getBaseName()),
5925+
[&]() {
5926+
eltDecl->diagnose(
5927+
diag::
5928+
moveonly_copyable_type_that_contains_moveonly_type_location,
5929+
fieldKind, parentName.userFacingName(),
5930+
fieldName.userFacingName());
5931+
});
5932+
return;
5933+
}
5934+
5935+
auto *varDecl = topFieldToError.get<VarDecl *>();
5936+
DE.diagnoseWithNotes(
5937+
copyableNominalType->diagnose(
5938+
diag::moveonly_copyable_type_that_contains_moveonly_type,
5939+
copyableNominalType->getDescriptiveKind(),
5940+
copyableNominalType->getBaseName()),
5941+
[&]() {
5942+
varDecl->diagnose(
5943+
diag::moveonly_copyable_type_that_contains_moveonly_type_location,
5944+
fieldKind, parentName.userFacingName(),
5945+
fieldName.userFacingName());
5946+
});
5947+
};
5948+
5949+
// If we have a struct decl...
5950+
if (auto *structDecl = dyn_cast<StructDecl>(copyableNominalType)) {
5951+
// Visit each of the stored property var decls of the struct decl...
5952+
for (auto *fieldDecl : structDecl->getStoredProperties()) {
5953+
LLVM_DEBUG(llvm::dbgs()
5954+
<< "Visiting struct field: " << fieldDecl->getName() << '\n');
5955+
if (!fieldDecl->getInterfaceType()->isPureMoveOnly())
5956+
continue;
5957+
emitError(fieldDecl, structDecl->getBaseName(),
5958+
fieldDecl->getDescriptiveKind(), fieldDecl->getBaseName());
5959+
}
5960+
// We completed our checking, just return.
5961+
return;
5962+
}
5963+
5964+
if (auto *enumDecl = dyn_cast<EnumDecl>(copyableNominalType)) {
5965+
// If we have an enum but we don't have any elements, just continue, we
5966+
// have nothing to check.
5967+
if (enumDecl->getAllElements().empty())
5968+
return;
5969+
5970+
// Otherwise for each element...
5971+
for (auto *enumEltDecl : enumDecl->getAllElements()) {
5972+
// If the element doesn't have any associated values, we have nothing to
5973+
// check, so continue.
5974+
if (!enumEltDecl->hasAssociatedValues())
5975+
continue;
5976+
5977+
LLVM_DEBUG(llvm::dbgs() << "Visiting enum elt decl: "
5978+
<< enumEltDecl->getName() << '\n');
5979+
5980+
// Otherwise, we have a case and need to check the types of the
5981+
// parameters of the case payload.
5982+
for (auto payloadParam : *enumEltDecl->getParameterList()) {
5983+
LLVM_DEBUG(llvm::dbgs() << "Visiting payload param: "
5984+
<< payloadParam->getName() << '\n');
5985+
if (payloadParam->getInterfaceType()->isPureMoveOnly()) {
5986+
emitError(enumEltDecl, enumDecl->getBaseName(),
5987+
enumEltDecl->getDescriptiveKind(),
5988+
enumEltDecl->getBaseName());
5989+
}
5990+
}
5991+
}
5992+
// We have finished processing this enum... so return.
5993+
return;
5994+
}
5995+
}

lib/Sema/MiscDiagnostics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ class BaseDiagnosticWalker : public ASTWalker {
136136
static bool shouldWalkIntoDeclInClosureContext(Decl *D);
137137
};
138138

139+
void diagnoseCopyableTypeContainingMoveOnlyType(
140+
NominalTypeDecl *copyableNominalType);
141+
139142
} // namespace swift
140143

141144
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "swift/AST/AccessScope.h"
3434
#include "swift/AST/Decl.h"
3535
#include "swift/AST/DeclContext.h"
36+
#include "swift/AST/DiagnosticsSema.h"
3637
#include "swift/AST/ExistentialLayout.h"
3738
#include "swift/AST/Expr.h"
3839
#include "swift/AST/ForeignErrorConvention.h"
@@ -2490,6 +2491,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24902491
}
24912492
}
24922493

2494+
diagnoseCopyableTypeContainingMoveOnlyType(ED);
2495+
24932496
checkExplicitAvailability(ED);
24942497

24952498
TypeChecker::checkDeclCircularity(ED);
@@ -2512,8 +2515,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25122515

25132516
TypeChecker::checkDeclAttributes(SD);
25142517

2515-
for (Decl *Member : SD->getMembers())
2518+
for (Decl *Member : SD->getMembers()) {
25162519
visit(Member);
2520+
}
25172521

25182522
TypeChecker::checkPatternBindingCaptures(SD);
25192523

@@ -2527,6 +2531,10 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25272531
TypeChecker::checkDeclCircularity(SD);
25282532

25292533
TypeChecker::checkConformancesInContext(SD);
2534+
2535+
// If this struct is not move only, check that all vardecls of nominal type
2536+
// are not move only.
2537+
diagnoseCopyableTypeContainingMoveOnlyType(SD);
25302538
}
25312539

25322540
/// Check whether the given properties can be @NSManaged in this class.

test/Sema/moveonly_restrictions.swift

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-move-only
2+
3+
class CopyableKlass {}
4+
@_moveOnly
5+
class MoveOnlyKlass {}
6+
7+
class C {
8+
var copyable: CopyableKlass? = nil
9+
var moveOnly: MoveOnlyKlass? = nil
10+
}
11+
12+
@_moveOnly
13+
class CMoveOnly {
14+
var copyable: CopyableKlass? = nil
15+
var moveOnly: MoveOnlyKlass? = nil
16+
}
17+
18+
struct OptionalGrandField<T> { // expected-error {{generic struct 'OptionalGrandField' cannot contain a move-only type without also being move-only}}
19+
var moveOnly3: T?
20+
var moveOnly2: MoveOnlyKlass // expected-note {{contained move-only property 'OptionalGrandField.moveOnly2'}}
21+
}
22+
23+
struct S0 {
24+
var moveOnly3: OptionalGrandField<MoveOnlyKlass>
25+
}
26+
27+
struct SCopyable {
28+
var copyable: CopyableKlass
29+
}
30+
31+
struct S { // expected-error {{struct 'S' cannot contain a move-only type without also being move-only}}
32+
var copyable: CopyableKlass
33+
var moveOnly2: MoveOnlyKlass?
34+
var moveOnly: MoveOnlyKlass // expected-note {{contained move-only property 'S.moveOnly'}}
35+
var moveOnly3: OptionalGrandField<MoveOnlyKlass>
36+
}
37+
38+
@_moveOnly
39+
struct SMoveOnly {
40+
var copyable: CopyableKlass
41+
var moveOnly: MoveOnlyKlass
42+
}
43+
44+
enum E { // expected-error {{enum 'E' cannot contain a move-only type without also being move-only}}
45+
case lhs(CopyableKlass)
46+
case rhs(MoveOnlyKlass) // expected-note {{contained move-only enum case 'E.rhs'}}
47+
case rhs2(OptionalGrandField<MoveOnlyKlass>)
48+
}
49+
50+
@_moveOnly
51+
enum EMoveOnly {
52+
case lhs(CopyableKlass)
53+
case rhs(MoveOnlyKlass)
54+
}
55+
56+
func foo() {
57+
class C2 {
58+
var copyable: CopyableKlass? = nil
59+
var moveOnly: MoveOnlyKlass? = nil
60+
}
61+
62+
@_moveOnly
63+
class C2MoveOnly {
64+
var copyable: CopyableKlass? = nil
65+
var moveOnly: MoveOnlyKlass? = nil
66+
}
67+
68+
struct S2 { // expected-error {{struct 'S2' cannot contain a move-only type without also being move-only}}
69+
var copyable: CopyableKlass
70+
var moveOnly: MoveOnlyKlass // expected-note {{contained move-only property 'S2.moveOnly'}}
71+
}
72+
73+
@_moveOnly
74+
struct S2MoveOnly {
75+
var copyable: CopyableKlass
76+
var moveOnly: MoveOnlyKlass
77+
}
78+
79+
enum E2 { // expected-error {{enum 'E2' cannot contain a move-only type without also being move-only}}
80+
case lhs(CopyableKlass)
81+
case rhs(MoveOnlyKlass) // expected-note {{contained move-only enum case 'E2.rhs'}}
82+
}
83+
84+
@_moveOnly
85+
enum E2MoveOnly {
86+
case lhs(CopyableKlass)
87+
case rhs(MoveOnlyKlass)
88+
}
89+
{
90+
class C3 {
91+
var copyable: CopyableKlass? = nil
92+
var moveOnly: MoveOnlyKlass? = nil
93+
}
94+
95+
@_moveOnly
96+
class C3MoveOnly {
97+
var copyable: CopyableKlass? = nil
98+
var moveOnly: MoveOnlyKlass? = nil
99+
}
100+
101+
struct S3 { // expected-error {{struct 'S3' cannot contain a move-only type without also being move-only}}
102+
var copyable: CopyableKlass
103+
var moveOnly: MoveOnlyKlass // expected-note {{contained move-only property 'S3.moveOnly'}}
104+
}
105+
106+
@_moveOnly
107+
struct S3MoveOnly {
108+
var copyable: CopyableKlass
109+
var moveOnly: MoveOnlyKlass
110+
}
111+
112+
enum E3 { // expected-error {{enum 'E3' cannot contain a move-only type without also being move-only}}
113+
case lhs(CopyableKlass)
114+
case rhs(MoveOnlyKlass) // expected-note {{contained move-only enum case 'E3.rhs'}}
115+
}
116+
117+
@_moveOnly
118+
enum E3MoveOnly {
119+
case lhs(CopyableKlass)
120+
case rhs(MoveOnlyKlass)
121+
}
122+
print("1")
123+
print("2")
124+
}()
125+
}
126+
127+
// Make sure we do not crash on this.
128+
struct UnsafePointerWithOwner<T> {
129+
var owner: AnyObject? = nil
130+
var data: UnsafePointer<T>? = nil
131+
132+
func doNothing() {}
133+
}
134+

0 commit comments

Comments
 (0)