mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When re-typechecking an expression during diagnostics, we begin by erasing all the types in the expression. However, any expressions created as part of applying the solution will remain. CSGen was doing the wrong thing when it encountered EnumIsCaseExpr, which can only appear in already-type checked ASTs. Returning expr->getType() is not correct, because the type is not yet set; returning a null type is intended to signal that a diagnostic was already emitted, which causes Sema to stop type checking. The end result is that certain combinations of invalid code with an 'is' cast nested inside would crash either the AST verifier (with asserts on) or in SILGen (with asserts off), because we would stop trying to diagnose the issue prematurely, and possibly not emit a diagnostic at all. Fixes <https://bugs.swift.org/browse/SR-5050> and <rdar://problem/32487948>.
224 lines
5.0 KiB
Swift
224 lines
5.0 KiB
Swift
// RUN: %target-typecheck-verify-swift
|
|
|
|
class B {
|
|
init() {}
|
|
}
|
|
class D : B {
|
|
override init() { super.init() }
|
|
}
|
|
|
|
var seven : Double = 7
|
|
|
|
var pair : (Int, Double) = (1, 2)
|
|
|
|
var closure : (Int, Int) -> Int = { $0 + $1 }
|
|
|
|
var d_as_b : B = D()
|
|
var b_as_d = B() as! D
|
|
var bad_b_as_d : D = B() // expected-error{{cannot convert value of type 'B' to specified type 'D'}}
|
|
|
|
var d = D()
|
|
var b = B()
|
|
|
|
var d_as_b_2 : B = d
|
|
var b_as_d_2 = b as! D
|
|
|
|
var b_is_d:Bool = B() is D
|
|
// FIXME: Poor diagnostic below.
|
|
var bad_d_is_b:Bool = D() is B // expected-warning{{always true}}
|
|
|
|
func base_class_archetype_casts<T : B>(_ t: T) {
|
|
var _ : B = t
|
|
_ = B() as! T
|
|
var _ : T = B() // expected-error{{cannot convert value of type 'B' to specified type 'T'}}
|
|
|
|
let b = B()
|
|
|
|
_ = b as! T
|
|
|
|
var _:Bool = B() is T
|
|
var _:Bool = b is T
|
|
var _:Bool = t is B // expected-warning{{always true}}
|
|
|
|
_ = t as! D
|
|
}
|
|
|
|
protocol P1 { func p1() }
|
|
protocol P2 { func p2() }
|
|
|
|
struct S1 : P1 {
|
|
func p1() {}
|
|
}
|
|
class C1 : P1 {
|
|
func p1() {}
|
|
}
|
|
class D1 : C1 {}
|
|
|
|
struct S2 : P2 {
|
|
func p2() {}
|
|
}
|
|
|
|
struct S3 {}
|
|
|
|
struct S12 : P1, P2 {
|
|
func p1() {}
|
|
func p2() {}
|
|
}
|
|
|
|
func protocol_archetype_casts<T : P1>(_ t: T, p1: P1, p2: P2, p12: P1 & P2) {
|
|
// Coercions.
|
|
var _ : P1 = t
|
|
var _ : P2 = t // expected-error{{value of type 'T' does not conform to specified type 'P2'}}
|
|
|
|
// Checked unconditional casts.
|
|
_ = p1 as! T
|
|
_ = p2 as! T
|
|
_ = p12 as! T
|
|
|
|
_ = t as! S1
|
|
_ = t as! S12
|
|
_ = t as! C1
|
|
_ = t as! D1
|
|
|
|
_ = t as! S2
|
|
|
|
_ = S1() as! T
|
|
_ = S12() as! T
|
|
_ = C1() as! T
|
|
_ = D1() as! T
|
|
|
|
_ = S2() as! T
|
|
|
|
// Type queries.
|
|
var _:Bool = p1 is T
|
|
var _:Bool = p2 is T
|
|
var _:Bool = p12 is T
|
|
|
|
var _:Bool = t is S1
|
|
var _:Bool = t is S12
|
|
var _:Bool = t is C1
|
|
var _:Bool = t is D1
|
|
|
|
var _:Bool = t is S2
|
|
}
|
|
|
|
func protocol_concrete_casts(_ p1: P1, p2: P2, p12: P1 & P2) {
|
|
// Checked unconditional casts.
|
|
_ = p1 as! S1
|
|
_ = p1 as! C1
|
|
_ = p1 as! D1
|
|
_ = p1 as! S12
|
|
|
|
_ = p1 as! P1 & P2
|
|
|
|
_ = p2 as! S1
|
|
|
|
_ = p12 as! S1
|
|
_ = p12 as! S2
|
|
_ = p12 as! S12
|
|
_ = p12 as! S3
|
|
|
|
// Type queries.
|
|
var _:Bool = p1 is S1
|
|
var _:Bool = p1 is C1
|
|
var _:Bool = p1 is D1
|
|
var _:Bool = p1 is S12
|
|
|
|
var _:Bool = p1 is P1 & P2
|
|
|
|
var _:Bool = p2 is S1
|
|
|
|
var _:Bool = p12 is S1
|
|
var _:Bool = p12 is S2
|
|
var _:Bool = p12 is S12
|
|
var _:Bool = p12 is S3
|
|
}
|
|
|
|
func conditional_cast(_ b: B) -> D? {
|
|
return b as? D
|
|
}
|
|
|
|
@objc protocol ObjCProto1 {}
|
|
@objc protocol ObjCProto2 {}
|
|
protocol NonObjCProto : class {}
|
|
|
|
@objc class ObjCClass {}
|
|
class NonObjCClass {}
|
|
|
|
func objc_protocol_casts(_ op1: ObjCProto1, opn: NonObjCProto) {
|
|
_ = ObjCClass() as! ObjCProto1
|
|
_ = ObjCClass() as! ObjCProto2
|
|
_ = ObjCClass() as! ObjCProto1 & ObjCProto2
|
|
_ = ObjCClass() as! NonObjCProto
|
|
_ = ObjCClass() as! ObjCProto1 & NonObjCProto
|
|
|
|
_ = op1 as! ObjCProto1 & ObjCProto2
|
|
_ = op1 as! ObjCProto2
|
|
_ = op1 as! ObjCProto1 & NonObjCProto
|
|
_ = opn as! ObjCProto1
|
|
|
|
_ = NonObjCClass() as! ObjCProto1
|
|
}
|
|
|
|
func dynamic_lookup_cast(_ dl: AnyObject) {
|
|
_ = dl as! ObjCProto1
|
|
_ = dl as! ObjCProto2
|
|
_ = dl as! ObjCProto1 & ObjCProto2
|
|
}
|
|
|
|
// Cast to subclass with generic parameter inference
|
|
class C2<T> : B { }
|
|
class C3<T> : C2<[T]> {
|
|
func f(_ x: T) { }
|
|
}
|
|
var c2i : C2<[Int]> = C3()
|
|
var c3iOpt = c2i as? C3
|
|
c3iOpt?.f(5)
|
|
var b1 = c2i is C3
|
|
var c2f: C2<Float>? = b as? C2
|
|
var c2f2: C2<[Float]>? = b as! C3
|
|
|
|
|
|
// <rdar://problem/15633178>
|
|
var f: (Float) -> Float = { $0 as Float }
|
|
var f2: (B) -> Bool = { $0 is D }
|
|
|
|
func metatype_casts<T, U>(_ b: B.Type, t:T.Type, u: U.Type) {
|
|
_ = b is D.Type
|
|
_ = T.self is U.Type
|
|
_ = type(of: T.self) is U.Type.Type
|
|
_ = type(of: b) is D.Type // expected-warning{{always fails}}
|
|
_ = b is D.Type.Type // expected-warning{{always fails}}
|
|
|
|
}
|
|
|
|
// <rdar://problem/17017851>
|
|
func forcedDowncastToOptional(_ b: B) {
|
|
var dOpt: D? = b as! D // expected-warning{{treating a forced downcast to 'D' as optional will never produce 'nil'}}
|
|
// expected-note@-1{{use 'as?' to perform a conditional downcast to 'D'}}{{22-23=?}}
|
|
// expected-note@-2{{add parentheses around the cast to silence this warning}}{{18-18=(}}{{25-25=)}}
|
|
dOpt = b as! D // expected-warning{{treating a forced downcast to 'D' as optional will never produce 'nil'}}
|
|
// expected-note@-1{{use 'as?' to perform a conditional downcast to 'D'}}{{14-15=?}}
|
|
// expected-note@-2{{add parentheses around the cast to silence this warning}}{{10-10=(}}{{17-17=)}}
|
|
dOpt = (b as! D)
|
|
_ = dOpt
|
|
}
|
|
|
|
_ = b1 as Int // expected-error {{cannot convert value of type 'Bool' to type 'Int' in coercion}}
|
|
_ = seven as Int // expected-error {{cannot convert value of type 'Double' to type 'Int' in coercion}}
|
|
|
|
func rdar29894174(v: B?) {
|
|
let _ = [v].flatMap { $0 as? D }
|
|
}
|
|
|
|
// When re-typechecking a solution with an 'is' cast applied,
|
|
// we would fail to produce a diagnostic.
|
|
func process(p: Any?) {
|
|
compare(p is String)
|
|
// expected-error@-1 {{cannot invoke 'compare' with an argument list of type '(Bool)'}}
|
|
// expected-note@-2 {{overloads for 'compare' exist with these partially matching parameter lists: (T, T), (T?, T?)}}
|
|
}
|
|
|
|
func compare<T>(_: T, _: T) {}
|
|
func compare<T>(_: T?, _: T?) {}
|