Skip to content

Commit 9a18422

Browse files
committed
statically prevent runtime casts of move-only type
It appears that conditional casts require a check of the type's metadata at runtime. There's no reason for us to permit that at this time, since all such conditional casts are going to fail, unless its the identity cast. That is, a move-only type is currently only a subtype of itself. So for now, `is`, `as?`, `as!` casts from or to a move-only type are now an error. The `as` casts are permitted only if the two move-only types are equal.
1 parent bebd0d0 commit 9a18422

File tree

5 files changed

+81
-7
lines changed

5 files changed

+81
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6681,6 +6681,8 @@ ERROR(noimplicitcopy_attr_not_allowed_on_moveonlytype,none,
66816681
"'@_noImplicitCopy' has no effect when applied to a move only type", ())
66826682
ERROR(moveonly_enums_do_not_support_indirect,none,
66836683
"move-only enum %0 cannot be marked indirect or have indirect cases yet", (Identifier))
6684+
ERROR(moveonly_cast,none,
6685+
"move-only types cannot be conditionally cast", ())
66846686

66856687
//------------------------------------------------------------------------------
66866688
// MARK: Type inference from default expressions

lib/Sema/MiscDiagnostics.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
336336
tupleExpr->getElementNames());
337337
}
338338

339-
// Diagnose checked casts that involve marker protocols.
339+
// Specially diagnose some checked casts that are illegal.
340340
if (auto cast = dyn_cast<CheckedCastExpr>(E)) {
341341
checkCheckedCastExpr(cast);
342342
}
@@ -384,16 +384,39 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
384384
}
385385

