Files
swift-mirror/test/Constraints/casts.swift
Slava Pestov 65c3565c2d Sema: Fix failure to produce diagnostics when 'is' casts are involved
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>.
2017-06-13 20:19:43 -07:00

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?) {}