Skip to content

Instantly share code, notes, and snippets.

@omochi
Last active June 3, 2022 03:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save omochi/1c8a0711903447c4213addd6a5febe9a to your computer and use it in GitHub Desktop.
Save omochi/1c8a0711903447c4213addd6a5febe9a to your computer and use it in GitHub Desktop.

slidenumber: true autoscale: true

Swift6のprotocol

わいわいswiftc #36

@omochimetaru


protocol革命が来る

  • SE-0309: Unlock existentials for all protocols
  • SE-0328: Structural opaque result types
  • SE-0335: Introduce existential any
  • SE-0341: Opaque Parameter Declarations
  • SE-0346: Lightweight same-type requirements for primary associated types
  • SE-0352: Implicitly Opened Existentials
  • SE-0353: Constrained Existential Types

  • SE-0309: Unlock existentials for all protocols1
  • SE-0328: Structural opaque result types2
  • SE-0335: Introduce existential any3
  • SE-0341: Opaque Parameter Declarations4
  • SE-0346: Lightweight same-type requirements for primary associated types5
  • SE-0352: Implicitly Opened Existentials6
  • SE-0353: Constrained Existential Types7

今日の目標

  • 新しい言語機能の文法意味実装を理解する

話の流れ

  • 歴史
  • 既存機能の復習
  • 新機能の紹介

歴史


Generics in Swift8

Swiftのジェネリクスの初期設計書。

Self Type, Associated Type, Explicit Conformance, Retroactive Conformance, Constraints, Existential などの主要な特徴が提唱されている。


Generics Manifesto9

Swiftのジェネリクスの将来の方向性を示す文書。 Swift 3の頃に公開され、かなり先端的な機能にも言及している。

Swift 4で軽微なものは実現されたが、 高度なものは Swift 5 時代にあまり進捗は無かった。


Improving the UI of generics10

Swiftが今後集中していくジェネリクスの方向性を示すフォーラムの書き込み。 Swift 5の頃に公開され、マニフェストの一部の内容を掘り下げている。

Swift 6に向けてここで言及された内容が導入されそう。


既存機能の復習


protocolの機能

  1. 型の機能を宣言する

  2. ジェネリックパラメータに制約(Constraint)を与える

  3. Existentialを作る


型の機能を宣言する

protocol P {
    associatedtype A
    func f(a: A)
    static func s() -> Self
}

struct S: P { ... }
  • Pは型 ではなく あくまでも型の機能の宣言

associated typeとSelf type

Self typeは機能上はassociated typeと近い。 準拠(conform)する側で自身を割り当てる事が強いられているだけ。

protocol P {
    associatedtype CustomSelf
}

struct S: P {
    typealias CustomSelf = S
}

static member と metatype

static memberはmetatypeに対するmember定義と捉えられる。

protocol P {
    associatedtype A
    static func s(a: A) -> Self
}
struct S: P { ... }
func foo(type: P.Type) { ... }
func main() {
    foo(type: S.self)
}

protocol P {
    associatedtype A
    static func s(a: A) -> Self
}

// 以下のような気持ち
protocol PMetatype {
    associatedtype A
    associatedtype P
    func s(a: A) -> P
}

制約を与える

まず制約を与えない場合を確認する

func foo<T>(t: T) -> T { ... }
  • <T> について定義された関数

実装

; a.foo<A>(t: A) -> A
define hidden swiftcc void @"$s1a3foo1txx_tlF"(
    ; T型の返り値がopaqueなポインタ引数になる
    %swift.opaque* noalias nocapture sret(%swift.opaque) %0, 
    ; T型の引数はopaqueなポインタ
    %swift.opaque* noalias nocapture %1, 
    ; Tの真の型
    %swift.type* %T
) #0 { ... }

Value Witness Table11

  • メタタイプ(%swift.type*) からは Value Witness Table が取り出せる。

  • ジェネリックな型 <T> の値(value)に対する基本操作が入った関数テーブル。

  • init, copy, destroy, sizeなど。

  • 呼び出す側が <T> の真の型 S のメタタイプ(とVWT)をセットで渡す。


制約を与える

func foo<T: P>(t: P) -> T { ... }
  • <T>P に準拠(conform)するという制約(constraint)を追加

実装

; a.foo<A where A: a.P>(t: A) -> A
define hidden swiftcc void @"$s1a3foo1txx_tAA1PRzlF"(
    ; T型の返り値
    %swift.opaque* noalias nocapture sret(%swift.opaque) %0,
    ; T型の引数
    %swift.opaque* noalias nocapture %1, 
    ; Tの真の型
    %swift.type* %T,
    ; TのPへの準拠(Protocol Witness Table)
    i8** %T.P
) #0 { ... }

Protocol Witness Table12

  • <T>P に準拠するための操作が入った関数テーブル。

  • その内容は P の定義とほぼ同じ形になる。

  • 呼び出す側が <T> の真の型 SP へのPWTをセットで渡す。


protocol P {
    associatedtype A
    func foo(a: A)
    static func s() -> Self
}

struct S: P {
    typealias A = Int
    func foo(a: Int) { }
    static func s() -> S { S() }
}

; protocol witness table for a.S : a.P in a
@"$s1a1SVAA1PAAWP" = hidden global [4 x i8*] [
    ; protocol conformance descriptor
    i8* bitcast (%swift.protocol_conformance_descriptor* @"$s1a1SVAA1PAAMc" to i8*),
    ; A = Swift.Int
    i8* getelementptr inbounds (
        <{ [2 x i8], i8 }>, <{ [2 x i8], i8 }>* @"symbolic Si", i32 0, i32 0, i64 1
    ),
    ; func foo
    i8* bitcast (void (%TSi*, %T1a1SV*, %swift.type*, i8**)* @"$s1a1SVAA1PA2aDP3fooAAy1AQz_tFTW" to i8*),
    ; static func s
    i8* bitcast (void (%T1a1SV*, %swift.type*, %swift.type*, i8**)* @"$s1a1SVAA1PA2aDP1sxyFZTW" to i8*)
], align 8

associated typeにも制約を与える

protocol Q {}

protocol P {
    associatedtype A: Q
    func foo(a: A)
    static func s() -> Self
}

; protocol witness table for a.S : a.P in a
@"$s1a1SVAA1PAAWP" = hidden global [5 x i8*] [
    ; protocol conformance descriptor
    i8* bitcast (%swift.protocol_conformance_descriptor* @"$s1a1SVAA1PAAMc" to i8*), 
    ; SのP準拠におけるS.AのQ準拠の情報
    i8* getelementptr (
        i8, 
        i8* getelementptr inbounds (
            <{ i8, i8, i32, i8 }>, 
            <{ i8, i8, i32, i8 }>* @"associated conformance 1a1SVAA1PAA1AAaDP_AA1Q", 
            i32 0, i32 0
        ), 
        i64 1
    ),
    ; A = Swift.Int
    i8* getelementptr inbounds (<{ [2 x i8], i8 }>, <{ [2 x i8], i8 }>* @"symbolic Si", i32 0, i32 0, i64 1), 
    ; func foo
    i8* bitcast (void (%TSi*, %T1a1SV*, %swift.type*, i8**)* @"$s1a1SVAA1PA2aDP3fooAAy1AQz_tFTW" to i8*), 
    ; static func s
    i8* bitcast (void (%T1a1SV*, %swift.type*, %swift.type*, i8**)* @"$s1a1SVAA1PA2aDP1sxyFZTW" to i8*)
], align 8

制約のまとめ

  • ジェネリックな型に対して行える操作をプロトコルで制約する事で宣言する

  • 呼び出すときの真の型はコンパイル時に決定する

  • calleeは、値をopaqueなポインタ、基本操作のためのVWT、プロトコルとしての操作のためのPWTを受け取り、 それらのテーブルの関数を通して値を操作する

  • callerは、渡そうとしている真の型の専用のVWTとPWTを一緒に渡す


Existential


プロトコルを型として使える(?)

protocol P {
    func foo()
}

func makeFoo() -> P { ... }

func callFoo(_ p: P) { ... }

func main() {
    let p: P = makeFoo()
    callFoo(p)
}

struct S: P {}

func makeFoo() -> P {
    // S を P に格納
    return S()
}

func callFoo(_ p: P) {
    // P から <T: P> を取り出す
    p.foo()
}

Existential Container12

// 気持ち表現
struct PContainer {
    var value<T: P>: T

    func foo() { value.foo() }
}
  • 🙅‍♂️ プロトコルを型として使える

  • 🙆‍♂️ <T: P> を保持できるコンテナ型を使える


Existential Metatype Container

func useMetatype(type: P.Type) {}

func callUseMetatype(p: P) {
    useMetatype(type: type(of: p))
}

func main() {
    useMetatype(type: S.self)
    callUseMetatype(p: S())
}
  • P に準拠する型 T のメタタイプのコンテナ

Existential Container Metatype

func useExistentialContainerMetatype(type: P.Protocol) {}

func main() {
    useExistentialContainerMetatype(type: P.self)
}
  • Existential Containerのメタタイプ

Existentialの制限


associated typeがあると使えない

protocol P {
    associatedtype A
}

// error: protocol 'P' can only be used as a generic constraint 
//        because it has Self or associated type requirements
func useP(p: P) {}

もし使えたとすると問題が生じる。

func useP(p: P) {
    // P.Aがコンパイル時に決定できない
    func f(a: P.A) {}
}

Any型を使ってもcontravariantな場合にうまくいかない。

protocol P {
    associatedtype A
    func useA(a: A)
}

struct S: P {
    func useA(a: Int) {}
}

func useP(p: P) {
    // useA(a: Any) ?
    p.useA(a: "string")
}

contravariantなSelf typeがあると使えない

protocol P {
    func foo(_ a: Self)
}

// error: protocol 'P' can only be used as a generic constraint 
//        because it has Self or associated type requirements
func useP(p: P) {}

もし使えたとすると問題が生じる。

func makeP() -> P { ... }

func useP(p: P) {
    // pの真の型とmakePが返す真の型が同一とは限らない
    p.foo(makeP())
}

