From 065cbd8015b1731aad56f32aff9baf34140e254f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 12 Oct 2017 18:12:22 -0700 Subject: [PATCH] AST: Struct initializers that assign to self can now be resilient Initializers for non-fixed-layout structs that are inlinable or are defined in a different module are treated as delegating initializers. Previously, only initializers containing a 'self.init' call were delegating; initializers that assigned to 'self' were not, which resulted in DI treating them as a root initializer where the stored 'self' value was exploded into a series of stores to each stored property member. They were not resilient as a result. Fixes , . --- lib/AST/Decl.cpp | 20 ++++++++++++++++-- test/SILGen/struct_resilience.swift | 32 +++++++++++++++++++++++++++++ test/decl/init/resilience.swift | 25 +++++++++------------- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index de99423b04920..9e4a90b9e3777 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5306,10 +5306,26 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags, // get the kind out of the finder. auto Kind = finder.Kind; + auto *NTD = getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext(); + // Protocol extension and enum initializers are always delegating. if (Kind == BodyInitKind::None) { - if (getDeclContext()->getAsProtocolExtensionContext() || - getDeclContext()->getAsEnumOrEnumExtensionContext()) { + if (isa(NTD) || isa(NTD)) { + Kind = BodyInitKind::Delegating; + } + } + + // Struct initializers that cannot see the layout of the struct type are + // always delegating. This occurs if the struct type is not fixed layout, + // and the constructor is either inlinable or defined in another module. + // + // FIXME: Figure out the right condition to use here that does not depend + // on the -enable-resilience flag, and make it conditional on + // -swift-version 5 instead, once the "disallow memberwise cross-module + // initializer" proposal lands. + if (Kind == BodyInitKind::None) { + if (isa(NTD) && + !NTD->hasFixedLayout(getParentModule(), getResilienceExpansion())) { Kind = BodyInitKind::Delegating; } } diff --git a/test/SILGen/struct_resilience.swift b/test/SILGen/struct_resilience.swift index beb7461bea7ab..27c6bb0b80c1c 100644 --- a/test/SILGen/struct_resilience.swift +++ b/test/SILGen/struct_resilience.swift @@ -229,6 +229,26 @@ public func functionWithMyResilientTypes(_ s: MySize, f: (MySize) -> MySize) -> self.x = x self.y = y } + + // Non-inlineable initializer, assigns to self -- treated as a root initializer + + // CHECK-LABEL: sil @_T017struct_resilience24VersionedResilientStructVA2C5other_tcfC : $@convention(method) (@in VersionedResilientStruct, @thin VersionedResilientStruct.Type) -> @out VersionedResilientStruct + // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var VersionedResilientStruct } + // CHECK-NEXT: [[SELF_UNINIT:%.*]] = mark_uninitialized [rootself] [[SELF_BOX]] + // CHECK: return + @_versioned init(other: VersionedResilientStruct) { + self = other + } + + // Inlineable initializer, assigns to self -- treated as a delegating initializer + + // CHECK-LABEL: sil [serialized] @_T017struct_resilience24VersionedResilientStructVA2C6other2_tcfC : $@convention(method) (@in VersionedResilientStruct, @thin VersionedResilientStruct.Type) -> @out VersionedResilientStruct + // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var VersionedResilientStruct } + // CHECK-NEXT: [[SELF_UNINIT:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] + // CHECK: return + @_versioned @_inlineable init(other2: VersionedResilientStruct) { + self = other2 + } } // CHECK-LABEL: sil [transparent] [serialized] @_T017struct_resilience27useVersionedResilientStructAA0deF0VADF : $@convention(thin) (@in VersionedResilientStruct) -> @out VersionedResilientStruct @@ -241,3 +261,15 @@ public func functionWithMyResilientTypes(_ s: MySize, f: (MySize) -> MySize) -> return VersionedResilientStruct(x: s.y, y: s.x) } + +// Initializers for resilient structs +extension Size { + + // CHECK-LABEL: sil hidden @_T016resilient_struct4SizeV0B11_resilienceEA2C5other_tcfC : $@convention(method) (@in Size, @thin Size.Type) -> @out Size + // CHECK: [[SELF_BOX:%.*]] = alloc_box ${ var Size } + // CHECK-NEXT: [[SELF_UNINIT:%.*]] = mark_uninitialized [delegatingself] [[SELF_BOX]] : ${ var Size } + // CHECK: return + init(other: Size) { + self = other + } +} diff --git a/test/decl/init/resilience.swift b/test/decl/init/resilience.swift index bd2c93a016f81..1c0491584c263 100644 --- a/test/decl/init/resilience.swift +++ b/test/decl/init/resilience.swift @@ -16,10 +16,10 @@ extension Point { // Size is not @_fixed_layout, so we cannot define a new designated initializer extension Size { + // FIXME: Produce a decent diagnostic here init(ww: Int, hh: Int) { - // expected-error@-1 {{initializer declared in an extension of non-'@_fixed_layout' type 'Size' must delegate to another initializer}} self.w = ww - self.h = hh + self.h = hh // expected-error {{cannot assign to property: 'h' is a 'let' constant}} } // This is OK @@ -27,32 +27,29 @@ extension Size { self.init(w: www, h: hhh) } - // FIXME: This should be allowed, but Sema doesn't distinguish this - // case from memberwise initialization, and DI explodes the value type + // This is OK init(other: Size) { - // expected-error@-1 {{initializer declared in an extension of non-'@_fixed_layout' type 'Size' must delegate to another initializer}} self = other } } // Animal is not @_fixed_layout, so we cannot define an @_inlineable // designated initializer +// +// FIXME: Crap diagnostics public struct Animal { - public let name: String + public let name: String // expected-note 3{{change 'let' to 'var' to make it mutable}} @_inlineable public init(name: String) { - // expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_inlineable' and must delegate to another initializer}} - self.name = name + self.name = name // expected-error {{cannot assign to property: 'name' is a 'let' constant}} } @inline(__always) public init(dog: String) { - // expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@inline(__always)' and must delegate to another initializer}} - self.name = dog + self.name = dog // expected-error {{cannot assign to property: 'name' is a 'let' constant}} } @_transparent public init(cat: String) { - // expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_transparent' and must delegate to another initializer}} - self.name = cat + self.name = cat // expected-error {{cannot assign to property: 'name' is a 'let' constant}} } // This is OK @@ -60,10 +57,8 @@ public struct Animal { self.init(name: cow) } - // FIXME: This should be allowed, but Sema doesn't distinguish this - // case from memberwise initialization, and DI explodes the value type + // This is OK @_inlineable public init(other: Animal) { - // expected-error@-1 {{initializer for non-'@_fixed_layout' type 'Animal' is '@_inlineable' and must delegate to another initializer}} self = other } }