Skip to content

Commit c40985d

Browse files
committed
describe how generic parameters implicitly require Copyable
1 parent 66bf3c0 commit c40985d

File tree

7 files changed

+137
-59
lines changed

7 files changed

+137
-59
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7276,6 +7276,15 @@ ERROR(noncopyable_generics_erasure, none,
72767276
ERROR(noncopyable_generics_metatype_cast, none,
72777277
"metatype %0 cannot be cast to %1 because %2 is noncopyable",
72787278
(Type, Type, Type))
7279+
ERROR(noncopyable_generics_generic_param, none,
7280+
"noncopyable type %0 cannot be substituted for copyable %1 %2 in %3",
7281+
(Type, DescriptiveDeclKind, Type, DeclName))
7282+
ERROR(noncopyable_generics_generic_param_metatype, none,
7283+
"metatype %4 of noncopyable type %0 cannot be substituted for copyable %1 %2 in %3",
7284+
(Type, DescriptiveDeclKind, Type, DeclName, Type))
7285+
NOTE(noncopyable_generics_implicit_copyable, none,
7286+
"%0 %1 has an implicit Copyable requirement",
7287+
(DescriptiveDeclKind, Type))
72797288
ERROR(noncopyable_effectful_getter,none,
72807289
"%0 of noncopyable type cannot be 'async' or 'throws'", (DescriptiveDeclKind))
72817290
ERROR(noncopyable_enums_do_not_support_indirect,none,

include/swift/Sema/CSFix.h

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,17 +2084,41 @@ class NotCompileTimeConst final : public ContextualMismatch {
20842084

20852085
/// Describes the reason why the type must be copyable
20862086
struct NoncopyableMatchFailure {
2087-
enum Reason {
2088-
Unknown,
2089-
CastToExistential,
2087+
enum Kind {
2088+
CopyableConstraint,
2089+
ExistentialCast,
20902090
};
20912091

2092-
Type type;
2093-
Reason reason;
2092+
private:
2093+
Kind reason;
2094+
union {
2095+
Type type;
2096+
};
2097+
2098+
NoncopyableMatchFailure(Kind reason, Type type)
2099+
: reason(reason), type(type) {}
2100+
2101+
public:
2102+
Kind getKind() const { return reason; }
2103+
2104+
Type getType() const {
2105+
switch (reason) {
2106+
case ExistentialCast:
2107+
return type;
20942108

2095-
NoncopyableMatchFailure() : type(Type()), reason(Unknown) {}
2096-
NoncopyableMatchFailure(Type type, Reason reason)
2097-
: type(type), reason(reason) {}
2109+
case CopyableConstraint:
2110+
llvm_unreachable("no type payload");
2111+
};
2112+
}
2113+
2114+
static NoncopyableMatchFailure forCopyableConstraint() {
2115+
return NoncopyableMatchFailure(CopyableConstraint, Type());
2116+
}
2117+
2118+
static NoncopyableMatchFailure forExistentialCast(Type existential) {
2119+
assert(existential->isAnyExistentialType());
2120+
return NoncopyableMatchFailure(ExistentialCast, existential);
2121+
}
20982122
};
20992123

21002124
class MustBeCopyable final : public ConstraintFix {

lib/Sema/CSDiagnostics.cpp

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6140,27 +6140,60 @@ bool NotCompileTimeConstFailure::diagnoseAsError() {
61406140
}
61416141

61426142
bool NotCopyableFailure::diagnoseAsError() {
6143-
switch (failure.reason) {
6144-
case NoncopyableMatchFailure::CastToExistential: {
6145-
assert(failure.type->is<ExistentialType>());
6146-
6143+
switch (failure.getKind()) {
6144+
case NoncopyableMatchFailure::ExistentialCast: {
61476145
if (noncopyableTy->is<AnyMetatypeType>())
61486146
emitDiagnostic(diag::noncopyable_generics_metatype_cast,
61496147
noncopyableTy,
6150-
failure.type,
6148+
failure.getType(),
61516149
noncopyableTy->getMetatypeInstanceType());
61526150
else
61536151
emitDiagnostic(diag::noncopyable_generics_erasure,
61546152
noncopyableTy,
6155-
failure.type);
6156-
break;
6153+
failure.getType());
6154+
return true;
61576155
}
6158-
case NoncopyableMatchFailure::Unknown:
6159-
emitDiagnostic(diag::noncopyable_generics,
6160-
noncopyableTy);
6156+
6157+
case NoncopyableMatchFailure::CopyableConstraint: {
6158+
auto *loc = getLocator();
6159+
// a bit paranoid of nulls and such...
6160+
if (auto *genericParam = loc->getGenericParameter()) {
6161+
if (auto *paramDecl = genericParam->getDecl()) {
6162+
if (auto *owningDecl =
6163+
dyn_cast_or_null<ValueDecl>(paramDecl->getDeclContext()->getAsDecl())) {
6164+
6165+
// FIXME: these owningDecl names are kinda bad. like just `init(describing:)`
6166+
if (noncopyableTy->is<AnyMetatypeType>())
6167+
emitDiagnostic(diag::noncopyable_generics_generic_param_metatype,
6168+
noncopyableTy->getMetatypeInstanceType(),
6169+
paramDecl->getDescriptiveKind(),
6170+
genericParam,
6171+
owningDecl->getName(),
6172+
noncopyableTy);
6173+
else
6174+
emitDiagnostic(diag::noncopyable_generics_generic_param,
6175+
noncopyableTy,
6176+
paramDecl->getDescriptiveKind(),
6177+
genericParam,
6178+
owningDecl->getName());
6179+
6180+
// If we have a location for the parameter, point it out in a note.
6181+
if (auto loc = paramDecl->getNameLoc()) {
6182+
emitDiagnosticAt(loc,
6183+
diag::noncopyable_generics_implicit_copyable,
6184+
paramDecl->getDescriptiveKind(),
6185+
genericParam);
6186+
}
6187+
6188+
return true;
6189+
}
6190+
}
6191+
}
61616192
break;
61626193
}
6194+
}
61636195

6196+
emitDiagnostic(diag::noncopyable_generics, noncopyableTy);
61646197
return true;
61656198
}
61666199

lib/Sema/CSSimplify.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3832,11 +3832,11 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
38323832
if (type1->getMetatypeInstanceType()->isPureMoveOnly()) {
38333833
// tailor error message
38343834
if (shouldAttemptFixes()) {
3835-
auto *fix =
3836-
MustBeCopyable::create(*this, type1,
3837-
{type2,
3838-
NoncopyableMatchFailure::CastToExistential},
3839-
getConstraintLocator(locator));
3835+
auto *fix = MustBeCopyable::create(*this,
3836+
type1,
3837+
NoncopyableMatchFailure::forExistentialCast(
3838+
type2),
3839+
getConstraintLocator(locator));
38403840
if (!recordFix(fix))
38413841
return getTypeMatchSuccess();
38423842
}
@@ -8495,7 +8495,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
84958495
// If this is a failure to conform to Copyable, tailor the error message.
84968496
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
84978497
auto *fix =
8498-
MustBeCopyable::create(*this, type, {}, getConstraintLocator(locator));
8498+
MustBeCopyable::create(*this,
8499+
type,
8500+
NoncopyableMatchFailure::forCopyableConstraint(),
8501+
getConstraintLocator(locator));
84998502
if (!recordFix(fix))
85008503
return SolutionKind::Solved;
85018504
}

test/Constraints/moveonly_constraints.swift

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ protocol Box<T> {
2020
func get() -> T
2121
}
2222

23-
class RefBox<T>: Box {
23+
class RefBox<T>: Box { // expected-note@:14 6{{generic parameter 'T' has an implicit Copyable requirement}}
2424
var val: T
2525
init(_ t: T) { val = t }
2626
func get() -> T { return val }
2727
}
2828

29-
struct ValBox<T>: Box {
29+
struct ValBox<T>: Box { // expected-note@:15 6{{generic parameter 'T' has an implicit Copyable requirement}}
3030
var val: T
3131
init(_ t: T) { val = t }
3232
func get() -> T { return val }
@@ -37,20 +37,20 @@ class NotStoredGenerically<T> {
3737
func give() -> T { fatalError("todo") }
3838
}
3939

40-
enum Maybe<T> {
40+
enum Maybe<T> { // expected-note@:12 3{{generic parameter 'T' has an implicit Copyable requirement}}
4141
case none
4242
case just(T)
4343
}
4444

4545
func takeConcrete(_ m: borrowing MO) {}
46-
func takeGeneric<T>(_ t: T) {}
46+
func takeGeneric<T>(_ t: T) {} // expected-note@:18 5{{generic parameter 'T' has an implicit Copyable requirement}}
4747
func takeGenericSendable<T>(_ t: T) where T: Sendable {}
48-
func takeMaybe<T>(_ m: Maybe<T>) {}
48+
func takeMaybe<T>(_ m: Maybe<T>) {} // expected-note@:16 2{{generic parameter 'T' has an implicit Copyable requirement}}
4949
func takeAnyBoxErased(_ b: any Box) {}
50-
func takeAnyBox<T>(_ b: any Box<T>) {}
50+
func takeAnyBox<T>(_ b: any Box<T>) {} // expected-note@:17 2{{generic parameter 'T' has an implicit Copyable requirement}}
5151
func takeAny(_ a: Any) {}
5252
func takeAnyObject(_ a: AnyObject) {}
53-
func genericVarArg<T>(_ t: T...) {}
53+
func genericVarArg<T>(_ t: T...) {} // expected-note@:20 {{generic parameter 'T' has an implicit Copyable requirement}}
5454

5555
var globalMO: MO = MO()
5656

@@ -61,7 +61,7 @@ var globalMO: MO = MO()
6161

6262
// some top-level tests
6363
let _: MO = globalMO
64-
takeGeneric(globalMO) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
64+
takeGeneric(globalMO) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeGeneric'}}
6565

6666

6767

@@ -75,24 +75,24 @@ func testBasic(_ mo: borrowing MO) {
7575
takeConcrete(globalMO)
7676
takeConcrete(MO())
7777

78-
takeGeneric(globalMO) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
79-
takeGeneric(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
80-
takeGeneric(mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
78+
takeGeneric(globalMO) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeGeneric'}}
79+
takeGeneric(MO()) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeGeneric'}}
80+
takeGeneric(mo) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeGeneric'}}
8181

8282
takeAny(mo) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
8383
print(mo) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
84-
_ = "\(mo)" // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
85-
let _: String = String(describing: mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
84+
_ = "\(mo)" // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'appendInterpolation'}}
85+
let _: String = String(describing: mo) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'Subject' in 'init(describing:)'}}
8686

8787
takeGeneric { () -> Int? in mo.x }
8888
genericVarArg(5)
89-
genericVarArg(mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
89+
genericVarArg(mo) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'genericVarArg'}}
9090

9191
takeGeneric( (mo, 5) ) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
9292
takeGenericSendable((mo, mo)) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
9393

9494
let singleton : (MO) = (mo)
95-
takeGeneric(singleton) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
95+
takeGeneric(singleton) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeGeneric'}}
9696

9797
takeAny((mo)) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
9898
takeAny((mo, mo)) // expected-error {{noncopyable type '(MO, MO)' cannot be erased to copyable existential type 'Any'}}
@@ -101,29 +101,29 @@ func testBasic(_ mo: borrowing MO) {
101101
func checkBasicBoxes() {
102102
let mo = MO()
103103

104-
let vb = ValBox(consume mo) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
104+
let vb = ValBox(consume mo) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'ValBox'}}
105105
_ = vb.get()
106106
_ = vb.val
107107

108-
let rb = RefBox(MO()) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
108+
let rb = RefBox(MO()) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'RefBox'}}
109109
_ = rb.get()
110110
_ = rb.val
111111

112112
let vb2: ValBox<MO> = .init(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generic type 'ValBox<T>' yet}}
113113
}
114114

115115
func checkExistential() {
116-
takeAnyBox( // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
117-
RefBox(MO())) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
116+
takeAnyBox( // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeAnyBox'}}
117+
RefBox(MO())) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'RefBox'}}
118118

119-
takeAnyBox( // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
120-
ValBox(globalMO)) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
119+
takeAnyBox( // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeAnyBox'}}
120+
ValBox(globalMO)) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'ValBox'}}
121121

122122
takeAnyBoxErased(
123-
RefBox(MO())) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
123+
RefBox(MO())) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'RefBox'}}
124124

125125
takeAnyBoxErased(
126-
ValBox(globalMO)) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
126+
ValBox(globalMO)) // expected-error 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'ValBox'}}
127127
}
128128

