| // RUN: %target-swift-frontend -emit-silgen -disable-objc-attr-requires-foundation-module %s -module-name failable_initializers | %FileCheck %s |
| |
| // High-level tests that silgen properly emits code for failable and thorwing |
| // initializers. |
| |
| //// |
| // Structs with failable initializers |
| //// |
| |
| protocol Pachyderm { |
| init() |
| } |
| |
| class Canary : Pachyderm { |
| required init() {} |
| } |
| |
| // <rdar://problem/20941576> SILGen crash: Failable struct init cannot delegate to another failable initializer |
| struct TrivialFailableStruct { |
| init?(blah: ()) { } |
| init?(wibble: ()) { |
| self.init(blah: wibble) |
| } |
| } |
| |
| struct FailableStruct { |
| let x, y: Canary |
| |
| init(noFail: ()) { |
| x = Canary() |
| y = Canary() |
| } |
| |
| init?(failBeforeInitialization: ()) { |
| return nil |
| } |
| |
| init?(failAfterPartialInitialization: ()) { |
| x = Canary() |
| return nil |
| } |
| |
| init?(failAfterFullInitialization: ()) { |
| x = Canary() |
| y = Canary() |
| return nil |
| } |
| |
| init?(failAfterWholeObjectInitializationByAssignment: ()) { |
| self = FailableStruct(noFail: ()) |
| return nil |
| } |
| |
| init?(failAfterWholeObjectInitializationByDelegation: ()) { |
| self.init(noFail: ()) |
| return nil |
| } |
| |
| // Optional to optional |
| init?(failDuringDelegation: ()) { |
| self.init(failBeforeInitialization: ()) |
| } |
| |
| // IUO to optional |
| init!(failDuringDelegation2: ()) { |
| self.init(failBeforeInitialization: ())! // unnecessary-but-correct '!' |
| } |
| |
| // IUO to IUO |
| init!(failDuringDelegation3: ()) { |
| self.init(failDuringDelegation2: ())! // unnecessary-but-correct '!' |
| } |
| |
| // non-optional to optional |
| init(failDuringDelegation4: ()) { |
| self.init(failBeforeInitialization: ())! // necessary '!' |
| } |
| |
| // non-optional to IUO |
| init(failDuringDelegation5: ()) { |
| self.init(failDuringDelegation2: ())! // unnecessary-but-correct '!' |
| } |
| } |
| |
| extension FailableStruct { |
| init?(failInExtension: ()) { |
| self.init(failInExtension: failInExtension) |
| } |
| |
| init?(assignInExtension: ()) { |
| self = FailableStruct(noFail: ()) |
| } |
| } |
| |
| struct FailableAddrOnlyStruct<T : Pachyderm> { |
| var x, y: T |
| |
| init(noFail: ()) { |
| x = T() |
| y = T() |
| } |
| |
| init?(failBeforeInitialization: ()) { |
| return nil |
| } |
| |
| init?(failAfterPartialInitialization: ()) { |
| x = T() |
| return nil |
| } |
| |
| init?(failAfterFullInitialization: ()) { |
| x = T() |
| y = T() |
| return nil |
| } |
| |
| init?(failAfterWholeObjectInitializationByAssignment: ()) { |
| self = FailableAddrOnlyStruct(noFail: ()) |
| return nil |
| } |
| |
| init?(failAfterWholeObjectInitializationByDelegation: ()) { |
| self.init(noFail: ()) |
| return nil |
| } |
| |
| // Optional to optional |
| init?(failDuringDelegation: ()) { |
| self.init(failBeforeInitialization: ()) |
| } |
| |
| // IUO to optional |
| init!(failDuringDelegation2: ()) { |
| self.init(failBeforeInitialization: ())! // unnecessary-but-correct '!' |
| } |
| |
| // non-optional to optional |
| init(failDuringDelegation3: ()) { |
| self.init(failBeforeInitialization: ())! // necessary '!' |
| } |
| |
| // non-optional to IUO |
| init(failDuringDelegation4: ()) { |
| self.init(failDuringDelegation2: ())! // unnecessary-but-correct '!' |
| } |
| } |
| |
| extension FailableAddrOnlyStruct { |
| init?(failInExtension: ()) { |
| self.init(failBeforeInitialization: failInExtension) |
| } |
| |
| init?(assignInExtension: ()) { |
| self = FailableAddrOnlyStruct(noFail: ()) |
| } |
| } |
| |
| //// |
| // Structs with throwing initializers |
| //// |
| |
| func unwrap(_ x: Int) throws -> Int { return x } |
| |
| struct ThrowStruct { |
| var x: Canary |
| |
| init(fail: ()) throws { x = Canary() } |
| |
| init(noFail: ()) { x = Canary() } |
| |
| init(failBeforeDelegation: Int) throws { |
| try unwrap(failBeforeDelegation) |
| self.init(noFail: ()) |
| } |
| |
| init(failBeforeOrDuringDelegation: Int) throws { |
| try unwrap(failBeforeOrDuringDelegation) |
| try self.init(fail: ()) |
| } |
| |
| init(failBeforeOrDuringDelegation2: Int) throws { |
| try self.init(failBeforeDelegation: unwrap(failBeforeOrDuringDelegation2)) |
| } |
| |
| init(failDuringDelegation: Int) throws { |
| try self.init(fail: ()) |
| } |
| |
| init(failAfterDelegation: Int) throws { |
| self.init(noFail: ()) |
| try unwrap(failAfterDelegation) |
| } |
| |
| init(failDuringOrAfterDelegation: Int) throws { |
| try self.init(fail: ()) |
| try unwrap(failDuringOrAfterDelegation) |
| } |
| |
| init(failBeforeOrAfterDelegation: Int) throws { |
| try unwrap(failBeforeOrAfterDelegation) |
| self.init(noFail: ()) |
| try unwrap(failBeforeOrAfterDelegation) |
| } |
| |
| init?(throwsToOptional: Int) { |
| try? self.init(failDuringDelegation: throwsToOptional) |
| } |
| |
| init(throwsToIUO: Int) { |
| try! self.init(failDuringDelegation: throwsToIUO) |
| } |
| |
| init?(throwsToOptionalThrows: Int) throws { |
| try? self.init(fail: ()) |
| } |
| |
| init(throwsOptionalToThrows: Int) throws { |
| self.init(throwsToOptional: throwsOptionalToThrows)! |
| } |
| |
| init?(throwsOptionalToOptional: Int) { |
| try! self.init(throwsToOptionalThrows: throwsOptionalToOptional) |
| } |
| |
| init(failBeforeSelfReplacement: Int) throws { |
| try unwrap(failBeforeSelfReplacement) |
| self = ThrowStruct(noFail: ()) |
| } |
| |
| init(failDuringSelfReplacement: Int) throws { |
| try self = ThrowStruct(fail: ()) |
| } |
| |
| init(failAfterSelfReplacement: Int) throws { |
| self = ThrowStruct(noFail: ()) |
| try unwrap(failAfterSelfReplacement) |
| } |
| } |
| |
| extension ThrowStruct { |
| init(failInExtension: ()) throws { |
| try self.init(fail: failInExtension) |
| } |
| |
| init(assignInExtension: ()) throws { |
| try self = ThrowStruct(fail: ()) |
| } |
| } |
| |
| struct ThrowAddrOnlyStruct<T : Pachyderm> { |
| var x : T |
| |
| init(fail: ()) throws { x = T() } |
| |
| init(noFail: ()) { x = T() } |
| |
| init(failBeforeDelegation: Int) throws { |
| try unwrap(failBeforeDelegation) |
| self.init(noFail: ()) |
| } |
| |
| init(failBeforeOrDuringDelegation: Int) throws { |
| try unwrap(failBeforeOrDuringDelegation) |
| try self.init(fail: ()) |
| } |
| |
| init(failBeforeOrDuringDelegation2: Int) throws { |
| try self.init(failBeforeDelegation: unwrap(failBeforeOrDuringDelegation2)) |
| } |
| |
| init(failDuringDelegation: Int) throws { |
| try self.init(fail: ()) |
| } |
| |
| init(failAfterDelegation: Int) throws { |
| self.init(noFail: ()) |
| try unwrap(failAfterDelegation) |
| } |
| |
| init(failDuringOrAfterDelegation: Int) throws { |
| try self.init(fail: ()) |
| try unwrap(failDuringOrAfterDelegation) |
| } |
| |
| init(failBeforeOrAfterDelegation: Int) throws { |
| try unwrap(failBeforeOrAfterDelegation) |
| self.init(noFail: ()) |
| try unwrap(failBeforeOrAfterDelegation) |
| } |
| |
| init?(throwsToOptional: Int) { |
| try? self.init(failDuringDelegation: throwsToOptional) |
| } |
| |
| init(throwsToIUO: Int) { |
| try! self.init(failDuringDelegation: throwsToIUO) |
| } |
| |
| init?(throwsToOptionalThrows: Int) throws { |
| try? self.init(fail: ()) |
| } |
| |
| init(throwsOptionalToThrows: Int) throws { |
| self.init(throwsToOptional: throwsOptionalToThrows)! |
| } |
| |
| init?(throwsOptionalToOptional: Int) { |
| try! self.init(throwsOptionalToThrows: throwsOptionalToOptional) |
| } |
| |
| init(failBeforeSelfReplacement: Int) throws { |
| try unwrap(failBeforeSelfReplacement) |
| self = ThrowAddrOnlyStruct(noFail: ()) |
| } |
| |
| init(failAfterSelfReplacement: Int) throws { |
| self = ThrowAddrOnlyStruct(noFail: ()) |
| try unwrap(failAfterSelfReplacement) |
| } |
| } |
| |
| extension ThrowAddrOnlyStruct { |
| init(failInExtension: ()) throws { |
| try self.init(fail: failInExtension) |
| } |
| |
| init(assignInExtension: ()) throws { |
| self = ThrowAddrOnlyStruct(noFail: ()) |
| } |
| } |
| |
| //// |
| // Classes |
| //// |
| |
| //// |
| // Classes with failable initializers |
| //// |
| |
| class FailableBaseClass { |
| var member: Canary |
| |
| init(noFail: ()) { |
| member = Canary() |
| } |
| |
| init?(failBeforeFullInitialization: ()) { |
| return nil |
| } |
| |
| init?(failAfterFullInitialization: ()) { |
| member = Canary() |
| return nil |
| } |
| |
| convenience init?(failBeforeDelegation: ()) { |
| return nil |
| } |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17FailableBaseClassCACSgyt19failAfterDelegation_tcfc : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> { |
| // CHECK: bb0([[OLD_SELF:%.*]] : $FailableBaseClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var FailableBaseClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[TAKE_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK: [[NEW_SELF:%.*]] = apply {{.*}}([[TAKE_SELF]]) : $@convention(method) (@owned FailableBaseClass) -> @owned FailableBaseClass |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK: [[RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.none!enumelt |
| // CHECK: br bb2([[RESULT]] : $Optional<FailableBaseClass>) |
| // CHECK: bb2([[RESULT:%.*]] : $Optional<FailableBaseClass>): |
| // CHECK: return [[RESULT]] |
| // CHECK: } // end sil function '_T021failable_initializers17FailableBaseClassCACSgyt19failAfterDelegation_tcfc |
| convenience init?(failAfterDelegation: ()) { |
| self.init(noFail: ()) |
| return nil |
| } |
| |
| // Optional to optional |
| // |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17FailableBaseClassCACSgyt20failDuringDelegation_tcfc : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> { |
| // CHECK: bb0([[OLD_SELF:%.*]] : $FailableBaseClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var FailableBaseClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[TAKE_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK: [[NEW_SELF:%.*]] = apply {{.*}}([[TAKE_SELF]]) : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> |
| // CHECK: cond_br {{.*}}, [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] |
| // |
| // CHECK: [[FAIL_BB:bb[0-9]+]]: |
| // CHECK: destroy_value [[NEW_SELF]] |
| // CHECK: br [[FAIL_EPILOG_BB:bb[0-9]+]] |
| // |
| // CHECK: [[SUCC_BB]]: |
| // CHECK: [[UNWRAPPED_NEW_SELF:%.*]] = unchecked_enum_data [[NEW_SELF]] : $Optional<FailableBaseClass>, #Optional.some!enumelt.1 |
| // CHECK: store [[UNWRAPPED_NEW_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[RESULT:%.*]] = load [copy] [[PB_BOX]] |
| // CHECK: [[WRAPPED_RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.some!enumelt.1, [[RESULT]] |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK: br [[EPILOG_BB:bb[0-9]+]]([[WRAPPED_RESULT]] |
| // |
| // CHECK: [[FAIL_EPILOG_BB]]: |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK: [[WRAPPED_RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.none!enumelt |
| // CHECK: br [[EPILOG_BB]]([[WRAPPED_RESULT]] |
| // |
| // CHECK: [[EPILOG_BB]]([[WRAPPED_RESULT:%.*]] : $Optional<FailableBaseClass>): |
| // CHECK: return [[WRAPPED_RESULT]] |
| convenience init?(failDuringDelegation: ()) { |
| self.init(failBeforeFullInitialization: ()) |
| } |
| |
| // IUO to optional |
| // |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17FailableBaseClassCSQyACGyt21failDuringDelegation2_tcfc : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> { |
| // CHECK: bb0([[OLD_SELF:%.*]] : $FailableBaseClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var FailableBaseClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK-NEXT: [[TAKE_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK-NOT: [[OLD_SELF]] |
| // CHECK-NOT: copy_value |
| // CHECK: [[NEW_SELF:%.*]] = apply {{.*}}([[TAKE_SELF]]) : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> |
| // CHECK: switch_enum [[NEW_SELF]] : $Optional<FailableBaseClass>, case #Optional.some!enumelt.1: [[SUCC_BB:bb[0-9]+]], case #Optional.none!enumelt: [[FAIL_BB:bb[0-9]+]] |
| // |
| // CHECK: [[FAIL_BB]]: |
| // CHECK: unreachable |
| // |
| // CHECK: [[SUCC_BB]]([[RESULT:%.*]] : $FailableBaseClass): |
| // CHECK: store [[RESULT]] to [init] [[PB_BOX]] |
| // CHECK: [[RESULT:%.*]] = load [copy] [[PB_BOX]] |
| // CHECK: [[WRAPPED_RESULT:%.*]] = enum $Optional<FailableBaseClass>, #Optional.some!enumelt.1, [[RESULT]] : $FailableBaseClass |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK: br [[EPILOG_BB:bb[0-9]+]]([[WRAPPED_RESULT]] |
| // |
| // CHECK: [[EPILOG_BB]]([[WRAPPED_RESULT:%.*]] : $Optional<FailableBaseClass>): |
| // CHECK: return [[WRAPPED_RESULT]] |
| // CHECK: } // end sil function '_T021failable_initializers17FailableBaseClassCSQyACGyt21failDuringDelegation2_tcfc' |
| convenience init!(failDuringDelegation2: ()) { |
| self.init(failBeforeFullInitialization: ())! // unnecessary-but-correct '!' |
| } |
| |
| // IUO to IUO |
| convenience init!(noFailDuringDelegation: ()) { |
| self.init(failDuringDelegation2: ())! // unnecessary-but-correct '!' |
| } |
| |
| // non-optional to optional |
| convenience init(noFailDuringDelegation2: ()) { |
| self.init(failBeforeFullInitialization: ())! // necessary '!' |
| } |
| } |
| |
| extension FailableBaseClass { |
| convenience init?(failInExtension: ()) throws { |
| self.init(failBeforeFullInitialization: failInExtension) |
| } |
| } |
| |
| // Chaining to failable initializers in a superclass |
| class FailableDerivedClass : FailableBaseClass { |
| var otherMember: Canary |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers20FailableDerivedClassCACSgyt27derivedFailBeforeDelegation_tcfc : $@convention(method) (@owned FailableDerivedClass) -> @owned Optional<FailableDerivedClass> { |
| // CHECK: bb0([[OLD_SELF:%.*]] : $FailableDerivedClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var FailableDerivedClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [derivedself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK-NEXT: br bb1 |
| // |
| // CHECK: bb1: |
| // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK-NEXT: [[RESULT:%.*]] = enum $Optional<FailableDerivedClass>, #Optional.none!enumelt |
| // CHECK-NEXT: br bb2([[RESULT]] |
| // |
| // CHECK: bb2([[RESULT:%.*]] : $Optional<FailableDerivedClass>): |
| // CHECK-NEXT: return [[RESULT]] |
| init?(derivedFailBeforeDelegation: ()) { |
| return nil |
| } |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers20FailableDerivedClassCACSgyt27derivedFailDuringDelegation_tcfc : $@convention(method) (@owned FailableDerivedClass) -> @owned Optional<FailableDerivedClass> { |
| // CHECK: bb0([[OLD_SELF:%.*]] : $FailableDerivedClass): |
| init?(derivedFailDuringDelegation: ()) { |
| // First initialize the lvalue for self. |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var FailableDerivedClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [derivedself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // |
| // Then assign canary to other member using a borrow. |
| // CHECK: [[BORROWED_SELF:%.*]] = load_borrow [[PB_BOX]] |
| // CHECK: [[CANARY_VALUE:%.*]] = apply |
| // CHECK: [[CANARY_GEP:%.*]] = ref_element_addr [[BORROWED_SELF]] |
| // CHECK: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[CANARY_GEP]] : $*Canary |
| // CHECK: assign [[CANARY_VALUE]] to [[WRITE]] |
| self.otherMember = Canary() |
| |
| // Finally, begin the super init sequence. |
| // CHECK: [[LOADED_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK: [[UPCAST_SELF:%.*]] = upcast [[LOADED_SELF]] |
| // CHECK: [[OPT_NEW_SELF:%.*]] = apply {{.*}}([[UPCAST_SELF]]) : $@convention(method) (@owned FailableBaseClass) -> @owned Optional<FailableBaseClass> |
| // CHECK: [[IS_NIL:%.*]] = select_enum [[OPT_NEW_SELF]] |
| // CHECK: cond_br [[IS_NIL]], [[SUCC_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] |
| // |
| // And then return nil making sure that we do not insert any extra values anywhere. |
| // CHECK: [[SUCC_BB]]: |
| // CHECK: [[NEW_SELF:%.*]] = unchecked_enum_data [[OPT_NEW_SELF]] |
| // CHECK: [[DOWNCAST_NEW_SELF:%.*]] = unchecked_ref_cast [[NEW_SELF]] |
| // CHECK: store [[DOWNCAST_NEW_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[RESULT:%.*]] = load [copy] [[PB_BOX]] |
| // CHECK: [[WRAPPED_RESULT:%.*]] = enum $Optional<FailableDerivedClass>, #Optional.some!enumelt.1, [[RESULT]] |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK: br [[EPILOG_BB:bb[0-9]+]]([[WRAPPED_RESULT]] |
| // |
| // CHECK: [[FAIL_BB]]: |
| // CHECK: destroy_value [[MARKED_SELF_BOX]] |
| super.init(failBeforeFullInitialization: ()) |
| } |
| |
| init?(derivedFailAfterDelegation: ()) { |
| self.otherMember = Canary() |
| super.init(noFail: ()) |
| return nil |
| } |
| |
| // non-optional to IUO |
| init(derivedNoFailDuringDelegation: ()) { |
| self.otherMember = Canary() |
| super.init(failAfterFullInitialization: ())! // necessary '!' |
| } |
| |
| // IUO to IUO |
| init!(derivedFailDuringDelegation2: ()) { |
| self.otherMember = Canary() |
| super.init(failAfterFullInitialization: ())! // unnecessary-but-correct '!' |
| } |
| } |
| |
| extension FailableDerivedClass { |
| convenience init?(derivedFailInExtension: ()) throws { |
| self.init(derivedFailDuringDelegation: derivedFailInExtension) |
| } |
| } |
| |
| //// |
| // Classes with throwing initializers |
| //// |
| |
| class ThrowBaseClass { |
| required init() throws {} |
| required init(throwingCanary: Canary) throws {} |
| init(canary: Canary) {} |
| init(noFail: ()) {} |
| init(noFail: Int) {} |
| } |
| |
| class ThrowDerivedClass : ThrowBaseClass { |
| var canary: Canary? |
| |
| required init(throwingCanary: Canary) throws { |
| } |
| |
| required init() throws { |
| try super.init() |
| } |
| |
| override init(noFail: ()) { |
| try! super.init() |
| } |
| |
| init(fail: Int) {} |
| |
| init(failBeforeFullInitialization: Int) throws { |
| try unwrap(failBeforeFullInitialization) |
| super.init(noFail: ()) |
| } |
| |
| init(failBeforeFullInitialization: Int, failDuringFullInitialization: Int) throws { |
| try unwrap(failBeforeFullInitialization) |
| try super.init() |
| } |
| |
| init(failAfterFullInitialization: Int) throws { |
| super.init(noFail: ()) |
| try unwrap(failAfterFullInitialization) |
| } |
| |
| init(failAfterFullInitialization: Int, failDuringFullInitialization: Int) throws { |
| try super.init() |
| try unwrap(failAfterFullInitialization) |
| } |
| |
| init(failBeforeFullInitialization: Int, failAfterFullInitialization: Int) throws { |
| try unwrap(failBeforeFullInitialization) |
| super.init(noFail: ()) |
| try unwrap(failAfterFullInitialization) |
| } |
| |
| init(failBeforeFullInitialization: Int, failDuringFullInitialization: Int, failAfterFullInitialization: Int) throws { |
| try unwrap(failBeforeFullInitialization) |
| try super.init() |
| try unwrap(failAfterFullInitialization) |
| } |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17ThrowDerivedClassCACSi41delegatingFailDuringDelegationArgEmission_tKcfc : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error Error) { |
| // CHECK: bb0( |
| // First initialize. |
| // CHECK: [[REF:%.*]] = alloc_box ${ var ThrowDerivedClass }, let, name "self" |
| // CHECK: [[MARK_UNINIT:%.*]] = mark_uninitialized [derivedself] [[REF]] : ${ var ThrowDerivedClass } |
| // CHECK: [[PROJ:%.*]] = project_box [[MARK_UNINIT]] |
| // CHECK: store {{%.*}} to [init] [[PROJ]] |
| // |
| // Then initialize the canary with nil. We are able to borrow the initialized self to avoid retain/release overhead. |
| // CHECK: [[CANARY_FUNC:%.*]] = function_ref @_T021failable_initializers17ThrowDerivedClassC6canaryAA6CanaryCSgvfi : |
| // CHECK: [[OPT_CANARY:%.*]] = apply [[CANARY_FUNC]]() |
| // CHECK: [[SELF:%.*]] = load_borrow [[PROJ]] |
| // CHECK: [[CANARY_ADDR:%.*]] = ref_element_addr [[SELF]] |
| // CHECK: [[CANARY_ACCESS:%.*]] = begin_access [modify] [dynamic] [[CANARY_ADDR]] |
| // CHECK: assign [[OPT_CANARY]] to [[CANARY_ACCESS]] |
| // CHECK: end_access [[CANARY_ACCESS]] |
| // CHECK: end_borrow [[SELF]] from [[PROJ]] |
| // |
| // Now we begin argument emission where we perform the unwrap. |
| // CHECK: [[SELF:%.*]] = load [take] [[PROJ]] |
| // CHECK: [[BASE_SELF:%.*]] = upcast [[SELF]] : $ThrowDerivedClass to $ThrowBaseClass |
| // CHECK: [[INIT_FN:%.*]] = function_ref @_T021failable_initializers14ThrowBaseClassCACSi6noFail_tcfc : $@convention(method) |
| // CHECK: [[UNWRAP_FN:%.*]] = function_ref @_T021failable_initializers6unwrapS2iKF : $@convention(thin) |
| // CHECK: try_apply [[UNWRAP_FN]]({{%.*}}) : $@convention(thin) (Int) -> (Int, @error Error), normal [[NORMAL_BB:bb[0-9]+]], error [[ERROR_BB:bb[0-9]+]] |
| // |
| // Now we emit the call to the initializer. Notice how we return self back to |
| // its memory locatio nbefore any other work is done. |
| // CHECK: [[NORMAL_BB]]( |
| // CHECK: [[BASE_SELF_INIT:%.*]] = apply [[INIT_FN]]({{%.*}}, [[BASE_SELF]]) |
| // CHECK: [[SELF:%.*]] = unchecked_ref_cast [[BASE_SELF_INIT]] : $ThrowBaseClass to $ThrowDerivedClass |
| // CHECK: store [[SELF]] to [init] [[PROJ]] |
| // |
| // Handle the return value. |
| // CHECK: [[SELF:%.*]] = load [copy] [[PROJ]] |
| // CHECK: destroy_value [[MARK_UNINIT]] |
| // CHECK: return [[SELF]] |
| // |
| // When the error is thrown, we need to: |
| // 1. Store self back into the "conceptually" uninitialized box. |
| // 2. destroy the box. |
| // 3. Perform the rethrow. |
| // CHECK: [[ERROR_BB]]([[ERROR:%.*]] : $Error): |
| // CHECK: [[SELF:%.*]] = unchecked_ref_cast [[BASE_SELF]] : $ThrowBaseClass to $ThrowDerivedClass |
| // CHECK: store [[SELF]] to [init] [[PROJ]] |
| // CHECK: destroy_value [[MARK_UNINIT]] |
| // CHECK: throw [[ERROR]] |
| // CHECK: } // end sil function '_T021failable_initializers17ThrowDerivedClassCACSi41delegatingFailDuringDelegationArgEmission_tKcfc' |
| init(delegatingFailDuringDelegationArgEmission : Int) throws { |
| super.init(noFail: try unwrap(delegatingFailDuringDelegationArgEmission)) |
| } |
| |
| convenience init(noFail2: ()) { |
| try! self.init() |
| } |
| |
| convenience init(failBeforeDelegation: Int) throws { |
| try unwrap(failBeforeDelegation) |
| self.init(noFail: ()) |
| } |
| |
| convenience init(failDuringDelegation: Int) throws { |
| try self.init() |
| } |
| |
| convenience init(failBeforeOrDuringDelegation: Int) throws { |
| try unwrap(failBeforeOrDuringDelegation) |
| try self.init() |
| } |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17ThrowDerivedClassCACSi29failBeforeOrDuringDelegation2_tKcfc |
| // CHECK: bb0({{.*}}, [[OLD_SELF:%.*]] : $ThrowDerivedClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var ThrowDerivedClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[MOVE_ONLY_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK: try_apply {{.*}}({{.*}}) : $@convention(thin) (Int) -> (Int, @error Error), normal [[SUCC_BB1:bb[0-9]+]], error [[ERROR_BB1:bb[0-9]+]] |
| // |
| // CHECK: [[SUCC_BB1]]( |
| // CHECK: try_apply {{.*}}({{.*}}, [[MOVE_ONLY_SELF]]) : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error Error), normal [[SUCC_BB2:bb[0-9]+]], error [[ERROR_BB2:bb[0-9]+]] |
| // |
| // CHECK: [[SUCC_BB2]]([[NEW_SELF:%.*]] : $ThrowDerivedClass): |
| // CHECK-NEXT: store [[NEW_SELF]] to [init] [[PB_BOX]] |
| // CHECK-NEXT: [[RESULT:%.*]] = load [copy] [[PB_BOX]] |
| // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK-NEXT: return [[RESULT]] |
| // |
| // CHECK: [[ERROR_BB1]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: store [[MOVE_ONLY_SELF]] to [init] [[PB_BOX]] |
| // CHECK-NEXT: br [[THROWING_BB:bb[0-9]+]]([[ERROR]] |
| // |
| // CHECK: [[ERROR_BB2]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: br [[THROWING_BB]]([[ERROR]] |
| // |
| // CHECK: [[THROWING_BB]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK-NEXT: throw [[ERROR]] |
| convenience init(failBeforeOrDuringDelegation2: Int) throws { |
| try self.init(failBeforeDelegation: unwrap(failBeforeOrDuringDelegation2)) |
| } |
| |
| convenience init(failAfterDelegation: Int) throws { |
| self.init(noFail: ()) |
| try unwrap(failAfterDelegation) |
| } |
| |
| // CHECK-LABEL: sil hidden @_T021failable_initializers17ThrowDerivedClassCACSi27failDuringOrAfterDelegation_tKcfc : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error Error) { |
| // CHECK: bb0({{.*}}, [[OLD_SELF:%.*]] : $ThrowDerivedClass): |
| // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var ThrowDerivedClass }, let, name "self" |
| // CHECK: [[MARKED_SELF_BOX:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] |
| // CHECK: [[PB_BOX:%.*]] = project_box [[MARKED_SELF_BOX]] |
| // CHECK: store [[OLD_SELF]] to [init] [[PB_BOX]] |
| // CHECK: [[MOVE_ONLY_SELF:%.*]] = load [take] [[PB_BOX]] |
| // CHECK: try_apply {{.*}}([[MOVE_ONLY_SELF]]) : $@convention(method) (@owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error Error), normal [[SUCC_BB1:bb[0-9]+]], error [[ERROR_BB1:bb[0-9]+]] |
| // |
| // CHECK: [[SUCC_BB1]]([[NEW_SELF:%.*]] : $ThrowDerivedClass): |
| // CHECK-NEXT: store [[NEW_SELF]] to [init] [[PB_BOX]] |
| // CHECK-NEXT: // function_ref |
| // CHECK-NEXT: function_ref @ |
| // CHECK-NEXT: try_apply {{.*}}({{.*}}) : $@convention(thin) (Int) -> (Int, @error Error), normal [[SUCC_BB2:bb[0-9]+]], error [[ERROR_BB2:bb[0-9]+]] |
| // |
| // CHECK: [[SUCC_BB2]]( |
| // CHECK-NEXT: [[RESULT:%.*]] = load [copy] [[PB_BOX]] |
| // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK-NEXT: return [[RESULT]] |
| // |
| // CHECK: [[ERROR_BB1]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: br [[THROWING_BB:bb[0-9]+]]([[ERROR]] |
| // |
| // CHECK: [[ERROR_BB2]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: br [[THROWING_BB]]([[ERROR]] |
| // |
| // CHECK: [[THROWING_BB]]([[ERROR:%.*]] : $Error): |
| // CHECK-NEXT: destroy_value [[MARKED_SELF_BOX]] |
| // CHECK-NEXT: throw [[ERROR]] |
| convenience init(failDuringOrAfterDelegation: Int) throws { |
| try self.init() |
| try unwrap(failDuringOrAfterDelegation) |
| } |
| |
| convenience init(failBeforeOrAfterDelegation: Int) throws { |
| try unwrap(failBeforeOrAfterDelegation) |
| self.init(noFail: ()) |
| try unwrap(failBeforeOrAfterDelegation) |
| } |
| } |
| |
| //// |
| // Enums with failable initializers |
| //// |
| |
| enum FailableEnum { |
| case A |
| |
| init?(a: Int64) { self = .A } |
| |
| init!(b: Int64) { |
| self.init(a: b)! // unnecessary-but-correct '!' |
| } |
| |
| init(c: Int64) { |
| self.init(a: c)! // necessary '!' |
| } |
| |
| init(d: Int64) { |
| self.init(b: d)! // unnecessary-but-correct '!' |
| } |
| } |
| |
| //// |
| // Protocols and protocol extensions |
| //// |
| |
| // Delegating to failable initializers from a protocol extension to a |
| // protocol. |
| protocol P1 { |
| init?(p1: Int64) |
| } |
| |
| extension P1 { |
| init!(p1a: Int64) { |
| self.init(p1: p1a)! // unnecessary-but-correct '!' |
| } |
| |
| init(p1b: Int64) { |
| self.init(p1: p1b)! // necessary '!' |
| } |
| } |
| |
| protocol P2 : class { |
| init?(p2: Int64) |
| } |
| |
| extension P2 { |
| init!(p2a: Int64) { |
| self.init(p2: p2a)! // unnecessary-but-correct '!' |
| } |
| |
| init(p2b: Int64) { |
| self.init(p2: p2b)! // necessary '!' |
| } |
| } |
| |
| @objc protocol P3 { |
| init?(p3: Int64) |
| } |
| |
| extension P3 { |
| init!(p3a: Int64) { |
| self.init(p3: p3a)! // unnecessary-but-correct '!' |
| } |
| |
| init(p3b: Int64) { |
| self.init(p3: p3b)! // necessary '!' |
| } |
| } |
| |
| // Delegating to failable initializers from a protocol extension to a |
| // protocol extension. |
| extension P1 { |
| init?(p1c: Int64) { |
| self.init(p1: p1c) |
| } |
| |
| init!(p1d: Int64) { |
| self.init(p1c: p1d)! // unnecessary-but-correct '!' |
| } |
| |
| init(p1e: Int64) { |
| self.init(p1c: p1e)! // necessary '!' |
| } |
| } |
| |
| extension P2 { |
| init?(p2c: Int64) { |
| self.init(p2: p2c) |
| } |
| |
| init!(p2d: Int64) { |
| self.init(p2c: p2d)! // unnecessary-but-correct '!' |
| } |
| |
| init(p2e: Int64) { |
| self.init(p2c: p2e)! // necessary '!' |
| } |
| } |
| |
| //// |
| // type(of: self) with uninitialized self |
| //// |
| |
| func use(_ a : Any) {} |
| |
| class DynamicTypeBase { |
| var x: Int |
| |
| init() { |
| use(type(of: self)) |
| x = 0 |
| } |
| |
| convenience init(a : Int) { |
| use(type(of: self)) |
| self.init() |
| } |
| } |
| |
| class DynamicTypeDerived : DynamicTypeBase { |
| override init() { |
| use(type(of: self)) |
| super.init() |
| } |
| |
| convenience init(a : Int) { |
| use(type(of: self)) |
| self.init() |
| } |
| } |
| |
| struct DynamicTypeStruct { |
| var x: Int |
| |
| init() { |
| use(type(of: self)) |
| x = 0 |
| } |
| |
| init(a : Int) { |
| use(type(of: self)) |
| self.init() |
| } |
| } |