1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnose-lifetime-issues -o /dev/null -verify
import Builtin
import Swift
enum FakeOptional<T> {
case none
case some(T)
}
class Delegate {
func foo() { }
}
class MyDelegate : Delegate {}
final class Container {
weak var delegate: Delegate?
var strongRef: Delegate
func callDelegate()
func strongDelegate(d: Delegate)
}
class WeakCycle {
weak var c: WeakCycle?
}
sil [ossa] @$s24diagnose_lifetime_issues8DelegateCACycfC : $@convention(method) (@thick Delegate.Type) -> @owned Delegate
sil [ossa] @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate
sil [ossa] @$s24diagnose_lifetime_issues14strongDelegate1dyAA0D0C_tF : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> ()
// Test a warning for a reference that is copied, cast, and assigned
// to an enum before it is assigned to a weak reference.
sil [ossa] @testCastWarn : $@convention(thin) (@guaranteed Container) -> () {
bb0(%0 : @guaranteed $Container):
debug_value %0 : $Container, let, name "container", argno 1
%2 = metatype $@thick MyDelegate.Type
// function_ref MyDelegate.__allocating_init()
%3 = function_ref @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate
// This is the owned allocation.
%4 = apply %3(%2) : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate
%11 = copy_value %4 : $MyDelegate
// This upcast+enum is not an escape.
%12 = upcast %11 : $MyDelegate to $Delegate
%13 = enum $Optional<Delegate>, #Optional.some!enumelt, %12 : $Delegate
%14 = ref_element_addr %0 : $Container, #Container.delegate
%15 = begin_access [modify] [dynamic] %14 : $*@sil_weak Optional<Delegate>
// This is the weak assignment.
store_weak %13 to %15 : $*@sil_weak Optional<Delegate> // expected-warning {{weak reference will always be nil because the referenced object is deallocated here}}
destroy_value %13 : $Optional<Delegate>
end_access %15 : $*@sil_weak Optional<Delegate>
destroy_value %4 : $MyDelegate
%20 = tuple ()
return %20 : $()
}
// Test that a reference that escapes within a borrow scope has no warning.
sil hidden [ossa] @testBorrowNoWarn : $@convention(thin) (@guaranteed Container) -> () {
// %0 "container"
bb0(%0 : @guaranteed $Container):
debug_value %0 : $Container, let, name "container", argno 1
%2 = metatype $@thick MyDelegate.Type
// function_ref MyDelegate.__allocating_init()
%3 = function_ref @$s24diagnose_lifetime_issues10MyDelegateCACycfC : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate
// This is the owned allocation.
%4 = apply %3(%2) : $@convention(method) (@thick MyDelegate.Type) -> @owned MyDelegate
debug_value %4 : $MyDelegate, let, name "delegate"
%6 = begin_borrow %4 : $MyDelegate
%7 = upcast %6 : $MyDelegate to $Delegate
// function_ref Container.strongDelegate(d:)
%8 = function_ref @$s24diagnose_lifetime_issues14strongDelegate1dyAA0D0C_tF : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> ()
// This apply is an escape.
%9 = apply %8(%7, %0) : $@convention(method) (@guaranteed Delegate, @guaranteed Container) -> ()
end_borrow %6 : $MyDelegate
%11 = copy_value %4 : $MyDelegate
%12 = upcast %11 : $MyDelegate to $Delegate
%13 = enum $Optional<Delegate>, #Optional.some!enumelt, %12 : $Delegate
%14 = ref_element_addr %0 : $Container, #Container.delegate
%15 = begin_access [modify] [dynamic] %14 : $*@sil_weak Optional<Delegate>
// This is the weak assignment.
store_weak %13 to %15 : $*@sil_weak Optional<Delegate>
destroy_value %13 : $Optional<Delegate>
end_access %15 : $*@sil_weak Optional<Delegate>
destroy_value %4 : $MyDelegate
%20 = tuple ()
return %20 : $()
}
// Helper for testReturnsAfterStore
sil hidden [ossa] @testStoresWeakly : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle {
bb0(%0 : @owned $WeakCycle):
%18 = begin_borrow %0 : $WeakCycle
%19 = copy_value %0 : $WeakCycle
%20 = enum $Optional<WeakCycle>, #Optional.some!enumelt, %19 : $WeakCycle
%21 = ref_element_addr %18 : $WeakCycle, #WeakCycle.c
%22 = begin_access [modify] [dynamic] %21 : $*@sil_weak Optional<WeakCycle>
store_weak %20 to %22 : $*@sil_weak Optional<WeakCycle>
destroy_value %20 : $Optional<WeakCycle>
end_access %22 : $*@sil_weak Optional<WeakCycle>
end_borrow %18 : $WeakCycle
%27 = copy_value %0 : $WeakCycle
destroy_value %0 : $WeakCycle
return %27 : $WeakCycle
}
// Test no warning for a value returned after a weak store.
sil hidden [exact_self_class] [ossa] @testReturnsAfterStore : $@convention(method) (@thick WeakCycle.Type) -> @owned WeakCycle {
bb0(%0 : $@thick WeakCycle.Type):
%1 = alloc_ref $WeakCycle
%2 = function_ref @testStoresWeakly : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle
%3 = apply %2(%1) : $@convention(method) (@owned WeakCycle) -> @owned WeakCycle
return %3 : $WeakCycle
}
// Helper
sil private [ossa] @testBorrowInDefer$defer : $@convention(thin) (@guaranteed Delegate) -> () {
bb0(%0 : @closureCapture @guaranteed $Delegate):
debug_value %0 : $Delegate, let, name "delegate", argno 1
fix_lifetime %0 : $Delegate
%8 = tuple ()
return %8 : $()
}
// Test no warning for a value kept alive within a call which does not escape its argument.
sil hidden [ossa] @testBorrowinDefer : $@convention(thin) (@guaranteed Container) -> () {
bb0(%0 : @guaranteed $Container):
debug_value %0 : $Container, let, name "container", argno 1
%2 = metatype $@thick Delegate.Type
// function_ref Delegate.__allocating_init()
%3 = function_ref @$s24diagnose_lifetime_issues8DelegateCACycfC : $@convention(method) (@thick Delegate.Type) -> @owned Delegate
// This is the owned allocation.
%4 = apply %3(%2) : $@convention(method) (@thick Delegate.Type) -> @owned Delegate
%6 = copy_value %4 : $Delegate
%7 = enum $Optional<Delegate>, #Optional.some!enumelt, %6 : $Delegate
%8 = ref_element_addr %0 : $Container, #Container.delegate
%9 = begin_access [modify] [dynamic] %8 : $*@sil_weak Optional<Delegate>
// This is the weak assignment.
store_weak %7 to %9 : $*@sil_weak Optional<Delegate>
destroy_value %7 : $Optional<Delegate>
end_access %9 : $*@sil_weak Optional<Delegate>
// This call keeps the parent alive
%15 = function_ref @testBorrowInDefer$defer : $@convention(thin) (@guaranteed Delegate) -> () // user: %16
%16 = apply %15(%4) : $@convention(thin) (@guaranteed Delegate) -> ()
destroy_value %4 : $Delegate
%18 = tuple ()
return %18 : $()
}
|