covariantなSelf typeは大丈夫

protocol P {
    func s() -> Self
    func os() -> Self?
    func fs() -> () -> Self
    func ts() -> (Self, Self)
    func ffs() -> ((Self) -> Void) -> Void
}

// OK
func useFoo(p: P) {}

Self = existenal P とラップするだけ


Existential の self conformance


PはPではない

protocol P {}

// error: protocol 'P' as a type cannot 
//        conform to the protocol itself
func useGenericP<T: P>(p: P) {}

func useExistentialP(p: P) {
    useGenericP(p: p)
}

Pのexistentialはprotocol Pに準拠していない


associated typeが解決できない

protocol P {
    associatedtype A
}

P.A は真の型によって異なる


contravariantなSelf typeが解決できない

protocol P {
    func foo(p: Self)
}

Self は真の型によって異なる


static memberが実装できない

protocol P {
    static func s()
}

func main() {
    // error: static member 's' cannot be used 
    //        on protocol metatype 'P.Protocol'
    P.s()
}

@objc protocolは自己準拠する

@objc protocol P {
    func foo()
}

func useGenericP<T: P>(p: T) {}

func useExistentialP(p: P) {
    useGenericP(p: p)
}
  • static memberが無い場合のみ
  • associatedtypeはそもそも禁止
  • Selfはあってもよい(?)

import Foundation

@objc protocol P {
    // contravariance Self ?
    func foo(_ p: Self)
}

@objc class S: NSObject, P {
    var x: String = "s"

    func foo(_ s: S) {
        print("expected: s, actual: \(s.s())")
    }

    func s() -> String { x }
}

@objc class K: NSObject, P {
    var y: Int = 2

    func foo(_ k: K) {
        print("expected: 2, actual: \(k.k())")
    }

    func k() -> Int { y }
}

func makeSP() -> P { S() }
func makeKP() -> P { K() }

func useP<T: P>(p1: T, p2: T) {
    p1.foo(p2)
}

// Segmentation fault: 11
useP(p1: makeSP(), p2: makeKP())

Swift.Error は自己準拠する

func useGenericError<T: Error>(error: T) {}

func useError(error: Error) {
    useGenericError(error: error)
}
  • 超特別待遇
  • メンバは何もないので問題はない

Existentialのopen


open

P から <T: P> を取り出す事を open と呼ぶ

func callFoo(p: P) {
    // ここでopenしている
    p.foo()
}

extensionによるopen

func proc<T: P>(_ t: T) { print(t) }

func useP(p: P) {
    // error: protocol 'P' as a type cannot
    //        conform to the protocol itself
    proc(p)
}

P が手元にあるけど <T: P> がほしい事がある


extension P {
    func callProc() {
        // let self: <Self: P>

        proc(self)
    }
}

func useP(p: P) {
    p.callProc()
}

protocol extensionのメソッドのselfはopenされた型 <Self: P> になっている。


existentialの実装


protocol P {
    func foo()
}

func useP(p: P) {
    p.foo()
}

  • Opaque Existential Containers12
struct OpaqueExistentialContainer {
  void *fixedSizeBuffer[3];
  Metadata *type;
  WitnessTable *witnessTables[NUM_WITNESS_TABLES];
};

%T1b1PP = type { [24 x i8], %swift.type*, i8** }

%__opaque_existential_type_1 = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1b4useP1pyAA1P_p_tF"(
    %T1b1PP* noalias nocapture dereferenceable(40) %0
) #0 {
entry:
  %p.debug = alloca %T1b1PP*, align 8
  %1 = bitcast %T1b1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  store %T1b1PP* %0, %T1b1PP** %p.debug, align 8
  %2 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 1
  %3 = load %swift.type*, %swift.type** %2, align 8
  %4 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 2
  %5 = load i8**, i8*** %4, align 8
  %6 = bitcast %T1b1PP* %0 to %__opaque_existential_type_1*
  %7 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(
    %__opaque_existential_type_1* %6, %swift.type* %3
  ) #3
  %8 = getelementptr inbounds i8*, i8** %5, i32 1
  %9 = load i8*, i8** %8, align 8, !invariant.load !22
  %10 = bitcast i8* %9 to void (%swift.opaque*, %swift.type*, i8**)*
  call swiftcc void %10(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %5)
  ret void
}

型の取り出し

[.code-highlight: 1, 13-14]

%T1b1PP = type { [24 x i8], %swift.type*, i8** }

%__opaque_existential_type_1 = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1b4useP1pyAA1P_p_tF"(
    %T1b1PP* noalias nocapture dereferenceable(40) %0
) #0 {
entry:
  %p.debug = alloca %T1b1PP*, align 8
  %1 = bitcast %T1b1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  store %T1b1PP* %0, %T1b1PP** %p.debug, align 8
  %2 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 1
  %3 = load %swift.type*, %swift.type** %2, align 8
  %4 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 2
  %5 = load i8**, i8*** %4, align 8
  %6 = bitcast %T1b1PP* %0 to %__opaque_existential_type_1*
  %7 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(
    %__opaque_existential_type_1* %6, %swift.type* %3
  ) #3
  %8 = getelementptr inbounds i8*, i8** %5, i32 1
  %9 = load i8*, i8** %8, align 8, !invariant.load !22
  %10 = bitcast i8* %9 to void (%swift.opaque*, %swift.type*, i8**)*
  call swiftcc void %10(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %5)
  ret void
}

PWTとその中の関数の取り出し

[.code-highlight: 1, 15-16, 21-23]

%T1b1PP = type { [24 x i8], %swift.type*, i8** }

%__opaque_existential_type_1 = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1b4useP1pyAA1P_p_tF"(
    %T1b1PP* noalias nocapture dereferenceable(40) %0
) #0 {
entry:
  %p.debug = alloca %T1b1PP*, align 8
  %1 = bitcast %T1b1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  store %T1b1PP* %0, %T1b1PP** %p.debug, align 8
  %2 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 1
  %3 = load %swift.type*, %swift.type** %2, align 8
  %4 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 2
  %5 = load i8**, i8*** %4, align 8
  %6 = bitcast %T1b1PP* %0 to %__opaque_existential_type_1*
  %7 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(
    %__opaque_existential_type_1* %6, %swift.type* %3
  ) #3
  %8 = getelementptr inbounds i8*, i8** %5, i32 1
  %9 = load i8*, i8** %8, align 8, !invariant.load !22
  %10 = bitcast i8* %9 to void (%swift.opaque*, %swift.type*, i8**)*
  call swiftcc void %10(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %5)
  ret void
}

値の取り出し

[.code-highlight: 1, 17-20]

%T1b1PP = type { [24 x i8], %swift.type*, i8** }

%__opaque_existential_type_1 = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1b4useP1pyAA1P_p_tF"(
    %T1b1PP* noalias nocapture dereferenceable(40) %0
) #0 {
entry:
  %p.debug = alloca %T1b1PP*, align 8
  %1 = bitcast %T1b1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  store %T1b1PP* %0, %T1b1PP** %p.debug, align 8
  %2 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 1
  %3 = load %swift.type*, %swift.type** %2, align 8
  %4 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 2
  %5 = load i8**, i8*** %4, align 8
  %6 = bitcast %T1b1PP* %0 to %__opaque_existential_type_1*
  %7 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(
    %__opaque_existential_type_1* %6, %swift.type* %3
  ) #3
  %8 = getelementptr inbounds i8*, i8** %5, i32 1
  %9 = load i8*, i8** %8, align 8, !invariant.load !22
  %10 = bitcast i8* %9 to void (%swift.opaque*, %swift.type*, i8**)*
  call swiftcc void %10(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %5)
  ret void
}

取り出した関数に、値、型、PWTを渡して呼び出す

[.code-highlight: 14, 16, 23, 24]

%T1b1PP = type { [24 x i8], %swift.type*, i8** }

%__opaque_existential_type_1 = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1b4useP1pyAA1P_p_tF"(
    %T1b1PP* noalias nocapture dereferenceable(40) %0
) #0 {
entry:
  %p.debug = alloca %T1b1PP*, align 8
  %1 = bitcast %T1b1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  store %T1b1PP* %0, %T1b1PP** %p.debug, align 8
  %2 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 1
  %3 = load %swift.type*, %swift.type** %2, align 8
  %4 = getelementptr inbounds %T1b1PP, %T1b1PP* %0, i32 0, i32 2
  %5 = load i8**, i8*** %4, align 8
  %6 = bitcast %T1b1PP* %0 to %__opaque_existential_type_1*
  %7 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(
    %__opaque_existential_type_1* %6, %swift.type* %3
  ) #3
  %8 = getelementptr inbounds i8*, i8** %5, i32 1
  %9 = load i8*, i8** %8, align 8, !invariant.load !22
  %10 = bitcast i8* %9 to void (%swift.opaque*, %swift.type*, i8**)*
  call swiftcc void %10(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %3, i8** %5)
  ret void
}

Existentialの実装のまとめ

  • Existential Containerは、真の値、その型、PWTをまとめたもの。
  • PWTから取り出した関数を呼び出す形式は、ジェネリック関数における <T: P> の操作と全く同じで、openしていると言える。

Existentialのまとめ

  • Existentialは <T: P> を保持するコンテナ
  • メソッド呼び出しによるopenで <T: P> を取り出せる
  • associated type, Self typeはvarianceの問題を含む
  • 実装では、真の値、その型、PWTを持っていて、これはGeneric関数を呼び出すときの引数と同じもの

GenericsとExistentialの違い


Generic Parameter Typeはある特定の型を意味する

func foo<T: P>(t: T) -> T {
    var u: T = t

    // NG
    // u = S()

    return u
}

<T> はそのコンテキストにおける、ある特定の型であり、 たとえ実行時にそうだったとしても、型チェックにおいては S でも K でもない。

引数で渡した t: T と、そのコピーの u: T や返り値の T は同じ型であり、実行時においても渡した型が返ってくる。


Existential Containerは特定の型を意味しない

func foo(p: P) -> P {
    var u: P = p

    // OK
    u = S()

    return u
}

