RFC-029f: コンパイル対象ロールとインポート面セマンティクス
摘要
RFC-029「子RFC計画」の拡張スロットを実現する(029b–029eは既に占有済み、029fに順延):ソースファイルにコンパイル対象ロールモデル(Script / Bin / Lib / Test / Internalの5種類)およびそのインポート面セマンティクスを定義する。manifestがない場合はエントリ到達性で推測(ゼロコンフィグ)、manifestがある場合は [lib] / [[bin]] / [exports](RFC-015で既に定義されたフィールド)で明示宣言する。四つの未確定セマンティクスを埋める:パッケージ間インポート面の算出基準、デッドコード警告のpub免除適用範囲(#321 決定B)、[exports]とRegistryの到達性の関係、およびRFC-014b [binaries](プリコンパイル配布成果物)とbinロールソースファイルの用語曖昧性解消。
動機
ホストと境界の根拠
RFC-029(承認済み)のエントリ選択と可視性決定は本RFCの直接の上流である:
エントリファイル選択の優先順位:1.
[run].main2.[[bin]]の最初の項目のpath3.src/main.yx(規約デフォルト)。—— RFC-029 §パッケージ間循環:再説
可視性:存在しない。スコープ + 配布境界が全シナリオをカバーし、新しいキーワードは不要。—— RFC-029 §設計決定記録(2026-07-30)
第二条は本RFCの設計レッドラインである:インポート面セマンティクスは新しい可視性キーワードを導入してはならず、担うのは「配布境界」(ファイルロール + 宣言エクスポート面)のみである。
四つの消費者がそれぞれ同じ壁にぶつかった:
| 消費者 | 行き詰まり点 |
|---|---|
| #321 デッドコード警告 | 決定B「pub = 外部インタフェースは永警告」は、真の外部と外部のように見えるものを区別するロールモデルがないため;bin内の未使用pubを報告するか(案A)は今まで保留 |
| RFC-014c ワークスペース(レビュー中、#113) | メンバーパッケージが互いにuseする際、インポート面をどう算出するか——[exports]、[lib]単一ファイル、それとも全pubファイル——本文に規定が一切ない |
| RFC-015 コンフィグシステム(承認済み) | [lib] / [[bin]] / [exports]フィールドは定義済み。RFC-029がエントリ優先順位を消費した以外に、他フィールドのセマンティクス(特に[exports]とRegistry到達性の関係)は誰も整合されていない |
| RFC-037 パッケージング / RFC-029a キャッシュ(ドラフト、#293) | パッケージング成果物選択、増分再コンパイルのキャッシュユニット境界、いずれもロールモデルを前提とする |
現在の問題
四つのセマンティクス空白(2026-09-12 棚卸し、014/014a/014b/014c/015/029の本文を確認):
- パッケージ間インポート面が未定義。014cのメンバーパッケージ相互参照の例にはディレクトリ構造(
src/lib.yx)のみがあり、use utils.helperで何が取得できるかについて規定がない。現在の暗黙ルールは「このパッケージの全トップレベルpubファイル」——内部実装ファイルもネームスペースに引きずり込まれ、依存境界が形骸化している。 - デッドコードpub免除の適用範囲が未定義。#321 決定Bの「pubは永警告」は単一ファイルスクリプトでは過度に保守的(スクリプトには外部消費者がおらず、pubは無意味)、多ファイルプロジェクトのエントリファイルでは過度に寛容(誰もimportしないルートファイルのpubはデッドコード)。ロールモデルがないため、A/B両案の分割線が引けない。
- 三つのエクスポート概念が整合されていない。
[exports](015、パスマッピング)、[lib](015、単一ファイルパス)、Registryの到達性(029、エントリからuseに沿って到達可能)——三者の包含関係・同値関係・独立関係について規定がない。 - 用語衝突。RFC-014bの
[binaries]はプリコンパイル配布成果物(.so/exeのダウンロード優先戦略)であり、「bin対象(ソースロール)」とは完全に異なる層である。014cが実装された後、両者は必ず同時に現れるため、早めに曖昧性を解消しないと誤解が継続的に発生する。
実証:決定Bの保守性は既に実際の痛点。#321 M2レトロスペクティブ記録:デフォルト設定でW1001–W1005の全ファミリーがサイレントである構造的原因はまさに「全pubが外部インタフェース扱いされている」——これはモデル欠如の補償動作であり、終端状態ではない。
提案
コア設計:ファイルロールモデル
ロールはファイルレベル属性(015のフィールド粒度と一致:[lib]は単一ファイル、[[bin]]はファイルリスト、[exports]はファイルマッピング)であり、パッケージレベルのスイッチではない。
| ロール | 判定 | セマンティクス |
|---|---|---|
| Bin | [run].main / [[bin]].pathが指すファイル;または(manifestがない場合)mainを含みどのファイルにもuseされていないファイル | プログラムエントリ。mainを要求し関数でなければならない;さもなければコンパイルエラー(E3020 main欠如 / E3021 main非関数)、トップレベルに実行可能文は許されない。そのpubはデッドコード報告対象(パッケージ外消費者なし);mainは到達性ルート |
| Lib | [exports]マッピングが指すファイル;または[lib].path;または(manifestがない場合)他ファイルのuseから参照されているファイル | 配布境界。そのpubはデッドコード免除(パッケージ外消費者は不可見、漏れ報告を許容);これを経由してエクスポートされたシンボルはパッケージ間可視 |
| Test | RFC-036テストファイルルールに合致するファイル(tests/ディレクトリ、*_test.yx等の既存規約);[[test]]明示宣言は設けない | テストコード。デッドコード判定に参加しない;その参照は被テストコードの到達性ルートに加算される |
| Internal | パッケージにmanifestがあるとき、エクスポート面にもmainも含まないファイル | パッケージ内実装。pubはフェーズ2まで免除(パッケージ内useグラフ到達性で絞り込み、Binロールコーパス検証を前提とする);パッケージ外useは到達不能 |
| Script | 単一ファイル直接実行(run foo.yx)のファイル | エントリ概念なし。トップレベル文(バインディング初期化を含む)は記述順がそのままプログラム本体;mainは通常のバインディングで、自動呼び出しされない——実行したければmain()を記述。Registryはstdのみ |
判定優先順位:manifest明示宣言 > エントリ到達性推測 > 036テスト規約。明示宣言が常に勝つ——推測は宣言がないときのデフォルトに過ぎない。
エントリ判定
ロールモデルはデッドコード解析だけでなく、エントリ判定も駆動する。各ロールのエントリルール:
| ロール | エントリルール |
|---|---|
| Script(manifestなし) | エントリ概念なし。トップレベル文(バインディング初期化を含む)は記述順がそのままプログラム本体;mainは通常のバインディングで、自動呼び出しされない——実行したければmain()を記述 |
| Bin(manifestあり) | mainを要求し関数でなければならない;さもなければコンパイルエラー(E3020 main欠如 / E3021 main非関数)。トップレベルに実行可能文は許されない |
| Lib / Internal / Test | mainを要求しない(これらはエントリではない) |
ScriptとBinのエントリは相互排他でなければならない:「Scriptではmainは特別ではない」と「Binではmainはエントリ」は同時に成立できない——Scriptがトップレベル文を実行しつつ暗黙的にmainを呼ぶと、明示的にmain()を記述したスクリプトが二重実行される(#356)。したがってScriptには実行エントリが一つしかない:トップレベル文。
ロール判定とエントリ判定の違い(実装注記):エントリ判定は「manifestの有無」(find_project_root().is_some())を使い、roles::classify()は使わない。理由:classifyは「このファイルが誰に消費されるか」に答え(デッドコード解析に供する);manifestがあるのにmainがないファイルはclassifyではInternal(他ファイルからuseされない、合理的)、しかしユーザーがそれをエントリとして実行する場合、このときエントリが必要。二つの問題は異なり、判定基準も当然異なる。
詳細はdocs/src/reference/language-spec/syntax.md §3.11参照。
例
# yaoxiang.toml(字段全部是 RFC-015 已定义的,本 RFC 不新增配置面)
[lib]
path = "src/lib.yx"
[[bin]]
name = "my-cli"
path = "src/cli.yx"
[exports]
"." = "src/lib.yx"
"./internal-helper" = "src/helper.yx" # ← 决定跨包可见性的是它,不是 pub# src/cli.yx(Bin 角色)
pub unused_fn = (x: Int) => x # ← W1001 可报:bin 无包外消费者
main: () -> Void = { ... }
# src/lib.yx(Lib 角色,在导出面上)
pub api_fn = ... # ← 永不报:包外消费者不可见
# src/other.yx(Internal 角色,不在导出面)
pub semi_api = ... # ← 豁免至二阶段(宁漏报),届时按 use 图收紧インポート面セマンティクス
パッケージ間use pkg.xの解決順序(最初にヒットしたものが有効):
[exports]マッピングが存在 → インポート面 = マッピングに列挙されたファイル集合、xはこれらのファイルのトップレベルバインディングに存在しなければならない;- なければ
[lib].pathが存在 → インポート面 = その単一ファイル; - どちらもなし(manifestなし依存、パス依存生ディレクトリ)→ 現状維持:全トップレベルファイルがインポート可能(既存挙動と互換、絞り込みは別途議論)。
ワークスペースメンバー:エクスポート面はメンバーパッケージ自身のmanifestのみで定義され、ワークスペースルートはメンバーのエクスポート面をオーバーライドも拡張もしてはならない——メンバー自己完結は014cの中核設計であり、ルートによる上書きはカプセル化を破壊する。
Registry到達性との関係(029に整合):[exports]/[lib]はパッケージ間解決の合法な起点集合を定義する;起点が確定した後、Registryは引き続き029の「エントリからuseに沿って到達可能」に従い拡張する——エクスポートファイル自身がuseで取り込んだ内部ファイルはリンクに従って参加するが、新たなパッケージ間起点は生まない。
既存計画との境界
| 隣接RFC | 境界 |
|---|---|
RFC-029d「CLI --entryによるエントリオーバーライド」(企画中) | 029fがロールモデルを定義;029dがそれを消費する——--entryはBinロールのCLIレベルオーバーライド手段 |
RFC-014b [binaries] | 用語曖昧性解消:[binaries]はプリコンパイル配布成果物(ダウンロード優先戦略)、[[bin]]はソースコードロール宣言。前者は依存パッケージのmanifestに存在し、後者は自パッケージのmanifestに存在し、相互に置き換わらない |
| RFC-014c ワークスペース | メンバーパッケージ相互参照のインポート面 = 本RFC解決順序;ワークスペース調整機構(members、共有lockfile)は引き続き014cに帰属 |
| RFC-036 テスト | Testロール判定は036の既存ファイルルールを直接採用し、新たな規約は追加しない |
| #321 デッドコード | 決定B(pub一律免除)を明示的に「targetがないときの過渡的セマンティクス」に降格;Binロールのpub報告可能は本RFCが案Aを実現する形態 |
詳細設計
ロール判定アルゴリズム(オーケストレーター内)
fn classify(files, manifest, entry_reach) -> Map<File, Role>:
# 1. 显式层:manifest 声明优先
for f in manifest.exports.values(): role[f] = Lib
if manifest.lib_path: role[lib_path] = Lib
for b in manifest.binaries: role[b.path] = Bin
if manifest.run_main: role[run_main] = Bin
# 2. 推断层:无声明处按入口可达性
for f in files where role[f] 未定:
if f 被 ≥1 个非自身文件 use: role[f] = Lib
elif f 含 main 且无其他文件 use 它: role[f] = Bin
else: role[f] = Internal
# 3. 测试层:036 规则命中 → Test(覆盖 Lib/Internal,不覆盖显式 Bin)
apply_rfc036_test_rules(files, &role)
# 单文件直跑:整个模型旁路,行为不变
if no manifest and single_file: all Scriptコンパイラ変更
frontend/config.rs:manifest解析に[exports]→ ロールテーブルのマッピングを補足(フィールド解析は015で既に存在)。frontend/module/orchestrator.rs:Registry構築前にロール分類フェーズを挿入;パッケージ間解決はインポート面順に従ってuseターゲットを検証し、範囲外は既存のmodule_not_foundファミリーで報告(新規エラーコードは追加せず、メッセージに「エクスポート面にない」ヒントを追加)。typecheck/passes/dead_code.rs:エントリ点集合を「main + 全pub」から「main + Libロールファイルのpub」に変更(Binは即座に報告可能;Internalはフェーズ2まで免除、Phase 2は実装戦略を参照)。- LSP(RFC-017):補完/ホバーの「インポート可能項目」をインポート面でフィルタリング;
main欠如診断はBinロールファイルに対してのみ報告。
ランタイム挙動
変更なし。ロールモデルは純粋なコンパイル時/解析時セマンティクスであり、IRと実行に影響しない。
後方互換性
- manifestなしプロジェクト:挙動は完全に不変(推測層は現状を再現——useされるものはLib、mainを含むものはBin)。デッドコード警告の増分はBinファイルのみ:他者にimportされないルートファイルの未使用pubがサイレントからW1001に変化——これは#321 案Aの予期される挙動であり、破壊ではない。
- manifestありプロジェクト:
[exports]が宣言済みの場合、インポート面が「全pubファイル」から宣言面に絞り込まれる。これは唯一の挙動絞り込み点:他人パッケージの内部ファイルに依存するコードはmodule_not_foundを報告し始める。パッケージエコシステムが未確立(029:「現在サードパーティパッケージエコシステムなし」)のため、破壊面はゼロ;それでもマイナーバージョン過渡期を設定(範囲外は先に警告、後にエラー)。
トレードオフ
メリット
- 新規コンフィグ面ゼロ:フィールドは全て015で既に定義済み、本RFCはセマンティクスのみを補足。
- 新規キーワードゼロ:インポート面は配布境界が担い、029「可視性存在せず」決定と同型。
- 四つの消費者(#321 / 014c / 029a / 037)を一度にモデル化し、各々が壁にぶつかることはなくなる。
- manifestなし経路はゼロコスト:スクリプト言語のゼロコンフィグ遺伝子を完全に保持。
デメリット
- ロール推測(useされるものはLib)は「エントリファイルがユーティリティファイルをuseする」という一般的形態でユーティリティファイルをLibと判定し、そのpubは免除される——理想粒度より粗い。これは漏れ報告許容方向の意図的選択であり、絞り込みにはuseグラフ上の呼び出し方向分析が必要で、初版では行わない。
[exports]によるインポート面絞り込みは破壊的なセマンティクスの引き締め(過渡期あり)。
代替案
| 案 | 説明 | 不採用の理由 |
|---|---|---|
| トップレベル RFC-040 | 新たにトップレベル番号を採番 | モジュール図セマンティクス(インポート面/到達性/エントリ)は全て029の領域にあり、トップレベルを分割すると同一対象に二部のトップレベル文書が生じてしまう;さらに029b–029eスロットパターンが確立済み |
| 014cに統合 | workspaceの一節として | 依存方向が逆:言語層(029x)がセマンティクスを定義し、パッケージ層(014x)がセマンティクスを消費する。014cはまだレビュー中で、途中で範囲を拡大するとその実装を遅延させる |
| 案Aそのまま(#321メニュー) | manifestに[[bin]]/[[lib]] targetを強制して初めてデッドコードセマンティクスが有効 | 狭いスライス(main付きルートファイルの未使用pub)のため強制設定を導入;本RFCの推測層はゼロコンフィグで同等の効果を得る |
| 純粋エントリ推測(manifest層なし) | 015フィールドを消費しない | [exports]は既に015で承認されたフィールドであり、パッケージ間インポート面はこれを避けられない;定義しないことは014cに空白を残すことと同義 |
実装戦略
段階分け
- Phase 1:ロール分類 + インポート面解決 + Bin pub報告可能(Internal/Testは免除を維持)
- Phase 2:Internal pubを「パッケージ内useグラフで到達不能なら報告可能」に絞り込み——前提条件はPhase 1実装後にBinロール警告コーパス検証で誤報告がないことの確認;トリガー次第で実施、タイムラインは設けない
依存関係
- 前提:RFC-029 オーケストレーター(実装済み)、RFC-015 manifestフィールド(定義済み)。
- 依存される側:#321(Bin pub報告可能)、RFC-014c(インポート面)、RFC-029a(ロールをキャッシュユニット境界として)、RFC-037(成果物選択)、RFC-029d(
--entryオーバーライド)。 - RFC-014cとの実装順序:014cのレビュー承認前に、029fのインポート面順序を先に確定させ、014cは参照方式で消費し、レビュー中の範囲拡大を避ける。
リスク
- ロール推測とユーザー直感の乖離(上述トレードオフ第一条)→ 警告(中断しない)形式で増分挙動を全て提示し、いつでも無効化可能。
[exports]絞り込みの過渡期管理 → マイナーバージョン内で先に警告。
開放問題
ドラフト段階の三項目は全て決定済み(2026-09-13、決議は付録Bと本文に収録)、未決項目なし:
- [x] Internal pubを絞り込むか → 決定:フェーズ2でパッケージ内useグラフ到達性に基づき絞り込み、Binロールコーパス検証を前提とする(実装戦略Phase 2参照)
- [x]
[[test]]明示宣言 → 決定:導入しない、Testロールは036ルールのみで判定;036が将来明示宣言を必要とする場合は036自身による拡張 - [x] workspaceルートがメンバーエクスポート面をオーバーライド → 決定:許可しない、メンバー自己完結は014cの中核設計であり、ルートオーバーライドはカプセル化を破壊する
付録B:設計決定記録
| 決定事項 | 決定 | 日付 | 記録者 | 根拠 |
|---|---|---|---|---|
| 番号 | 029f(029bはホットリロード用に予約済み、029cは削除済み、029d/029eはプレースホルダ) | 2026-09-12 | 晨煦 | RFC-029 §子RFC計画 |
| 029ファミリへの帰属(トップレベルRFCではない) | 該当する | 2026-09-12 | 晨煦 | インポート面/到達性/エントリはすべてモジュール図セマンティクス;029は既に[[bin]]エントリ優先順位を消費 |
| 可視性キーワード | 導入しない | 2026-09-12 | 晨煦 | RFC-029「可視性存在せず」決定と同型、インポート面は配布境界が担う |
| #321 決定Bの地位 | 「targetがないときの過渡的セマンティクス」に降格 | 2026-09-12 | 晨煦 | Binロールpub報告可能が#321 案Aの実現形態 |
| ロール粒度 | ファイルレベル | 2026-09-12 | 晨煦 | 015フィールド粒度([lib]単一ファイル、[[bin]]リスト、[exports]マッピング) |
[binaries]曖昧性解消 | 014b プリコンパイル配布成果物 ≠ [[bin]]ソースロール | 2026-09-12 | 晨煦 | 二層概念(ダウンロード戦略 vs コンパイルロール)、014c実装前に釘付け |
| Internal pub絞り込み | フェーズ2でパッケージ内useグラフ到達性に基づき絞り込み、前提 = Binロールコーパス検証 | 2026-09-13 | 晨煦 | 漏れ報告許容で段階的に絞り込み、コーパス駆動で早期誤報告を回避 |
[[test]]明示宣言 | 導入しない;Testは036ルールのみで判定 | 2026-09-13 | 晨煦 | 036に要望がないうちはコンフィグ面を追加しない |
| workspaceルートのメンバーエクスポート面オーバーライド | 許可しない | 2026-09-13 | 晨煦 | メンバー自己完結は014cの中核設計、ルートオーバーライドはカプセル化を破壊 |
Scriptでmainを自動呼び出すか | 自動呼び出ししない——トップレベル文がそのままプログラム、main()は明示が必要 | 2026-09-17 | 晨煦 | 二つのルールが共存すると明示main()が二重実行される(#356);Scriptは実行エントリを一つしか持てない |
| ロールモデルをエントリ判定に使うか | 使う(以前はデッドコード警告のみ) | 2026-09-17 | 晨煦 | 本RFCはBinの「mainは到達性ルート」を定義、エントリと接続しないとこのセマンティクスが空回りする |
付録C:用語集
| 用語 | 定義 |
|---|---|
| ロール(Role) | ソースファイルのコンパイル対象識別:Script / Bin / Lib / Test / Internal |
| インポート面(Import Surface) | パッケージ間use時の合法な解決起点となるファイル集合、[exports]/[lib]で宣言または推測される |
| 配布境界 | 029用語:パッケージの対外露出範囲;本RFCはこれを暗黙的(全pubファイル)からインポート面に精密化する |
| Script態 | manifestなし単一ファイル直接実行のバイパス形態、挙動は現状と完全に一致 |
参考文献
- RFC-029 モジュールセマンティクス(親RFC:エントリ選択、可視性決定、子RFC計画)
- RFC-015 コンフィグシステム(
[lib]/[[bin]]/[exports]フィールド定義) - RFC-014b ビルドシステムとバイナリ配布(
[binaries]、用語曖昧性解消対象) - RFC-014c ワークスペースサポート(インポート面の最初の消費者、レビュー中)
- RFC-036 テストフレームワーク(Testロールルール由来)
- #321 M2 警告コード独立発射チャネル(決定Bと案Aの出所)