386386
void checkCheckedCastExpr(CheckedCastExpr *cast) {
387+
Type castType = cast->getCastType();
388+
if (!castType)
389+
return;
390+
391+
if (castType->isPureMoveOnly()) {
392+
// can't cast anything to move-only; there should be no valid ones.
393+
Ctx.Diags.diagnose(cast->getLoc(), diag::moveonly_cast);
394+
return;
395+
}
396+
397+
// no support for runtime casts from move-only types.
398+
// as of now there is no type it could be cast to except itself, so
399+
// there's no reason for it to happen at runtime.
400+
if (auto fromType = cast->getSubExpr()->getType()) {
401+
if (fromType->isPureMoveOnly()) {
402+
// can't cast move-only to anything.
403+
Ctx.Diags.diagnose(cast->getLoc(), diag::moveonly_cast);
404+
return;
405+
}
406+
}
407+
408+
// now, look for conditional casts to marker protocols.
409+
387410
if (!isa<ConditionalCheckedCastExpr>(cast) && !isa<IsExpr>(cast))
388411
return;
389412

390-
Type castType = cast->getCastType();
391-
if (!castType || !castType->isExistentialType())
413+
if(!castType->isExistentialType())
392414
return;
393415

394416
auto layout = castType->getExistentialLayout();
395417
for (auto proto : layout.getProtocols()) {
396418
if (proto->isMarkerProtocol()) {
419+
// can't conditionally cast to a marker protocol
397420
Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
398421
proto->getName());
399422
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,7 +1678,7 @@ TypeChecker::typeCheckCheckedCast(Type fromType, Type toType,
16781678
//
16791679
//
16801680
// Thus, right now, a move-only type is only a subtype of itself.
1681-
if (fromType->isPureMoveOnly())
1681+
if (fromType->isPureMoveOnly() || toType->isPureMoveOnly())
16821682
return CheckedCastKind::Unresolved;
16831683

16841684
// Check for a bridging conversion.

test/Constraints/moveonly_constraints.swift

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func checkMethodCalls() {
139139
takeMaybe(true ? .none : .just(MO())) // expected-error 3{{move-only type 'MO' cannot be used with generics yet}}
140140
}
141141

142-
func checkCasting(_ b: any Box, _ mo: MO) {
142+
func checkCasting(_ b: any Box, _ mo: MO, _ a: Any) {
143143
// casting dynamically is allowed, but should always fail since you can't
144144
// construct such a type.
145145
let box = b as! ValBox<MO> // expected-error {{move-only type 'MO' cannot be used with generics yet}}
@@ -159,26 +159,65 @@ func checkCasting(_ b: any Box, _ mo: MO) {
159159
_ = MO() as Any // expected-error {{move-only type 'MO' cannot be used with generics yet}}
160160
_ = MO() as MO
161161
_ = MO() as AnyObject // expected-error {{move-only type 'MO' cannot be used with generics yet}}
162+
_ = 5 as MO // expected-error {{cannot convert value of type 'Int' to type 'MO' in coercion}}
163+
_ = a as MO // expected-error {{cannot convert value of type 'Any' to type 'MO' in coercion}}
164+
_ = b as MO // expected-error {{cannot convert value of type 'any Box' to type 'MO' in coercion}}
162165

163166
// FIXME(kavon): make sure at runtime these casts actually fail, or just make them errors? (rdar://104900293)
164167

165168
_ = MO() is AnyHashable // expected-warning {{cast from 'MO' to unrelated type 'AnyHashable' always fails}}
169+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
166170
_ = MO() is AnyObject // expected-warning {{cast from 'MO' to unrelated type 'AnyObject' always fails}}
171+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
167172
_ = MO() is Any // expected-warning {{cast from 'MO' to unrelated type 'Any' always fails}}
173+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
168174
_ = MO() is P // expected-warning {{cast from 'MO' to unrelated type 'any P' always fails}}
175+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
169176
_ = MO() is MO // expected-warning {{'is' test is always true}}
177+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
178+
179+
_ = 5 is MO // expected-warning {{cast from 'Int' to unrelated type 'MO' always fails}}
180+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
181+
_ = a is MO // expected-warning {{cast from 'Any' to unrelated type 'MO' always fails}}
182+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
183+
_ = b is MO // expected-warning {{cast from 'any Box' to unrelated type 'MO' always fails}}
184+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
170185

171186
_ = MO() as! AnyHashable // expected-warning {{cast from 'MO' to unrelated type 'AnyHashable' always fails}}
187+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
172188
_ = MO() as! AnyObject // expected-warning {{cast from 'MO' to unrelated type 'AnyObject' always fails}}
189+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
173190
_ = MO() as! Any // expected-warning {{cast from 'MO' to unrelated type 'Any' always fails}}
191+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
174192
_ = MO() as! P // expected-warning {{cast from 'MO' to unrelated type 'any P' always fails}}
193+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
175194
_ = MO() as! MO // expected-warning {{forced cast of 'MO' to same type has no effect}}
195+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
196+
197+
_ = 5 as! MO // expected-warning {{cast from 'Int' to unrelated type 'MO' always fails}}
198+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
199+
_ = a as! MO // expected-warning {{cast from 'Any' to unrelated type 'MO' always fails}}
200+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
201+
_ = b as! MO // expected-warning {{cast from 'any Box' to unrelated type 'MO' always fails}}
202+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
176203

177204
_ = MO() as? AnyHashable // expected-warning {{cast from 'MO' to unrelated type 'AnyHashable' always fails}}
205+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
178206
_ = MO() as? AnyObject // expected-warning {{cast from 'MO' to unrelated type 'AnyObject' always fails}}
207+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
179208
_ = MO() as? Any // expected-warning {{cast from 'MO' to unrelated type 'Any' always fails}}
209+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
180210
_ = MO() as? P // expected-warning {{cast from 'MO' to unrelated type 'any P' always fails}}
211+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
181212
_ = MO() as? MO // expected-warning {{conditional cast from 'MO' to 'MO' always succeeds}}
213+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
214+
215+
_ = 5 as? MO // expected-warning {{cast from 'Int' to unrelated type 'MO' always fails}}
216+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
217+
_ = a as? MO // expected-warning {{cast from 'Any' to unrelated type 'MO' always fails}}
218+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
219+
_ = b as? MO // expected-warning {{cast from 'any Box' to unrelated type 'MO' always fails}}
220+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
182221

183222
}
184223

test/Sema/moveonly_sendable.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,22 @@ func tryToCastIt(_ fd: FileDescriptor) {
106106
let _ = fd as Sendable // expected-error {{move-only type 'FileDescriptor' cannot be used with generics yet}}
107107

108108
let _ = fd as? Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}}
109-
// expected-error@-1 {{marker protocol 'Sendable' cannot be used in a conditional cast}}
109+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
110110

111111
let _ = fd as! Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}}
112+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
112113

113114
let _ = fd is Sendable // expected-warning {{cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails}}
114-
// expected-error@-1 {{marker protocol 'Sendable' cannot be used in a conditional cast}}
115+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
116+
117+
let sendy = mkSendable()
118+
let _ = sendy as FileDescriptor // expected-error {{cannot convert value of type 'any Sendable' to type 'FileDescriptor' in coercion}}
119+
let _ = sendy is FileDescriptor // expected-warning {{cast from 'any Sendable' to unrelated type 'FileDescriptor' always fails}}
120+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
121+
let _ = sendy as! FileDescriptor // expected-warning {{cast from 'any Sendable' to unrelated type 'FileDescriptor' always fails}}
122+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
123+
let _ = sendy as? FileDescriptor// expected-warning {{cast from 'any Sendable' to unrelated type 'FileDescriptor' always fails}}
124+
// expected-error@-1 {{move-only types cannot be conditionally cast}}
115125
}
116126

117127
protocol GiveSendable<T> {

0 commit comments

Comments
 (0)