P<T: P> を格納できるコンテナで、中身の型は動的に変化しうる。

引数で渡した p: P からコピーした u: P に対して、 既存の中身とは無関係に S を代入したり、それを返したりできる。


Type Erasure


Type Erasure

associated typeと自己準拠に対応したexistentialのようなものを自作する事があり、Type Erasureと呼ばれる。

標準ライブラリでは AnySequence<Element>, AnyHashable, KeyedDecodingContainer<K> などがある。

言語機能ではなく単なる実装パターンなので実装は割愛。


Opaque Result Types


Opaque Result Types

Improving the UI of generics の内容を受けて Swift 5.1 で実装された。

protocol P {
    func foo()
}

struct S: P {
    func foo() {}
}

func makeP() -> some P {
    S()
}

Reverse Generics

ORTは callee 側が決定する Generic Parameter Type と言える。 Reverse Genericsという架空の構文を考えると理解しやすい。

// ORT
func makeP() -> some P { S() }

// Reverse Generics
func makeP() -> <T: P> T { S() }

利用側の解釈

protocol P {
    associatedtype A
    func a() -> A
    func useA(a: A)
}

func main() {
    let p = makeP()
    let a = p.a()
    p.useA(a: a)
}

ORTはExistentialではなくGenericsなので、associatedtypeが使える。


makePの呼び出しが、型パラメータを導入し、決定していると考えるとわかりやすいかもしれない。

func main<T>() {
    //    ↑このTの真の型は
    //         ↓このmakePが決める
    let p: T = makeP()
    let a: T.A = p.a()
    p.useA(a: a)
}

func main() {
    let p = makeP()
    let p2 = makeP()
    let a = p2.a()
    p.useA(a: a)
}

pp2 は異なる変数だが、同じ 「makeP の返り値の型」なので、 同じ型であり、associatedtypeも同じ型。


型パラメータを書くとこう捉えられる。

func main<T>() {
    //    ↑このTは「makePの返り値の型」

    let p: T = makeP()
    let p2: T = makeP()
    let a: T.A = p2.a()
    p.useA(a: a)
}

外側にパラメータが露出しているのが気になるなら、こう考えても良い。

func main() {
    func body<T: P>(p: T) {
        let p: T = p
        let p2: T = makeP()
        let a: T.A = p2.a()
        p.useA(a: a)
    }

    body(p: makeP())
}

真の型はその関数ごとに定まるので、たとえ実際には同じ型になるとしても、異なる some P は別の型として扱われる。

func makeP() -> some P { S() }
func makeP2() -> some P { S() }

func main() {
    let p = makeP()
    let p2 = makeP2()
    let a = p2.a()
    // error: cannot convert value of type 
    //        '(some b.P).A' (associated type of protocol 'P')
    //        to expected argument type
    //        '(some b.P).A' (associated type of protocol 'P')
    p.useA(a: a)
}

これもパラメータ表示するとすぐわかる。

func main<T1, T2>() {
    let p: T1 = makeP()
    let p2: T2 = makeP2()
    let a: T2.A = p2.a()

    // T2.A は T1.A として渡せない
    p.useA(a: a)
}

ORTの実装


// c.swift
public protocol P {
    func foo() -> Int
}

struct S: P {
    func foo() -> Int { 1 }
}

public func makeP() -> some P {
    S()
}

// b.swift
import c

func main() {
    let p = makeP()
    p.foo()
}

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

makeP の返り値の真の型の取り出し

[.code-highlight: 1-2, 9]

; $s1c5makePQryFQOyQo_MD ---> 
; demangling cache variable for type metadata for <<opaque return type of c.makeP() -> some>>.0

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

真の型、VWT、型のサイズを取り出してローカル変数をスタックに確保する

[.code-highlight: 7-13, 15]

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

makeP の呼び出し。返り値を第1引数のopaque pointerで受け取る。

[.code-highlight: 17]

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

真の型の P の PWT を取得

[.code-highlight: 1, 20]

; $s1c5makePQryFQOMQ ---> opaque type descriptor for <<opaque return type of c.makeP() -> some>>

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

PWTから取り出した関数を呼び出す

[.code-highlight: 19-22]

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

VWTのdestroyでローカル変数を削除する

[.code-highlight: 23-26]

define hidden swiftcc void @"$s1b4mainyyF"() #0 {
entry:
  %p.debug = alloca i8*, align 8
  %0 = bitcast i8** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1c5makePQryFQOyQo_MD") #7
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !18, !dereferenceable !19
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !18
  %6 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %6)
  %7 = bitcast i8* %6 to %swift.opaque*
  store i8* %6, i8** %p.debug, align 8
  call swiftcc void @"$s1c5makePQryF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %7)
  %8 = call swiftcc i8** @swift_getOpaqueTypeConformance(i8* undef, %swift.type_descriptor* @"$s1c5makePQryFQOMQ", i64 1) #8
  %9 = getelementptr inbounds i8*, i8** %8, i32 1
  %10 = load i8*, i8** %9, align 8, !invariant.load !18
  %11 = bitcast i8* %10 to i64 (%swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc i64 %11(%swift.opaque* noalias nocapture swiftself %7, %swift.type* %1, i8** %8)
  %13 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %14 = load i8*, i8** %13, align 8, !invariant.load !18
  %destroy = bitcast i8* %14 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %7, %swift.type* %1) #9
  %15 = bitcast %swift.opaque* %7 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %15)
  ret void
}

__swift_instantiateConcreteTypeFromMangledNameswift_getTypeByMangledNameInContext のキャッシュ付きラッパー。

define linkonce_odr hidden %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* %0) #1 {
entry:
  %1 = bitcast { i32, i32 }* %0 to i64*
  %2 = load atomic i64, i64* %1 monotonic, align 8
  %3 = icmp slt i64 %2, 0
  %4 = call i1 @llvm.expect.i1(i1 %3, i1 false)
  br i1 %4, label %8, label %5

5:                                                ; preds = %8, %entry
  %6 = phi i64 [ %2, %entry ], [ %17, %8 ]
  %7 = inttoptr i64 %6 to %swift.type*
  ret %swift.type* %7

8:                                                ; preds = %entry
  %9 = ashr i64 %2, 32
  %10 = sub i64 0, %9
  %11 = trunc i64 %2 to i32
  %12 = sext i32 %11 to i64
  %13 = ptrtoint { i32, i32 }* %0 to i64
  %14 = add i64 %13, %12
  %15 = inttoptr i64 %14 to i8*
  %16 = call swiftcc %swift.type* @swift_getTypeByMangledNameInContext(i8* %15, i64 %10, %swift.type_descriptor* null, i8** null) #7
  %17 = ptrtoint %swift.type* %16 to i64
  store atomic i64 %17, i64* %1 monotonic, align 8
  br label %5
}

ORTの実装のまとめ

  • インターフェースはGenericsと同じopaque pointerを使うが、真の型やPWTは渡さない。
  • ORTの真の型とPWTは、そのdescriptorをランタイム関数に渡すことで、呼び出し側で取得できるようになっている。

新機能


SE-0335: Introduce existential any

protocol P {}

func useP(_ p: any P) {}

Existentialの型名が any P になった事で、<T: P> と別の概念であることが直感的にわかりやすくなる


Any と AnyObject は不要

func useAny(_ a: Any) {}
func useAnyObject(_ a: AnyObject) {}

すでに名前に Any の文字が付いていてわかりやすい


Existential Metatype Container

func useMetatype(_ p: any P.Type) {}

useMetatype(S.self)

P に準拠する型のメタタイプの型は any P.Type と表記する。 anyP.Type に対してかかっている。

従来は P.Type と書いていた。


Existential Container Metatype

func useExistentialMetatype(_ p: (any P).Type) {}

useExistentialMetatype((any P).self)

Existential Containerのメタタイプは (any P).Type と表記する。 値は (any P).self で生成できる。

従来な P.ProtocolP.self と書いていた。


Protocol Composition

func useExistential(_ e: any P & Q) {}
func useMetatype(_ t: any (P & Q).Type) {}
func useExistentialMetatype(_ t: (any P & Q).Type) {}

useExistential(S())
useMetatype(S.self)
useExistentialMetatype((any P & Q).self)

SE-0309: Unlock existentials for all protocols

protocol P {
    associatedtype A
}

func useP(_ p: any P) {}

あらゆるプロトコルが Existential を構成できるようになった


contravariantなassociated typeは使えない

protocol P {
    associatedtype A

    func a() -> A
    func useA(_ a: A)
}

func useP(_ p: any P) {
    // ok
    p.a()

    // error: member 'useA' cannot be used on value of type 'any P';
    //        consider using a generic constraint instead
    p.useA(1)
}

contravariantなSelf typeは使えない

protocol P {
    func p() -> Self
    func useP(_ p: Self)
}

func useP(_ p: any P) {
    // ok
    p.p()

    // error: member 'useP' cannot be used on value of type 'any P';
    //        consider using a generic constraint instead
    p.useP(p)
}

covariantなメンバはupper boundになる

protocol BP {}

protocol P {
    associatedtype A
    associatedtype B: BP
    func p() -> Self
    func a() -> A
    func b() -> B
}

func useP(_ p: any P) {
    let p2: any P = p.p()
    let a: Any = p.a()
    let b: any BP = p.b()
}

この upper boundなexistential型の生成は全く新しいロジック


複雑なcovariance

protocol P {
    func passSelf(_ f: (Self) -> Void)
}

func useP(_ p: any P) {
    p.passSelf { (p: any P) in
    }
}

継承時のconstraintによる型の固定

protocol P {
    associatedtype A
    func a() -> A
    func useA(_ a: A)
}

protocol Q: P where Self.A == Int {}

func useQ(q: any Q) {
    let a: Int = q.a()
    q.useA(1)
}

compositionによる型の固定

protocol P {
    associatedtype A
}

class C: P {
    typealias A = Int
}

protocol Q: P {
    func a() -> A
    func useA(_ a: A)
}

func useQ(q: any Q & C) {
    let a: Int = q.a()
    q.useA(1)
}

