| |
| // RUN: %empty-directory(%t) |
| // RUN: %target-swift-frontend -module-name struct_resilience -emit-module -enable-resilience -emit-module-path=%t/resilient_struct.swiftmodule -enable-sil-ownership -module-name=resilient_struct %S/../Inputs/resilient_struct.swift |
| // RUN: %target-swift-emit-silgen -module-name struct_resilience -I %t -enable-sil-ownership -enable-resilience %s | %FileCheck %s |
| |
| import resilient_struct |
| |
| // Resilient structs are always address-only |
| |
| // CHECK-LABEL: sil hidden @$s17struct_resilience26functionWithResilientTypes_1f010resilient_A04SizeVAF_A2FXEtF : $@convention(thin) (@in_guaranteed Size, @noescape @callee_guaranteed (@in_guaranteed Size) -> @out Size) -> @out Size |
| // CHECK: bb0(%0 : @trivial $*Size, %1 : @trivial $*Size, %2 : @trivial $@noescape @callee_guaranteed (@in_guaranteed Size) -> @out Size): |
| func functionWithResilientTypes(_ s: Size, f: (Size) -> Size) -> Size { |
| |
| // Stored properties of resilient structs from outside our resilience |
| // domain are accessed through accessors |
| |
| // CHECK: copy_addr %1 to [initialization] [[OTHER_SIZE_BOX:%[0-9]*]] : $*Size |
| var s2 = s |
| |
| // CHECK: copy_addr %1 to [initialization] [[SIZE_BOX:%.*]] : $*Size |
| // CHECK: [[GETTER:%.*]] = function_ref @$s16resilient_struct4SizeV1wSivg : $@convention(method) (@in_guaranteed Size) -> Int |
| // CHECK: [[RESULT:%.*]] = apply [[GETTER]]([[SIZE_BOX]]) |
| // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[OTHER_SIZE_BOX]] : $*Size |
| // CHECK: [[SETTER:%.*]] = function_ref @$s16resilient_struct4SizeV1wSivs : $@convention(method) (Int, @inout Size) -> () |
| // CHECK: apply [[SETTER]]([[RESULT]], [[WRITE]]) |
| s2.w = s.w |
| |
| // CHECK: copy_addr %1 to [initialization] [[SIZE_BOX:%.*]] : $*Size |
| // CHECK: [[FN:%.*]] = function_ref @$s16resilient_struct4SizeV1hSivg : $@convention(method) (@in_guaranteed Size) -> Int |
| // CHECK: [[RESULT:%.*]] = apply [[FN]]([[SIZE_BOX]]) |
| _ = s.h |
| |
| // CHECK: apply %2(%0, %1) |
| // CHECK-NOT: destroy_value %2 |
| // CHECK: return |
| return f(s) |
| } |
| |
| // Use modify for inout access of properties in resilient structs |
| // from a different resilience domain |
| |
| public func inoutFunc(_ x: inout Int) {} |
| |
| // CHECK-LABEL: sil hidden @$s17struct_resilience18resilientInOutTestyy0c1_A04SizeVzF : $@convention(thin) (@inout Size) -> () |
| |
| func resilientInOutTest(_ s: inout Size) { |
| |
| // CHECK: function_ref @$s16resilient_struct4SizeV1wSivM |
| // CHECK: function_ref @$s17struct_resilience9inoutFuncyySizF |
| |
| inoutFunc(&s.w) |
| |
| // CHECK: return |
| } |
| |
| // Fixed-layout structs may be trivial or loadable |
| |
| // CHECK-LABEL: sil hidden @$s17struct_resilience28functionWithFixedLayoutTypes_1f010resilient_A05PointVAF_A2FXEtF : $@convention(thin) (Point, @noescape @callee_guaranteed (Point) -> Point) -> Point |
| // CHECK: bb0(%0 : @trivial $Point, %1 : @trivial $@noescape @callee_guaranteed (Point) -> Point): |
| func functionWithFixedLayoutTypes(_ p: Point, f: (Point) -> Point) -> Point { |
| |
| // Stored properties of fixed layout structs are accessed directly |
| var p2 = p |
| |
| // CHECK: [[RESULT:%.*]] = struct_extract %0 : $Point, #Point.x |
| // CHECK: [[DEST:%.*]] = struct_element_addr [[POINT_BOX:%[0-9]*]] : $*Point, #Point.x |
| // CHECK: assign [[RESULT]] to [[DEST]] : $*Int |
| p2.x = p.x |
| |
| // CHECK: [[RESULT:%.*]] = struct_extract %0 : $Point, #Point.y |
| _ = p.y |
| |
| // CHECK: [[NEW_POINT:%.*]] = apply %1(%0) |
| // CHECK: return [[NEW_POINT]] |
| return f(p) |
| } |
| |
| // Fixed-layout struct with resilient stored properties is still address-only |
| |
| // CHECK-LABEL: sil hidden @$s17struct_resilience39functionWithFixedLayoutOfResilientTypes_1f010resilient_A09RectangleVAF_A2FXEtF : $@convention(thin) (@in_guaranteed Rectangle, @noescape @callee_guaranteed (@in_guaranteed Rectangle) -> @out Rectangle) -> @out Rectangle |
| // CHECK: bb0(%0 : @trivial $*Rectangle, %1 : @trivial $*Rectangle, %2 : @trivial $@noescape @callee_guaranteed (@in_guaranteed Rectangle) -> @out Rectangle): |
| func functionWithFixedLayoutOfResilientTypes(_ r: Rectangle, f: (Rectangle) -> Rectangle) -> Rectangle { |
| return f(r) |
| } |
| |
| // Make sure we generate getters and setters for stored properties of |
| // resilient structs |
| |
| public struct MySize { |
| |
| // Static computed property |
| |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV10expirationSivgZ : $@convention(method) (@thin MySize.Type) -> Int |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV10expirationSivsZ : $@convention(method) (Int, @thin MySize.Type) -> () |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV10expirationSivMZ : $@yield_once @convention(method) (@thin MySize.Type) -> @yields @inout Int |
| public static var expiration: Int { |
| get { return copyright + 70 } |
| set { copyright = newValue - 70 } |
| } |
| |
| // Instance computed property |
| |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1dSivg : $@convention(method) (@in_guaranteed MySize) -> Int |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1dSivs : $@convention(method) (Int, @inout MySize) -> () |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1dSivM : $@yield_once @convention(method) (@inout MySize) -> @yields @inout Int |
| public var d: Int { |
| get { return 0 } |
| set { } |
| } |
| |
| // Instance stored property |
| |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1wSivg : $@convention(method) (@in_guaranteed MySize) -> Int |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1wSivs : $@convention(method) (Int, @inout MySize) -> () |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1wSivM : $@yield_once @convention(method) (@inout MySize) -> @yields @inout Int |
| public var w: Int |
| |
| // Read-only instance stored property |
| |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV1hSivg : $@convention(method) (@in_guaranteed MySize) -> Int |
| public let h: Int |
| |
| // Static stored property |
| |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV9copyrightSivgZ : $@convention(method) (@thin MySize.Type) -> Int |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV9copyrightSivsZ : $@convention(method) (Int, @thin MySize.Type) -> () |
| // CHECK-LABEL: sil @$s17struct_resilience6MySizeV9copyrightSivMZ : $@yield_once @convention(method) (@thin MySize.Type) -> @yields @inout Int |
| public static var copyright: Int = 0 |
| } |
| |
| // CHECK-LABEL: sil @$s17struct_resilience28functionWithMyResilientTypes_1fAA0E4SizeVAE_A2EXEtF : $@convention(thin) (@in_guaranteed MySize, @noescape @callee_guaranteed (@in_guaranteed MySize) -> @out MySize) -> @out MySize |
| public func functionWithMyResilientTypes(_ s: MySize, f: (MySize) -> MySize) -> MySize { |
| |
| // Stored properties of resilient structs from inside our resilience |
| // domain are accessed directly |
| |
| // CHECK: copy_addr %1 to [initialization] [[SIZE_BOX:%[0-9]*]] : $*MySize |
| var s2 = s |
| |
| // CHECK: [[SRC_ADDR:%.*]] = struct_element_addr %1 : $*MySize, #MySize.w |
| // CHECK: [[SRC:%.*]] = load [trivial] [[SRC_ADDR]] : $*Int |
| // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[SIZE_BOX]] : $*MySize |
| // CHECK: [[DEST_ADDR:%.*]] = struct_element_addr [[WRITE]] : $*MySize, #MySize.w |
| // CHECK: assign [[SRC]] to [[DEST_ADDR]] : $*Int |
| s2.w = s.w |
| |
| // CHECK: [[RESULT_ADDR:%.*]] = struct_element_addr %1 : $*MySize, #MySize.h |
| // CHECK: [[RESULT:%.*]] = load [trivial] [[RESULT_ADDR]] : $*Int |
| _ = s.h |
| |
| // CHECK: apply %2(%0, %1) |
| // CHECK-NOT: destroy_value %2 |
| // CHECK: return |
| return f(s) |
| } |
| |
| // CHECK-LABEL: sil [transparent] [serialized] @$s17struct_resilience25publicTransparentFunctionySiAA6MySizeVF : $@convention(thin) (@in_guaranteed MySize) -> Int |
| @_transparent public func publicTransparentFunction(_ s: MySize) -> Int { |
| |
| // Since the body of a public transparent function might be inlined into |
| // other resilience domains, we have to use accessors |
| |
| // CHECK: [[SELF:%.*]] = alloc_stack $MySize |
| // CHECK-NEXT: copy_addr %0 to [initialization] [[SELF]] |
| |
| // CHECK: [[GETTER:%.*]] = function_ref @$s17struct_resilience6MySizeV1wSivg |
| // CHECK-NEXT: [[RESULT:%.*]] = apply [[GETTER]]([[SELF]]) |
| // CHECK-NEXT: destroy_addr [[SELF]] |
| // CHECK-NEXT: dealloc_stack [[SELF]] |
| // CHECK-NEXT: return [[RESULT]] |
| return s.w |
| } |
| |
| // CHECK-LABEL: sil [transparent] [serialized] @$s17struct_resilience30publicTransparentLocalFunctionySiycAA6MySizeVF : $@convention(thin) (@in_guaranteed MySize) -> @owned @callee_guaranteed () -> Int |
| @_transparent public func publicTransparentLocalFunction(_ s: MySize) -> () -> Int { |
| |
| // CHECK-LABEL: sil shared [serialized] @$s17struct_resilience30publicTransparentLocalFunctionySiycAA6MySizeVFSiycfU_ : $@convention(thin) (@guaranteed { var MySize }) -> Int |
| // CHECK: function_ref @$s17struct_resilience6MySizeV1wSivg : $@convention(method) (@in_guaranteed MySize) -> Int |
| // CHECK: return {{.*}} : $Int |
| |
| return { s.w } |
| |
| } |
| |
| // CHECK-LABEL: sil hidden [transparent] @$s17struct_resilience27internalTransparentFunctionySiAA6MySizeVF : $@convention(thin) (@in_guaranteed MySize) -> Int |
| // CHECK: bb0([[ARG:%.*]] : @trivial $*MySize): |
| @_transparent func internalTransparentFunction(_ s: MySize) -> Int { |
| |
| // The body of an internal transparent function will not be inlined into |
| // other resilience domains, so we can access storage directly |
| |
| // CHECK: [[W_ADDR:%.*]] = struct_element_addr [[ARG]] : $*MySize, #MySize.w |
| // CHECK-NEXT: [[RESULT:%.*]] = load [trivial] [[W_ADDR]] : $*Int |
| // CHECK-NEXT: return [[RESULT]] |
| return s.w |
| } |
| |
| // CHECK-LABEL: sil [serialized] [always_inline] @$s17struct_resilience26publicInlineAlwaysFunctionySiAA6MySizeVF : $@convention(thin) (@in_guaranteed MySize) -> Int |
| @inline(__always) public func publicInlineAlwaysFunction(_ s: MySize) -> Int { |
| |
| // Since the body of a public transparent function might be inlined into |
| // other resilience domains, we have to use accessors |
| |
| // CHECK: [[SELF:%.*]] = alloc_stack $MySize |
| // CHECK-NEXT: copy_addr %0 to [initialization] [[SELF]] |
| |
| // CHECK: [[GETTER:%.*]] = function_ref @$s17struct_resilience6MySizeV1wSivg |
| // CHECK-NEXT: [[RESULT:%.*]] = apply [[GETTER]]([[SELF]]) |
| // CHECK-NEXT: destroy_addr [[SELF]] |
| // CHECK-NEXT: dealloc_stack [[SELF]] |
| // CHECK-NEXT: return [[RESULT]] |
| return s.w |
| |
| } |
| |
| // Make sure that @usableFromInline entities can be resilient |
| |
| @usableFromInline struct VersionedResilientStruct { |
| @usableFromInline let x: Int |
| @usableFromInline let y: Int |
| |
| @usableFromInline init(x: Int, y: Int) { |
| self.x = x |
| self.y = y |
| } |
| |
| // Non-inlinable initializer, assigns to self -- treated as a root initializer |
| |
| // CHECK-LABEL: sil @$s17struct_resilience24VersionedResilientStructV5otherA2C_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 |
| @usableFromInline init(other: VersionedResilientStruct) { |
| self = other |
| } |
| |
| // Inlinable initializer, assigns to self -- treated as a delegating initializer |
| |
| // CHECK-LABEL: sil [serialized] @$s17struct_resilience24VersionedResilientStructV6other2A2C_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 |
| @usableFromInline @inlinable init(other2: VersionedResilientStruct) { |
| self = other2 |
| } |
| } |
| |
| // CHECK-LABEL: sil [transparent] [serialized] @$s17struct_resilience27useVersionedResilientStructyAA0deF0VADF : $@convention(thin) (@in_guaranteed VersionedResilientStruct) -> @out VersionedResilientStruct |
| @usableFromInline |
| @_transparent func useVersionedResilientStruct(_ s: VersionedResilientStruct) |
| -> VersionedResilientStruct { |
| // CHECK: function_ref @$s17struct_resilience24VersionedResilientStructV1ySivg |
| // CHECK: function_ref @$s17struct_resilience24VersionedResilientStructV1xSivg |
| // CHECK: function_ref @$s17struct_resilience24VersionedResilientStructV1x1yACSi_SitcfC |
| |
| return VersionedResilientStruct(x: s.y, y: s.x) |
| |
| // CHECK: return |
| } |
| |
| // CHECK-LABEL: sil [serialized] @$s17struct_resilience18inlinableInoutTestyyAA6MySizeVzF : $@convention(thin) (@inout MySize) -> () |
| @inlinable public func inlinableInoutTest(_ s: inout MySize) { |
| // Inlinable functions can be inlined in other resiliene domains. |
| // |
| // Make sure we use modify for an inout access of a resilient struct |
| // property inside an inlinable function. |
| |
| // CHECK: function_ref @$s17struct_resilience6MySizeV1wSivM |
| inoutFunc(&s.w) |
| |
| // CHECK: return |
| } |
| |
| // Initializers for resilient structs |
| extension Size { |
| |
| // CHECK-LABEL: sil hidden @$s16resilient_struct4SizeV0B11_resilienceE5otherA2C_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 |
| } |
| } |