跳转到内容

ADR-0017: Standard Library Modules and Extern Boundary

此内容尚不支持你的语言。

Accepted

ADR-0008では、useをCLR名前空間、型、staticメソッドのコンパイル時インポートとして定義していた。この設計ではSobakasuのモジュールグラフを提供できず、ソースモジュールのインポートと外部APIの検出という両方の責務を同じ構文が担うことになる。ADR-0016で明示的なextern境界と外部型ラッパーが導入されたため、今後useはSobakasuの宣言専用にできる。

標準ライブラリはSobakasuで記述され、コンパイラによって直接読み込まれ、呼び出し元の単一のUdonプログラムへ統合されなければならない。ライブラリの読み込みによって、UdonBehaviourの状態やイベントエントリーポイントが暗黙に追加されてはならない。

初期決定では論理モジュール名と物理パスをmanifest.jsonで対応付けた。しかし階層モジュールが論理名の.とディレクトリの/を一対一に対応させたため、マニフェストのモジュール一覧はソース配置と同じ情報を重複して保持する設定になった。この規模では、名前とパスを任意に分離できる利点より、追加・移動時に二箇所を同期するコストと不整合の可能性が大きい。

標準ライブラリのソースは次の場所に置く。

Packages/com.skytomo221.sobakasu/StandardLibrary~/

これらはコンパイラ内部のモジュール入力であり、Project ウィンドウへ個別に import される .sobakasu Udon Program Source ではない。論理モジュール名は次の単一規約で物理パスへ変換する。

  1. 論理名の.を/へ置換する。
  2. 末尾へ.sobakasuを付ける。
example.math
-> example/math.sobakasu

コンパイラはモジュール一覧を列挙するマニフェストを使用しない。useまたはmodで到達した論理名について、この規約から得たファイルだけを確認する。ルート配下の全.sobakasuファイルを走査することもしない。

唯一の組み込み特別規約として、StandardLibrary~/prelude.sobakasuが存在する場合、それを論理モジュールpreludeかつ暗黙Preludeとして読み込む。ファイルが存在しない場合は暗黙Preludeなしとする。将来パッケージ名、バージョン、依存関係などのメタデータが必要になった場合は、それらを表現するマニフェストを別途再導入できるが、モジュール一覧や論理名とパスの対応を重複記載しない。

公開ルート名は、このADRでは意図的に標準化しない。初期のexample.*ソースは内部検証用モジュールとする。

モジュールパスには.を使用する。::、glob、グループ化、相対インポート、パッケージインポートは導入しない。

use example.math.twice;
use example.math.twice as double_value;

Module Resolverは、use example.math.twice;に対して、まずexample/math/twice.sobakasuをモジュールとして確認し、存在しなければexample/math.sobakasuの宣言twiceとして解釈する。対象モジュールを一度だけ読み込み、そのuseとmodを再帰的に解決し、モジュールの欠落と循環を検出する。規約変換はSobakasu識別子だけを入力とするため、任意の絶対パスや..によるルート外への逸脱を受け付けない。

読み込まれた各ソースは、その論理名と正規化された物理パスを保持する。Parserの結果は、正規化されたパスとソース内容の完全一致によってメモリ内にキャッシュされる。モジュール位置は解決のたびに規約から再計算されるため、ソースが追加、移動、削除された場合に古い対応が再利用されることはない。

useはSobakasuのモジュールグラフだけを検索し、publicな宣言だけをインポートする。リフレクションメタデータ、CLR名前空間または型、externカタログ、互換性シンボルは一切検索しない。外部APIには、ADR-0016のextern式を通じてのみアクセスできる。これは、ADR-0008の外部use形式と、失敗時のすべてのフォールバック動作を置き換える。

Binderはモジュールごとに宣言インデックスを構築し、エイリアスを適用し、エイリアスの重複とインポートされた名前の曖昧さを診断し、通常のローカル変数/パラメーターによるシャドーイングを維持し、単純名が一致するというだけでモジュールローカルなシンボルが衝突することを防ぐ。インポートされた関数、型、impl、演算子は、宣言元モジュールの識別情報を保持する。