Existentialの

upper bound変換の実装


protocol BP {}

protocol P {
    associatedtype A
    associatedtype B: BP
    func p() -> Self
    func a() -> A
    func b() -> B
}

func callP(_ p: any P) -> any P {
    p.p()
}

Existential Containerの形は特に変わらない

%T1d1PP = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1d5callPyAA1P_pAaC_pF"(
    %T1d1PP* noalias nocapture sret(%T1d1PP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 4
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 1
  store %swift.type* %4, %swift.type** %12, align 8
  %13 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 2
  store i8** %6, i8*** %13, align 8
  %14 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %15 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %16 = bitcast %T1d1PP* %0 to %__opaque_existential_type_1*
  %17 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %16) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %17, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

引数のopenとwitnessメソッド取り出し

[.code-highlight: 3, 10-18]

define hidden swiftcc void @"$s1d5callPyAA1P_pAaC_pF"(
    %T1d1PP* noalias nocapture sret(%T1d1PP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 4
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 1
  store %swift.type* %4, %swift.type** %12, align 8
  %13 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 2
  store i8** %6, i8*** %13, align 8
  %14 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %15 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %16 = bitcast %T1d1PP* %0 to %__opaque_existential_type_1*
  %17 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %16) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %17, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

返り値の any P への型とwitness tableの書き込み

[.code-highlight: 2, 19-24]

define hidden swiftcc void @"$s1d5callPyAA1P_pAaC_pF"(
    %T1d1PP* noalias nocapture sret(%T1d1PP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 4
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 1
  store %swift.type* %4, %swift.type** %12, align 8
  %13 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 2
  store i8** %6, i8*** %13, align 8
  %14 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %15 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %16 = bitcast %T1d1PP* %0 to %__opaque_existential_type_1*
  %17 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %16) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %17, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

返り値 any P の値部分のポインタの取り出し

[.code-highlight: 25-26]

define hidden swiftcc void @"$s1d5callPyAA1P_pAaC_pF"(
    %T1d1PP* noalias nocapture sret(%T1d1PP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 4
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 1
  store %swift.type* %4, %swift.type** %12, align 8
  %13 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 2
  store i8** %6, i8*** %13, align 8
  %14 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %15 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %16 = bitcast %T1d1PP* %0 to %__opaque_existential_type_1*
  %17 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %16) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %17, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

witnessメソッドのpの呼び出し

[.code-highlight: 27-30, 18, 26, 15, 13, 11]

define hidden swiftcc void @"$s1d5callPyAA1P_pAaC_pF"(
    %T1d1PP* noalias nocapture sret(%T1d1PP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 4
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 1
  store %swift.type* %4, %swift.type** %12, align 8
  %13 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 2
  store i8** %6, i8*** %13, align 8
  %14 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %15 = getelementptr inbounds %T1d1PP, %T1d1PP* %0, i32 0, i32 0
  %16 = bitcast %T1d1PP* %0 to %__opaque_existential_type_1*
  %17 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %16) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %17, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

func callA(_ p: any P) -> Any {
    p.a()
}

define hidden swiftcc void @"$s1d5callAyypAA1P_pF"(
    %Any* noalias nocapture sret(%Any) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 5
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 10
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %16 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %17 = bitcast %Any* %0 to %__opaque_existential_type_0*
  %18 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%__opaque_existential_type_0* %17) #4
  call swiftcc void %11(%swift.opaque* noalias nocapture sret(%swift.opaque) %18, %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6)
  ret void
}

返り値の Any は witness table を持たないexistentialの形をしている

[.code-highlight: 1, 4]

%Any = type { [24 x i8], %swift.type* }

define hidden swiftcc void @"$s1d5callAyypAA1P_pF"(
    %Any* noalias nocapture sret(%Any) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 5
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 10
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %16 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %17 = bitcast %Any* %0 to %__opaque_existential_type_0*
  %18 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%__opaque_existential_type_0* %17) #4
  call swiftcc void %11(%swift.opaque* noalias nocapture sret(%swift.opaque) %18, %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6)
  ret void
}

引数の any P から真の型とWTの取り出し

[.code-highlight: 3, 10-13]

define hidden swiftcc void @"$s1d5callAyypAA1P_pF"(
    %Any* noalias nocapture sret(%Any) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 5
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 10
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %16 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %17 = bitcast %Any* %0 to %__opaque_existential_type_0*
  %18 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%__opaque_existential_type_0* %17) #4
  call swiftcc void %11(%swift.opaque* noalias nocapture sret(%swift.opaque) %18, %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6)
  ret void
}

ランタイム関数で associatedtype A の真の型を取り出し

[.code-highlight: 11, 13, 19-30]

define hidden swiftcc void @"$s1d5callAyypAA1P_pF"(
    %Any* noalias nocapture sret(%Any) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 5
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 10
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %16 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %17 = bitcast %Any* %0 to %__opaque_existential_type_0*
  %18 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%__opaque_existential_type_0* %17) #4
  call swiftcc void %11(%swift.opaque* noalias nocapture sret(%swift.opaque) %18, %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6)
  ret void
}

取り出した型を返り値の型としてセット、他同様

[.code-highlight: 30-32]

define hidden swiftcc void @"$s1d5callAyypAA1P_pF"(
    %Any* noalias nocapture sret(%Any) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1
) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 5
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 10
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %16 = getelementptr inbounds %Any, %Any* %0, i32 0, i32 0
  %17 = bitcast %Any* %0 to %__opaque_existential_type_0*
  %18 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_0(%__opaque_existential_type_0* %17) #4
  call swiftcc void %11(%swift.opaque* noalias nocapture sret(%swift.opaque) %18, %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6)
  ret void
}

func callB(_ p: any P) -> any BP {
    p.b()
}

define hidden swiftcc void @"$s1d5callByAA2BP_pAA1P_pF"(
    %T1d2BPP* noalias nocapture sret(%T1d2BPP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 6
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 11
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = call swiftcc i8** @swift_getAssociatedConformanceWitness(
    i8** %6, %swift.type* %4, %swift.type* %13, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
    )
  ) #9
  %16 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 2
  store i8** %15, i8*** %16, align 8
  %17 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %18 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %19 = bitcast %T1d2BPP* %0 to %__opaque_existential_type_1*
  %20 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %19) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %20, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

ランタイム関数で associatedtype B の真の型の取り出し

[.code-highlight: 18-29]

define hidden swiftcc void @"$s1d5callByAA2BP_pAA1P_pF"(
    %T1d2BPP* noalias nocapture sret(%T1d2BPP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 6
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 11
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = call swiftcc i8** @swift_getAssociatedConformanceWitness(
    i8** %6, %swift.type* %4, %swift.type* %13, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
    )
  ) #9
  %16 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 2
  store i8** %15, i8*** %16, align 8
  %17 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %18 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %19 = bitcast %T1d2BPP* %0 to %__opaque_existential_type_1*
  %20 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %19) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %20, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

ランタイム関数で associatedtype B の PB の PWT を取り出し、以下同様

[.code-highlight: 33-44]

define hidden swiftcc void @"$s1d5callByAA2BP_pAA1P_pF"(
    %T1d2BPP* noalias nocapture sret(%T1d2BPP) %0, 
    %T1d1PP* noalias nocapture dereferenceable(40) %1) #0 {
entry:
  %p.debug = alloca %T1d1PP*, align 8
  %2 = bitcast %T1d1PP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %2, i8 0, i64 8, i1 false)
  store %T1d1PP* %1, %T1d1PP** %p.debug, align 8
  %3 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 1
  %4 = load %swift.type*, %swift.type** %3, align 8
  %5 = getelementptr inbounds %T1d1PP, %T1d1PP* %1, i32 0, i32 2
  %6 = load i8**, i8*** %5, align 8
  %7 = bitcast %T1d1PP* %1 to %__opaque_existential_type_1*
  %8 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %7, %swift.type* %4) #4
  %9 = getelementptr inbounds i8*, i8** %6, i32 6
  %10 = load i8*, i8** %9, align 8, !invariant.load !39
  %11 = bitcast i8* %10 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %12 = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(
    i64 0, i8** %6, %swift.type* %4, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 11
    )
  ) #9
  %13 = extractvalue %swift.metadata_response %12, 0
  %14 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 1
  store %swift.type* %13, %swift.type** %14, align 8
  %15 = call swiftcc i8** @swift_getAssociatedConformanceWitness(
    i8** %6, %swift.type* %4, %swift.type* %13, 
    %swift.protocol_requirement* getelementptr (
        %swift.protocol_requirement, %swift.protocol_requirement* getelementptr inbounds (
            <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
        ), 
        i32 -1
    ), 
    %swift.protocol_requirement* getelementptr inbounds (
        <{ ... }>, <{ ... }>* @"$s1d1PMp", i32 0, i32 9
    )
  ) #9
  %16 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 2
  store i8** %15, i8*** %16, align 8
  %17 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %18 = getelementptr inbounds %T1d2BPP, %T1d2BPP* %0, i32 0, i32 0
  %19 = bitcast %T1d2BPP* %0 to %__opaque_existential_type_1*
  %20 = call %swift.opaque* @__swift_allocate_boxed_opaque_existential_1(%__opaque_existential_type_1* %19) #4
  call swiftcc void %11(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %20, 
    %swift.opaque* noalias nocapture swiftself %8, %swift.type* %4, i8** %6
  )
  ret void
}

Existentialの制限廃止のまとめ

  • どんなprotocolでもexistentialに使えるようになった
  • 困るvarianceのメンバは使用不能にした
  • covarianceではupper boundに包んだ
  • existentialの型の部分を先に埋めると、値の部分は通常のGenericsとABI互換性がある
  • associated typeの情報はランタイム関数で取り出せる

SE-0328: Structural opaque result types

func ortOptional() -> (some P)? { S() }

func ortTuple() -> (some P, some Q) { (S(), S()) }

func ortArray() -> [some P] { [S(), S()] }

func ortGeneric() -> C<some P> { C<S>() }

some が返り値の型パラメータ部分で使える


