blob: 5981c2b67ade782c7f15d7e5f1418fd5ee4592e3 [file] [log] [blame]
// RUN: %target-swift-frontend -module-name devirt_covariant_return -Xllvm -sil-full-demangle -O -Xllvm -disable-sil-cm-rr-cm=0 -Xllvm -sil-inline-generics=false -primary-file %s -emit-sil -sil-inline-threshold 1000 -Xllvm -sil-disable-pass=ObjectOutliner -sil-verify-all | %FileCheck %s
// Make sure that we can dig all the way through the class hierarchy and
// protocol conformances with covariant return types correctly. The verifier
// should trip if we do not handle things correctly.
//
// TODO: this is not working right now: rdar://problem/33461095
// As a side-test it also checks if all allocs can be promoted to the stack.
// CHECK-LABEL: sil hidden @$s23devirt_covariant_return6driveryyF : $@convention(thin) () -> () {
// CHECK: bb0
// CHECK-NOT: alloc_ref
// CHECK: function_ref @unknown1a : $@convention(thin) () -> ()
// CHECK: apply
// CHECK: function_ref @defenestrate : $@convention(thin) () -> ()
// CHECK: apply
// CHECK: function_ref @unknown2a : $@convention(thin) () -> ()
// CHECK: apply
// CHECK: apply
// CHECK: function_ref @unknown3a : $@convention(thin) () -> ()
// CHECK: apply
// CHECK: apply
// CHECK: tuple
// CHECK: return
@_silgen_name("unknown1a")
func unknown1a() -> ()
@_silgen_name("unknown1b")
func unknown1b() -> ()
@_silgen_name("unknown2a")
func unknown2a() -> ()
@_silgen_name("unknown2b")
func unknown2b() -> ()
@_silgen_name("unknown3a")
func unknown3a() -> ()
@_silgen_name("unknown3b")
func unknown3b() -> ()
@_silgen_name("defenestrate")
func defenestrate() -> ()
class B<T> {
// We do not specialize typealias's correctly now.
//typealias X = B
func doSomething() -> B<T> {
unknown1a()
return self
}
// See comment in protocol P
//class func doSomethingMeta() {
// unknown1b()
//}
func doSomethingElse() {
defenestrate()
}
}
class B2<T> : B<T> {
// When we have covariance in protocols, change this to B2.
// We do not specialize typealias correctly now.
//typealias X = B
override func doSomething() -> B2<T> {
unknown2a()
return self
}
// See comment in protocol P
//override class func doSomethingMeta() {
// unknown2b()
//}
}
class B3<T> : B2<T> {
override func doSomething() -> B3<T> {
unknown3a()
return self
}
}
func WhatShouldIDo<T>(_ b : B<T>) -> B<T> {
b.doSomething().doSomethingElse()
return b
}
func doSomethingWithB<T>(_ b : B<T>) {
}
struct S {}
func driver() -> () {
let b = B<S>()
let b2 = B2<S>()
let b3 = B3<S>()
WhatShouldIDo(b)
WhatShouldIDo(b2)
WhatShouldIDo(b3)
}
driver()
public class Bear {
public init?(fail: Bool) {
if fail { return nil }
}
// Check that devirtualizer can handle convenience initializers, which have covariant optional
// return types.
// CHECK-LABEL: sil @$s23devirt_covariant_return4BearC{{[_0-9a-zA-Z]*}}fC
// CHECK: checked_cast_br [exact] %{{.*}} : $@thick Bear.Type to $@thick GummyBear.Type
// CHECK: upcast %{{.*}} : $Optional<GummyBear> to $Optional<Bear>
// CHECK: }
public convenience init?(delegateFailure: Bool, failAfter: Bool) {
self.init(fail: delegateFailure)
if failAfter { return nil }
}
}
final class GummyBear: Bear {
override init?(fail: Bool) {
super.init(fail: fail)
}
init?(chainFailure: Bool, failAfter: Bool) {
super.init(fail: chainFailure)
if failAfter { return nil }
}
}
class Payload {
let value: Int32
init(_ n: Int32) {
value = n
}
func getValue() -> Int32 {
return value
}
}
final class Payload1: Payload {
override init(_ n: Int32) {
super.init(n)
}
}
class C {
func doSomething() -> Payload? {
return Payload(1)
}
}
final class C1:C {
// Override base method, but return a non-optional result
override func doSomething() -> Payload {
return Payload(2)
}
}
final class C2:C {
// Override base method, but return a non-optional result of a type,
// which is derived from the expected type.
override func doSomething() -> Payload1 {
return Payload1(2)
}
}
// Check that the Optional return value from doSomething
// gets properly unwrapped into a Payload object and then further
// devirtualized.
// CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver1ys5Int32VAA2C1CF
// CHECK: integer_literal $Builtin.Int32, 2
// CHECK: struct $Int32 (%{{.*}} : $Builtin.Int32)
// CHECK-NOT: class_method
// CHECK-NOT: function_ref
// CHECK: return
@inline(never)
func driver1(_ c: C1) -> Int32 {
return c.doSomething().getValue()
}
// Check that the Optional return value from doSomething
// gets properly unwrapped into a Payload object and then further
// devirtualized.
// CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver3ys5Int32VAA1CCF
// CHECK: bb{{[0-9]+}}(%{{[0-9]+}} : $C2):
// CHECK-NOT: bb{{.*}}:
// check that for C2, we convert the non-optional result into an optional and then cast.
// CHECK: enum $Optional
// CHECK-NEXT: upcast
// CHECK: return
@inline(never)
func driver3(_ c: C) -> Int32 {
return c.doSomething()!.getValue()
}
public class D {
let v: Int32
init(_ n: Int32) {
v = n
}
}
public class D1 : D {
public func foo() -> D? {
return nil
}
public func boo() -> Int32 {
return foo()!.v
}
}
let sD = D(0)
public class D2: D1 {
// Override base method, but return a non-optional result
override public func foo() -> D {
return sD
}
}
// Check that the boo call gets properly devirtualized and that
// that D2.foo() is inlined thanks to this.
// CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return7driver2ys5Int32VAA2D2CF
// CHECK-NOT: class_method
// CHECK: checked_cast_br [exact] %{{.*}} : $D1 to $D2
// CHECK: bb2
// CHECK: global_addr
// CHECK: load
// CHECK: ref_element_addr
// CHECK: bb3
// CHECK: class_method
// CHECK: }
@inline(never)
func driver2(_ d: D2) -> Int32 {
return d.boo()
}
class AA {
}
class BB : AA {
}
class CCC {
@inline(never)
func foo(_ b: BB) -> (AA, AA) {
return (b, b)
}
}
class DDD : CCC {
@inline(never)
override func foo(_ b: BB) -> (BB, BB) {
return (b, b)
}
}
class EEE : CCC {
@inline(never)
override func foo(_ b: BB) -> (AA, AA) {
return (b, b)
}
}
// Check that c.foo() is devirtualized, because the optimizer can handle the casting the return type
// correctly, i.e. it can cast (BBB, BBB) into (AAA, AAA)
// CHECK-LABEL: sil hidden [noinline] @$s23devirt_covariant_return37testDevirtOfMethodReturningTupleTypes_1bAA2AAC_AEtAA3CCCC_AA2BBCtF
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $CCC
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $DDD
// CHECK: checked_cast_br [exact] %{{.*}} : $CCC to $EEE
// CHECK: class_method
// CHECK: }
@inline(never)
func testDevirtOfMethodReturningTupleTypes(_ c: CCC, b: BB) -> (AA, AA) {
return c.foo(b)
}
class AAAA {
}
class BBBB : AAAA {
}
class CCCC {
let a: BBBB
var foo : (AAAA, AAAA) {
@inline(never)
get {
return (a, a)
}
}
init(x: BBBB) { a = x }
}
class DDDD : CCCC {
override var foo : (BBBB, BBBB) {
@inline(never)
get {
return (a, a)
}
}
}
// Check devirtualization of methods with optional results, where
// optional results need to be casted.
// CHECK-LABEL: sil [noinline] @{{.*}}testOverridingMethodWithOptionalResult
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $F
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $G
// CHECK: switch_enum
// CHECK: checked_cast_br [exact] %{{.*}} : $F to $H
// CHECK: switch_enum
@inline(never)
public func testOverridingMethodWithOptionalResult(_ f: F) -> (F?, Int)? {
return f.foo()
}
public class F {
@inline(never)
public func foo() -> (F?, Int)? {
return (F(), 1)
}
}
public class G: F {
@inline(never)
override public func foo() -> (G?, Int)? {
return (G(), 2)
}
}
public class H: F {
@inline(never)
override public func foo() -> (H?, Int)? {
return nil
}
}