Skip to content

Commit e800217

Browse files
authored
Merge pull request swiftlang#63564 from kavon/noncopyable-conformance-fixes
[move-only] fix conformance synthesis & conformances in extensions
2 parents 3f933d0 + 60d9e61 commit e800217

File tree

6 files changed

+118
-32
lines changed

6 files changed

+118
-32
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6855,12 +6855,9 @@ ERROR(moveonly_copyable_type_that_contains_moveonly_type, none,
68556855
NOTE(moveonly_copyable_type_that_contains_moveonly_type_location, none,
68566856
"contained move-only %0 '%1.%2'",
68576857
(DescriptiveDeclKind, StringRef, StringRef))
6858-
ERROR(moveonly_cannot_conform_to_protocol, none,
6859-
"move-only %0 %1 cannot conform yet to any protocols",
6860-
(DescriptiveDeclKind, DeclName))
6861-
ERROR(moveonly_cannot_conform_to_protocol_with_name, none,
6862-
"move-only %0 %1 cannot conform to protocol %2",
6863-
(DescriptiveDeclKind, DeclName, DeclName))
6858+
ERROR(moveonly_cannot_conform_to_type, none,
6859+
"move-only %0 %1 cannot conform to %2",
6860+
(DescriptiveDeclKind, DeclName, Type))
68646861
ERROR(moveonly_non_final_class_cannot_contain_moveonly_field, none,
68656862
"non-final classes containing move only fields is not yet supported", ())
68666863

lib/AST/ProtocolConformance.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,13 @@ void NominalTypeDecl::prepareConformanceTable() const {
10561056
if (!proto)
10571057
return;
10581058

1059+
// No synthesized conformances for move-only nominals.
1060+
if (isMoveOnly()) {
1061+
// assumption is Sendable gets synthesized elsewhere.
1062+
assert(!proto->isSpecificProtocol(KnownProtocolKind::Sendable));
1063+
return;
1064+
}
1065+
10591066
if (protocols.count(proto) == 0) {
10601067
ConformanceTable->addSynthesizedConformance(
10611068
mutableThis, proto, mutableThis);

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2586,7 +2586,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
25862586

25872587
// FIXME(kavon): see if these can be integrated into other parts of Sema
25882588
diagnoseCopyableTypeContainingMoveOnlyType(ED);
2589-
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(ED);
2589+
diagnoseIncompatibleProtocolsForMoveOnlyType(ED);
25902590

25912591
checkExplicitAvailability(ED);
25922592

@@ -2645,7 +2645,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26452645
// are not move only.
26462646
diagnoseCopyableTypeContainingMoveOnlyType(SD);
26472647

2648-
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(SD);
2648+
diagnoseIncompatibleProtocolsForMoveOnlyType(SD);
26492649
}
26502650

26512651
/// Check whether the given properties can be @NSManaged in this class.
@@ -2747,21 +2747,53 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
27472747
}
27482748
}
27492749

