Files
swift-mirror/test/SILOptimizer/definite_init_root_class.swift
Slava Pestov 233eeea6cb DI: Correctly handle conditional destroy of 'self' in root class
In a designated initializer of a non-root class, 'self' becomes
fully initialized after the 'super.init' call, at which point
escaping uses of 'self' become valid, and releases of 'self' are
lowered to a 'strong_release' instruction, which runs the
deinitializer.

In a root class, 'self' becomes fully initialized after all stored
properties have been initialized, at which point escaping uses of
'self' become valid.

However, DI would still lower a conditional destroy of 'self' by
destroying any initialized stored properties and freeing the object
with 'dealloc_partial_ref'. This is incorrect, because 'self' may
have escaped.

In the non-conditional destroy case, we correctly lowered the
release to a 'strong_release' if all stored properties are known
to be initialized.

Fix DI to handle the conditional destroy case by first checking if all
bits in the control variable are set, and releasing the instance with
'strong_release' if so. The 'dealloc_partial_ref' is only emitted
if not at least one stored property was not initialized.

This ensures that we don't deallocate an instance that may have
escaped.

Fixes <https://bugs.swift.org/browse/SR-13439>, <rdar://problem/67746791>,
<https://bugs.swift.org/browse/SR-13355>, <rdar://problem/67361228>.
2020-08-31 22:14:36 -04:00

215 lines
9.1 KiB
Swift

