コンテンツにスキップ

ADR-0019: Built-in `object` Type for Udon Values

Accepted (partially superseded by ADR-0020 and ADR-0026)

ADR-0026により、objectへのsource null boxingとstate value: object = null;は廃止された。nullable objectはMaybe<object>で表す。Udon ABIのSystem.Object slotが内部placeholderとしてnullを保持できることと、通常の非null値をobjectへboxingする規則は維持する。

SobakasuはUdon-firstの言語であり、Udon externにはSystem.Objectを引数に取るAPIがある。従来のコンパイラにはTypeSymbol.Object、CLRのSystem.Objectからそのシンボルへの対応、extern候補選択時の大きな変換距離が部分的に存在していた。しかし、objectは組み込み型名として解決できず、変換はextern候補選択だけの特例だったため、ローカル、通常関数、戻り値、トップレベル状態を一貫して扱えなかった。

objectをSobakasuの正式な組み込み参照型とする。ソース名はobject、runtime/ABI名はSystem.Object、UASMデータ型はSystemObjectである。現在の型等価性、CLR型マッピング、外部バインディングとの区別を維持できるため、専用のTypeKind.Objectは追加せず、TypeKind.NamedかつIsBuiltIn == trueの単一のTypeSymbol.Objectとして表現する。

objectはUdonで表現可能な値を保持し、受け渡すための汎用値型である。C#互換の完全な型階層、dynamic、型検査の無効化、runtime reflection、実行時型に基づく動的メンバー解決ではない。

objectとUnityEngine.Objectは異なる型である。

object
-> System.Objectに対応するSobakasu組み込み型
UnityEngine.Object
-> Unityランタイムオブジェクトの基底型

期待型がobjectであるとき、次の具体型からの暗黙boxing変換を許可する。

  • bool、char
  • すべての組み込み整数型と浮動小数点型
  • string
  • null
  • Udonへ公開された外部バインディング型
  • コンパイラがUdonストレージとしてloweringできるその他の具体的なnamed型

()、内部Never、エラー型、namespace/module/method group疑似型、値としてloweringできない内部型はboxing対象にしない。Neverとエラー型に既存の到達不能・エラー回復規則が適用される場合も、それをboxingとは扱わない。

Binderは通常の代入・引数・戻り値の変換判定でboxingを確定する。IR Lowererは解決済みの対象型からSystemObjectのローカル、引数、戻り値、一時領域、状態スロットを作り、既存のCOPYで値を格納する。Udonのheap変数は初期型を持つ一方で実行時に値の型を保持でき、COPYはheap値を宛先へ複製するため、新しいboxing専用IR命令は導入しない。extern呼び出しでは、Binderが選択済みのSystem.ObjectシグネチャへUdonのheap値を渡す。UASM Assemblerはソース型やCLR型を再解決しない。

変換距離は完全一致や既存の具体的変換より大きくし、具体的な型一致を優先する。ただし、現時点のSobakasuにはユーザー向けオーバーロード機能を導入しない。

objectから具体型への暗黙変換は禁止する。このADRでは、明示的unboxing、as、is、型パターン、checked cast、runtime type testを導入しない。

期待型のない式をobjectへ変換しない。let value = 123;は従来どおりi32である。if分岐、loopのbreak型、配列を含む通常の型統一で、異なる型を共通のobjectへまとめない。boxingは型注釈、引数、戻り値など、期待型が明示された境界だけで適用する。

objectはローカル変数の型注釈、単純代入、関数とimplメソッドの引数・戻り値、トップレベル状態、extern引数・戻り値との照合で利用できる。

トップレベル状態では ADR-0025 の state value: object = null; を許可し、実行時の代入では通常のboxing規則を使用する。現在のpost-assemble heap patch manifestは具体的なTypeKindだけを永続化し、boxed値の元の型情報を保持できない。そのため、非nullのobject状態初期値は安全に復元できる形式が決まるまで診断SBK2090で拒否する。誤ったUASMや復元不能なpatchは生成しない。

objectはUdon同期可能型へ追加しない。sync state value: object = null;は同期非対応型として拒否する。同期しないpub stateは既存規則に従う。

