Skip to content

Commit 238ecbf

Browse files
committed
[Dynamic Casting] Allow casts to "more optional" types
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 ce2377d commit 238ecbf

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
@@ -1038,9 +1038,6 @@ WARNING(downcast_to_unrelated,none,
10381038
"cast from %0 to unrelated type %1 always fails", (Type, Type))
10391039
NOTE(downcast_to_unrelated_fixit,none,
10401040
"did you mean to call %0 with '()'?", (Identifier))
1041-
ERROR(downcast_to_more_optional,none,
1042-
"cannot downcast from %0 to a more optional type %1",
1043-
(Type, Type))
10441041
ERROR(optional_chain_noop,none,
10451042
"optional chain has no effect, expression already produces %0",
10461043
(Type))

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3810,17 +3810,14 @@ namespace {
38103810
};
38113811

38123812
// There's nothing special to do if the operand isn't optional
3813-
// and we don't need any bridging.
3814-
if (srcOptionals.empty()) {
3813+
// (or is insufficiently optional) and we don't need any bridging.
3814+
if (srcOptionals.empty()
3815+
|| (srcOptionals.size() < destOptionals.size() - destExtraOptionals)) {
38153816
Expr *result = buildInnerOperation(subExpr, finalResultType);
38163817
if (!result) return nullptr;
38173818
return addFinalOptionalInjections(result);
38183819
}
38193820

3820-
// The result type (without the final optional) is a subtype of
3821-
// the operand type, so it will never have a higher depth.
3822-
assert(destOptionals.size() - destExtraOptionals <= srcOptionals.size());
3823-
38243821
// The outermost N levels of optionals on the operand must all
38253822
// be present or the cast fails. The innermost M levels of
38263823
// 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
@@ -3126,21 +3126,14 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
31263126
return CheckedCastKind::ValueCast;
31273127
};
31283128

3129-
// Strip optional wrappers off of the destination type in sync with
3130-
// stripping them off the origin type.
3129+
// TODO: Explore optionals using the same strategy used by the
3130+
// runtime.
3131+
// For now, if the target is more optional than the source,
3132+
// just defer it out for the runtime to handle.
31313133
while (auto toValueType = toType->getOptionalObjectType()) {
3132-
// Complain if we're trying to increase optionality, e.g.
3133-
// casting an NSObject? to an NSString??. That's not a subtype
3134-
// relationship.
31353134
auto fromValueType = fromType->getOptionalObjectType();
31363135
if (!fromValueType) {
3137-
if (!suppressDiagnostics) {
3138-
diags.diagnose(diagLoc, diag::downcast_to_more_optional,
3139-
origFromType, origToType)
3140-
.highlight(diagFromRange)
3141-
.highlight(diagToRange);
3142-
}
3143-
return CheckedCastKind::Unresolved;
3136+
return CheckedCastKind::ValueCast;
31443137
}
31453138

31463139
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)