// RUN: %target-swift-frontend -emit-sil %s | %FileCheck %s
class OtherClass {}
class FirstClass {
var x: OtherClass
// CHECK-LABEL: sil hidden @$s24definite_init_root_class10FirstClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned FirstClass) -> @owned Optional<FirstClass>
init?(n: Int32) {
// CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int1
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int1, 0
// CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int1
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ZERO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb1, bb2
if n == 0 {
return nil
}
// CHECK: bb1:
// CHECK: br bb5
// CHECK: bb2:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[X_ADDR:%.*]] = ref_element_addr %1 : $FirstClass, #FirstClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [modify] [dynamic] %15 : $*OtherClass
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int1
// CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
x = OtherClass()
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 1
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ONE]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb3, bb4
if n == 1 {
return nil
}
// CHECK: bb3:
// CHECK: br bb5
// CHECK: bb4:
// CHECK: [[RESULT:%.*]] = enum $Optional<FirstClass>, #Optional.some!enumelt, %1 : $FirstClass
// CHECK: br bb12([[RESULT]] : $Optional<FirstClass>)
// CHECK: bb5:
// CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1
// CHECK: cond_br [[BIT]], bb6, bb7
// CHECK: bb6:
// CHECK: strong_release %1 : $FirstClass
// CHECK: br bb11
// CHECK: bb7:
// CHECK: [[BIT:%.*]] = load [[CONTROL]] : $*Builtin.Int1
// CHECK: cond_br [[BIT]], bb8, bb9
// CHECK: bb8:
// CHECK: [[X_ADDR:%.*]] = ref_element_addr %1 : $FirstClass, #FirstClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
// CHECK: br bb10
// CHECK: bb9:
// CHECK: br bb10
// CHECK: [[METATYPE:%.*]] = metatype $@thick FirstClass.Type
// CHECK: dealloc_partial_ref %1 : $FirstClass, [[METATYPE]] : $@thick FirstClass.Type
// CHECK: br bb11
// CHECK: bb11:
// CHECK: [[NIL:%.*]] = enum $Optional<FirstClass>, #Optional.none!enumelt
// CHECK: br bb12([[NIL]] : $Optional<FirstClass>)
// CHECK: bb12([[RESULT:%.*]] : $Optional<FirstClass>):
// CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int1
// CHECK: return [[RESULT]] : $Optional<FirstClass>
}
}
class SecondClass {
var x: OtherClass
var y: OtherClass
// CHECK-LABEL: sil hidden @$s24definite_init_root_class11SecondClassC1nACSgs5Int32V_tcfc : $@convention(method) (Int32, @owned SecondClass) -> @owned Optional<SecondClass> {
init?(n: Int32) {
// CHECK: [[CONTROL:%.*]] = alloc_stack $Builtin.Int2
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int2, 0
// CHECK: store [[ZERO]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: [[ZERO:%.*]] = integer_literal $Builtin.Int32, 0
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ZERO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb1, bb2
if n == 0 {
return nil
}
// CHECK: bb1:
// CHECK: br bb7
// CHECK: bb2:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[X_ADDR:%.*]] = ref_element_addr %1 : $SecondClass, #SecondClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [modify] [dynamic] [[X_ADDR]] : $*OtherClass
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1
// CHECK: store [[ONE]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: store [[OTHER]] to [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
x = OtherClass()
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int32, 1
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[ONE]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb3, bb4
if n == 1 {
return nil
}
// CHECK: bb3:
// CHECK: br bb7
// CHECK: bb4:
// CHECK: [[METATYPE:%.*]] = metatype $@thick OtherClass.Type
// CHECK: [[INIT:%.*]] = function_ref @$s24definite_init_root_class10OtherClassCACycfC : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[OTHER:%.*]] = apply [[INIT]]([[METATYPE]]) : $@convention(method) (@thick OtherClass.Type) -> @owned OtherClass
// CHECK: [[Y_ADDR:%.*]] = ref_element_addr %1 : $SecondClass, #SecondClass.y
// CHECK: [[Y_ACCESS:%.*]] = begin_access [modify] [dynamic] [[Y_ADDR]] : $*OtherClass
// CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1
// CHECK: store [[THREE]] to [[CONTROL]] : $*Builtin.Int2
// CHECK: store [[OTHER]] to [[Y_ACCESS]] : $*OtherClass
// CHECK: end_access [[Y_ACCESS]] : $*OtherClass
y = OtherClass()
// CHECK: [[TWO:%.*]] = integer_literal $Builtin.Int32, 2
// CHECK: [[N:%.*]] = struct_extract %0 : $Int32, #Int32._value
// CHECK: [[CMP:%.*]] = builtin "cmp_eq_Int32"([[N]] : $Builtin.Int32, [[TWO]] : $Builtin.Int32) : $Builtin.Int1
// CHECK: cond_br [[CMP]], bb5, bb6
if n == 2 {
return nil
}
// CHECK: bb5:
// CHECK: br bb7
// CHECK: bb6:
// CHECK: [[RESULT:%.*]] = enum $Optional<SecondClass>, #Optional.some!enumelt, %1 : $SecondClass
// CHECK: br bb17([[RESULT]] : $Optional<SecondClass>)
// CHECK: bb7:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[THREE:%.*]] = integer_literal $Builtin.Int2, -1
// CHECK: [[BIT:%.*]] = builtin "cmp_eq_Int2"([[BITS]] : $Builtin.Int2, [[THREE]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb8, bb9
// CHECK: bb8:
// CHECK: strong_release %1 : $SecondClass
// CHECK: br bb16
// CHECK: bb9:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[BITS]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb10, bb11
// CHECK: bb10:
// CHECK: [[X_ADDR:%.*]] = ref_element_addr %1 : $SecondClass, #SecondClass.x
// CHECK: [[X_ACCESS:%.*]] = begin_access [deinit] [static] [[X_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[X_ACCESS]] : $*OtherClass
// CHECK: end_access [[X_ACCESS]] : $*OtherClass
// CHECK: br bb12
// CHECK: bb11:
// CHECK: br bb12
// CHECK: bb12:
// CHECK: [[BITS:%.*]] = load [[CONTROL]] : $*Builtin.Int2
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int2, 1
// CHECK: [[TMP:%.*]] = builtin "lshr_Int2"([[BITS]] : $Builtin.Int2, [[ONE]] : $Builtin.Int2) : $Builtin.Int2
// CHECK: [[BIT:%.*]] = builtin "trunc_Int2_Int1"([[TMP]] : $Builtin.Int2) : $Builtin.Int1
// CHECK: cond_br [[BIT]], bb13, bb14
// CHECK: bb13:
// CHECK: [[Y_ADDR:%.*]] = ref_element_addr %1 : $SecondClass, #SecondClass.y
// CHECK: [[Y_ACCESS:%.*]] = begin_access [deinit] [static] [[Y_ADDR]] : $*OtherClass
// CHECK: destroy_addr [[Y_ACCESS]] : $*OtherClass
// CHECK: end_access [[Y_ACCESS]] : $*OtherClass
// CHECK: br bb15
// CHECK: bb14:
// CHECK: br bb15
// CHECK: bb15:
// CHECK: [[METATYPE:%.*]] = metatype $@thick SecondClass.Type
// CHECK: dealloc_partial_ref %1 : $SecondClass, [[METATYPE]] : $@thick SecondClass.Type
// CHECK: br bb16
// CHECK: bb16:
// CHECK: [[NIL:%.*]] = enum $Optional<SecondClass>, #Optional.none!enumelt
// CHECK: br bb17([[NIL]] : $Optional<SecondClass>)
// CHECK: bb17([[RESULT:%.*]] : $Optional<SecondClass>):
// CHECK: dealloc_stack [[CONTROL]] : $*Builtin.Int2
// CHECK: return [[RESULT]] : $Optional<SecondClass>
}
}