// RUN: %target-sil-opt -enable-objc-interop -module-name devirt_speculative -enable-sil-verify-all %s -specdevirt | %FileCheck %s sil_stage canonical import Builtin import Swift @objc class MyNSObject {} private class Base : MyNSObject { override init() @inline(never) func foo() @inline(never) func exit() -> Never } private class Sub : Base { override init() @inline(never) override func foo() @inline(never) override func exit() -> Never } sil private [noinline] @_TBaseFooFun : $@convention(method) (@guaranteed Base) -> () { bb0(%0 : $Base): %1 = tuple() return %1 : $() } sil private [noinline] @_TSubFooFun : $@convention(method) (@guaranteed Sub) -> () { bb0(%0 : $Sub): %1 = tuple() return %1 : $() } sil @Base_exit : $@convention(method) (@guaranteed Base) -> Never sil @Sub_exit : $@convention(method) (@guaranteed Sub) -> Never sil_vtable Base { #Base.foo: @_TBaseFooFun #Base.exit: @Base_exit } sil_vtable Sub { #Base.foo: @_TSubFooFun #Base.exit: @Sub_exit } sil @test_objc_ancestry : $@convention(thin) (@guaranteed Base) -> () { bb0(%0: $Base): %1 = class_method %0 : $Base, #Base.foo : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () %2 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () %3 = tuple() return %3 : $() } // Make sure we leave the generic method call because an objc derived class can be extended at runtime. // CHECK-LABEL: sil @test_objc_ancestry // CHECK: bb0 // CHECK: [[METH:%.*]] = class_method %0 : $Base, #Base.foo : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () // CHECK: checked_cast_br [exact] %0 : $Base to Base, bb{{.*}}, bb[[CHECK2:[0-9]+]] // CHECK: bb[[CHECK2]]{{.*}}: // CHECK: checked_cast_br [exact] %0 : $Base to Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]] // CHECK: bb[[GENCALL]]{{.*}}: // CHECK: apply [[METH]] struct MyValue {} private class Generic : MyNSObject { override init() } private class Base2 : Generic { override init() @inline(never) func foo() } private class Sub2 : Base2 { override init() @inline(never) override func foo() } sil private [noinline] @_TBase2FooFun : $@convention(method) (@guaranteed Base2) -> () { bb0(%0 : $Base2): %1 = tuple() return %1 : $() } sil private [noinline] @_TSub2FooFun : $@convention(method) (@guaranteed Sub2) -> () { bb0(%0 : $Sub2): %1 = tuple() return %1 : $() } sil_vtable Base2 { #Base2.foo: @_TBase2FooFun } sil_vtable Sub2 { #Base2.foo: @_TSub2FooFun } sil @test_objc_ancestry2 : $@convention(thin) (@guaranteed Base2) -> () { bb0(%0: $Base2): %1 = class_method %0 : $Base2, #Base2.foo : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () %2 = apply %1(%0) : $@convention(method) (@guaranteed Base2) -> () %3 = tuple() return %3 : $() } // CHECK-LABEL: sil @test_objc_ancestry2 // CHECK: bb0 // CHECK: [[METH:%.*]] = class_method %0 : $Base2, #Base2.foo : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () // CHECK: checked_cast_br [exact] %0 : $Base2 to Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]] // CHECK: bb[[CHECK2]]{{.*}}: // CHECK: checked_cast_br [exact] %0 : $Base2 to Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]] // CHECK: bb[[GENCALL]]{{.*}}: // CHECK: apply [[METH]] // Don't devirtualize 'unsafeGuaranteed' self calls. // CHECK-LABEL: sil @test_unsafeGuaranteed // CHECK-NOT: check_cast_br // CHECK: return sil @test_unsafeGuaranteed : $@convention(thin) (@guaranteed Base2) -> () { bb0(%0: $Base2): strong_retain %0 : $Base2 %4 = builtin "unsafeGuaranteed"(%0 : $Base2) : $(Base2, Builtin.Int8) %5 = tuple_extract %4 : $(Base2, Builtin.Int8), 0 %6 = tuple_extract %4 : $(Base2, Builtin.Int8), 1 %1 = class_method %0 : $Base2, #Base2.foo : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () %2 = apply %1(%5) : $@convention(method) (@guaranteed Base2) -> () strong_release %5 : $Base2 %16 = builtin "unsafeGuaranteedEnd"(%6 : $Builtin.Int8) : $() %3 = tuple() return %3 : $() } // Check that NoReturn functions are devirtualized properly. // The new apply should be followed by an unreachable instruction // instead of a branch instruction. // CHECK-LABEL: sil{{.*}}test_devirt_of_noreturn_function // CHECK: checked_cast_br // CHECK: function_ref @Base_exit // CHECK-NEXT: apply // CHECK-NEXT: unreachable // CHECK: checked_cast_br // CHECK: function_ref @Sub_exit // CHECK-NEXT: apply // CHECK-NEXT: unreachable sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { bb0(%0 : $Base): %2 = class_method %0 : $Base, #Base.exit : (Base) -> () -> Never, $@convention(method) (@guaranteed Base) -> Never %3 = apply %2(%0) : $@convention(method) (@guaranteed Base) -> Never unreachable } class Throw { func mayThrow() throws } class S1 : Throw { override func mayThrow() } class S2 : Throw { override func mayThrow() throws } sil hidden [thunk] @$S4main4ThrowC8mayThrowyyKF : $@convention(method) (@guaranteed Throw) -> @error Error { bb0(%0 : $Throw): %1 = tuple () return %1 : $() } sil hidden @$S4main2S1C8mayThrowyyF : $@convention(method) (@guaranteed S1) -> () { bb0(%0 : $S1): %1 = tuple () return %1 : $() } sil hidden [thunk] [always_inline] @$S4main2S2C8mayThrowyyKF : $@convention(method) (@guaranteed S2) -> @error Error { bb0(%0 : $S2): %1 = tuple () return %1 : $() } // CHECK-LABEL: sil{{.*}} @test_devirt_of_throw_without_error // CHECK-NOT: checked_cast_br [exact] %0 : $Throw to S1 // CHECK: return sil hidden [noinline] @test_devirt_of_throw_without_error : $@convention(thin) (@owned Throw) -> () { bb0(%0 : $Throw): %80 = class_method %0 : $Throw, #Throw.mayThrow : (Throw) -> () throws -> (), $@convention(method) (@guaranteed Throw) -> @error Error // user: %81 try_apply %80(%0) : $@convention(method) (@guaranteed Throw) -> @error Error, normal bb7, error bb6 bb6(%82 : $Error): br bb1 bb7(%84 : $()): br bb1 bb1: %3 = tuple () return %3 : $() } sil_vtable Throw { #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main4ThrowC8mayThrowyyKF } sil_vtable S1 { #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main2S1C8mayThrowyyF [override] } sil_vtable S2 { #Throw.mayThrow: (Throw) -> () throws -> () : @$S4main2S2C8mayThrowyyKF [override] }