129129
func checkMethodCalls() {
@@ -134,9 +134,13 @@ func checkMethodCalls() {
134134
let _: Maybe<MO> = .none // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Maybe<T>' yet}}
135135
let _ = Maybe<MO>.just(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Maybe<T>' yet}}
136136
let _: Maybe<MO> = .just(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Maybe<T>' yet}}
137-
takeMaybe(.just(MO())) // expected-error 2{{noncopyable type 'MO' cannot be used with generics yet}}
138137

139-
takeMaybe(true ? .none : .just(MO())) // expected-error 3{{noncopyable type 'MO' cannot be used with generics yet}}
138+
// FIXME: MO isn't logically being substituted into takeMaybe. seems like nonsense duplication?
139+
takeMaybe(.just(MO())) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'Maybe'}}
140+
// expected-error@-1 {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeMaybe'}}
141+
takeMaybe(true ? .none : .just(MO()))
142+
// expected-error@-1 {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'takeMaybe'}}
143+
// expected-error@-2 2{{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'Maybe'}}
140144
}
141145

142146
func checkCasting(_ b: any Box, _ mo: borrowing MO, _ a: Any) {
@@ -233,13 +237,12 @@ func checkStdlibTypes(_ mo: borrowing MO) {
233237
let _: [String: MO] = // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Dictionary<Key, Value>' yet}}
234238
["hello" : MO()] // expected-error{{tuples with noncopyable elements are not supported}}
235239

236-
// i think this one's only caught b/c of the 'Any' change
237-
_ = [MO()] // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
240+
_ = [MO()] // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'Element' in 'Array'}}
238241

239242
let _: Array<MO> = .init() // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Array<Element>' yet}}
240243
_ = [MO]() // expected-error {{noncopyable type 'MO' cannot be used with generic type 'Array<Element>' yet}}
241244

242-
let s: String = "hello \(mo)" // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
245+
let s: String = "hello \(mo)" // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'appendInterpolation'}}
243246
}
244247

245248
func copyableExistentials(_ a: Any, _ e1: Error, _ e2: any Error, _ ah: AnyHashable) {
@@ -272,13 +275,18 @@ struct GenerousGuy: Gives { // expected-error {{type 'GenerousGuy' does not conf
272275
func give() -> Ty {}
273276
}
274277

275-
func doBadMetatypeStuff<T>(_ t: T) {
278+
func doBadMetatypeStuff<T>(_ t: T) { // expected-note@:25 {{generic parameter 'T' has an implicit Copyable requirement}}
276279
let y = t as! Any.Type
277280
if let MO_MetaType = y as? MO.Type { // expected-warning {{cast from 'any Any.Type' to unrelated type 'MO.Type' always fails}}
278281
let x = MO_MetaType.init()
279282
let _ = x
280283
}
281284
}
282285
func tryToDoBadMetatypeStuff() {
283-
doBadMetatypeStuff(MO.self) // expected-error {{noncopyable type 'MO.Type' cannot be used with generics yet}}
286+
doBadMetatypeStuff(MO.self) // expected-error {{metatype 'MO.Type' of noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'doBadMetatypeStuff'}}
287+
}
288+
289+
func packingHeat<each T>(_ t: repeat each T) {}
290+
func packIt() {
291+
packingHeat(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
284292
}

test/Sema/copyable_constraint.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
@_marker public protocol _Copyable {}
99

10-
func nextTime<T>(_ t: T) {}
10+
func nextTime<T>(_ t: T) {} // expected-note {{generic parameter 'T' has an implicit Copyable requirement}}
1111

1212
@_moveOnly struct MO {}
1313

14-
nextTime(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
14+
nextTime(MO()) // expected-error {{noncopyable type 'MO' cannot be substituted for copyable generic parameter 'T' in 'nextTime'}}

test/Sema/moveonly_sendable.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ enum Wrong_NoncopyableOption<T> : Sendable { // expected-note {{consider making
9191
}
9292

9393
func takeAnySendable(_ s: any Sendable) {}
94-
func takeSomeSendable(_ s: some Sendable) {}
94+
func takeSomeSendable(_ s: some Sendable) {} // expected-note {{generic parameter 'some Sendable' has an implicit Copyable requirement}}
9595

9696
// expected-error@+1 {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
9797
func mkSendable() -> Sendable { return FileDescriptor(id: 0) }
@@ -101,7 +101,7 @@ func tryToCastIt(_ fd: borrowing FileDescriptor) {
101101
let _: Sendable = fd // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
102102

103103
takeAnySendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
104-
takeSomeSendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
104+
takeSomeSendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be substituted for copyable generic parameter 'some Sendable' in 'takeSomeSendable'}}
105105

106106
let _ = fd as Sendable // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
107107

@@ -159,7 +159,8 @@ extension Sendable {
159159
}
160160

161161
func tryToDupe(_ fd: borrowing FileDescriptor) {
162-
fd.doIllegalThings() // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
162+
// FIXME: this should describe 'Self' as 'any Sendable' or something.
163+
fd.doIllegalThings() // expected-error {{noncopyable type 'FileDescriptor' cannot be substituted for copyable generic parameter 'Self' in 'Sendable'}}
163164
}
164165

165166
@_moveOnly

0 commit comments

Comments
 (0)