ADR-0016: Impl Blocks, External Type Bindings, Extern Expressions, and Operator Overloading
Status
Section titled “Status”Accepted
Context
Section titled “Context”Sobakasuはトップレベル関数を宣言でき、リフレクションを通じてUdon extern候補を検出できるが、ソースレベルの型ラッパー、メソッド、関連関数、または外部への直接アクセスを示す明示的な境界を持たない。ADR-0008では外部インポートをuseに結び付けていた一方、ADR-0009ではユーザー定義の演算子オーバーロードを意図的に除外していた。Sobakasuで記述された標準ライブラリがUnityおよびVRChatの型をラップするには、名前解決や型解決をUASMバックエンドへ移すことなく実現する必要がある。
ADR-0003では、Parserが構文を保持し、Binderが名前と型を解決し、IRが選択済みの呼び出しを保持し、UASMアセンブラが選択済みのexternシグネチャを出力することを求めている。ADR-0015では、引数がゼロの呼び出し可能要素において括弧を省略可能とすることがすでに定められている。
Decision
Section titled “Decision”implと外部型バインディング
Section titled “implと外部型バインディング”Sobakasuは次の宣言を採用する。
pub impl GameObject = extern UnityEngine.GameObject { pub fn set_active(active: bool) { extern self.SetActive(active); }}
impl GameObject { pub fn toggle { self.set_active(!self.active?); }}外部バインディングは新しいSobakasu型を宣言し、そのUdon/CLR ABI型を記録する。そのpub修飾子は、モジュールがその型をインポートできるかどうかを制御する。通常のimplは既知の型を拡張し、型レベルのpub修飾子を持たない。組み込み型をバインドすること、1つのSobakasu型を二重にバインドすること、または1つのランタイム型を複数のSobakasu型へバインドすることはエラーとなる。通常のimplはプリミティブ型を拡張してもよい。
外部バインディング型は、名前解決およびオーバーロード解決においては別個のSobakasu型である一方、データの格納とexternシグネチャにはバインド先のランタイム型を使用する。これはABIマッピングであり、暗黙の型変換でも型エイリアスでもない。
メソッド、self、Self、関連関数
Section titled “メソッド、self、Self、関連関数”impl内のfnはインスタンスメソッドである。Binderはイミュータブルな暗黙のself: Selfを導入し、selfという名前の明示的なパラメーターは拒否される。Selfは、それを含むSobakasu型へ解決される。static fnは関連関数であり、レシーバーを持たない。その中でselfを参照するとエラーになる。
メソッドは既存の型付き関数本体とインライン呼び出しのloweringを再利用する。バインド済みの呼び出しは、レシーバーを明示的な引数とは別に格納する。loweringはレシーバーを一度だけ評価し、それを合成されたselfパラメーターへバインドした後、既存のインラインreturn経路を使用する。引数がゼロの宣言と呼び出しはADR-0015に従い、ドキュメントでは括弧なしの形式を標準とする。
メソッドは、明示的なパラメーターの型によってオーバーロードできる。完全一致を優先し、次に既存のプリミティブ拡大変換を優先する。最良候補が同順位の場合は曖昧となる。同じ型内でのシグネチャの重複は拒否される。
extern式
Section titled “extern式”externはキーワードであり、マクロではない。これは1つの完全な外部アクセス式の前に置かれ、すべてコンパイル時に解決される。
extern UnityEngine.Debug.Log(value)extern self.SetActive(active)extern self.activeSelfextern self.name = valueextern new Self(x, y, z)extern self + rhsextern -selfBinderは、staticメソッドとインスタンスメソッド、プロパティとフィールドのgetter/setter、コンストラクター、外部の単項演算子と二項演算子を解決する。引数は通常のSobakasu式のままである。Selfおよび外部バインディングされたSobakasu値は、外部候補との照合中に限り、それぞれのABIランタイム型へ変換され、一致するランタイムの結果はSobakasuのラッパー型へ戻される。
リフレクションは候補を検出するが、シグネチャの型と最終的なexternシグネチャがUdonによって公開されている候補だけが呼び出し可能である。選択済みのexternシグネチャと、必要な場合のインスタンスレシーバーを含む順序付き引数リストは、バインド済みツリーとIRに格納される。ランタイムリフレクション、動的ディスパッチ、動的な名前、バックエンドでの再解決は存在しない。
演算子オーバーロード
Section titled “演算子オーバーロード”二項演算子メソッドは、その記号をメソッド名として使用し、明示的な右辺パラメーターを1つ取る。単項演算子メソッドは、+、-、!、~のいずれかの前に@を付け、明示的なパラメーターを取らない。
impl Vector3 { pub fn +(rhs: Self) -> Self { extern self + rhs } pub fn @- -> Self { extern -self }}オーバーロード可能な二項演算子は+ - * / % == != < <= > >= & | ^ << >>である。&&、||、代入、複合代入の記号は演算子として宣言できない。比較演算子はboolを返さなければならない。単項・二項の引数の個数は宣言時に検査される。
lhs op rhsについて、Binderは次の順序で解決する。
- コンパイラ組み込み演算子との完全一致
- 左オペランドの型に定義されたメソッドとの完全一致
- 既存のプリミティブ拡大変換によって適用可能なメソッド
- それ以外の場合、利用不可能または曖昧なオーバーロードの診断
コンパイラに既存のプリミティブ演算子シグネチャは再定義できない。競合しないメソッドと演算子はプリミティブ型へ追加してもよい。&&と||はADR-0009の短絡評価CFG loweringを維持し、複合代入は引き続き、その基となる演算子と代入を通じてloweringされる。解決済みのユーザー演算子は、他の解決済みメソッド呼び出しとまったく同じようにloweringされる。
プリミティブABI型
Section titled “プリミティブABI型”bool、char、i8、u8、i16、u16、i32、u32、i64、u64、f32、f64、string、()は、引き続きコンパイラ組み込み型である。それぞれのABIランタイムマッピングは、順にSystem.Boolean、System.Char、System.SByte、System.Byte、System.Int16、System.UInt16、System.Int32、System.UInt32、System.Int64、System.UInt64、System.Single、System.Double、System.String、System.Voidである。
ADR-0009への変更
Section titled “ADR-0009への変更”このADRは、ADR-0009におけるユーザー定義の演算子オーバーロードの除外のみを置き換える。ADR-0009の優先順位、結合規則、Binderが担う解決、明示的な短絡評価IR、複合代入のlowering、出力のみを行うバックエンドは引き続き有効である。
Alternatives
Section titled “Alternatives”externをマクロとして扱う
Section titled “externをマクロとして扱う”マクロ展開では、型付きの候補選択とフロントエンド/バックエンドの責務境界を維持できないため却下した。
メソッドを新しいランタイム呼び出しフレームとして表現する
Section titled “メソッドを新しいランタイム呼び出しフレームとして表現する”初期実装では却下した。既存の型付き関数とインラインloweringを拡張することで、メソッドのセマンティクスを現在の関数および再帰制限と一貫させられる。
UASMアセンブラでメンバーや演算子を解決する
Section titled “UASMアセンブラでメンバーや演算子を解決する”バインド後にソースレベルの名前解決とオーバーロード解決を繰り返すことになり、ADR-0003に違反するため却下した。
すべてのpublicなCLRメンバーを許可する
Section titled “すべてのpublicなCLRメンバーを許可する”CLRでpublicであることは、Udonで公開されていることを意味しないため却下した。
Rationale
Section titled “Rationale”明示的な外部バインディングにより、Sobakasuはランタイム型に安定した名前を与えられる一方、externは外部への直接アクセスごとにunsafe/外部境界を明示する。リフレクションカタログと関数のloweringを再利用することで、並行するコンパイラ機構を最小限に抑えられる。メソッド、演算子、外部オーバーロードの解決をBinderに保持することで、IRとバックエンドは決定的なままとなる。
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- Sobakasuライブラリは、Udonで公開されたUnityおよびVRChatのAPIをラップできる。
- メソッドと演算子は、既存の型検査とインラインloweringを共有する。
- ランタイム型のマッピングは一元化され、プリミティブ型とラッパー型で再利用できる。
- 外部への直接アクセスは明示的であり、コンパイル時に検査される。
Negative
Section titled “Negative”- 型の収集とメソッドの収集には、Binderの追加パスが必要となる。
- インラインメソッド呼び出しは、生成されるコードのサイズを増加させる可能性がある。
- サポートするすべてのメンバー種別について、リフレクションメタデータとUdonノードシグネチャの両方が利用可能でなければならない。
- ユーザー定義変換、ジェネリックimpl、トレイト、継承は引き続き利用できない。