some Pはある一つの型を意味する

func ortArray() -> [some P] { ... }

// 気持ち
func ortArray() -> <T: P> [T] { ... }

配列であれば、要素の型は全て同一。


実装漏れ13

func ortFunc() -> () -> some P { { S() } }

提案書によるとこれもできるはずらしい


返す関数のパラメータ部分は駄目

func ortFuncParam() -> (some P) -> () { ... }

caller側で取得した関数が受け取る some P を作る方法が無い。14


S-ORTの実装


func ortTuple() -> (some P, some Q) { (S(), S()) }

func main() {
    let t = ortTuple()
}

SILを見ておく

// ortTuple()
sil @$s1e8ortTupleQr_QR_tyF : $@convention(thin) @substituted <τ_0_0, τ_0_1> () 
    -> (@out τ_0_0, @out τ_0_1) for <
        @_opaqueReturnTypeOf("$s1e8ortTupleQr_QR_tyF", 0) __, 
        @_opaqueReturnTypeOf("$s1e8ortTupleQr_QR_tyF", 1) __
    > 
{
    ...
}

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

返り値のメタタイプを取得

[.code-highlight: 1-5, 12-14]

; $s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD ---> 
;   demangling cache variable for type metadata for (
;     <<opaque return type of e.ortTuple() -> (some, some)>>.0, 
;     <<opaque return type of e.ortTuple() -> (some, some)>>.1
;   )

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

VWTを取り出して返り値のタプルのメモリ領域を確保

[.code-highlight: 9-15]

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

タプルの0番要素のポインタを取得

[.code-highlight: 17, 19]

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

タプルの1番要素のオフセットを取得して、ポインタを計算

[.code-highlight: 20-25]

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

タプルの2つの要素のポインタを引数で渡して返り値を取得

[.code-highlight: 19, 25, 26]

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

取得したタプルを削除

[.code-highlight: 1-5, 33]

; $s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh ---> 
;   outlined destroy of (
;     <<opaque return type of e.ortTuple() -> (some, some)>>.0, 
;     <<opaque return type of e.ortTuple() -> (some, some)>>.1
;   )

define hidden swiftcc void @"$s1f4mainyyF"() #0 {
entry:
  %t.debug = alloca i8*, align 8
  %0 = bitcast i8** %t.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD"
  ) #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %t = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %t)
  %6 = bitcast i8* %t to <{}>*
  store i8* %t, i8** %t.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  call swiftcc void @"$s1e8ortTupleQr_QR_tyF"(%swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1)
  %11 = call <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %6)
  %12 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %12)
  ret void
}

削除関数

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

返り値のタプルの0番要素の型を取得

[.code-highlight: 4]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

VWTを使って0番要素を削除

[.code-highlight: 11]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

タプルの型を取得

[.code-highlight: 12]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

タプルの第1要素のポインタを取得

[.code-highlight: 17]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

タプルの第1要素の型を取得

[.code-highlight: 19]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

VWTを使ってタプルの第1要素を削除

[.code-highlight: 26]

define linkonce_odr hidden <{}>* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tWOh"(<{}>* %0) #7 {
entry:
  %.elt = bitcast <{}>* %0 to %swift.opaque*
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo_MD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 1
  %5 = load i8*, i8** %4, align 8, !invariant.load !19
  %destroy = bitcast i8* %5 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %.elt, %swift.type* %1) #9
  %6 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo__AaBQr_QR_tyFQOyQo0_tMD") #8
  %7 = bitcast %swift.type* %6 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %0 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e8ortTupleQr_QR_tyFQOyQo0_MD") #8
  %12 = bitcast %swift.type* %11 to i8***
  %13 = getelementptr inbounds i8**, i8*** %12, i64 -1
  %.valueWitnesses2 = load i8**, i8*** %13, align 8, !invariant.load !19, !dereferenceable !20
  %14 = getelementptr inbounds i8*, i8** %.valueWitnesses2, i32 1
  %15 = load i8*, i8** %14, align 8, !invariant.load !19
  %destroy3 = bitcast i8* %15 to void (%swift.opaque*, %swift.type*)*
  call void %destroy3(%swift.opaque* noalias %.elt1, %swift.type* %11) #9
  ret <{}>* %0
}

ジェネリック型でsomeの個数を変えた場合

public struct C<A, B, C> {
    var a: A
    var b: B
    var c: C
    public func getA() -> A { a }
    public func getB() -> B { b }
    public func getC() -> C { c }
}
public func ortGeneric1() -> C<Int, Int, some P> {
    C<Int, Int, S>(a: 0, b: 1, c: S())
}
public func ortGeneric2() -> C<some P, (some P, some P), some P> {
    C<S, (S, S), S>(a: S(), b: (S(), S()), c: S())
}

func main1() {
    ortGeneric1().getC()
}

func main2() {
    ortGeneric2().getC()
}

全体の型

[.code-highlight: 1-4, 7]

; $s1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_GMD ---> 
;   demangling cache variable for type metadata for 
;   e.C<Swift.Int, Swift.Int, <<opaque return type of e.ortGeneric1() -> e.C<Swift.Int, Swift.Int, some>>>.0>

define hidden swiftcc void @"$s1f5main1yyF"() #0 {
entry:
  %0 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_GMD") #7
  %1 = bitcast %swift.type* %0 to i8***
  %2 = getelementptr inbounds i8**, i8*** %1, i64 -1
  %.valueWitnesses = load i8**, i8*** %2, align 8, !invariant.load !24, !dereferenceable !25
  %3 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %4 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %3, i32 0, i32 8
  %size = load i64, i64* %4, align 8, !invariant.load !24
  %5 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5)
  %6 = bitcast i8* %5 to %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G*
  %7 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e11ortGeneric1AA1CVyS2iQrGyFQOyQo_MD") #7
  %8 = bitcast %swift.type* %7 to i8***
  %9 = getelementptr inbounds i8**, i8*** %8, i64 -1
  %.valueWitnesses1 = load i8**, i8*** %9, align 8, !invariant.load !24, !dereferenceable !25
  %10 = bitcast i8** %.valueWitnesses1 to %swift.vwtable*
  %11 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %10, i32 0, i32 8
  %size2 = load i64, i64* %11, align 8, !invariant.load !24
  %12 = alloca i8, i64 %size2, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12)
  %13 = bitcast i8* %12 to %swift.opaque*
  %14 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to %swift.opaque*
  call swiftcc void @"$s1e11ortGeneric1AA1CVyS2iQrGyF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %14)
  %15 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to %T1e1CV*
  call swiftcc void @"$s1e1CV4getCq0_yF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %13, %swift.type* %0, %T1e1CV* noalias nocapture swiftself %15)
  %16 = call %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* @"$s1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_GWOh"(%T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6)
  %17 = getelementptr inbounds i8*, i8** %.valueWitnesses1, i32 1
  %18 = load i8*, i8** %17, align 8, !invariant.load !24
  %destroy = bitcast i8* %18 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %13, %swift.type* %7) #8
  %19 = bitcast %swift.opaque* %13 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19)
  %20 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20)
  ret void
}

opaque result typeの0番

[.code-highlight: 1-4, 18]

; $s1e11ortGeneric1AA1CVyS2iQrGyFQOyQo_MD ---> 
;   demangling cache variable for type metadata for <<
;     opaque return type of e.ortGeneric1() -> e.C<Swift.Int, Swift.Int, some>
;   >>.0

define hidden swiftcc void @"$s1f5main1yyF"() #0 {
entry:
  %0 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_GMD") #7
  %1 = bitcast %swift.type* %0 to i8***
  %2 = getelementptr inbounds i8**, i8*** %1, i64 -1
  %.valueWitnesses = load i8**, i8*** %2, align 8, !invariant.load !24, !dereferenceable !25
  %3 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %4 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %3, i32 0, i32 8
  %size = load i64, i64* %4, align 8, !invariant.load !24
  %5 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5)
  %6 = bitcast i8* %5 to %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G*
  %7 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e11ortGeneric1AA1CVyS2iQrGyFQOyQo_MD") #7
  %8 = bitcast %swift.type* %7 to i8***
  %9 = getelementptr inbounds i8**, i8*** %8, i64 -1
  %.valueWitnesses1 = load i8**, i8*** %9, align 8, !invariant.load !24, !dereferenceable !25
  %10 = bitcast i8** %.valueWitnesses1 to %swift.vwtable*
  %11 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %10, i32 0, i32 8
  %size2 = load i64, i64* %11, align 8, !invariant.load !24
  %12 = alloca i8, i64 %size2, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12)
  %13 = bitcast i8* %12 to %swift.opaque*
  %14 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to %swift.opaque*
  call swiftcc void @"$s1e11ortGeneric1AA1CVyS2iQrGyF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %14)
  %15 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to %T1e1CV*
  call swiftcc void @"$s1e1CV4getCq0_yF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %13, %swift.type* %0, %T1e1CV* noalias nocapture swiftself %15)
  %16 = call %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* @"$s1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_GWOh"(%T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6)
  %17 = getelementptr inbounds i8*, i8** %.valueWitnesses1, i32 1
  %18 = load i8*, i8** %17, align 8, !invariant.load !24
  %destroy = bitcast i8* %18 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %13, %swift.type* %7) #8
  %19 = bitcast %swift.opaque* %13 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19)
  %20 = bitcast %T1e1CVyS2iAA11ortGeneric1ACyS2iQrGyFQOyQo_G* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20)
  ret void
}

opaque return typeの3番

[.code-highlight: 1-5, 18]

; $s1e11ortGeneric2AA1CVyQrQR__QR0_tQR1_GyFQOyQo2_MD ---> 
;   demangling cache variable for type metadata for <<
;     opaque return type of e.ortGeneric2() -> e.C<some, (some, some), some>
;   >>.3