コンパイラはエントリーソースを解析し、到達可能な標準ライブラリモジュールを解決し、統合されたモジュールグラフをバインドし、到達可能な呼び出しを1つのBoundProgram、1つのIRプログラム、1つのUdonプログラムへloweringする。標準ライブラリの関数とメソッドは既存のインラインloweringを使用するため、独立したランタイムプログラムにはならない。各論理モジュールはグラフに追加され、その宣言は一度だけインデックス化される。関数本体は通常のインライン呼び出し箇所ごとに展開される。アセンブラはファイルリゾルバーもモジュールリゾルバーも受け取らない。

初期実装では読み込んだモジュール内のすべての宣言をバインドする一方、呼び出されたインライン関数だけが命令を生成する。将来、非インラインの内部呼び出しモデルによって、モジュールのセマンティクスを変更することなく宣言レベルのデッドコード削除を追加してもよい。

標準ライブラリモジュールには、use、publicまたはprivateな関数、通常のimpl、外部バインディングのimplを含められる。ADR-0025 により public または private な compile-time const も許可する。pubおよびsync形式を含むトップレベルの state 宣言と、すべてのイベント宣言はコンパイル時エラーとなる。型エイリアスは引き続き今後の課題とする。

Module Resolver/コンパイラドライバーは、物理ルート、論理名からパスへの規約変換、組み込みPrelude、ソースの読み込み、キャッシュ、依存関係グラフ、循環の検証を担う。Parserは構文だけを担う。Binderはモジュールシンボル、可視性、エイリアス、曖昧さ、型、メソッド、演算子、externの選択を担う。IR lowererは解決済みの宣言を統合する。UASMアセンブラは解決済みIRの出力だけを行い、モジュールや外部名を検索することはない。

インポートの失敗が暗黙の外部境界を引き続き越えることになり、モジュール名がCLR名前空間と衝突する可能性があるため却下した。

StandardLibrary~を論理ルートとして使用する

Section titled “StandardLibrary~を論理ルートとして使用する”

StandardLibrary~はコンパイラが知る格納場所であり、利用者向けの論理名にパッケージ内部のディレクトリ名を露出させるべきではないため却下した。

各モジュールを独立したUdonプログラムとしてコンパイルする

Section titled “各モジュールを独立したUdonプログラムとしてコンパイルする”

ランタイムリンクが必要となり、1つのbehaviour内で通常の関数/メソッドのセマンティクスを提供できないため却下した。

ルート配下のすべての.sobakasuファイルを検出する

Section titled “ルート配下のすべての.sobakasuファイルを検出する”

到達不能な検証用・作業中ファイルまでコンパイラ入力となるため却下した。規約は論理名から要求されたパスを得るためだけに使用し、ルート全体の列挙には使用しない。

モジュール一覧マニフェストを維持する

Section titled “モジュール一覧マニフェストを維持する”

同じ論理名とパス対応をソース配置と設定ファイルへ二重に記述することになり、追加や移動のたびに同期が必要になるため却下した。パッケージ名、バージョン、外部依存関係など、ファイル規約だけでは表現できないメタデータが必要になった時点で再検討する。

論理名から物理パスへの単一規約により、追加設定なしでモジュール名を決定的にできる。到達したパスだけを確認するため、全ファイル走査も不要である。モジュールの読み込みと外部要素の検出を分離することで、useとexternはそれぞれ1つの責務だけを持つ。コンパイル時の統合は、既存のインライン関数モデルとUdonの単一プログラム実行モデルに合致する。

  • useは、Sobakasuのみを対象とする決定的な解決を行う。
  • 外部APIへのアクセスは、externを通じて可視化され、レビュー可能となる。
  • 標準ライブラリコードは、呼び出し元の型、メソッド、IR、Udonプログラムを共有する。
  • モジュール追加時に一覧設定を更新する必要がなく、ソース配置との不整合が発生しない。
  • 規約から得た到達可能なパスだけを確認し、任意パスやディレクトリ外への逸脱を受け付けない。
  • ライブラリのインポートによって、永続的な状態やイベントハンドラーが注入されることはない。
  • コンパイルには、バインド前のファイルシステムへのアクセスが必要となる。
  • 論理モジュール名と物理配置を独立に変更することはできない。
  • ファイルをまたぐ診断は、spanだけでなくソースの識別情報も保持しなければならない。
  • 初期検証用の名前空間は、恒久的な標準ライブラリAPIではない。
  • パッケージ外のユーザーモジュールとディスクキャッシュは、引き続き今後の課題である。