メンバーとexternは引き続きBinderで静的に解決する。objectへ格納された値の実行時型を使って、新しいメンバーやextern候補を探索しない。静的なSystem.Objectの型情報から解決可能なメンバーがある場合だけ、その通常の静的解決を適用できる。

Preludeはlog、warning、errorを暗黙に公開し、それぞれobject引数を対応するUnityEngine.Debug externへ渡す。戻り値は()である。標準ライブラリのモジュール構造と規約ベースの読み込み方式は変更しない。

このADR単独では配列とobject[]を対象外とする。この制限はADR-0020により部分的にsupersedeされ、期待型付き[object]のboxingと、boxing元型を保持する配列heap patchが導入される。期待型のない異種配列推論、objectからのunboxing、単独の非null object状態初期値の制限は維持する。

明示的unboxing、runtime type test、dynamic dispatchも対象外であり、必要になった場合は別ADRで決定する。

  • ADR-0003: Binderで型と変換を確定し、IR/UASMは解決済み情報を出力する責務分離を維持する。
  • ADR-0005: 既存の組み込み型集合へobjectを追加する。数値型の仕様は変更しない。
  • ADR-0007: ローカルletの型注釈と推論済み変換先としてobjectを利用可能にする。letとmutの意味は変更しない。
  • ADR-0011: 関数の引数と戻り値でobjectを利用可能にする。関数呼び出しモデルは変更しない。
  • ADR-0013: objectを共有可能な参照として扱い、Rust風所有権は導入しない。
  • ADR-0014: トップレベル状態では利用可能だが、Udon同期型には含めない。
  • ADR-0016: ABI型をSystem.Objectとし、extern解決はBinderで完了させる。runtime reflectionとdynamic dispatchは導入しない。
  • ADR-0017: Sobakasu標準ライブラリからobjectを使ったUdon APIラッパーを提供する。

これらのADRをSupersededにはしない。本ADRは組み込み型集合、変換規則、extern境界を必要な範囲で拡張する。

  1. extern候補選択だけで任意の値をSystem.Objectへ適合させる。通常関数、ローカル、戻り値で一貫せず、backendが異なる型の意味を推測することになるため却下した。
  2. boxing専用のBound/IR命令を追加する。Udonの既存COPYとextern引数渡しでheap値を安全に扱えるため、現時点では追加の命令を持つ利点がない。
  3. TypeKind.Objectを追加する。現行のnamed型等価性、ABI名生成、extern catalogと自然に統合でき、heap patchの非null boxed値には別途元型の永続化が必要なため、専用enumだけを追加しても問題を解決しない。
  4. objectをすべての型推論の共通型にする。暗黙の型拡大が増え、配列や制御フローの既存の完全一致中心の型統一を変更するため却下した。
  5. objectから具体型への暗黙unboxingを許可する。実行時検査と失敗規則が必要で、静的型安全性を損なうため却下した。

単一のbuilt-inシンボルと期待型境界でのboxingにより、Sobakasuの静的型検査を維持しながらUdonのSystem.Object APIを利用できる。Binderで変換を確定し、既存の型付きストレージとCOPYへloweringすることで、ADR-0003の責務分離を保てる。型推論と配列の統一規則を変えないため、objectの導入が既存プログラムの推論結果を変えない。

  • Sobakasuソースでobjectを通常の組み込み型として記述できる。
  • primitive、文字列、外部バインディング値を通常関数とexternの両方へ渡せる。
  • 標準ライブラリのログ関数が値の具体型ごとの重複なしに利用できる。
  • 逆方向変換、同期、動的メンバー解決は静的に拒否される。
  • 配列と通常の型推論の既存挙動を維持できる。
  • objectから値を取り出す言語機能はまだない。
  • 非nullのobject状態初期値は、heap patch manifestがboxed値の元型を保持できるようになるまで利用できない。
  • objectはUdon同期変数として利用できない。
  • boxingには大きな変換距離を割り当てる必要があり、将来オーバーロードを正式導入する際には選択規則を改めて仕様化する必要がある。