2750-
void diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(
2751-
NominalTypeDecl *nomDecl) {
2752-
if (!nomDecl->isMoveOnly())
2753-
return;
2750+
/// check to see if a move-only type can ever conform to the given type.
2751+
/// \returns true iff a diagnostic was emitted because it was not compatible
2752+
static bool diagnoseIncompatibleWithMoveOnlyType(SourceLoc loc,
2753+
NominalTypeDecl *moveonlyType,
2754+
Type type) {
2755+
assert(type && "got an empty type?");
2756+
assert(moveonlyType->isMoveOnly());
27542757

2755-
for (auto *prot : nomDecl->getLocalProtocols()) {
2758+
auto canType = type->getCanonicalType();
2759+
if (auto prot = canType->getAs<ProtocolType>()) {
27562760
// Permit conformance to marker protocol Sendable.
2757-
if (prot->isSpecificProtocol(KnownProtocolKind::Sendable)) {
2758-
assert(prot->isMarkerProtocol());
2759-
continue;
2761+
if (prot->getDecl()->isSpecificProtocol(KnownProtocolKind::Sendable)) {
2762+
assert(prot->getDecl()->isMarkerProtocol());
2763+
return false;
27602764
}
2765+
}
27612766

2762-
nomDecl->diagnose(diag::moveonly_cannot_conform_to_protocol_with_name,
2763-
nomDecl->getDescriptiveKind(),
2764-
nomDecl->getBaseName(), prot->getBaseName());
2767+
auto &ctx = moveonlyType->getASTContext();
2768+
ctx.Diags.diagnose(loc,
2769+
diag::moveonly_cannot_conform_to_type,
2770+
moveonlyType->getDescriptiveKind(),
2771+
moveonlyType->getBaseName(),
2772+
type);
2773+
return true;
2774+
}
2775+
2776+
static void diagnoseIncompatibleProtocolsForMoveOnlyType(Decl *decl) {
2777+
if (auto *nomDecl = dyn_cast<NominalTypeDecl>(decl)) {
2778+
if (!nomDecl->isMoveOnly())
2779+
return;
2780+
2781+
// go over the all protocols directly conformed-to by this nominal
2782+
for (auto *prot : nomDecl->getLocalProtocols())
2783+
diagnoseIncompatibleWithMoveOnlyType(nomDecl->getLoc(), nomDecl,
2784+
prot->getDeclaredInterfaceType());
2785+
2786+
} else if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
2787+
if (auto *nomDecl = extension->getExtendedNominal()) {
2788+
if (!nomDecl->isMoveOnly())
2789+
return;
2790+
2791+
// go over the all types directly conformed-to by the extension
2792+
for (auto entry : extension->getInherited()) {
2793+
diagnoseIncompatibleWithMoveOnlyType(extension->getLoc(), nomDecl,
2794+
entry.getType());
2795+
}
2796+
}
27652797
}
27662798
}
27672799

@@ -2928,7 +2960,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
29282960

29292961
maybeDiagnoseClassWithoutInitializers(CD);
29302962

2931-
diagnoseMoveOnlyNominalDeclDoesntConformToProtocols(CD);
2963+
diagnoseIncompatibleProtocolsForMoveOnlyType(CD);
29322964

29332965
// Ban non-final classes from having move only fields.
29342966
if (!CD->isFinal()) {
@@ -3404,11 +3436,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34043436
if (nominal->isDistributedActor())
34053437
TypeChecker::checkDistributedActor(SF, nominal);
34063438

3407-
// If we have a move only type and allow it to extend any protocol, error.
3408-
if (nominal->isMoveOnly() && ED->getInherited().size()) {
3409-
ED->diagnose(diag::moveonly_cannot_conform_to_protocol,
3410-
nominal->getDescriptiveKind(), nominal->getBaseName());
3411-
}
3439+
diagnoseIncompatibleProtocolsForMoveOnlyType(ED);
34123440

34133441
TypeChecker::checkReflectionMetadataAttributes(ED);
34143442
}

test/Sema/copyable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ struct S: P {}
55

66
class C: _Copyable {}
77

8-
@_moveOnly struct MOStruct: _Copyable {} // expected-error {{move-only struct 'MOStruct' cannot conform to protocol '_Copyable'}}
8+
@_moveOnly struct MOStruct: _Copyable {} // expected-error {{move-only struct 'MOStruct' cannot conform to '_Copyable'}}

test/Sema/moveonly_restrictions.swift

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,32 @@ struct UnsafePointerWithOwner<T> {
141141
// Make sure we error whenever we attempt to conform a move only type to a
142142
// protocol.
143143
protocol P {}
144+
protocol Q {}
144145
@_moveOnly class ProtocolCheckMoveOnlyKlass {}
145146
@_moveOnly struct ProtocolCheckMoveOnlyStruct {
146147
var k: MoveOnlyKlass
147148
}
148149
@_moveOnly enum ProtocolCheckMoveOnlyEnum {}
149150

150-
extension ProtocolCheckMoveOnlyKlass : P {} // expected-error {{move-only class 'ProtocolCheckMoveOnlyKlass' cannot conform yet to any protocols}}
151-
extension ProtocolCheckMoveOnlyStruct : P {} // expected-error {{move-only struct 'ProtocolCheckMoveOnlyStruct' cannot conform yet to any protocols}}
152-
extension ProtocolCheckMoveOnlyEnum : P {} // expected-error {{move-only enum 'ProtocolCheckMoveOnlyEnum' cannot conform yet to any protocols}}
151+
extension ProtocolCheckMoveOnlyKlass : P {} // expected-error {{move-only class 'ProtocolCheckMoveOnlyKlass' cannot conform to 'P'}}
152+
extension ProtocolCheckMoveOnlyStruct : P, Q {}
153+
// expected-error@-1 {{move-only struct 'ProtocolCheckMoveOnlyStruct' cannot conform to 'P'}}
154+
// expected-error@-2 {{move-only struct 'ProtocolCheckMoveOnlyStruct' cannot conform to 'Q'}}
155+
// expected-note@-3 {{'ProtocolCheckMoveOnlyStruct' declares conformance to protocol 'P' here}}
156+
157+
extension ProtocolCheckMoveOnlyStruct: P {}
158+
// expected-error@-1 {{redundant conformance of 'ProtocolCheckMoveOnlyStruct' to protocol 'P'}}
159+
// expected-error@-2 {{move-only struct 'ProtocolCheckMoveOnlyStruct' cannot conform to 'P'}}
160+
161+
extension ProtocolCheckMoveOnlyEnum : P & Q, Sendable {}
162+
// expected-error@-1 {{move-only enum 'ProtocolCheckMoveOnlyEnum' cannot conform to 'P & Q'}}
163+
164+
extension ProtocolCheckMoveOnlyEnum : Any {}
165+
// expected-error@-1 {{move-only enum 'ProtocolCheckMoveOnlyEnum' cannot conform to 'Any'}}
166+
167+
extension ProtocolCheckMoveOnlyEnum : AnyHashable {}
168+
// expected-error@-1 {{move-only enum 'ProtocolCheckMoveOnlyEnum' cannot conform to 'AnyHashable'}}
169+
// expected-error@-2 {{inheritance from non-protocol type 'AnyHashable'}}
153170

154171
// But a normal extension is ok.
155172
extension ProtocolCheckMoveOnlyKlass {}
@@ -158,10 +175,40 @@ extension ProtocolCheckMoveOnlyEnum {}
158175

159176
// Check if we define a move only type and make it conform on the base type
160177
@_moveOnly
161-
class MoveOnlyKlassP : P {} // expected-error {{move-only class 'MoveOnlyKlassP' cannot conform to protocol 'P'}}
178+
class MoveOnlyKlassP : P {} // expected-error {{move-only class 'MoveOnlyKlassP' cannot conform to 'P'}}
162179
@_moveOnly
163-
struct MoveOnlyStructP : P { // expected-error {{move-only struct 'MoveOnlyStructP' cannot conform to protocol 'P'}}
180+
struct MoveOnlyStructP : P { // expected-error {{move-only struct 'MoveOnlyStructP' cannot conform to 'P'}}
164181
var mv: MoveOnlyKlass
165182
}
166183
@_moveOnly
167-
enum MoveOnlyEnumP : P {} // expected-error {{move-only enum 'MoveOnlyEnumP' cannot conform to protocol 'P'}}
184+
enum MoveOnlyEnumP : P {} // expected-error {{move-only enum 'MoveOnlyEnumP' cannot conform to 'P'}}
185+
186+
// ensure there is no auto-synthesis of Equatable, Hashable, etc, for this move-only enum,
187+
// because it normally would be synthesized since it only has cases without associated values.
188+
@_moveOnly
189+
enum Color {
190+
case red
191+
case green
192+
case blue
193+
194+
static func same(_ c1: __shared Color, c2: __shared Color) -> Bool {
195+
return c1 == c2
196+
// expected-error@-1 {{binary operator '==' cannot be applied to two 'Color' operands}}
197+
}
198+
}
199+
200+
@_moveOnly
201+
enum StrengthLevel: Int { // ensure move-only raw enums do not conform to RawRepresentable
202+
case none = 0
203+
case low
204+
case high
205+
206+
static func lowEnergy() {
207+
_ = StrengthLevel(rawValue: 1)
208+
// expected-error@-1 {{'StrengthLevel' cannot be constructed because it has no accessible initializers}}
209+
}
210+
}
211+
212+
213+
214+

test/Sema/moveonly_sendable.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,10 @@ extension Sendable {
161161
func tryToDupe(_ fd: FileDescriptor) {
162162
fd.doIllegalThings() // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}}
163163
}
164+
165+
@_moveOnly
166+
struct PaperAirplaneFile {
167+
var fd: FileDescriptor
168+
}
169+
170+
extension PaperAirplaneFile: Sendable {}

0 commit comments

Comments
 (0)