Skip to content

Commit 82c43a8

Browse files
authored
[Dynamic Casting] Allow casts to "more optional" types (#33684)
Generally, casting consistency demands that we be able to extract anything from an existential that can be put into that existential. (Which is why the casting spec requires that casting permit arbitrary injection and projection of optionals.) This particular diagnostic prevented optionals from being projected back out of existentials: let i: Int? let a: Any = i // Inject Int? into Any // Error prevents projecting Int? back out of Any a as? Int? This also broke certain uses of Mirror (weak variables get reflected as optionals stored in Any existentials).
1 parent 8d5ff75 commit 82c43a8

File tree

6 files changed

+21
-30
lines changed

6 files changed

+21
-30
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,9 +1045,6 @@ WARNING(downcast_to_unrelated,none,
10451045
"cast from %0 to unrelated type %1 always fails", (Type, Type))
10461046
NOTE(downcast_to_unrelated_fixit,none,
10471047
"did you mean to call %0 with '()'?", (Identifier))
1048-
ERROR(downcast_to_more_optional,none,
1049-
"cannot downcast from %0 to a more optional type %1",
1050-
(Type, Type))
10511048
ERROR(optional_chain_noop,none,
10521049
"optional chain has no effect, expression already produces %0",
10531050
(Type))

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3790,17 +3790,14 @@ namespace {
37903790
};
37913791

37923792
// There's nothing special to do if the operand isn't optional
3793-
// and we don't need any bridging.
3794-
if (srcOptionals.empty()) {
3793+
// (or is insufficiently optional) and we don't need any bridging.
3794+
if (srcOptionals.empty()
3795+
|| (srcOptionals.size() < destOptionals.size() - destExtraOptionals)) {
37953796
Expr *result = buildInnerOperation(subExpr, finalResultType);
37963797
if (!result) return nullptr;
37973798
return addFinalOptionalInjections(result);
37983799
}
37993800

3800-
// The result type (without the final optional) is a subtype of
3801-
// the operand type, so it will never have a higher depth.
3802-
assert(destOptionals.size() - destExtraOptionals <= srcOptionals.size());
3803-
38043801
// The outermost N levels of optionals on the operand must all
38053802
// be present or the cast fails. The innermost M levels of
38063803
// optionals on the operand are reflected in the requested

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3167,21 +3167,14 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
31673167
return CheckedCastKind::ValueCast;
31683168
};
31693169

3170-
// Strip optional wrappers off of the destination type in sync with
3171-
// stripping them off the origin type.
3170+
// TODO: Explore optionals using the same strategy used by the
3171+
// runtime.
3172+
// For now, if the target is more optional than the source,
3173+
// just defer it out for the runtime to handle.
31723174
while (auto toValueType = toType->getOptionalObjectType()) {
3173-
// Complain if we're trying to increase optionality, e.g.
3174-
// casting an NSObject? to an NSString??. That's not a subtype
3175-
// relationship.
31763175
auto fromValueType = fromType->getOptionalObjectType();
31773176
if (!fromValueType) {
3178-
if (!suppressDiagnostics) {
3179-
diags.diagnose(diagLoc, diag::downcast_to_more_optional,
3180-
origFromType, origToType)
3181-
.highlight(diagFromRange)
3182-
.highlight(diagToRange);
3183-
}
3184-
return CheckedCastKind::Unresolved;
3177+
return CheckedCastKind::ValueCast;
31853178
}
31863179

31873180
toType = toValueType;

test/Constraints/casts.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,11 @@ func test_coercions_with_overloaded_operator(str: String, optStr: String?, veryO
252252

253253
_ = (str ?? "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
254254
_ = (optStr ?? "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
255-
_ = (optStr ?? "") as Int? // expected-error {{cannot convert value of type 'String' to type 'Int?' in coercion}}
255+
_ = (optStr ?? "") as Int? // expected-error {{'String' is not convertible to 'Int?'; did you mean to use 'as!' to force downcast?}}
256256

257257
_ = (str ^^^ "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
258258
_ = (optStr ^^^ "") as Int // expected-error {{cannot convert value of type 'String' to type 'Int' in coercion}}
259-
_ = (optStr ^^^ "") as Int? // expected-error {{cannot convert value of type 'String' to type 'Int?' in coercion}}
259+
_ = (optStr ^^^ "") as Int? // expected-error {{'String' is not convertible to 'Int?'; did you mean to use 'as!' to force downcast?}}
260260

261261
_ = ([] ?? []) as String // expected-error {{cannot convert value of type '[Any]' to type 'String' in coercion}}
262262
_ = ([""] ?? []) as [Int: Int] // expected-error {{cannot convert value of type '[String]' to type '[Int : Int]' in coercion}}
@@ -290,7 +290,7 @@ func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [Strin
290290
// expected-note@-1 {{arguments to generic parameter 'Element' ('Int' and 'String') are expected to be equal}}
291291
_ = dict as [String: String] // expected-error {{cannot convert value of type '[String : Int]' to type '[String : String]' in coercion}}
292292
// expected-note@-1 {{arguments to generic parameter 'Value' ('Int' and 'String') are expected to be equal}}
293-
_ = dict as [String: String]? // expected-error {{cannot convert value of type '[String : Int]' to type '[String : String]?' in coercion}}
293+
_ = dict as [String: String]? // expected-error {{'[String : Int]' is not convertible to '[String : String]?'; did you mean to use 'as!' to force downcast?}}
294294
_ = (dict as [String: Int]?) as [String: Int] // expected-error {{value of optional type '[String : Int]?' must be unwrapped to a value of type '[String : Int]'}}
295295
// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}}
296296
// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}

test/Constraints/optional.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,17 @@ func test5() -> Int? {
6464
}
6565

6666
func test6<T>(_ x : T) {
67-
// FIXME: this code should work; T could be Int? or Int??
68-
// or something like that at runtime. rdar://16374053
69-
_ = x as? Int? // expected-error {{cannot downcast from 'T' to a more optional type 'Int?'}}
67+
_ = x as? Int? // Okay. We know nothing about T, so cannot judge.
7068
}
7169

7270
class B : A { }
7371

7472
func test7(_ x : A) {
75-
_ = x as? B? // expected-error{{cannot downcast from 'A' to a more optional type 'B?'}}
73+
_ = x as? B? // Okay: Injecting into an Optional
74+
}
75+
76+
func test7a(_ x : B) {
77+
_ = x as? A // expected-warning{{conditional cast from 'B' to 'A' always succeeds}}
7678
}
7779

7880
func test8(_ x : AnyObject?) {

test/expr/cast/as_coerce.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,16 @@ class C5 {}
6868

6969
var c: AnyObject = C3()
7070

71-
if let castX = c as! C4? {} // expected-error {{cannot downcast from 'AnyObject' to a more optional type 'C4?'}}
71+
// XXX TODO: Constant-folding should generate an error about 'C3' not being convertible to 'C4'
72+
//if let castX = c as! C4? {}
7273

73-
// Only suggest replacing 'as' with 'as!' if it would fix the error.
74+
// XXX TODO: Only suggest replacing 'as' with 'as!' if it would fix the error.
7475
C3() as C4 // expected-error {{'C3' is not convertible to 'C4'; did you mean to use 'as!' to force downcast?}} {{6-8=as!}}
7576
C3() as C5 // expected-error {{cannot convert value of type 'C3' to type 'C5' in coercion}}
7677

7778
// Diagnostic shouldn't include @lvalue in type of c3.
7879
var c3 = C3()
80+
// XXX TODO: This should not suggest `as!`
7981
c3 as C4 // expected-error {{'C3' is not convertible to 'C4'; did you mean to use 'as!' to force downcast?}} {{4-6=as!}}
8082

8183
// <rdar://problem/19495142> Various incorrect diagnostics for explicit type conversions

0 commit comments

Comments
 (0)