$30 off During Our Annual Pro Sale. View Details »

Deep Dive into "any" and "some"

Deep Dive into "any" and "some"

At SwiftHeroes 2023

freddi(Yuki Aki)

May 04, 2023
Tweet

More Decks by freddi(Yuki Aki)

Other Decks in Technology

Transcript

  1. Deep Dive into “any” and “some” Yuki ‘freddi’ Aki, LINE

    SwiftHeroes 2023 Secrets from deep side of Swift
  2. protocol Shape { . . . } struct Rectangle: Shape

    {} struct Triangle: Shape {} Example of Existential Type
  3. protocol Shape { . . . } = /* make

    assignable any object conform to Shape */ Example of Existential Type var shape
  4. protocol Shape { . . . } : Shape =

    Rectangle() Example of Existential Type var shape
  5. protocol Shape { . . . } var shape: Shape

    = Rectangle() shape = Triangle() Example of Existential Type
  6. protocol Shape { . . . } var shape: shape

    = Triangle() ‘Shape’ is Existential Type Example of Existential Type Shape = Rectangle() Shape
  7. protocol Shape { . . . } SE-0335 Introduce existential

    ‘any’ ‘any Shape’ is Existential Type // SE-0355, any is forced from Swift 6 var shape: shape = Triangle() Shape = Rectangle() any any
  8. Existential Type’s cost Existential Container any Shape Read value Call

    Functions Performance Cost var shape: shape = Triangle() Shape = Rectangle() any
  9. any Existential Type? Class? Structure? // It is more clear

    that ‘Shape’ is existential type var shape: Shape
  10. let x: SE-0309 Unlock existentials for all protocols protocol P

    { associatedtype A } ~ Swift 5.6 P = /* ... */
  11. protocol P<A> { associatedtype A } P<A> Introduced by SE-0346

    “Lightweight same-type requirements for primary associated types”
  12. public protocol Clock<Duration>: Sendable public protocol BidirectionalCollection<Element>: Collection public protocol

    Collection<Element>: Sequence public protocol RawRepresentable<RawValue> public protocol Identifiable<ID> public protocol InstantProtocol<Duration> public protocol MutableCollection<Element> public protocol RandomAccessCollection<Element> public protocol RangeExpression<Bound> public protocol RangeReplaceableCollection<Element> public protocol SIMD<Scalar> public protocol IteratorProtocol<Element> public protocol Sequence<Element> public protocol SetAlgebra<Element> public protocol Strideable<Stride> Introduced by SE-0358 “Primary Associated Types in the Standard Library” Collection
  13. func apply(_ collection: any Collection<Int>) { . . . }

    Introduced by SE-0353 “Constrained Existential Types”
  14. protocol P<A> { associatedtype A } func method(_ p: any

    P<Int> ) { . . . } extension P<Int> { . . . } P<A> P<Int> Don’t need to write “P where A == Int” P<Int>
  15. protocol P<A> { associatedtype A } func method(_ p: any

    P<Int> ) { . . . } extension P<Int> { . . . } let p: any P<Int> P<A> P<Int> Don’t need to prepare hand-made type erasure P<Int>
  16. Existential Type’s cost func method(_ p: . . . }

    ‘any’ uses Existential Container P<Int>) { any
  17. Existential Type’s cost Opaque Type (‘some’) doesn’t require Existential Container

    (SE-341 Opaque Parameter Declarations) some func method(_ p: . . . } P<Int>) {
  18. Generics and Opaque Type in argument • Generics doesn’t use

    Existential Container because “Keeps type information" func method<T: P>(_ p: T) { . . . }
  19. Generics and Opaque Type in argument func method(_ p: some

    P) { . . . } ← • Generics doesn’t use Existential Container because “Keeps type information" • Opaque Type argument is syntax sugar of Generics func method<T: P>(_ p: T) { . . . } swiftc
  20. Type-level abstraction protocol Shape { . . . } func

    draw<T: Shape>(shape: T) { . . . } • Example: Generics (argument), Opaque Type (argument and return) • Abstract by using concrete type information (Type variable)
  21. Value-level abstraction protocol Shape { . . . } var

    shape: any Shape = Rectangle() shape = Triangle() • Example: Existential Type (argument and return) • Abstract by erasing type information of value and use underlying (protocol) type
  22. Cannot convert return expression of type ‘P‘ to return type

    ‘PImpl' <PType: P> PType func method PImpl() } () -> {
  23. let p = method() p.pImplFunc() No room for the developer

    to change the return type of the API Too opened implementation Detail PImpl func method PImpl() } () -> {
  24. var p = method() “Reverse Generics” (not implemented) Treat type

    as “some type conforms P” -> <PType: P> PType func method PImpl() } () -> {
  25. var p = method() Introduced by SE-0244 “Opaque Result Type”

    some P func method PImpl() } some () -> {
  26. Return Argument Existential type Existential type Type-level abstraction Value-level abstraction

    Generics Opaque Type (5.7) Opaque Type (5.1) Swift 5 (5.1 and 5.7) ~ Protocol abstraction
  27. How Existential Type works? var shape: any Shape = Rectangle()

    shape = Triangle() Existential Container any Shape
  28. How Existential Type works? var shape: any Shape = Rectangle()

    shape = Triangle() Existential Container any Shape
  29. How Existential Type works? var shape: any Shape = Rectangle()

    shape = Triangle() Existential Container any Shape
  30. struct Triangle: Shape { var vertex = 3 } struct

    BigColoredRectangle: Shape { var color = UIColor.green var vertex = 4 }
  31. struct Triangle: Shape { var vertex = 3 } struct

    BigColoredRectangle: Shape { var color = UIColor.green var vertex = 4 } print(MemoryLayout.size(ofValue: Triangle())) print(MemoryLayout.size(ofValue: BigColoredRectangle())) 8 16
  32. struct Triangle: Shape { var vertex = 3 } struct

    BigColoredRectangle: Shape { var color = UIColor.green var vertex = 4 } print(MemoryLayout.size(ofValue: Triangle())) print(MemoryLayout.size(ofValue: BigColoredRectangle())) let shape1: any Shape = Triangle() let shape2: any Shape = BigColoredRectangle() 8 16
  33. struct Triangle: Shape { var vertex = 3 } struct

    BigColoredRectangle: Shape { var color = UIColor.green var vertex = 4 } print(MemoryLayout.size(ofValue: Triangle())) print(MemoryLayout.size(ofValue: BigColoredRectangle())) let shape1: any Shape = Triangle() let shape2: any Shape = BigColoredRectangle() print(MemoryLayout.size(ofValue: shape1)) print(MemoryLayout.size(ofValue: shape2)) 8 16 40 40
  34. struct VeryBigRectangle: Shape { . . . } print(MemoryLayout.size(ofValue: VeryBigRectangle()))

    let shape: any Shape = VeryBigRectangle() print(MemoryLayout.size(ofValue: shape)) 48 40
  35. any Shape Object in Existential Container (struct) small struct var

    x: Int64 var y: Int64 Existential Container let shape: = Triangle()
  36. Object in Existential Container (struct) small struct var x: Int64

    var y: Int64 Existential Container = 40byte any Shape let shape: Triangle()
  37. Object in Existential Container (struct) Buffer(64bit) Buffer(64bit) Buffer(64bit) small struct

    var x: Int64 var y: Int64 Existential Container = any Shape let shape: Triangle()
  38. Object in Existential Container (struct) x: Int64 y: Int64 Buffer(64bit)

    small struct var x: Int64 var y: Int64 Existential Container = any Shape let shape: Triangle()
  39. VeryBigRectangle() Object in Existential Container (struct) Buffer(64bit) Buffer(64bit) Buffer(64bit) large

    struct var x: Int64 var y: Int64 var z: Int64 var w: Int64 Existential Container = any Shape let shape:
  40. Object in Existential Container (struct) Buffer(64bit) Buffer(64bit) Heap large struct

    var x: Int64 var y: Int64 var z: Int64 var w: Int64 Existential Container = VeryBigRectangle() any Shape let shape: Reference
  41. Object in Existential Container (struct) Buffer(64bit) Buffer(64bit) Heap large struct

    var x: Int64 var y: Int64 var z: Int64 var w: Int64 Existential Container = Copy VeryBigRectangle() any Shape let shape: Reference x: Int y: Int z: Int w: Int
  42. Object in Existential Container (struct) Reference Buffer(64bit) Buffer(64bit) Heap large

    struct var x: Int64 var y: Int64 var z: Int64 var w: Int64 Existential Container = Copy VeryBigRectangle() any Shape let shape: x: Int y: Int z: Int w: Int
  43. struct VeryBigRectangle: Shape { . . . } print(MemoryLayout.size(ofValue: VeryBigRectangle()))

    let shape: any Shape = VeryBigRectangle() print(MemoryLayout.size(ofValue: shape)) 40 (without Heap area size)
  44. Witness Table Value Witness Table Protocol Witness Table Information of

    Type Allocation, Deinitialization, size, stride …
  45. Witness Table Value Witness Table Protocol Witness Table Information for

    Method Dispatch (Method Table) Used when selecting the function of protocol Information of Type Allocation, Deinitialization, size, stride …
  46. Method Dispatch of Existential Container x: Int64 y: Int64 Buffer(64bit)

    small struct var x: Int64 var y: Int64 Existential Container = Protocol Witness Table any Shape let shape: Triangle()
  47. Method Dispatch of Existential Container x: Int64 y: Int64 Buffer(64bit)

    small struct var x: Int64 var y: Int64 func draw() Existential Container = Shape protocol func draw() Protocol Witness Table any Shape let shape: Triangle()
  48. x: Int64 y: Int64 Buffer(64bit) small struct var x: Int64

    var y: Int64 func draw() Existential Container = Shape protocol func draw() Name Which func needs call? draw() Triangle.draw() Protocol Witness Table Method Dispatch of Existential Container any Shape let shape: Triangle() Triangle’s Protocol Witness Table
  49. x: Int64 y: Int64 Buffer(64bit) small struct var x: Int64

    var y: Int64 func draw() Existential Container = Shape protocol func draw() Name Which func needs call? draw() Triangle.draw() Protocol Witness Table Method Dispatch of Existential Container any Shape let : Triangle() Triangle’s Protocol Witness Table shape
  50. x: Int64 y: Int64 Buffer(64bit) Existential Container .draw() Protocol Witness

    Table Method Dispatch of Existential Container shape
  51. x: Int64 y: Int64 Buffer(64bit) Existential Container Name Which func

    needs call? draw() Triangle.draw() combine() Triangle.combine() … … Protocol Witness Table Method Dispatch of Existential Container shape .draw()
  52. x: Int64 y: Int64 Buffer(64bit) Existential Container Protocol Witness Table

    Method Dispatch of Existential Container shape .draw() Name Which func needs call? draw() Triangle.draw() combine() Triangle.combine() … …
  53. Method Dispatch of Existential Type Dynamic Dispatch Static Dispatch Method

    is selected on runtime Existential Container (Witness Table), Class (Virtual Table), Objective-C Runtime (Message Dispatch) … Method is selected on compile time struct, global function call, extension … Runtime Cost
  54. Existential Container (struct) Buffer(64bit) Buffer(64bit) Buffer(64bit) Existential Container Metadata Protocol

    Witness Table 64bit + 64bit + 64bit = 192bit (24byte) 64bit (8byte) 8byte 40byte
  55. Summary: Existential Container • Existential Container allocates heap if the

    object exceeds its buffer • Existential Container size is fixed regardless of type (struct: 40Byte in 64bit CPU)
  56. Summary: Existential Container • Existential Container size is fixed regardless

    of type (struct: 40Byte in 64bit CPU) • Existential Container allocates heap if the object exceeds its buffer • Existential Container method dispatch: Dynamic Dispatch
  57. How Existential Container actually works? • We can see how

    Existential type is initialized via Swift Intermediate Language(SIL)
  58. How Existential Container actually works? • We can see how

    Existential type is initialized via Swift Intermediate Language(SIL) AST AST (Typed) SIL IR (LLVM) 📱 App
  59. How Existential Container actually works? • We can see how

    Existential type is initialized via Swift Intermediate Language(SIL) AST AST (Typed) SIL IR (LLVM) 📱 App • SIL shows many codes which is hidden in normal Swift code • Reference Count • Witness Table …
  60. protocol Shape { func draw() } struct Triangle: Shape {

    func draw() {} } let shape: any Shape = Triangle() shape.draw()
  61. protocol Shape { func draw() } struct Triangle: Shape {

    func draw() {} } let shape: any Shape = Triangle() shape.draw() $ swiftc heroes.swift -emit-sil -o heroes.sil
  62. sil_stage canonical import Builtin import Swift import SwiftShims protocol Shape

    { func draw() } struct Triangle : Shape { func draw() init() } @_hasStorage @_hasInitialValue let shape: Shape { get } // shape sil_global hidden [let] @$s6heroes5shapeAA5Shape_pvp : $Shape // main sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape // users: %9, %9, %8 %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7; user: %9 %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7 %10 = integer_literal $Builtin.Int32, 0 // user: %11 %11 = struct $Int32 (%10 : $Builtin.Int32) // user: %12 return %11 : $Int32 // id: %12 } // end sil function 'main' // Triangle.draw() sil hidden @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () { // %0 "self" // user: %1 bb0(%0 : $Triangle): debug_value %0 : $Triangle, let, name "self", argno 1, implicit // id: %1 %2 = tuple () // user: %3 return %2 : $() // id: %3 } // end sil function '$s6heroes8TriangleV4drawyyF' // Triangle.init() sil hidden @$s6heroes8TriangleVACycfC : $@convention(method) (@thin Triangle.Type) -> Triangle { // %0 "$metatype" bb0(%0 : $@thin Triangle.Type): %1 = struct $Triangle () // user: %2 return %1 : $Triangle // id: %2 } // end sil function '$s6heroes8TriangleVACycfC' // protocol witness for Shape.draw() in conformance Triangle sil private [transparent] [thunk] @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Triangle) -> () { // %0 // user: %1 bb0(%0 : $*Triangle): %1 = load %0 : $*Triangle // user: %3 // function_ref Triangle.draw() %2 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %3 %3 = apply %2(%1) : $@convention(method) (Triangle) -> () %4 = tuple () // user: %5 return %4 : $() // id: %5 } // end sil function ‘$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW’ // Mappings from '#fileID' to '#filePath': // 'heroes/heroes.swift' => 'heroes.swift' sil_witness_table hidden Triangle: Shape module heroes { method #Shape.draw: <Self where Self : Shape> (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW // protocol witness for Shape.draw() in conformance Triangle }
  63. sil_witness_table hidden Triangle: Shape module heroes { method #Shape.draw: <Self

    where Self : Shape> (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW } Protocol Witness Table of ‘Triangle’
  64. sil_witness_table hidden Triangle: Shape module heroes { method #Shape.draw: <Self

    where Self : Shape> (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW } Protocol Function (‘Shape.draw’) Protocol Witness Table of ‘Triangle’
  65. sil_witness_table hidden Triangle: Shape module heroes { method #Shape.draw: <Self

    where Self : Shape> (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW } Protocol Function (‘Shape.draw’) Symbol of ‘Triangle.draw’ Protocol Witness Table of ‘Triangle’
  66. sil_stage canonical import Builtin import Swift import SwiftShims protocol Shape

    { func draw() } struct Triangle : Shape { func draw() init() } @_hasStorage @_hasInitialValue let shape: Shape { get } // shape sil_global hidden [let] @$s6heroes5shapeAA5Shape_pvp : $Shape // main sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): %10 = integer_literal $Builtin.Int32, 0 // user: %11 %11 = struct $Int32 (%10 : $Builtin.Int32) // user: %12 return %11 : $Int32 // id: %12 } // end sil function 'main' // Triangle.draw() sil hidden @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () { // %0 "self" // user: %1 bb0(%0 : $Triangle): debug_value %0 : $Triangle, let, name "self", argno 1, implicit // id: %1 %2 = tuple () // user: %3 return %2 : $() // id: %3 } // end sil function '$s6heroes8TriangleV4drawyyF' // Triangle.init() sil hidden @$s6heroes8TriangleVACycfC : $@convention(method) (@thin Triangle.Type) -> Triangle { // %0 "$metatype" bb0(%0 : $@thin Triangle.Type): %1 = struct $Triangle () // user: %2 return %1 : $Triangle // id: %2 } // end sil function '$s6heroes8TriangleVACycfC' // protocol witness for Shape.draw() in conformance Triangle sil private [transparent] [thunk] @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW : $@convention(witness_method: Shape) (@in_guaranteed Triangle) -> () { // %0 // user: %1 bb0(%0 : $*Triangle): %1 = load %0 : $*Triangle // user: %3 // function_ref Triangle.draw() %2 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %3 %3 = apply %2(%1) : $@convention(method) (Triangle) -> () %4 = tuple () // user: %5 return %4 : $() // id: %5 } // end sil function ‘$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW’ // Mappings from '#fileID' to '#filePath': // 'heroes/heroes.swift' => 'heroes.swift' sil_witness_table hidden Triangle: Shape module heroes { method #Shape.draw: <Self where Self : Shape> (Self) -> () -> () : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW // protocol witness for Shape.draw() in conformance Triangle } alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp : $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape // users: %9, %9, %8 %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $*@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7; user %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@in_guaranteed τ_0_0) -> () // type-defs: %7
  67. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to $*@opened("CA01F70C-D958-11ED-8F40- %8 = witness_method $@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape, #Shape.draw : <Self %9 = apply %8<@opened("CA01F70C-D958-11ED-8F40-9E59F6EF2BA0") Shape>(%7) : $@convention(witness let shape: any Shape = Triangle() shape.draw() %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@
  68. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw()
  69. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() Allocating memory area for shape
  70. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() Call ‘Triangle’s initializer to create object Allocating memory area for shape
  71. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() Create Existential Container and assign ‘Triangle’ object Call ‘Triangle’s initializer to create object Allocating memory area for shape
  72. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw()
  73. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw()
  74. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() Load Existential Container
  75. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() Load Existential Container Get function symbol of Shape.draw from Protocol Witness Table in Existential Container
  76. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, #Shape.draw : <Self where Self : Shape> (Self) -> () -> (), %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() #Shape.draw : <Self where Self : Shape> (Self) -> () -> () Load Existential Container Get function symbol of Shape.draw from Protocol Witness Table in Existential Container
  77. sil_witness_table hidden Triangle: Shape module heroes { method : @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW

    } Protocol Function (‘Shape.draw’) Symbol of ‘Triangle.draw’ #Shape.draw : <Self where Self : Shape> (Self) -> () -> () @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW Protocol Witness Table of ‘Triangle’
  78. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, , %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() #Shape.draw : <Self where Self : Shape> (Self) -> () -> () Obtained: @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW Load Existential Container Get function symbol of Shape.draw from Protocol Witness Table in Existential Container
  79. alloc_global @$s6heroes5shapeAA5Shape_pvp // id: %2 %3 = global_addr @$s6heroes5shapeAA5Shape_pvp :

    $*Shape // users: %7, %5 %4 = struct $Triangle () // user: %6 %5 = init_existential_addr %3 : $*Shape, $Triangle // user: %6 store %4 to %5 : $*Triangle // id: %6 %7 = open_existential_addr immutable_access %3 : $*Shape to … Shape // users: %9, %9, %8 %8 = witness_method … Shape, , %7 : $ %9 = apply %8<… Shape>(%7) : $@convention(witness_method: Shape) <τ_0_0 where τ_0_0 : Shape> (@ let shape: any Shape = Triangle() shape.draw() #Shape.draw : <Self where Self : Shape> (Self) -> () -> () The function is called with selected information (Dynamic Dispatch) Load Existential Container Get function symbol of Shape.draw from Protocol Witness Table in Existential Container Obtained: @$s6heroes8TriangleVAA5ShapeA2aDP4drawyyFTW
  80. %8 = load %3 : $*Triangle // user: %10 //

    function_ref Triangle.draw() %9 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %10 = apply %9(%8) : $@convention(method) (Triangle) -> () let shape = Triangle() shape.draw()
  81. %8 = load %3 : $*Triangle // user: %10 //

    function_ref Triangle.draw() %9 = function_ref @$s6heroes8TriangleV4drawyyF : $@convention(method) (Triangle) -> () // user: %10 = apply %9(%8) : $@convention(method) (Triangle) -> () Function symbol ($s6heroes8TriangleV4drawyyF) is selected (Static Dispatch) let shape = Triangle() shape.draw()
  82. let shape: some Shape = Triangle() shape.draw() ??? Please try:$

    swiftc heroes.swift -emit-sil -o heroes.sil
  83. // draw<A>(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlF : . . . {

    . . . %11 = witness_method $T . . . 
 . . . } func draw<T: Shape>(shape: T) // draw is called inside of function Compile to SIL code
  84. // draw<A>(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlF : . . . {

    . . . %11 = witness_method $T . . . 
 . . . } Try to check Witness Table of ‘shape’ from type variable ’T’ func draw<T: Shape>(shape: T) // draw is called inside of function Compile to SIL code
  85. • Generics also uses Witness Table if Swift compiler doesn’t

    optimize function code Specialization of function argument with Generics
  86. • Generics also uses Witness Table if Swift compiler doesn’t

    optimize function code • Optimizer in Swift compiler generates “function with specialized argument“ Specialization of function argument with Generics
  87. func draw<T: Shape>(shape: T) draw(shape: Triangle()) Compile code with optimization

    (‘-O’ flag) // specialized draw<A>(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5
  88. func draw<T: Shape>(shape: T) draw(shape: Triangle()) Compile code with optimization

    (‘-O’ flag) // specialized draw<A>(shape:) sil hidden @$s6heroes4draw5shapeyx_tAA5ShapeRzlFAA8TriangleC_Tg5 ‘draw(shape: Triangle)’
  89. • Specialized function has concrete type information, and witness_table isn’t

    needed • Generics also uses Witness Table if Swift compiler doesn’t optimize code • Swift compiler’s optimizer generates “function with specialized argument“ Specialization of function argument with Generics
  90. public func draw<T: Shape>(shape: T) draw(shape: Triangle()) Module A Module

    B class OriginalShape: ModuleA.Shape draw(shape: OriginalShape())
  91. public func draw<T: Shape>(shape: T) draw(shape: Triangle()) Module A Module

    B class OriginalShape: ModuleA.Shape draw(shape: OriginalShape()) Specialized
  92. public func draw<T: Shape>(shape: T) draw(shape: Triangle()) Module A Module

    B class OriginalShape: ModuleA.Shape draw(shape: OriginalShape()) Specialized Not-Specialized
  93. • Different module, complex type … etc • Function’s argument

    is not specialized in some cases Limitation of argument Specialization
  94. • Use ‘@inlinable’ attribute to make specializable in outside of

    modules (or @_specialize) public func draw<T: Shape>(shape: T) draw(shape: Triangle()) @inlinable • Different module, complex type … etc • Function’s argument is not specialized in some cases Limitation of argument Specialization
  95. • ‘some’ argument still uses Witness Table as default •

    ‘any’ and ‘some’ argument are also specialized if possible Specialization of ‘any’ and ‘some’ argument
  96. // specialized draw(shape:) sil hidden @$s6heroes4draw5shapeyAA5Shape_p_..._nAA8TriangleC_Tg5 func draw(shape: any Shape)

    draw(shape: Triangle()) Compile code with optimization (‘-O’ flag) ‘draw(shape: Triangle)’
  97. • ‘some’ still uses Witness Table as default • ‘any’

    and ‘some’ argument are also specialized if possible Specialization of ‘some’ and ‘any’ argument
  98. • ‘some’ still uses Witness Table as default • ‘any’

    and ‘some’ argument are also specialized if possible Specialization of ‘some’ and ‘any’ argument • Use appropriate ‘some’ and ‘any’ regardless of specialization any some YES YES NO (inline only) YES Same module Different module(inlinable) Specialization of some an any
  99. Summary: Optimization • Existential Type, Opaque Type and Generics are

    “specialized” by compiler as possible • Use appropriate ‘some’ and ‘any’ as possible regardless of specialization
  100. Recap • Existential type and Opaque type evolution • Existential

    Container and Performance • Specialization for Generics, Existential type and Opaque type
  101. Learn more evolution SE-0360 Opaque result types with limited availability

    apple/swift-evolution SE-0375 Opening existential arguments to optional parameters SE-0352 Implicitly Opened Existentials SE-0328 Structural opaque result types apple/swift-evolution apple/swift-evolution apple/swift-evolution
  102. Learn API Design with ‘any’ and ‘some’ Embrace Swift generics

    WWDC 22 Design protocol interfaces in Swift WWDC 22
  103. Other Talks Generic Protocols and Associated types - POWER TOOLS

    FOR UNIT TESTING SwiftHeroes 2023 Swift Optimizing (at Compiler World) NSSpain 2020
  104. Thank you! Yuki ‘freddi’ Aki - iOS Engineer at LINE

    • Github @ freddi-kit • Twitter @ _ _ _freddi_ _ _ Special Thanks @koher (Author of “Heart of Swift” https://heart-of-swift.github.io/) @kateinoigakukun (Swift Compiler specialist) Engineers and Interpreters in LINE Corp SwiftHeroes Team