define hidden swiftcc void @"$s1f5main2yyF"() #0 {
entry:
  %0 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_GMD") #7
  %1 = bitcast %swift.type* %0 to i8***
  %2 = getelementptr inbounds i8**, i8*** %1, i64 -1
  %.valueWitnesses = load i8**, i8*** %2, align 8, !invariant.load !24, !dereferenceable !25
  %3 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %4 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %3, i32 0, i32 8
  %size = load i64, i64* %4, align 8, !invariant.load !24
  %5 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %5)
  %6 = bitcast i8* %5 to %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G*
  %7 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s1e11ortGeneric2AA1CVyQrQR__QR0_tQR1_GyFQOyQo2_MD") #7
  %8 = bitcast %swift.type* %7 to i8***
  %9 = getelementptr inbounds i8**, i8*** %8, i64 -1
  %.valueWitnesses1 = load i8**, i8*** %9, align 8, !invariant.load !24, !dereferenceable !25
  %10 = bitcast i8** %.valueWitnesses1 to %swift.vwtable*
  %11 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %10, i32 0, i32 8
  %size2 = load i64, i64* %11, align 8, !invariant.load !24
  %12 = alloca i8, i64 %size2, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %12)
  %13 = bitcast i8* %12 to %swift.opaque*
  %14 = bitcast %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G* %6 to %swift.opaque*
  call swiftcc void @"$s1e11ortGeneric2AA1CVyQrQR__QR0_tQR1_GyF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %14)
  %15 = bitcast %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G* %6 to %T1e1CV*
  call swiftcc void @"$s1e1CV4getCq0_yF"(%swift.opaque* noalias nocapture sret(%swift.opaque) %13, %swift.type* %0, %T1e1CV* noalias nocapture swiftself %15)
  %16 = call %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G* 
    @"$s1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_GWOh"(
      %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G* %6
    )
  %17 = getelementptr inbounds i8*, i8** %.valueWitnesses1, i32 1
  %18 = load i8*, i8** %17, align 8, !invariant.load !24
  %destroy = bitcast i8* %18 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %13, %swift.type* %7) #8
  %19 = bitcast %swift.opaque* %13 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %19)
  %20 = bitcast %T1e1CVyAA11ortGeneric2ACyQrQR__QR0_tQR1_GyFQOyQo_AadEyFQOyQo0__AadEyFQOyQo1_tAadEyFQOyQo2_G* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %20)
  ret void
}

番号の意味

public func ortGeneric1() -> C<Int, Int, some P> { ... }

public func ortGeneric2() -> C<some P, (some P, some P), some P> { ... }

// 気持ち

public func ortGeneric1() -> <T0: P> C<Int, Int, T0> { ... }
// C.CはT0

public func ortGeneric2() -> <T0: P, T1: P, T2: P, T3: P> C<T0, (T1, T2), T3> { ... }
// C.CはT3

Reverse Genericsで見たときのパラメータ型のインデックスに対応している?


S-ORT まとめ

  • 返り値でsomeを使える形が増えた
  • ランタイム関数でsomeを含む返り値自体の型を取得できる
  • 複数のsomeを含む場合、それぞれのsomeに番号が付いていて、個別の型の取得もできる

Named Opaque Types

Reverse Generics


実はもう実装があって試せる。

$ swiftc \
    -Xfrontend -enable-experimental-named-opaque-types \
    -emit-module i.swift

public protocol P {}
struct S: P {}

public func makeTuple() -> <T: P, U: P> (T, T, U) {
    (S(), S(), S())
}

func main() {
    let tuple = makeTuple()
}

こうすれば、Reverse Parameterは2つ、opaque typeは3つなので、番号付けの規則が調べられる。


define hidden swiftcc void @"$s1j4mainyyF"() #0 {
entry:
  %tuple.debug = alloca i8*, align 8
  %0 = bitcast i8** %tuple.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tMD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %tuple = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %tuple)
  %6 = bitcast i8* %tuple to <{}>*
  store i8* %tuple, i8** %tuple.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = bitcast %swift.type* %1 to %swift.tuple_type*
  %12 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %11, i64 0, i32 3, i64 2, i32 1
  %.2.offset = load i32, i32* %12, align 8
  %13 = bitcast <{}>* %6 to i8*
  %14 = getelementptr inbounds i8, i8* %13, i32 %.2.offset
  %.elt2 = bitcast i8* %14 to %swift.opaque*
  call swiftcc void @"$s1i9makeTupleQr_QrQR_tyF"(
    %swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1, %swift.opaque* noalias nocapture %.elt2)
  %15 = call <{}>* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tWOh"(<{}>* %6)
  %16 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16)
  ret void
}

シグネチャは some が3つ。

[.code-highlight: 1, 33-34]

; $s1i9makeTupleQr_QrQR_tyF ---> i.makeTuple() -> (some, some, some)

define hidden swiftcc void @"$s1j4mainyyF"() #0 {
entry:
  %tuple.debug = alloca i8*, align 8
  %0 = bitcast i8** %tuple.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tMD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %tuple = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %tuple)
  %6 = bitcast i8* %tuple to <{}>*
  store i8* %tuple, i8** %tuple.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = bitcast %swift.type* %1 to %swift.tuple_type*
  %12 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %11, i64 0, i32 3, i64 2, i32 1
  %.2.offset = load i32, i32* %12, align 8
  %13 = bitcast <{}>* %6 to i8*
  %14 = getelementptr inbounds i8, i8* %13, i32 %.2.offset
  %.elt2 = bitcast i8* %14 to %swift.opaque*
  call swiftcc void @"$s1i9makeTupleQr_QrQR_tyF"(
    %swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1, %swift.opaque* noalias nocapture %.elt2)
  %15 = call <{}>* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tWOh"(<{}>* %6)
  %16 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16)
  ret void
}

返り値の型情報を取得する箇所

[.code-highlight: 6-7]

define hidden swiftcc void @"$s1j4mainyyF"() #0 {
entry:
  %tuple.debug = alloca i8*, align 8
  %0 = bitcast i8** %tuple.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false)
  %1 = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName(
    { i32, i32 }* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tMD") #8
  %2 = bitcast %swift.type* %1 to i8***
  %3 = getelementptr inbounds i8**, i8*** %2, i64 -1
  %.valueWitnesses = load i8**, i8*** %3, align 8, !invariant.load !19, !dereferenceable !20
  %4 = bitcast i8** %.valueWitnesses to %swift.vwtable*
  %5 = getelementptr inbounds %swift.vwtable, %swift.vwtable* %4, i32 0, i32 8
  %size = load i64, i64* %5, align 8, !invariant.load !19
  %tuple = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %tuple)
  %6 = bitcast i8* %tuple to <{}>*
  store i8* %tuple, i8** %tuple.debug, align 8
  %.elt = bitcast <{}>* %6 to %swift.opaque*
  %7 = bitcast %swift.type* %1 to %swift.tuple_type*
  %8 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %7, i64 0, i32 3, i64 1, i32 1
  %.1.offset = load i32, i32* %8, align 8
  %9 = bitcast <{}>* %6 to i8*
  %10 = getelementptr inbounds i8, i8* %9, i32 %.1.offset
  %.elt1 = bitcast i8* %10 to %swift.opaque*
  %11 = bitcast %swift.type* %1 to %swift.tuple_type*
  %12 = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* %11, i64 0, i32 3, i64 2, i32 1
  %.2.offset = load i32, i32* %12, align 8
  %13 = bitcast <{}>* %6 to i8*
  %14 = getelementptr inbounds i8, i8* %13, i32 %.2.offset
  %.elt2 = bitcast i8* %14 to %swift.opaque*
  call swiftcc void @"$s1i9makeTupleQr_QrQR_tyF"(
    %swift.opaque* noalias nocapture %.elt, %swift.opaque* noalias nocapture %.elt1, %swift.opaque* noalias nocapture %.elt2)
  %15 = call <{}>* @"$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tWOh"(<{}>* %6)
  %16 = bitcast <{}>* %6 to i8*
  call void @llvm.lifetime.end.p0i8(i64 -1, i8* %16)
  ret void
}

$s1i9makeTupleQr_QrQR_tyFQOyQo__AcaBQr_QrQR_tyFQOyQo0_tMD ---> 
  demangling cache variable for type metadata for (
    <<opaque return type of i.makeTuple() -> (some, some, some)>>.0, 
    <<opaque return type of i.makeTuple() -> (some, some, some)>>.0, 
    <<opaque return type of i.makeTuple() -> (some, some, some)>>.1
  )

タプルの型は (0, 0, 1) なので、opaque return typeの番号は、Reverse Generic Parameterの並びに対応しているとわかる。


SE-0341: Opaque Parameter Declarations

func f(p: some P) { ... }

// 等価
func f<T: P>(p: T) { ... }

someを引数で使える。それぞれのsomeが個別の型パラメータとなる。


Structural

func foo(_ a: (some P, some P)) {}

もちろんタプルなどのジェネリックパラメータの中に使うこともできる。


引数の関数の引数部分はだめ

func foo(_ f: (some P) -> Void) {}

fooの中でfに渡す some P を作る方法がない


コード生成への影響はない

func foo(_ a: (some P, some P)) {}
define hidden swiftcc void @"$s1g3fooyyx_q_t_tAA1PRzAaCR_r0_lF"(
    %swift.opaque* noalias nocapture %0, 
    %swift.opaque* noalias nocapture %1, 
    %swift.type* %"<anonymous>", 
    %swift.type* %"<anonymous>1", 
    i8** %"<anonymous>.P", 
    i8** %"<anonymous>.P2"
) { ... }

; $s1g3fooyyx_q_t_tAA1PRzAaCR_r0_lF ---> 
;   g.foo<A, B where A: g.P, B: g.P>((A, B)) -> ()

デマングルすれば通常のジェネリクスになっている事が確認できる


SE-0346: Lightweight same-type requirements for primary associated types

func readSyntaxHighlightedLines(_ file: String) -> some Sequence<[Token]> {
  ...
}

プロトコル名の後ろに三角括弧でパラメータ指定して、associated typeの制約を書ける


宣言側でprimary指定する

protocol Sequence<Element> {
  associatedtype Element
  associatedtype Iterator : IteratorProtocol
    where Element == Iterator.Element
  ...
}

