Open9

Compiling Swift Generics を読む

おもちメタルおもちメタル

1-1 Functions

generic signature, interface type, substitution mapなどが説明された。
archetypeも少し出てきた。

おもちメタルおもちメタル

1-2 Nominal Types

ジェネリック型が出てきた
レイアウトやtype metadata, metadata access functionなどが説明された。

1-3 Protocols

これがあることでジェネリックな型に対してメソッドを呼ぶなどができるようになる
conformanceのためにwitness tableが渡される。


ここまでの内容でジェネリクスの実行時の仕組みの概要はかなり説明されている。

おもちメタルおもちメタル

1-4 Associated Types

dependent member typeが出てきた。
また、I.[IteratorProtocol]Element 形式の記述が出てきた。
bound/unboundを明示的に表現する事が後の議論で生きてくる。

archetypeの説明が追加された。
archetypeは仮想的な型で、generic signatureと紐づいている。
type parameterはただの名前で、generic signatureが外から紐づくのとは違う。

ジェネリック関数の中でジェネリック型を使うようなことをすると、type parameterをarchetypeで置換するようなことが起きる。

おもちメタルおもちメタル

1-5 Associated Requirements

associated typeにもrequirementが課せるという話。
これはランタイムではwitness tableの中でwitness table access functionをもたらす。

1-6 Related Work

Haskellにはassociated typeはあるけどassociated requirementはないという話が書いてあった。GPTによるとそうらしくて、似たことをやるには構造的に別の方法で記述するらしいが、知識不足で理解できなかった。
C++ Conceptsもルーツの時点ではassociated requirementについてはフワッとしていたらしい。でもGPTによると今はほぼ同じ形で書けるっぽいな。
RustはSwiftにない表現ができるが、same type requirementは制限があるらしい。whereもやや弱くて、再記述が生じるとか。

おもちメタルおもちメタル

2-1 Name Lookup

Swiftコンパイラにはname bindingフェーズがなくて、オンデマンドでname lookupが行われる。特に意識していなくて普通だと思っていたけど確かにそうだ。また、Swiftのスコープの親子関係はソース範囲の包含関係を保っているらしい。本当?
Unqualified lookupはスコープベースの上方向への探索。ソースレベルまで到達したら、次はモジュールレベルを調べて、最後にインポートしているモジュールを調べる。Qualified lookupはメンバの探索。単体のエンティティについては Direct lookupで、その先で継承しているプロトコルと親クラスも調べる。プロトコルと親クラスではsubstitutionが起きる。親クラスにも Self シンボルがあるからか。モジュール名にメンバアクセスする場合は Module lookup という。objCのDynamic lookupと演算子のOperator lookupについても説明された。

おもちメタルおもちメタル

2-2 Delayed Parsing

コンパイルにおいて、プライマリファイルは完全にパースされるが、セカンダリファイルは高速モードでトップレベルのシンボルだけ収集される。
プライマリファイルのタイプチェックの過程で必要に応じてセカンダリファイルの一部をちゃんとパースする。
Dynamic Lookup や Operator Lookup については、それが生じると、影響しうる領域を全てパースする。
つまり、AnyObjectに対するメソッド呼び出しは、全てのclass定義のパースを生じさせてしまう。

おもちメタルおもちメタル

2-3 Request Evaluator

コンパイラのさまざまな処理はリクエスト化されていて、共通のevaluatorを通して処理される。これはパラメータによるメモ化と、サイクルの検出を行う。例えばクラスの親が相互に継承されている場合など、この枠組みで対処できる。トップレベルコードがそれより下に書かれた関数を呼び出す場合、その関数のinterface type requestは呼び出し時に解決され、その後で宣言を処理するときにはキャッシュが使われる。