Skip to content

Commit 8fa5a0e

Browse files
authored
Merge pull request #61857 from gottesmm/pr-557d1bdfecfe6c7c67115122a6d499ff6c3e51f5
[move-only] Ban move only types being stored properties of copyable types or conforming to protocols
2 parents 02099a2 + 51e90ee commit 8fa5a0e

File tree

9 files changed

+344
-13
lines changed

9 files changed

+344
-13
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6705,5 +6705,22 @@ ERROR(macro_undefined,PointsToFirstBadToken,
67056705
"macro %0 is undefined; use `-load-plugin-library` to specify dynamic "
67066706
"libraries that contain this macro", (Identifier))
67076707

6708+
//------------------------------------------------------------------------------
6709+
// MARK: Move Only Errors
6710+
//------------------------------------------------------------------------------
6711+
6712+
ERROR(moveonly_copyable_type_that_contains_moveonly_type, none,
6713+
"%0 %1 cannot contain a move-only type without also being move-only",
6714+
(DescriptiveDeclKind, DeclName))
6715+
NOTE(moveonly_copyable_type_that_contains_moveonly_type_location, none,
6716+
"contained move-only %0 '%1.%2'",
6717+
(DescriptiveDeclKind, StringRef, StringRef))
6718+
ERROR(moveonly_cannot_conform_to_protocol, none,
6719+
"move-only %0 %1 cannot conform yet to any protocols",
6720+
(DescriptiveDeclKind, DeclName))
6721+
ERROR(moveonly_cannot_conform_to_protocol_with_name, none,
6722+
"move-only %0 %1 cannot conform to protocol %2",
6723+
(DescriptiveDeclKind, DeclName, DeclName))
67086724
#define UNDEFINE_DIAGNOSTIC_MACROS
67096725
#include "DefineDiagnosticMacros.h"
6726+

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/TypeCheckConcurrency.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4470,6 +4470,13 @@ ProtocolConformance *GetImplicitSendableRequest::evaluate(
44704470
if (isa<ProtocolDecl>(nominal))
44714471
return nullptr;
44724472

4473+
// Move only nominal types are currently never sendable since we have not yet
4474+
// finished the generics model for them.
4475+
//
4476+
// TODO: Remove this once this is complete!
4477+
if (nominal->isMoveOnly())
4478+
return nullptr;
4479+
44734480
// Actor types are always Sendable; they don't get it via this path.
44744481
auto classDecl = dyn_cast<ClassDecl>(nominal);
44754482
if (classDecl && classDecl->isActor())

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 31 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"
@@ -2491,6 +2492,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
24912492
}
24922493
}
24932494

2495+
diagnoseCopyableTypeContainingMoveOnlyType(ED);
2496+
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(ED);
2497+
24942498
checkExplicitAvailability(ED);
24952499

24962500
TypeChecker::checkDeclCircularity(ED);
@@ -2513,8 +2517,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25132517

25142518
TypeChecker::checkDeclAttributes(SD);
25152519

2516-
for (Decl *Member : SD->getMembers())
2520+
for (Decl *Member : SD->getMembers()) {
25172521
visit(Member);
2522+
}
25182523

25192524
TypeChecker::checkPatternBindingCaptures(SD);
25202525

@@ -2528,6 +2533,12 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25282533
TypeChecker::checkDeclCircularity(SD);
25292534

25302535
TypeChecker::checkConformancesInContext(SD);
2536+
2537+
// If this struct is not move only, check that all vardecls of nominal type
2538+
// are not move only.
2539+
diagnoseCopyableTypeContainingMoveOnlyType(SD);
2540+
2541+
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(SD);
25312542
}
25322543

25332544
/// Check whether the given properties can be @NSManaged in this class.
@@ -2629,6 +2640,17 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26292640
}
26302641
}
26312642

2643+
void diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(
2644+
NominalTypeDecl *nomDecl) {
2645+
if (!nomDecl->isMoveOnly())
2646+
return;
2647+
2648+
for (auto *prot : nomDecl->getAllProtocols()) {
2649+
nomDecl->diagnose(diag::moveonly_cannot_conform_to_protocol_with_name,
2650+
nomDecl->getDescriptiveKind(),
2651+
nomDecl->getBaseName(), prot->getBaseName());
2652+
}
2653+
}
26322654

26332655
void visitClassDecl(ClassDecl *CD) {
26342656
checkUnsupportedNestedType(CD);
@@ -2792,6 +2814,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27922814
TypeChecker::checkConformancesInContext(CD);
27932815

27942816
maybeDiagnoseClassWithoutInitializers(CD);
2817+
2818+
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(CD);
27952819
}
27962820

27972821
void visitProtocolDecl(ProtocolDecl *PD) {
@@ -3246,6 +3270,12 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
32463270

32473271
if (nominal->isDistributedActor())
32483272
TypeChecker::checkDistributedActor(SF, nominal);
3273+
3274+
// If we have a move only type and allow it to extend any protocol, error.
3275+
if (nominal->isMoveOnly() && ED->getInherited().size()) {
3276+
ED->diagnose(diag::moveonly_cannot_conform_to_protocol,
3277+
nominal->getDescriptiveKind(), nominal->getBaseName());
3278+
}
32493279
}
32503280

32513281
void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {

test/Interpreter/moveonly_bufferview.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
public struct BufferView<T> {
1111
var ptr: UnsafeBufferPointer<T>
1212

13-
deinit {}
14-
}
15-
16-
extension BufferView : Sequence {
17-
public typealias Iterator = UnsafeBufferPointer<T>.Iterator
18-
public typealias Element = UnsafeBufferPointer<T>.Element
13+
var count: Int {
14+
return ptr.count
15+
}
1916

20-
public func makeIterator() -> Self.Iterator {
21-
return ptr.makeIterator()
17+
subscript(_ x: Int) -> T {
18+
return ptr[x]
2219
}
20+
21+
deinit {}
2322
}
2423

2524
extension Array {
@@ -36,8 +35,8 @@ func testBufferView(_ x: __owned [Int]) {
3635
// CHECK: 2
3736
// CHECK: 3
3837
y.withBufferView {
39-
for x in $0 {
40-
print(x)
38+
for i in 0..<$0.count {
39+
print($0[i])
4140
}
4241
}
4342
}
@@ -52,8 +51,8 @@ func testConditionalBufferView(_ x: __owned [Int]) {
5251
// CHECK: 5
5352
// CHECK: 6
5453
if getBool() {
55-
for z in y {
56-
print(z)
54+
for i in 0..<y.count {
55+
print(y[i])
5756
}
5857
}
5958
}

0 commit comments

Comments
 (0)