プロトコル名の後ろに三角括弧でパラメータ宣言しておく。 associated typeの名前を書く。


Generic Protocolとの衝突回避

associated typeはprotocol conformanceの際に固定する内部パラメータだが、 外部パラメータごとに異なるconformanceとみなすGeneric Protocolというアイデアがある。

三角括弧は将来そのような機能に使いそうだったが、今回別の用途に消費してしまった。 提案では、Generic Protocolには丸括弧の案が示されている。

protocol Convertible(from: Self, to: Other) {
  static func convert(_: Self) -> Other
}

extension Convertible(from: String, to: Int) {
  static func convert(_: String) -> Int
}

extension Convertible(from: String, to: Double) {
  static func convert(_: String) -> Int
}

コード生成への影響はない

protocol P<A> {
    associatedtype A
}

struct S<A>: P {}

func foo(a: some P<Int>) -> some P<Bool> { ... }
define hidden swiftcc void @"$s1g3foo1aQrx_tAA1PRzSi1ARtzlF"(
    %swift.opaque* noalias nocapture sret(%swift.opaque) %0, 
    %swift.opaque* noalias nocapture %1, 
    %swift.type* %"<anonymous>", 
    i8** %"<anonymous>.P"
) #0 { ... }

; $s1g3foo1aQrx_tAA1PRzSi1ARtzlF ---> 
;   g.foo<A where A: g.P, A.A == Swift.Int>(a: A) -> some

疑問: ORTとシグネチャ

// $s1g3fooQryF ---> g.foo() -> some
func foo() -> some P<Int> { ... }

// $s1g3fooQryF ---> g.foo() -> some
func foo() -> some Q { ... }

// $s1g3fooQr_QR_tyF ---> g.foo() -> (some, some)
func foo() -> (some Q, some Q) { ... }

ORTのジェネリック制約部分はそれ自体の制約(P)やassociated type(Int)を含め全くシグネチャに乗らないので、 制約ではオーバーロードできないが、形ではオーバーロードできる。


SE-0352: Implicitly Opened Existentials

protocol P {
    associatedtype A
}

func useSomeP(_ p: some P) {}

func useAnyP(_ p: any P) {
    useSomeP(p)
}

existentialを関数呼び出しのときにopenできる。つまり、anyをsomeに変換できる。


ハックが不要になった

extension P {
    func callUseSomeP() {
        useSomeP(self)
    }
}

func useAnyP(_ p: any P) {
    p.callUseSomeP(p)
}

技術的にはexistentialによるopenでできていた事を、 どこでも手間なしで使えるようになったとみなせる。


inoutにも渡せる

func useSomeP(_ p: inout some P) {}

func useAnyP(_ p: any P) {
    var p = p
    useSomeP(&p)
}

渡せない例

func cannotOpen1<T: P>(_ array: [T]) { }
func cannotOpen2<T: P>(_ a: T, _ b: T) { }
func cannotOpen3<T: P>(_ values: T...) { }
struct X<T> { }
func cannotOpen4<T: P>(_ x: X<T>) { }
func cannotOpen5<T: P>(_ x: T, _ a: T.A) { }
func cannotOpen6<T: P>(_ x: T?) { }

func cannotOpenDemo(
    array: [any P],
    p1: any P, p2: any P,
    xp: X<any P>,
    pOpt: (any P)?
) {
    cannotOpen1(array) // 要素の型が異なるかもしれないし、空かもしれない
    cannotOpen2(p1, p2) // p1とp2の型が異なるかもしれない
    cannotOpen3(p1, p2) // p1とp2の型が異なるかもしれない
    cannotOpen4(xp) // 中身を取り出す方法がない
    cannotOpen5(p1, p2.getA()) // p1とp2の型が異なるかもしれない
    cannotOpen5(p1, p1.getA()) // p1とp1.getA()の型の関連は追跡されない
    cannotOpen6(pOpt) // 中身がないかもしれない
}

openするには any P の中身の真の値が必要。


Existential Metatype Container

func useSomePType<T: P>(_ t: T.Type) {}

func useAnyPType(_ t: any P.Type) {
    useSomePType(t)
}

メタタイプのexistentialもopenできる


open結果のerase

protocol P {
    associatedtype A: AP
    func getA() -> A
}
protocol AP {}

func decompose<T: P>(_ p: T) -> (T, T.A) {
    (p, p.getA())
}

func useAnyP(_ p: any P) {
    let (p, a) = decompose(p)
}

openした関数の返り値はupper boundのexistentialにeraseされる。

func useAnyP(_ p: any P) {
    let (p, a): (any P, any AP) = decompose(p)
}

SE-0309のexistentialの制限排除で導入された、covariantなassociated valueがeraseされるのと同じ仕組み。

func useAnyP(_ p: any P) {
    let a: any AP = p.getA()
}

contravariantだとopenできない

func makeFunc<T: P>(_ p: T) -> (T) -> () {
    return { (_) in }
}

func useAnyP(_ p: any P) {
    // error: type 'any P' cannot conform to 'P'
    makeFunc(p)
}

invariantもopenできない

struct X<T> { var t: T }
func wrapX<T: P>(_ p: T) -> X<T> { ... }

func useAnyP(_ p: any P) {
    // error: type 'any P' cannot conform to 'P'
    wrapX(p)
}

ユーザ定義genericsはinvariantなのでcontravariant


引数の引数はcovariant

func acceptValueAndFunction<T: P>(_ value: T, body: (T) -> Void) { ... }

func testContravariantErasure(p: any P) {
  acceptValueAndFunction(p) { (innerValue: any P) in
    ...
  }
}

eraseしないでそのままbindする

func takeP<U: P>(_: U) -> Void { ... }

func implicitOpeningArguments(p: any P) {
  acceptValueAndFunction(p, body: takeP)
}

takePのUが直接pの真の型になる。 eraseしてから再度openしているとも見做せる。


erase時の制約喪失問題

protocol P {
  associatedtype A
}
protocol Q {
  associatedtype B: P where B.A == Int
}
func getBFromQ<T: Q>(_ q: T) -> T.B { ... }

func eraseQAssoc(q: any Q) {
  let b = getBFromQ(q)
}

getBFromQの返り値の型は <U: P> where P.A == Int だが、 b にeraseするときに any P になり、A == Int の制約が喪失する。


この問題はexistentialが制約を持てない事に起因する。 将来的に解決した場合、b の型はより正確になるが、ソース互換性が壊れる。

// b: any P ならこっち
func f<T: P>(_: T) -> Int { 17 }

// b: any P<Int> ならこっち
func f<T: P>(_: T) -> Double where T.A == Int { 3.14159 }

func eraseQAssoc(q: any Q) {
  let b = getBFromQ(q)
  // どっち?
  f(b)
}

そこで、制約が喪失するeraseには明示的なasを強制する。

func eraseQAssoc(q: any Q) {
    let b = getBFromQ(q) as any P
}

SE-0309も同じ問題があったので明示的なasを強制する。

extension Q {
  func getBFromQ() -> B { ... }
}

func eraseQAssocWithSE0309(q: any Q) {
  let b = q.getBFromQ() as any P
}

引数の評価順とopenの前後順問題

extension Int: P { }

func getP() -> any P {
  print("getP()")
  return 17
}

func acceptFunctionStringAndValue<T: P>(body: (T) -> Void, string: String, value: T) { ... }

func hello() -> String {
  print("hello()")
  return "hello"
}

func implicitOpeningArgumentsBackwards() {
  acceptFunctionStringAndValue(body: takeP, string: hello(), value: getP())
}

acceptFunctionStringAndValue(body: takeP, string: hello(), value: getP())

takePを評価するためには、<T> に真の型が束縛されなければならない。 しかし、 <T>getP の返り値をopenしなければ決定できない。 その結果、getP()takePhello() の順に評価され、副作用として以下が出力される。

getP()
hello()

これはSwiftの左から右に引数を評価する規則と矛盾する。


そこで、すでに左側で使われたジェネリックパラメータを、 それより右側でopenする事はできない、というルールを追加する。

// error: type 'any P' cannot conform to 'P'
acceptFunctionStringAndValue(body: takeP, string: hello(), value: getP())

openの互換性問題

func acceptsBox<T>(_ value: T) -> Any { [value] }

func passBox(p: any P) {
  let result = acceptsBox(p)
  // これまでは result: [any P]
  // これからは result: [open(any P)]
}

自己準拠するexistential(Error@objc)も問題になる。

func takeError<E: Error>(_ error: E) { ... }

func passError(error: any Error) {
  takeError(error)
  // これまでは E = any Error
  // これからは E = open(any Error)
}

将来、existentialを自己準拠させる文法が追加される場合、これについては互換性の問題は生じないのでopenする。

extension any P: P {}

既存の問題については互換性維持のため、Swift5ではopenしない事にする。 Swift6では互換性破壊できるので、openできるときはopenするように変更する。


open抑制記法

明示的なasによって、openを抑制できる。

// (1)
func f<T>(_: T) { }

// (2)
func f<T: P>(_: T) { }

func test(p: any P) {
    // pをopenして(2)を呼ぶ
    f(p)

    // pのopenを抑制するため(1)を呼ぶ
    f(p as any P)
}

open抑制の無効化記法

丸括弧を付けるとopenの抑制を無効化できる

func test(p: any P) {
    // openの抑制を無効化するため、openして(1)を呼ぶ
    f((p as any P))
}

制約喪失時のasの明示との干渉

protocol P {
  associatedtype A
}
protocol Q {
  associatedtype B: P where B.A == Int
}

func getP<T: P>(_ p: T)
func getBFromQ<T: Q>(_ q: T) -> T.B { ... }

func eraseQAssoc(q: any Q) {
    // A == Int が喪失するので as any P が必要
    getP(getBFromQ(q))

    // 制約喪失が許容されたが、openも抑制するためgetPが呼び出せない
    getP(getBFromQ(q) as any P)
  
    // 制約喪失を許容し、openの抑制を無効化したので、getPが呼び出せる
    getP((getBFromQ(q) as any P))
}

