Skip to content

Commit 64b8238

Browse files
committed
describe illegal casts to existentials
1 parent 0c16428 commit 64b8238

File tree

8 files changed

+99
-40
lines changed

8 files changed

+99
-40
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7204,6 +7204,12 @@ ERROR(noncopyable_generics_variadic, none,
72047204
ERROR(noncopyable_generics_specific, none,
72057205
"noncopyable type %0 cannot be used with generic type %1 yet",
72067206
(Type, Type))
7207+
ERROR(noncopyable_generics_erasure, none,
7208+
"noncopyable type %0 cannot be erased to copyable existential type %1",
7209+
(Type, Type))
7210+
ERROR(noncopyable_generics_metatype_cast, none,
7211+
"metatype %0 cannot be cast to %1 because %2 is noncopyable",
7212+
(Type, Type, Type))
72077213
ERROR(noncopyable_effectful_getter,none,
72087214
"%0 of noncopyable type cannot be 'async' or 'throws'", (DescriptiveDeclKind))
72097215
ERROR(noncopyable_enums_do_not_support_indirect,none,

include/swift/Sema/CSFix.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2082,10 +2082,29 @@ class NotCompileTimeConst final : public ContextualMismatch {
20822082
}
20832083
};
20842084

2085+
/// Describes the reason why the type must be copyable
2086+
struct NoncopyableMatchFailure {
2087+
enum Reason {
2088+
Unknown,
2089+
CastToExistential,
2090+
};
2091+
2092+
Type type;
2093+
Reason reason;
2094+
2095+
NoncopyableMatchFailure() : type(Type()), reason(Unknown) {}
2096+
NoncopyableMatchFailure(Type type, Reason reason)
2097+
: type(type), reason(reason) {}
2098+
};
2099+
20852100
class MustBeCopyable final : public ConstraintFix {
20862101
Type noncopyableTy;
2102+
NoncopyableMatchFailure failure;
20872103

2088-
MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator);
2104+
MustBeCopyable(ConstraintSystem &cs,
2105+
Type noncopyableTy,
2106+
NoncopyableMatchFailure failure,
2107+
ConstraintLocator *locator);
20892108

20902109
public:
20912110
std::string getName() const override { return "remove move-only from type"; }
@@ -2096,6 +2115,7 @@ class MustBeCopyable final : public ConstraintFix {
20962115

20972116
static MustBeCopyable *create(ConstraintSystem &cs,
20982117
Type noncopyableTy,
2118+
NoncopyableMatchFailure failure,
20992119
ConstraintLocator *locator);
21002120

21012121
static bool classof(const ConstraintFix *fix) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6095,7 +6095,27 @@ bool NotCompileTimeConstFailure::diagnoseAsError() {
60956095
}
60966096

60976097
bool NotCopyableFailure::diagnoseAsError() {
6098-
emitDiagnostic(diag::noncopyable_generics, noncopyableTy);
6098+
switch (failure.reason) {
6099+
case NoncopyableMatchFailure::CastToExistential: {
6100+
assert(failure.type->is<ExistentialType>());
6101+
6102+
if (noncopyableTy->is<AnyMetatypeType>())
6103+
emitDiagnostic(diag::noncopyable_generics_metatype_cast,
6104+
noncopyableTy,
6105+
failure.type,
6106+
noncopyableTy->getMetatypeInstanceType());
6107+
else
6108+
emitDiagnostic(diag::noncopyable_generics_erasure,
6109+
noncopyableTy,
6110+
failure.type);
6111+
break;
6112+
}
6113+
case NoncopyableMatchFailure::Unknown:
6114+
emitDiagnostic(diag::noncopyable_generics,
6115+
noncopyableTy);
6116+
break;
6117+
}
6118+
60996119
return true;
61006120
}
61016121

lib/Sema/CSDiagnostics.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -539,16 +539,16 @@ class MemberAccessOnOptionalBaseFailure final : public MemberReferenceFailure {
539539
ResultTypeIsOptional(resultOptional) {}
540540

541541
bool diagnoseAsError() override;
542-
542+
543543
Type getMemberBaseType() const {
544544
return MemberBaseType;
545545
}
546-
546+
547547
SourceLoc getLoc() const override {
548548
// The end location points to the dot in the member access.
549549
return getSourceRange().End;
550550
}
551-
551+
552552
SourceRange getSourceRange() const override;
553553

554554
};
@@ -670,7 +670,7 @@ class ContextualFailure : public FailureDiagnostic {
670670

671671
/// If we're trying to convert something to `nil`.
672672
bool diagnoseConversionToNil() const;
673-
673+
674674
/// Diagnose failed conversion in a `CoerceExpr`.
675675
bool diagnoseCoercionToUnrelatedType() const;
676676

@@ -1816,9 +1816,14 @@ class NotCompileTimeConstFailure final : public FailureDiagnostic {
18161816

18171817
class NotCopyableFailure final : public FailureDiagnostic {
18181818
Type noncopyableTy;
1819+
NoncopyableMatchFailure failure;
18191820
public:
1820-
NotCopyableFailure(const Solution &solution, Type noncopyableTy, ConstraintLocator *locator)
1821-
: FailureDiagnostic(solution, locator), noncopyableTy(noncopyableTy) {}
1821+
NotCopyableFailure(const Solution &solution,
1822+
Type noncopyableTy,
1823+
NoncopyableMatchFailure failure,
1824+
ConstraintLocator *locator)
1825+
: FailureDiagnostic(solution, locator),
1826+
noncopyableTy(noncopyableTy), failure(failure) {}
18221827

18231828
bool diagnoseAsError() override;
18241829
};

lib/Sema/CSFix.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,19 +1368,24 @@ bool NotCompileTimeConst::diagnose(const Solution &solution, bool asNote) const
13681368
return failure.diagnose(asNote);
13691369
}
13701370

1371-
MustBeCopyable::MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator)
1371+
MustBeCopyable::MustBeCopyable(ConstraintSystem &cs,
1372+
Type noncopyableTy,
1373+
NoncopyableMatchFailure failure,
1374+
ConstraintLocator *locator)
13721375
: ConstraintFix(cs, FixKind::MustBeCopyable, locator, FixBehavior::Error),
1373-
noncopyableTy(noncopyableTy) {}
1376+
noncopyableTy(noncopyableTy), failure(failure) {}
13741377

13751378
bool MustBeCopyable::diagnose(const Solution &solution, bool asNote) const {
1376-
NotCopyableFailure failure(solution, noncopyableTy, getLocator());
1377-
return failure.diagnose(asNote);
1379+
NotCopyableFailure failDiag(solution, noncopyableTy, failure, getLocator());
1380+
return failDiag.diagnose(asNote);
13781381
}
13791382

13801383
MustBeCopyable* MustBeCopyable::create(ConstraintSystem &cs,
13811384
Type noncopyableTy,
1385+
NoncopyableMatchFailure failure,
13821386
ConstraintLocator *locator) {
1383-
return new (cs.getAllocator()) MustBeCopyable(cs, noncopyableTy, locator);
1387+
return new (cs.getAllocator()) MustBeCopyable(cs, noncopyableTy,
1388+
failure, locator);
13841389
}
13851390

13861391
bool MustBeCopyable::diagnoseForAmbiguity(CommonFixesArray commonFixes) const {

lib/Sema/CSSimplify.cpp

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

test/Constraints/moveonly_constraints.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ takeGeneric(globalMO) // expected-error {{noncopyable type 'MO' cannot be used w
6767

6868

6969
func testAny() {
70-
let _: Any = MO() // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
71-
takeAny(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
70+
let _: Any = MO() // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
71+
takeAny(MO()) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
7272
}
7373

7474
func testBasic(_ mo: borrowing MO) {
@@ -79,8 +79,8 @@ func testBasic(_ mo: borrowing MO) {
7979
takeGeneric(MO()) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
8080
takeGeneric(mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
8181

82-
takeAny(mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
83-
print(mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
82+
takeAny(mo) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
83+
print(mo) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
8484
_ = "\(mo)" // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
8585
let _: String = String(describing: mo) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
8686

@@ -94,8 +94,8 @@ func testBasic(_ mo: borrowing MO) {
9494
let singleton : (MO) = (mo)
9595
takeGeneric(singleton) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
9696

97-
takeAny((mo)) // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
98-
takeAny((mo, mo)) // expected-error {{noncopyable type '(MO, MO)' cannot be used with generics yet}}
97+
takeAny((mo)) // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
98+
takeAny((mo, mo)) // expected-error {{noncopyable type '(MO, MO)' cannot be erased to copyable existential type 'Any'}}
9999
}
100100

101101
func checkBasicBoxes() {
@@ -148,23 +148,23 @@ func checkCasting(_ b: any Box, _ mo: borrowing MO, _ a: Any) {
148148
let _: MO = dup.get()
149149
let _: MO = dup.val
150150

151-
let _: Any = MO.self // expected-error {{noncopyable type 'MO.Type' cannot be used with generics yet}}
152-
let _: AnyObject = MO.self // expected-error {{noncopyable type 'MO.Type' cannot be used with generics yet}}
153-
let _ = MO.self as Any // expected-error {{noncopyable type 'MO.Type' cannot be used with generics yet}}
151+
let _: Any = MO.self // expected-error {{metatype 'MO.Type' cannot be cast to 'Any' because 'MO' is noncopyable}}
152+
let _: AnyObject = MO.self // expected-error {{metatype 'MO.Type' cannot be cast to 'AnyObject' because 'MO' is noncopyable}}
153+
let _ = MO.self as Any // expected-error {{metatype 'MO.Type' cannot be cast to 'Any' because 'MO' is noncopyable}}
154154
let _ = MO.self is Any // expected-warning {{cast from 'MO.Type' to unrelated type 'Any' always fails}}
155155

156-
let _: Sendable = (MO(), MO()) // expected-error {{noncopyable type '(MO, MO)' cannot be used with generics yet}}
157-
let _: Sendable = MO() // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
156+
let _: Sendable = (MO(), MO()) // expected-error {{noncopyable type '(MO, MO)' cannot be erased to copyable existential type 'any Sendable'}}
157+
let _: Sendable = MO() // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'any Sendable'}}
158158
let _: _Copyable = mo // expected-error {{'_Copyable' is unavailable}}
159-
// expected-error@-1 {{noncopyable type 'MO' cannot be used with generics yet}}
160-
let _: AnyObject = MO() // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
161-
let _: Any = mo // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
159+
// expected-error@-1 {{noncopyable type 'MO' cannot be erased to copyable existential type 'any _Copyable'}}
160+
let _: AnyObject = MO() // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'AnyObject'}}
161+
let _: Any = mo // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
162162

163-
_ = MO() as P // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
164-
_ = MO() as any P // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
165-
_ = MO() as Any // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
163+
_ = MO() as P // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'any P'}}
164+
_ = MO() as any P // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'any P'}}
165+
_ = MO() as Any // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'Any'}}
166166
_ = MO() as MO
167-
_ = MO() as AnyObject // expected-error {{noncopyable type 'MO' cannot be used with generics yet}}
167+
_ = MO() as AnyObject // expected-error {{noncopyable type 'MO' cannot be erased to copyable existential type 'AnyObject'}}
168168
_ = 5 as MO // expected-error {{cannot convert value of type 'Int' to type 'MO' in coercion}}
169169
_ = a as MO // expected-error {{cannot convert value of type 'Any' to type 'MO' in coercion}}
170170
_ = b as MO // expected-error {{cannot convert value of type 'any Box' to type 'MO' in coercion}}

test/Sema/moveonly_sendable.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,17 @@ enum Wrong_NoncopyableOption<T> : Sendable { // expected-note {{consider making
9393
func takeAnySendable(_ s: any Sendable) {}
9494
func takeSomeSendable(_ s: some Sendable) {}
9595

96-
// expected-error@+1 {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
96+
// expected-error@+1 {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
9797
func mkSendable() -> Sendable { return FileDescriptor(id: 0) }
9898

9999
func tryToCastIt(_ fd: borrowing FileDescriptor) {
100-
let _: any Sendable = fd // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
101-
let _: Sendable = fd // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
100+
let _: any Sendable = fd // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
101+
let _: Sendable = fd // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
102102

103-
takeAnySendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
103+
takeAnySendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
104104
takeSomeSendable(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
105105

106-
let _ = fd as Sendable // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
106+
let _ = fd as Sendable // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
107107

108108
let _ = fd as? Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}}
109109
// expected-error@-1 {{noncopyable types cannot be conditionally cast}}
@@ -146,7 +146,7 @@ class Container<T> where T:Sendable {
146146
}
147147

148148
func createContainer(_ fd: borrowing FileDescriptor) {
149-
let _: Container<Sendable> = Container(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be used with generics yet}}
149+
let _: Container<Sendable> = Container(fd) // expected-error {{noncopyable type 'FileDescriptor' cannot be erased to copyable existential type 'any Sendable'}}
150150
let _: Container<Sendable> = Container(CopyableStruct())
151151
}
152152

0 commit comments

Comments
 (0)