暗黙のopenまとめ

  • 関数呼び出しでopenできる
  • openした型が戻ってくる場合eraseされる
  • 抑制記法もある

SE-0353: Constrained Existential Types

primary associated typeを指定したexistentialが使える

any Collection<Int>

upper boundへのeraseでの制約の喪失を防ぐ

protocol P<A> {
    associatedtype A
}
protocol Q {
    associatedtype B: P where B.A == Int
}

Existentialのメンバにアクセスする時

extension Q {
    func getB() -> B
}

func useQ(_ q: any Q) {
    let b: any P<Int> = q.getB()
}

Openする時

func getB<T: Q>(_ q: T) -> T.B { ... }

func useQ(_ q: any Q) {
    let b: any P<Int> = getB(q)
}

Constrained Existential Typeの実装


protocol P<A> {
    associatedtype A
    func getA() -> A
}

func useIntP(_ p: any P<Int>) {
    let a: Int = p.getA()
}

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
entry:
  %p.debug = alloca %T1h1P_pySiXP*, align 8
  %1 = bitcast %T1h1P_pySiXP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %2 = alloca %TSi, align 8
  %a.debug = alloca i64, align 8
  %3 = bitcast i64* %a.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
  store %T1h1P_pySiXP* %0, %T1h1P_pySiXP** %p.debug, align 8
  %4 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 1
  %5 = load %swift.type*, %swift.type** %4, align 8
  %6 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 2
  %7 = load i8**, i8*** %6, align 8
  %8 = bitcast %T1h1P_pySiXP* %0 to %__opaque_existential_type_1*
  %9 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %8, %swift.type* %5) #5
  %10 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %10)
  %11 = getelementptr inbounds i8*, i8** %7, i32 2
  %12 = load i8*, i8** %11, align 8, !invariant.load !21
  %13 = bitcast i8* %12 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %14 = bitcast %TSi* %2 to %swift.opaque*
  call swiftcc void %13(%swift.opaque* noalias nocapture sret(%swift.opaque) %14, %swift.opaque* noalias nocapture swiftself %9, %swift.type* %5, i8** %7)
  %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0
  %15 = load i64, i64* %._value, align 8
  store i64 %15, i64* %a.debug, align 8
  %16 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %16)
  ret void
}

; $s1h7useIntPyyAA1P_pySiXPF ---> h.useIntP(h.P<Swift.Int>) -> ()

%T1h1P_pySiXP = type { [24 x i8], %swift.type*, i8** }

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
    ...
}

Intのメモリ確保とopaque型への変換

[.code-highlight: 6, 22]

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
entry:
  %p.debug = alloca %T1h1P_pySiXP*, align 8
  %1 = bitcast %T1h1P_pySiXP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %2 = alloca %TSi, align 8
  %a.debug = alloca i64, align 8
  %3 = bitcast i64* %a.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
  store %T1h1P_pySiXP* %0, %T1h1P_pySiXP** %p.debug, align 8
  %4 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 1
  %5 = load %swift.type*, %swift.type** %4, align 8
  %6 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 2
  %7 = load i8**, i8*** %6, align 8
  %8 = bitcast %T1h1P_pySiXP* %0 to %__opaque_existential_type_1*
  %9 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %8, %swift.type* %5) #5
  %10 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %10)
  %11 = getelementptr inbounds i8*, i8** %7, i32 2
  %12 = load i8*, i8** %11, align 8, !invariant.load !21
  %13 = bitcast i8* %12 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %14 = bitcast %TSi* %2 to %swift.opaque*
  call swiftcc void %13(%swift.opaque* noalias nocapture sret(%swift.opaque) %14, %swift.opaque* noalias nocapture swiftself %9, %swift.type* %5, i8** %7)
  %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0
  %15 = load i64, i64* %._value, align 8
  store i64 %15, i64* %a.debug, align 8
  %16 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %16)
  ret void
}

openして型、PWT、値の取り出し

[.code-highlight: 11-16]

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
entry:
  %p.debug = alloca %T1h1P_pySiXP*, align 8
  %1 = bitcast %T1h1P_pySiXP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %2 = alloca %TSi, align 8
  %a.debug = alloca i64, align 8
  %3 = bitcast i64* %a.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
  store %T1h1P_pySiXP* %0, %T1h1P_pySiXP** %p.debug, align 8
  %4 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 1
  %5 = load %swift.type*, %swift.type** %4, align 8
  %6 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 2
  %7 = load i8**, i8*** %6, align 8
  %8 = bitcast %T1h1P_pySiXP* %0 to %__opaque_existential_type_1*
  %9 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %8, %swift.type* %5) #5
  %10 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %10)
  %11 = getelementptr inbounds i8*, i8** %7, i32 2
  %12 = load i8*, i8** %11, align 8, !invariant.load !21
  %13 = bitcast i8* %12 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %14 = bitcast %TSi* %2 to %swift.opaque*
  call swiftcc void %13(%swift.opaque* noalias nocapture sret(%swift.opaque) %14, %swift.opaque* noalias nocapture swiftself %9, %swift.type* %5, i8** %7)
  %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0
  %15 = load i64, i64* %._value, align 8
  store i64 %15, i64* %a.debug, align 8
  %16 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %16)
  ret void
}

PWTから関数の取り出し

[.code-highlight: 19-21]

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
entry:
  %p.debug = alloca %T1h1P_pySiXP*, align 8
  %1 = bitcast %T1h1P_pySiXP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %2 = alloca %TSi, align 8
  %a.debug = alloca i64, align 8
  %3 = bitcast i64* %a.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
  store %T1h1P_pySiXP* %0, %T1h1P_pySiXP** %p.debug, align 8
  %4 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 1
  %5 = load %swift.type*, %swift.type** %4, align 8
  %6 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 2
  %7 = load i8**, i8*** %6, align 8
  %8 = bitcast %T1h1P_pySiXP* %0 to %__opaque_existential_type_1*
  %9 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %8, %swift.type* %5) #5
  %10 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %10)
  %11 = getelementptr inbounds i8*, i8** %7, i32 2
  %12 = load i8*, i8** %11, align 8, !invariant.load !21
  %13 = bitcast i8* %12 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %14 = bitcast %TSi* %2 to %swift.opaque*
  call swiftcc void %13(%swift.opaque* noalias nocapture sret(%swift.opaque) %14, %swift.opaque* noalias nocapture swiftself %9, %swift.type* %5, i8** %7)
  %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0
  %15 = load i64, i64* %._value, align 8
  store i64 %15, i64* %a.debug, align 8
  %16 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %16)
  ret void
}

関数呼び出し

[.code-highlight: 22, 23, 16, 12, 21]

define hidden swiftcc void @"$s1h7useIntPyyAA1P_pySiXPF"(%T1h1P_pySiXP* noalias nocapture dereferenceable(40) %0) #0 {
entry:
  %p.debug = alloca %T1h1P_pySiXP*, align 8
  %1 = bitcast %T1h1P_pySiXP** %p.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 8, i1 false)
  %2 = alloca %TSi, align 8
  %a.debug = alloca i64, align 8
  %3 = bitcast i64* %a.debug to i8*
  call void @llvm.memset.p0i8.i64(i8* align 8 %3, i8 0, i64 8, i1 false)
  store %T1h1P_pySiXP* %0, %T1h1P_pySiXP** %p.debug, align 8
  %4 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 1
  %5 = load %swift.type*, %swift.type** %4, align 8
  %6 = getelementptr inbounds %T1h1P_pySiXP, %T1h1P_pySiXP* %0, i32 0, i32 2
  %7 = load i8**, i8*** %6, align 8
  %8 = bitcast %T1h1P_pySiXP* %0 to %__opaque_existential_type_1*
  %9 = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* %8, %swift.type* %5) #5
  %10 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.start.p0i8(i64 8, i8* %10)
  %11 = getelementptr inbounds i8*, i8** %7, i32 2
  %12 = load i8*, i8** %11, align 8, !invariant.load !21
  %13 = bitcast i8* %12 to void (%swift.opaque*, %swift.opaque*, %swift.type*, i8**)*
  %14 = bitcast %TSi* %2 to %swift.opaque*
  call swiftcc void %13(%swift.opaque* noalias nocapture sret(%swift.opaque) %14, %swift.opaque* noalias nocapture swiftself %9, %swift.type* %5, i8** %7)
  %._value = getelementptr inbounds %TSi, %TSi* %2, i32 0, i32 0
  %15 = load i64, i64* %._value, align 8
  store i64 %15, i64* %a.debug, align 8
  %16 = bitcast %TSi* %2 to i8*
  call void @llvm.lifetime.end.p0i8(i64 8, i8* %16)
  ret void
}

Constrained Existential Typeのまとめ

  • 三角括弧での型パラメータ指定をexistentialにも適用した
  • upper boundがより正確になる
  • 利用箇所のシグネチャに影響する
  • Existentialのレイアウトには変化なし

新機能まとめ

  • someの用法が拡大した
  • anyが導入され、用法が拡大した
  • upper boundによるeraseの概念が追加された
  • 自然なopenができるようになった
  • primary associated typeが導入された

今後の展望

  • 非primaryなassociated typeのexistentialへの指定
  • existentialの自己準拠文法
  • Reverse Genericsによる自由なORT

Footnotes

  1. https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md

  2. https://github.com/apple/swift-evolution/blob/main/proposals/0328-structural-opaque-result-types.md

  3. https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md

  4. https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md

  5. https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md

  6. https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md

  7. https://github.com/apple/swift-evolution/blob/main/proposals/0353-constrained-existential-types.md

  8. https://github.com/apple/swift/blob/main/docs/Generics.rst

  9. https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md

  10. https://forums.swift.org/t/improving-the-ui-of-generics/22814

  11. https://github.com/apple/swift/blob/main/include/swift/ABI/ValueWitness.def

  12. https://github.com/apple/swift/blob/main/docs/ABI/TypeLayout.rst#existential-container-layout 2 3

  13. https://github.com/apple/swift/blob/main/test/type/opaque_return_structural.swift

  14. https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md#opaque-parameters-in-consuming-positions-of-function-types

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment