JoinIR/SSA/Stage-3: sync CURRENT_TASK and dev env
This commit is contained in:
@ -17,12 +17,14 @@
|
|||||||
- 制御構造と PHI の意味論は **JoinIR(+LoopScopeShape/IfPhiContext 等の薄い箱)** に一本化する。
|
- 制御構造と PHI の意味論は **JoinIR(+LoopScopeShape/IfPhiContext 等の薄い箱)** に一本化する。
|
||||||
- 実行の SSOT は VM / LLVM ラインとし、JoinIR→MIR→VM/LLVM は「構造 SSOT → 実行 SSOT」への変換として扱う。
|
- 実行の SSOT は VM / LLVM ラインとし、JoinIR→MIR→VM/LLVM は「構造 SSOT → 実行 SSOT」への変換として扱う。
|
||||||
- 既存の PHI 箱(if_phi.rs / PhiBuilderBox / conservative.rs / Trio 等)は、JoinIR 側のカバレッジが十分になったところから順に削っていく。
|
- 既存の PHI 箱(if_phi.rs / PhiBuilderBox / conservative.rs / Trio 等)は、JoinIR 側のカバレッジが十分になったところから順に削っていく。
|
||||||
- Stage-3 parser デフォルトON化(Phase 30.1 完了): `config::env::parser_stage3_enabled()` で NYASH_FEATURES=stage3 をSSOT化し、legacy env は明示OFF専用の互換に縮退。
|
- Stage-3 parser デフォルトON化(Phase 30.1 完了): `config::env::parser_stage3_enabled()` で NYASH_FEATURES=stage3 をSSOT化し、legacy env は明示OFF専用の互換に縮退。
|
||||||
|
- JoinIR Strict 着手(Phase 81): `NYASH_JOINIR_STRICT=1` で代表パスのフォールバックを禁止(JoinIR失敗は即エラー)。dev/trace は観測のみ継続。
|
||||||
|
|
||||||
- **これから(Phase 69+)**
|
- **これから(Phase 69+)**
|
||||||
- wasm/Web デモライン: JoinIR ベースの軽量デモ実装。
|
- wasm/Web デモライン: JoinIR ベースの軽量デモ実装。
|
||||||
- 最適化ライン: JoinIR の最適化パスと LLVM/ny-llvmc 統合。
|
- 最適化ライン: JoinIR の最適化パスと LLVM/ny-llvmc 統合。
|
||||||
- Trio 削除ライン: 完了(Phase 70、LoopScopeShape SSOT)
|
- Trio 削除ライン: 完了(Phase 70、LoopScopeShape SSOT)
|
||||||
|
- JoinIR Strict ライン(Phase 81): 代表 If/Loop/VM ブリッジについては `NYASH_JOINIR_STRICT=1` で常に JoinIR 経路のみを通すようにし、レガシー if_phi / LoopBuilder / 旧 MIR Builder は「未対応関数専用」に縮退。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -97,7 +99,7 @@
|
|||||||
- 未了:
|
- 未了:
|
||||||
- 69-5: conservative.rs の docs/ 移設も今後の小フェーズとして残しておく。
|
- 69-5: conservative.rs の docs/ 移設も今後の小フェーズとして残しておく。
|
||||||
- 追加完了 (Phase 70):
|
- 追加完了 (Phase 70):
|
||||||
- 69-4: Trio 3 箱(LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox)を削除し、LoopScopeShape を SSOT とする構成に移行。
|
- 69-4: Trio 3 箱(LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox)を削除し、LoopScopeShape を SSOT とする構成に移行。2025-12-01 時点でコードベース再スキャン済みで、Trio 本体ファイルおよび Trio Box 直接参照は **src/mir/** から完全に除去されていることを確認(名称としての「Trio」は docs の歴史メモ内にのみ残存)。
|
||||||
|
|
||||||
## 2. 次の一手(Phase 69+)
|
## 2. 次の一手(Phase 69+)
|
||||||
|
|
||||||
@ -107,6 +109,15 @@
|
|||||||
- GenericTypeResolver 経由で全 P3-C ケースをカバー
|
- GenericTypeResolver 経由で全 P3-C ケースをカバー
|
||||||
- `infer_type_from_phi` 本体削除と if_phi.rs 大掃除
|
- `infer_type_from_phi` 本体削除と if_phi.rs 大掃除
|
||||||
|
|
||||||
|
- **Phase 71: Self‑Hosting 再ブートストラップ(docs 起点 / SSA デバッグモードで一時停止中)**
|
||||||
|
- `docs/private/roadmap2/phases/phase-71-selfhost-reboot/README.md` で代表パス 1 本(Stage‑3 + JoinIR 前提)と ENV 方針を整理済み。
|
||||||
|
- 代表パス(仮固定): `NYASH_FEATURES=stage3 NYASH_USE_NY_COMPILER=1 NYASH_NY_COMPILER_EMIT_ONLY=1 ./tools/selfhost/selfhost_build.sh --in apps/tests/stage1_run_min.hako --run`
|
||||||
|
- プラグイン初期化(nyash.toml 経由)は NYASH_DEBUG_PLUGIN=1 で成功確認済みだが、Stage‑B/SSA/JoinIR/dev verify の複合経路で Program(JSON v0) emit 前に失敗し、`Stage‑B emit failed` で selfhost_minimal が落ちている。
|
||||||
|
- RAW 観測フラグ追加済み: `NYASH_SELFHOST_KEEP_RAW` / `NYASH_EMIT_MIR_KEEP_RAW` で Stage‑B rc/stdout/stderr/抽出状況を `logs/selfhost` / `logs/emit_mir` に保存可能。`stageb_min_emit.sh` でも dev verify ON/OFF いずれも Program 行 0 件(ssa-undef-debug は trim/parse_params 系に集中)。
|
||||||
|
- SSA 修正進捗: `FuncScannerBox.parse_params/1` をダミーカンマ+静的 `_trim` 呼び出しに整理し、`ParserBox.trim` 系も `skip_ws` ベースに統一。`mir_funcscanner_parse_params_trim_min` は緑、Stage‑B RAW から ParserBox.* の `ssa-undef-debug` は消えたが、emit 失敗は継続(dev verify/birth 警告は残存)。
|
||||||
|
- dev verify 緩和トグル(`NYASH_STAGEB_DEV_VERIFY`)を実装済み。代表パスで ON/OFF を比較したが、OFF にしても emit は復活せず、SSA/Stage‑B 本体側の欠損が疑われる。
|
||||||
|
- quick プロファイルでは JoinIR/VM 系は緑維持を目標としつつ、selfhost_minimal / stageb_min_emit は「SSA ラインの観測窓」として赤許容。Stage‑B/SSA 起因の赤は Phase 71-SSA 側でハンドルする。
|
||||||
|
|
||||||
- **Phase 72: JoinIR dev フラグ棚卸し**(docs + env ポリシー整備済み、配線寄せ残)
|
- **Phase 72: JoinIR dev フラグ棚卸し**(docs + env ポリシー整備済み、配線寄せ残)
|
||||||
- `config::env::joinir_core_enabled()` / `joinir_dev_enabled()` を追加し、Core/DevOnly/Deprecated の区分を整理
|
- `config::env::joinir_core_enabled()` / `joinir_dev_enabled()` を追加し、Core/DevOnly/Deprecated の区分を整理
|
||||||
- docs/private/roadmap2/phases/phase-72-joinir-dev-flags/README.md に一覧表を追加済み
|
- docs/private/roadmap2/phases/phase-72-joinir-dev-flags/README.md に一覧表を追加済み
|
||||||
@ -142,6 +153,17 @@
|
|||||||
- `MirFunction.blocks: HashMap` → `BTreeMap` で非決定的テスト解消
|
- `MirFunction.blocks: HashMap` → `BTreeMap` で非決定的テスト解消
|
||||||
- Phase 25.1 同様のパターン適用
|
- Phase 25.1 同様のパターン適用
|
||||||
|
|
||||||
|
- Phase 71-SSA: Stage‑B / selfhost ラインは「SSA デバッグ用の観測窓」として切り出し、
|
||||||
|
代表パス(selfhost_build + stage1_run_min.hako)が JSON v0 emit まで通るかどうかを別フェーズで追う。
|
||||||
|
詳細: `docs/private/roadmap2/phases/phase-71-ssa-debug/README.md`
|
||||||
|
|
||||||
|
- Phase 81-JoinIR-Strict: JoinIR を「if/loop/PHI の真」として扱い、
|
||||||
|
JoinIR 対象関数では `if_phi` / `LoopBuilder` / 旧 MIR Builder にフォールバックしない Strict モードを導入する。
|
||||||
|
代表 If/Loop(mir_joinir_if_select / mir_stage1_staticcompiler_receiver / mir_joinir_stage1_using_resolver_min)は
|
||||||
|
`NYASH_JOINIR_CORE=1 NYASH_JOINIR_STRICT=1` で全て green を確認済み(Strict で lowering 失敗なし)。
|
||||||
|
代表パス(skip_ws / trim / resolve / print_tokens / filter / read_quoted / Stage‑1/Stage‑B 代表)では、
|
||||||
|
JoinIR lowering / VM ブリッジ失敗を即エラー扱いとし、レガシー経路は「未対応関数専用」に縮退させる。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 旧フェーズ(過去ログへのポインタ)
|
## 3. 旧フェーズ(過去ログへのポインタ)
|
||||||
|
|||||||
8
apps/tests/emit_boxcall_length_canary_vm.hako
Normal file
8
apps/tests/emit_boxcall_length_canary_vm.hako
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Minimal canary for Stage-B emit -> MIR(JSON) testing.
|
||||||
|
// StringBox.length boxcall should appear in MIR.
|
||||||
|
static box Main {
|
||||||
|
method main(args) {
|
||||||
|
local s = new StringBox("nyash");
|
||||||
|
return s.length()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,10 +36,17 @@ static box Main {
|
|||||||
local flags = { emit: 0, ret: null, source: null, stage_b: 0, prefer_cfg: 1, stage3: 0, v1_compat: 0 }
|
local flags = { emit: 0, ret: null, source: null, stage_b: 0, prefer_cfg: 1, stage3: 0, v1_compat: 0 }
|
||||||
if args == null { return flags }
|
if args == null { return flags }
|
||||||
|
|
||||||
|
local trace_flag = env.get("HAKO_STAGEB_TRACE")
|
||||||
|
local trace_on = 0
|
||||||
|
if trace_flag != null && ("" + trace_flag) == "1" { trace_on = 1 }
|
||||||
|
|
||||||
local i = 0
|
local i = 0
|
||||||
local n = args.length()
|
local n = args.length()
|
||||||
loop(i < n) {
|
loop(i < n) {
|
||||||
local token = "" + args.get(i)
|
local token = "" + args.get(i)
|
||||||
|
if trace_on == 1 {
|
||||||
|
print("[compiler/flags] arg[" + ("" + i) + "]=" + token)
|
||||||
|
}
|
||||||
if token == "--min-json" {
|
if token == "--min-json" {
|
||||||
flags.emit = 1
|
flags.emit = 1
|
||||||
} else if token == "--stage-b" {
|
} else if token == "--stage-b" {
|
||||||
@ -62,6 +69,14 @@ static box Main {
|
|||||||
}
|
}
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
|
if trace_on == 1 {
|
||||||
|
local src_len = 0
|
||||||
|
if flags.source != null { src_len = ("" + flags.source).length() }
|
||||||
|
print("[compiler/flags] summary stage_b=" + ("" + flags.stage_b) +
|
||||||
|
" stage3=" + ("" + flags.stage3) +
|
||||||
|
" emit=" + ("" + flags.emit) +
|
||||||
|
" src_len=" + ("" + src_len))
|
||||||
|
}
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,12 +493,30 @@ static box Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main(args) {
|
main(args) {
|
||||||
|
// Always emit entry tag (stdout/stderr) to confirm Main.main is running
|
||||||
|
print("[compiler/main] enter args=" + ("" + args))
|
||||||
|
env.error("[compiler/main] enter args=" + ("" + args))
|
||||||
|
|
||||||
local flags = me._collect_flags(args)
|
local flags = me._collect_flags(args)
|
||||||
|
{
|
||||||
|
local trace_flag = env.get("HAKO_STAGEB_TRACE")
|
||||||
|
if trace_flag != null && ("" + trace_flag) == "1" {
|
||||||
|
print("[compiler/main] flags stage_b=" + ("" + flags.stage_b) +
|
||||||
|
" stage3=" + ("" + flags.stage3) +
|
||||||
|
" emit=" + ("" + flags.emit))
|
||||||
|
}
|
||||||
|
}
|
||||||
if flags.stage_b == 1 {
|
if flags.stage_b == 1 {
|
||||||
// Phase 28.2 Quick Win 3: Direct call to SSOT
|
// Stage‑B 経路は SSOT: StageBDriverBox.main をそのまま叩く(末尾で print(ast_json) 済み)
|
||||||
local json = StageBDriverBox.compile(flags.source, flags.prefer_cfg, flags.stage3, flags.v1_compat)
|
// stdout / stderr 両方にタグを出して、実行経路を確実に観測する
|
||||||
print(json)
|
print("[compiler/main] enter stage-b args=" + ("" + args))
|
||||||
return 0
|
env.error("[compiler/main] enter stage-b args=" + ("" + args)) // dev trace
|
||||||
|
print("[compiler/main] stage-b entry")
|
||||||
|
env.error("[compiler/main] stage-b entry") // dev trace
|
||||||
|
local rc = StageBDriverBox.main(args)
|
||||||
|
print("[compiler/main] stage-b ret=" + ("" + rc))
|
||||||
|
env.error("[compiler/main] stage-b ret=" + ("" + rc)) // dev trace
|
||||||
|
return rc
|
||||||
}
|
}
|
||||||
if flags.emit == 1 {
|
if flags.emit == 1 {
|
||||||
local json = me._compile_source_to_json_v0(flags.source)
|
local json = me._compile_source_to_json_v0(flags.source)
|
||||||
|
|||||||
@ -1236,6 +1236,9 @@ static box StageBDriverBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main(args) {
|
main(args) {
|
||||||
|
// Dev trace to confirm entry dispatch
|
||||||
|
print("[stageb/main] enter")
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Phase 25.1c: Guaranteed marker for entry point confirmation (dev-only)
|
// Phase 25.1c: Guaranteed marker for entry point confirmation (dev-only)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -1252,16 +1255,19 @@ static box StageBDriverBox {
|
|||||||
{
|
{
|
||||||
local depth = env.get("HAKO_STAGEB_DRIVER_DEPTH")
|
local depth = env.get("HAKO_STAGEB_DRIVER_DEPTH")
|
||||||
if depth != null && ("" + depth) != "0" {
|
if depth != null && ("" + depth) != "0" {
|
||||||
print("[stageb/recursion] StageBDriverBox.main recursion detected")
|
print("[stageb/error] depth_guard tripped: HAKO_STAGEB_DRIVER_DEPTH!=0")
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
env.set("HAKO_STAGEB_DRIVER_DEPTH", "1")
|
env.set("HAKO_STAGEB_DRIVER_DEPTH", "1")
|
||||||
}
|
}
|
||||||
|
// Depth guard cleared → main 続行できることを明示
|
||||||
|
print("[stageb/main] depth ok")
|
||||||
|
|
||||||
// Dev-only: direct FuncScanner harness
|
// Dev-only: direct FuncScanner harness
|
||||||
{
|
{
|
||||||
local test_flag = env.get("HAKO_STAGEB_FUNCSCAN_TEST")
|
local test_flag = env.get("HAKO_STAGEB_FUNCSCAN_TEST")
|
||||||
if test_flag != null && ("" + test_flag) == "1" {
|
if test_flag != null && ("" + test_flag) == "1" {
|
||||||
|
print("[stageb/info] FUNC_SCAN_TEST=1, skipping emit")
|
||||||
StageBFuncScannerBox.test_fib_scan()
|
StageBFuncScannerBox.test_fib_scan()
|
||||||
// Clear depth guard before returning
|
// Clear depth guard before returning
|
||||||
env.set("HAKO_STAGEB_DRIVER_DEPTH", "0")
|
env.set("HAKO_STAGEB_DRIVER_DEPTH", "0")
|
||||||
@ -1293,6 +1299,9 @@ static box StageBDriverBox {
|
|||||||
// local externs_json = p.get_externs_json()
|
// local externs_json = p.get_externs_json()
|
||||||
|
|
||||||
local body_src = StageBBodyExtractorBox.build_body_src(src, args)
|
local body_src = StageBBodyExtractorBox.build_body_src(src, args)
|
||||||
|
if body_src == null {
|
||||||
|
print("[stageb/error] no body_src (StageBBodyExtractorBox returned null)")
|
||||||
|
}
|
||||||
{
|
{
|
||||||
local l2 = 0
|
local l2 = 0
|
||||||
if body_src != null { l2 = ("" + body_src).length() }
|
if body_src != null { l2 = ("" + body_src).length() }
|
||||||
@ -1465,7 +1474,9 @@ static box StageBDriverBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("[stageb/main] before ast_json")
|
||||||
print(ast_json)
|
print(ast_json)
|
||||||
|
print("[stageb/main] after ast_json")
|
||||||
{
|
{
|
||||||
local tracer = new StageBTraceBox()
|
local tracer = new StageBTraceBox()
|
||||||
tracer.log("StageBDriverBox.main:exit rc=0")
|
tracer.log("StageBDriverBox.main:exit rc=0")
|
||||||
|
|||||||
@ -416,30 +416,34 @@ static box FuncScannerBox {
|
|||||||
// FuncScannerBox._trim を使用(static helper パターン)
|
// FuncScannerBox._trim を使用(static helper パターン)
|
||||||
// 戻り値: ArrayBox(トリム済みパラメータ名のリスト)
|
// 戻り値: ArrayBox(トリム済みパラメータ名のリスト)
|
||||||
method parse_params(params_str) {
|
method parse_params(params_str) {
|
||||||
// NOTE: keep the control flow simple to reduce SSA/PHI complexity.
|
// NOTE: SSA/PHI が崩れにくいよう、1 ループ+単純状態でスキャンする。
|
||||||
// skip_whitespace/trim are already well‑tested helpers, so we reuse them here.
|
|
||||||
if params_str == null { return new ArrayBox() }
|
if params_str == null { return new ArrayBox() }
|
||||||
|
|
||||||
|
// 状態変数は params / cur / i のみを更新する。
|
||||||
local params = new ArrayBox()
|
local params = new ArrayBox()
|
||||||
local pstr = "" + params_str
|
local s = "" + params_str
|
||||||
local n = pstr.length()
|
local n = s.length()
|
||||||
local pos = 0
|
local cur = ""
|
||||||
|
local i = 0
|
||||||
|
|
||||||
loop(pos < n) {
|
// Scan characters plus a sentinel comma at the end to reuse the same branch.
|
||||||
pos = FuncScannerBox.skip_whitespace(pstr, pos)
|
loop(i <= n) {
|
||||||
if pos >= n { break }
|
// When i == n we synthesize a trailing comma to flush the last token.
|
||||||
|
local ch = ","
|
||||||
|
if i < n { ch = s.substring(i, i + 1) }
|
||||||
|
|
||||||
// Find next comma (or end of string).
|
if ch == "," {
|
||||||
local next = pos
|
// push current token if non-empty after trim, then reset
|
||||||
loop(next < n) {
|
// Use static helper to avoid capturing instance state (SSA-friendly).
|
||||||
if pstr.substring(next, next + 1) == "," { break }
|
local tok = FuncScannerBox._trim(cur)
|
||||||
next = next + 1
|
if tok.length() > 0 { params.push(tok) }
|
||||||
|
cur = ""
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
cur = cur + ch
|
||||||
}
|
}
|
||||||
|
i = i + 1
|
||||||
// Trim and collect the parameter name.
|
|
||||||
local pname = FuncScannerBox.trim(pstr.substring(pos, next))
|
|
||||||
if pname.length() > 0 { params.push(pname) }
|
|
||||||
|
|
||||||
pos = next + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
@ -532,6 +536,26 @@ static box FuncScannerBox {
|
|||||||
|
|
||||||
// Static helper: 前後空白削除(外部 API)
|
// Static helper: 前後空白削除(外部 API)
|
||||||
method _trim(s) {
|
method _trim(s) {
|
||||||
return FuncScannerBox.trim(s)
|
// NOTE: keep in sync with trim/1. 静的 alias でも SSA が崩れないよう、本体を薄くインラインする。
|
||||||
|
if s == null { return "" }
|
||||||
|
|
||||||
|
local n = s.length()
|
||||||
|
|
||||||
|
__mir__.log("trim/pre", n)
|
||||||
|
|
||||||
|
// Leading whitespace removal is delegated to skip_whitespace to keep SSA simple.
|
||||||
|
local b = FuncScannerBox.skip_whitespace(s, 0)
|
||||||
|
if b >= n { return "" }
|
||||||
|
|
||||||
|
// Trailing whitespace: walk backwards until a non-space is found.
|
||||||
|
local e = n
|
||||||
|
loop(e > b) {
|
||||||
|
local ch = s.substring(e - 1, e)
|
||||||
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
|
||||||
|
}
|
||||||
|
|
||||||
|
__mir__.log("trim/exit", b, e)
|
||||||
|
if e > b { return s.substring(b, e) }
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// ParserCommonUtilsBox — shared utility functions for parser boxes
|
// ParserCommonUtilsBox — shared utility functions for parser boxes
|
||||||
// Responsibility: Provide common string/character operations used across parser components
|
// Responsibility: Provide common string/character operations used across parser components
|
||||||
// Notes: Pure utility functions; no state, no dependencies
|
// Notes: Pure utility functions; no state, no dependencies
|
||||||
|
using lang.compiler.parser.scan.parser_string_utils_box as ParserStringUtilsBox
|
||||||
|
|
||||||
static box ParserCommonUtilsBox {
|
static box ParserCommonUtilsBox {
|
||||||
// ===== 数値・文字列変換 =====
|
// ===== 数値・文字列変換 =====
|
||||||
@ -47,12 +48,8 @@ static box ParserCommonUtilsBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trim(s) {
|
trim(s) {
|
||||||
local i = 0
|
// Delegate to string_utils to keep SSA/semantic consistency.
|
||||||
local n = s.length()
|
return ParserStringUtilsBox.trim(s)
|
||||||
loop(i < n && (s.substring(i,i+1) == " " || s.substring(i,i+1) == "\t")) { i = i + 1 }
|
|
||||||
local j = n
|
|
||||||
loop(j > i && (s.substring(j-1,j) == " " || s.substring(j-1,j) == "\t" || s.substring(j-1,j) == ";")) { j = j - 1 }
|
|
||||||
return s.substring(i, j)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esc_json(s) {
|
esc_json(s) {
|
||||||
@ -69,4 +66,3 @@ static box ParserCommonUtilsBox {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -71,16 +71,18 @@ static box ParserStringUtilsBox {
|
|||||||
if s == null { return "" }
|
if s == null { return "" }
|
||||||
local str = "" + s
|
local str = "" + s
|
||||||
local n = str.length()
|
local n = str.length()
|
||||||
local b = 0
|
|
||||||
loop(b < n) {
|
// Leading whitespace: reuse shared skip_ws to keep semantics aligned with Stage‑B.
|
||||||
local ch = str.substring(b, b + 1)
|
local b = StringHelpers.skip_ws(str, 0)
|
||||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { b = b + 1 } else { break }
|
if b >= n { return "" }
|
||||||
}
|
|
||||||
|
// Trailing whitespace: walk backwards until a non-space is found.
|
||||||
local e = n
|
local e = n
|
||||||
loop(e > b) {
|
loop(e > b) {
|
||||||
local ch = str.substring(e - 1, e)
|
local ch = str.substring(e - 1, e)
|
||||||
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
|
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" || ch == ";" { e = e - 1 } else { break }
|
||||||
}
|
}
|
||||||
|
|
||||||
if e > b { return str.substring(b, e) }
|
if e > b { return str.substring(b, e) }
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,6 +57,9 @@ path = "lang/src/shared/json/stringify.hako"
|
|||||||
[using.sh_core]
|
[using.sh_core]
|
||||||
path = "lang/src/shared/common/string_helpers.hako"
|
path = "lang/src/shared/common/string_helpers.hako"
|
||||||
|
|
||||||
|
[plugin_paths]
|
||||||
|
search_paths = ["target/release"]
|
||||||
|
|
||||||
[modules]
|
[modules]
|
||||||
# Core shared helpers (needed by parser/compiler in Stage-B)
|
# Core shared helpers (needed by parser/compiler in Stage-B)
|
||||||
"sh_core" = "lang/src/shared/common/string_helpers.hako"
|
"sh_core" = "lang/src/shared/common/string_helpers.hako"
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::basic_block::BasicBlock;
|
use crate::mir::basic_block::BasicBlock;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Write;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
impl MirInterpreter {
|
impl MirInterpreter {
|
||||||
@ -66,6 +68,16 @@ impl MirInterpreter {
|
|||||||
})
|
})
|
||||||
.unwrap_or(1_000_000);
|
.unwrap_or(1_000_000);
|
||||||
let mut steps: u64 = 0;
|
let mut steps: u64 = 0;
|
||||||
|
let trace_log_path: Option<String> = std::env::var("NYASH_VM_TRACE_LOG")
|
||||||
|
.ok()
|
||||||
|
.map(|v| {
|
||||||
|
let trimmed = v.trim();
|
||||||
|
if trimmed.is_empty() || trimmed == "1" {
|
||||||
|
"__mir__.log".to_string()
|
||||||
|
} else {
|
||||||
|
v
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
steps += 1;
|
steps += 1;
|
||||||
@ -94,6 +106,23 @@ impl MirInterpreter {
|
|||||||
.get(&cur)
|
.get(&cur)
|
||||||
.ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
|
.ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
|
||||||
|
|
||||||
|
if let Some(path) = trace_log_path.as_ref() {
|
||||||
|
let _ = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(path)
|
||||||
|
.and_then(|mut f| {
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"[vm-trace-log] fn={} bb={:?} pred={:?} step={}",
|
||||||
|
self.cur_fn.as_deref().unwrap_or(""),
|
||||||
|
cur,
|
||||||
|
last_pred,
|
||||||
|
steps
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if Self::trace_enabled() {
|
if Self::trace_enabled() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[vm-trace] enter bb={:?} pred={:?} fn={}",
|
"[vm-trace] enter bb={:?} pred={:?} fn={}",
|
||||||
|
|||||||
@ -225,16 +225,19 @@ impl MirInterpreter {
|
|||||||
|
|
||||||
// Try candidates in order
|
// Try candidates in order
|
||||||
let mut chosen: Option<&nyash_rust::mir::MirFunction> = None;
|
let mut chosen: Option<&nyash_rust::mir::MirFunction> = None;
|
||||||
|
let mut chosen_name: Option<String> = None;
|
||||||
for c in &candidates {
|
for c in &candidates {
|
||||||
// exact
|
// exact
|
||||||
if let Some(f) = module.functions.get(c) {
|
if let Some(f) = module.functions.get(c) {
|
||||||
chosen = Some(f);
|
chosen = Some(f);
|
||||||
|
chosen_name = Some(c.clone());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// if contains '/': try name before '/'
|
// if contains '/': try name before '/'
|
||||||
if let Some((head, _)) = c.split_once('/') {
|
if let Some((head, _)) = c.split_once('/') {
|
||||||
if let Some(f) = module.functions.get(head) {
|
if let Some(f) = module.functions.get(head) {
|
||||||
chosen = Some(f);
|
chosen = Some(f);
|
||||||
|
chosen_name = Some(head.to_string());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -242,6 +245,7 @@ impl MirInterpreter {
|
|||||||
if c.ends_with(".main") {
|
if c.ends_with(".main") {
|
||||||
if let Some(f) = module.functions.get("main") {
|
if let Some(f) = module.functions.get("main") {
|
||||||
chosen = Some(f);
|
chosen = Some(f);
|
||||||
|
chosen_name = Some("main".to_string());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,6 +272,16 @@ impl MirInterpreter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
if let Some(name) = chosen_name {
|
||||||
|
eprintln!("[vm/entry] main={}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare arguments if the entry takes parameters (pass script args as ArrayBox)
|
// Prepare arguments if the entry takes parameters (pass script args as ArrayBox)
|
||||||
let ret = if func.signature.params.len() == 0 {
|
let ret = if func.signature.params.len() == 0 {
|
||||||
self.execute_function(func)?
|
self.execute_function(func)?
|
||||||
|
|||||||
@ -150,6 +150,12 @@ pub fn verify_ret_purity() -> bool {
|
|||||||
env_bool("NYASH_VERIFY_RET_PURITY")
|
env_bool("NYASH_VERIFY_RET_PURITY")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stage-B/selfhost 専用の dev verify トグル(SSA などの厳格チェックを一時緩和するためのスイッチ)
|
||||||
|
/// Default: ON(現行挙動)。NYASH_STAGEB_DEV_VERIFY=0 で Stage-B 経路の dev verify をスキップ。
|
||||||
|
pub fn stageb_dev_verify_enabled() -> bool {
|
||||||
|
env_flag("NYASH_STAGEB_DEV_VERIFY").unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
// ---- LLVM harness toggle (llvmlite) ----
|
// ---- LLVM harness toggle (llvmlite) ----
|
||||||
pub fn llvm_use_harness() -> bool {
|
pub fn llvm_use_harness() -> bool {
|
||||||
// Phase 15: デフォルトON(LLVMバックエンドはPythonハーネス使用)
|
// Phase 15: デフォルトON(LLVMバックエンドはPythonハーネス使用)
|
||||||
@ -195,7 +201,11 @@ fn nyash_features_list() -> Option<Vec<String>> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if list.is_empty() { None } else { Some(list) }
|
if list.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feature_stage3_enabled() -> bool {
|
fn feature_stage3_enabled() -> bool {
|
||||||
@ -249,6 +259,12 @@ pub fn joinir_vm_bridge_enabled() -> bool {
|
|||||||
joinir_core_enabled() && env_bool("NYASH_JOINIR_VM_BRIDGE")
|
joinir_core_enabled() && env_bool("NYASH_JOINIR_VM_BRIDGE")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// JoinIR strict mode: when enabled, JoinIR 対象のフォールバックを禁止する。
|
||||||
|
/// 既定OFF。NYASH_JOINIR_STRICT=1 のときのみ有効。
|
||||||
|
pub fn joinir_strict_enabled() -> bool {
|
||||||
|
env_flag("NYASH_JOINIR_STRICT").unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// JoinIR VM bridge debug output. Enables verbose logging of JoinIR→MIR conversion.
|
/// JoinIR VM bridge debug output. Enables verbose logging of JoinIR→MIR conversion.
|
||||||
/// Set NYASH_JOINIR_VM_BRIDGE_DEBUG=1 to enable.
|
/// Set NYASH_JOINIR_VM_BRIDGE_DEBUG=1 to enable.
|
||||||
pub fn joinir_vm_bridge_debug() -> bool {
|
pub fn joinir_vm_bridge_debug() -> bool {
|
||||||
@ -266,6 +282,10 @@ pub fn joinir_llvm_experiment_enabled() -> bool {
|
|||||||
/// Phase 33: JoinIR If Select 実験の有効化
|
/// Phase 33: JoinIR If Select 実験の有効化
|
||||||
/// Primary: HAKO_JOINIR_IF_SELECT (Phase 33-8+).
|
/// Primary: HAKO_JOINIR_IF_SELECT (Phase 33-8+).
|
||||||
pub fn joinir_if_select_enabled() -> bool {
|
pub fn joinir_if_select_enabled() -> bool {
|
||||||
|
// Core ON なら既定で有効化(JoinIR 本線化を優先)
|
||||||
|
if joinir_core_enabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Primary: HAKO_JOINIR_IF_SELECT
|
// Primary: HAKO_JOINIR_IF_SELECT
|
||||||
if let Some(v) = env_flag("HAKO_JOINIR_IF_SELECT") {
|
if let Some(v) = env_flag("HAKO_JOINIR_IF_SELECT") {
|
||||||
return v;
|
return v;
|
||||||
|
|||||||
@ -36,37 +36,15 @@ pub static OPERATORS_DIV_RULES: &[(&str, &str, &str, &str)] = &[
|
|||||||
];
|
];
|
||||||
pub fn lookup_keyword(word: &str) -> Option<&'static str> {
|
pub fn lookup_keyword(word: &str) -> Option<&'static str> {
|
||||||
for (k, t) in KEYWORDS {
|
for (k, t) in KEYWORDS {
|
||||||
if *k == word { return Some(*t); }
|
if *k == word {
|
||||||
|
return Some(*t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
|
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
|
||||||
"box",
|
"box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait",
|
||||||
"global",
|
"include", "local", "outbox", "try", "throw", "using", "from",
|
||||||
"function",
|
|
||||||
"static",
|
|
||||||
"if",
|
|
||||||
"loop",
|
|
||||||
"break",
|
|
||||||
"return",
|
|
||||||
"print",
|
|
||||||
"nowait",
|
|
||||||
"include",
|
|
||||||
"local",
|
|
||||||
"outbox",
|
|
||||||
"try",
|
|
||||||
"throw",
|
|
||||||
"using",
|
|
||||||
"from",
|
|
||||||
];
|
];
|
||||||
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[
|
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"];
|
||||||
"add",
|
|
||||||
"sub",
|
|
||||||
"mul",
|
|
||||||
"div",
|
|
||||||
"and",
|
|
||||||
"or",
|
|
||||||
"eq",
|
|
||||||
"ne",
|
|
||||||
];
|
|
||||||
|
|||||||
@ -26,10 +26,12 @@ impl super::MirBuilder {
|
|||||||
///
|
///
|
||||||
/// This is the unified entry point for all loop lowering. Specific functions
|
/// This is the unified entry point for all loop lowering. Specific functions
|
||||||
/// are routed through JoinIR Frontend instead of the traditional LoopBuilder path
|
/// are routed through JoinIR Frontend instead of the traditional LoopBuilder path
|
||||||
/// when enabled via dev flags:
|
/// when enabled via dev flags (Phase 49) or Core policy (Phase 80):
|
||||||
///
|
///
|
||||||
/// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0
|
/// - Core ON (`joinir_core_enabled()`): print_tokens / ArrayExt.filter はまず JoinIR Frontend を試す
|
||||||
/// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2
|
/// - Dev フラグ(既存):
|
||||||
|
/// - `HAKO_JOINIR_PRINT_TOKENS_MAIN=1`: JsonTokenizer.print_tokens/0
|
||||||
|
/// - `HAKO_JOINIR_ARRAY_FILTER_MAIN=1`: ArrayExtBox.filter/2
|
||||||
///
|
///
|
||||||
/// Note: Arity does NOT include implicit `me` receiver.
|
/// Note: Arity does NOT include implicit `me` receiver.
|
||||||
pub(super) fn cf_loop(
|
pub(super) fn cf_loop(
|
||||||
@ -37,7 +39,7 @@ impl super::MirBuilder {
|
|||||||
condition: ASTNode,
|
condition: ASTNode,
|
||||||
body: Vec<ASTNode>,
|
body: Vec<ASTNode>,
|
||||||
) -> Result<ValueId, String> {
|
) -> Result<ValueId, String> {
|
||||||
// Phase 49: Try JoinIR Frontend route for mainline targets
|
// Phase 49/80: Try JoinIR Frontend route for mainline targets
|
||||||
if let Some(result) = self.try_cf_loop_joinir(&condition, &body)? {
|
if let Some(result) = self.try_cf_loop_joinir(&condition, &body)? {
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
@ -77,20 +79,31 @@ impl super::MirBuilder {
|
|||||||
.map(|f| f.signature.name.clone())
|
.map(|f| f.signature.name.clone())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Phase 49-4: Multi-target routing with separate dev flags
|
// Phase 49-4 + Phase 80: Multi-target routing
|
||||||
|
// - Core ON なら代表2本(print_tokens / ArrayExt.filter)は JoinIR を優先し、失敗したら LoopBuilder へフォールバック
|
||||||
|
// - Core OFF では従来通り dev フラグで opt-in
|
||||||
// Note: Arity does NOT include implicit `me` receiver
|
// Note: Arity does NOT include implicit `me` receiver
|
||||||
|
let core_on = crate::config::env::joinir_core_enabled();
|
||||||
let is_target = match func_name.as_str() {
|
let is_target = match func_name.as_str() {
|
||||||
"JsonTokenizer.print_tokens/0" => {
|
"JsonTokenizer.print_tokens/0" => {
|
||||||
std::env::var("HAKO_JOINIR_PRINT_TOKENS_MAIN")
|
if core_on {
|
||||||
.ok()
|
true
|
||||||
.as_deref()
|
} else {
|
||||||
== Some("1")
|
std::env::var("HAKO_JOINIR_PRINT_TOKENS_MAIN")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ArrayExtBox.filter/2" => {
|
"ArrayExtBox.filter/2" => {
|
||||||
std::env::var("HAKO_JOINIR_ARRAY_FILTER_MAIN")
|
if core_on {
|
||||||
.ok()
|
true
|
||||||
.as_deref()
|
} else {
|
||||||
== Some("1")
|
std::env::var("HAKO_JOINIR_ARRAY_FILTER_MAIN")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use super::{
|
|||||||
BasicBlockId, EffectMask, FunctionSignature, MirInstruction, MirModule, MirType, ValueId,
|
BasicBlockId, EffectMask, FunctionSignature, MirInstruction, MirModule, MirType, ValueId,
|
||||||
};
|
};
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
// Lifecycle routines extracted from builder.rs
|
// Lifecycle routines extracted from builder.rs
|
||||||
fn has_main_static(ast: &ASTNode) -> bool {
|
fn has_main_static(ast: &ASTNode) -> bool {
|
||||||
@ -299,7 +300,8 @@ impl super::MirBuilder {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
||||||
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
|
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name)
|
||||||
|
{
|
||||||
if let Some(mt) = GenericTypeResolver::resolve_from_phi(
|
if let Some(mt) = GenericTypeResolver::resolve_from_phi(
|
||||||
&function,
|
&function,
|
||||||
*v,
|
*v,
|
||||||
@ -342,11 +344,9 @@ impl super::MirBuilder {
|
|||||||
};
|
};
|
||||||
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
||||||
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
|
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
|
||||||
if let Some(mt) = GenericTypeResolver::resolve_from_phi(
|
if let Some(mt) =
|
||||||
&function,
|
GenericTypeResolver::resolve_from_phi(&function, *v, &self.value_types)
|
||||||
*v,
|
{
|
||||||
&self.value_types,
|
|
||||||
) {
|
|
||||||
if std::env::var("NYASH_P3C_DEBUG").is_ok() {
|
if std::env::var("NYASH_P3C_DEBUG").is_ok() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[lifecycle/p3c] {} type inferred via GenericTypeResolver: {:?}",
|
"[lifecycle/p3c] {} type inferred via GenericTypeResolver: {:?}",
|
||||||
@ -373,7 +373,9 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Dev-only verify: NewBox → birth() invariant (warn if missing)
|
// Dev-only verify: NewBox → birth() invariant (warn if missing)
|
||||||
|
// Stage‑B 用トグル: NYASH_STAGEB_DEV_VERIFY=0 のときは StageBDriverBox だけ警告をスキップする。
|
||||||
if crate::config::env::using_is_dev() {
|
if crate::config::env::using_is_dev() {
|
||||||
|
let stageb_dev_verify_on = config::env::stageb_dev_verify_enabled();
|
||||||
let mut warn_count = 0usize;
|
let mut warn_count = 0usize;
|
||||||
for (_bid, bb) in function.blocks.iter() {
|
for (_bid, bb) in function.blocks.iter() {
|
||||||
let insns = &bb.instructions;
|
let insns = &bb.instructions;
|
||||||
@ -385,6 +387,11 @@ impl super::MirBuilder {
|
|||||||
args,
|
args,
|
||||||
} = &insns[idx]
|
} = &insns[idx]
|
||||||
{
|
{
|
||||||
|
// Stage‑B dev verify OFF のときは StageBDriverBox の birth 警告のみスキップ
|
||||||
|
if !stageb_dev_verify_on && box_type == "StageBDriverBox" {
|
||||||
|
idx += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Skip StringBox (literal optimization path)
|
// Skip StringBox (literal optimization path)
|
||||||
if box_type != "StringBox" {
|
if box_type != "StringBox" {
|
||||||
let expect_tail = format!("{}.birth/{}", box_type, args.len());
|
let expect_tail = format!("{}.birth/{}", box_type, args.len());
|
||||||
|
|||||||
@ -154,10 +154,7 @@ mod tests {
|
|||||||
// P3-C 対象
|
// P3-C 対象
|
||||||
assert!(GenericTypeResolver::is_generic_method(&array_type, "get"));
|
assert!(GenericTypeResolver::is_generic_method(&array_type, "get"));
|
||||||
assert!(GenericTypeResolver::is_generic_method(&array_type, "pop"));
|
assert!(GenericTypeResolver::is_generic_method(&array_type, "pop"));
|
||||||
assert!(GenericTypeResolver::is_generic_method(
|
assert!(GenericTypeResolver::is_generic_method(&array_type, "first"));
|
||||||
&array_type,
|
|
||||||
"first"
|
|
||||||
));
|
|
||||||
assert!(GenericTypeResolver::is_generic_method(&array_type, "last"));
|
assert!(GenericTypeResolver::is_generic_method(&array_type, "last"));
|
||||||
|
|
||||||
// P3-A/P3-B 対象(非 P3-C)
|
// P3-A/P3-B 対象(非 P3-C)
|
||||||
@ -194,9 +191,7 @@ mod tests {
|
|||||||
fn test_is_p3c_candidate() {
|
fn test_is_p3c_candidate() {
|
||||||
// 全関数が P3-C 候補(P1/P2/P3-A/B 以外)
|
// 全関数が P3-C 候補(P1/P2/P3-A/B 以外)
|
||||||
assert!(GenericTypeResolver::is_p3c_candidate("Main.main/0"));
|
assert!(GenericTypeResolver::is_p3c_candidate("Main.main/0"));
|
||||||
assert!(GenericTypeResolver::is_p3c_candidate(
|
assert!(GenericTypeResolver::is_p3c_candidate("FuncScanner.parse/1"));
|
||||||
"FuncScanner.parse/1"
|
|
||||||
));
|
|
||||||
|
|
||||||
// 空文字列は false
|
// 空文字列は false
|
||||||
assert!(!GenericTypeResolver::is_p3c_candidate(""));
|
assert!(!GenericTypeResolver::is_p3c_candidate(""));
|
||||||
|
|||||||
@ -170,8 +170,16 @@ pub(crate) fn intake_loop_form(
|
|||||||
|
|
||||||
// Phase 70-1: Trio 分類を削除し、pinned_hint/carrier_hint をそのまま返す
|
// Phase 70-1: Trio 分類を削除し、pinned_hint/carrier_hint をそのまま返す
|
||||||
// 実際の分類は LoopScopeShape::from_loop_form() 内部で実施される(二重分類問題解消)
|
// 実際の分類は LoopScopeShape::from_loop_form() 内部で実施される(二重分類問題解消)
|
||||||
let ordered_pinned: Vec<String> = pinned_hint.into_iter().collect::<BTreeSet<_>>().into_iter().collect();
|
let ordered_pinned: Vec<String> = pinned_hint
|
||||||
let ordered_carriers: Vec<String> = carrier_hint.into_iter().collect::<BTreeSet<_>>().into_iter().collect();
|
.into_iter()
|
||||||
|
.collect::<BTreeSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
let ordered_carriers: Vec<String> = carrier_hint
|
||||||
|
.into_iter()
|
||||||
|
.collect::<BTreeSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
if ordered_pinned.is_empty() || ordered_carriers.is_empty() {
|
if ordered_pinned.is_empty() || ordered_carriers.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@ -58,10 +58,7 @@ impl LoopScopeShape {
|
|||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_from_intake(
|
fn build_from_intake(loop_form: &LoopForm, intake: &LoopFormIntake) -> Option<Self> {
|
||||||
loop_form: &LoopForm,
|
|
||||||
intake: &LoopFormIntake,
|
|
||||||
) -> Option<Self> {
|
|
||||||
let layout = block_layout(loop_form);
|
let layout = block_layout(loop_form);
|
||||||
|
|
||||||
if std::env::var("NYASH_LOOPSCOPE_DEBUG").is_ok() {
|
if std::env::var("NYASH_LOOPSCOPE_DEBUG").is_ok() {
|
||||||
@ -80,13 +77,8 @@ impl LoopScopeShape {
|
|||||||
let carriers: BTreeSet<String> = intake.carrier_ordered.iter().cloned().collect();
|
let carriers: BTreeSet<String> = intake.carrier_ordered.iter().cloned().collect();
|
||||||
|
|
||||||
let variable_definitions = collect_variable_definitions(intake, &layout);
|
let variable_definitions = collect_variable_definitions(intake, &layout);
|
||||||
let (body_locals, exit_live) = classify_body_and_exit(
|
let (body_locals, exit_live) =
|
||||||
intake,
|
classify_body_and_exit(intake, &pinned, &carriers, &variable_definitions, &layout);
|
||||||
&pinned,
|
|
||||||
&carriers,
|
|
||||||
&variable_definitions,
|
|
||||||
&layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
let progress_carrier = carriers.iter().next().cloned();
|
let progress_carrier = carriers.iter().next().cloned();
|
||||||
|
|
||||||
@ -150,10 +142,7 @@ fn collect_variable_definitions(
|
|||||||
|
|
||||||
for (bb, snap) in &intake.exit_snapshots {
|
for (bb, snap) in &intake.exit_snapshots {
|
||||||
for var_name in snap.keys() {
|
for var_name in snap.keys() {
|
||||||
var_defs
|
var_defs.entry(var_name.clone()).or_default().insert(*bb);
|
||||||
.entry(var_name.clone())
|
|
||||||
.or_default()
|
|
||||||
.insert(*bb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,14 +18,16 @@ pub enum LoopVarClass {
|
|||||||
impl LoopVarClass {
|
impl LoopVarClass {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn needs_exit_phi(self) -> bool {
|
pub fn needs_exit_phi(self) -> bool {
|
||||||
matches!(self, LoopVarClass::Pinned | LoopVarClass::Carrier | LoopVarClass::BodyLocalExit)
|
matches!(
|
||||||
|
self,
|
||||||
|
LoopVarClass::Pinned | LoopVarClass::Carrier | LoopVarClass::BodyLocalExit
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn needs_header_phi(self) -> bool {
|
pub fn needs_header_phi(self) -> bool {
|
||||||
matches!(self, LoopVarClass::Pinned | LoopVarClass::Carrier)
|
matches!(self, LoopVarClass::Pinned | LoopVarClass::Carrier)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ループ変数スコープの統合ビュー
|
/// ループ変数スコープの統合ビュー
|
||||||
|
|||||||
@ -151,9 +151,7 @@ mod tests {
|
|||||||
pinned: vec!["s".to_string()].into_iter().collect(),
|
pinned: vec!["s".to_string()].into_iter().collect(),
|
||||||
carriers: vec!["i".to_string()].into_iter().collect(),
|
carriers: vec!["i".to_string()].into_iter().collect(),
|
||||||
body_locals: BTreeSet::new(),
|
body_locals: BTreeSet::new(),
|
||||||
exit_live: vec!["s".to_string(), "i".to_string()]
|
exit_live: vec!["s".to_string(), "i".to_string()].into_iter().collect(),
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
progress_carrier: Some("i".to_string()),
|
progress_carrier: Some("i".to_string()),
|
||||||
variable_definitions: BTreeMap::new(),
|
variable_definitions: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
use super::shape::LoopVarClass;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::join_ir::lowering::loop_form_intake::LoopFormIntake;
|
use crate::mir::join_ir::lowering::loop_form_intake::LoopFormIntake;
|
||||||
use super::shape::LoopVarClass;
|
|
||||||
use crate::mir::{BasicBlockId, MirQuery, ValueId};
|
use crate::mir::{BasicBlockId, MirQuery, ValueId};
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
@ -362,9 +362,13 @@ fn test_is_available_in_all_phase48_5_future() {
|
|||||||
);
|
);
|
||||||
variable_definitions.insert(
|
variable_definitions.insert(
|
||||||
"i".to_string(),
|
"i".to_string(),
|
||||||
vec![BasicBlockId::new(2), BasicBlockId::new(3), BasicBlockId::new(4)]
|
vec![
|
||||||
.into_iter()
|
BasicBlockId::new(2),
|
||||||
.collect(),
|
BasicBlockId::new(3),
|
||||||
|
BasicBlockId::new(4),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let scope = LoopScopeShape {
|
let scope = LoopScopeShape {
|
||||||
@ -391,7 +395,11 @@ fn test_is_available_in_all_phase48_5_future() {
|
|||||||
// i は block 2, 3, 4 で定義 → すべて要求しても true
|
// i は block 2, 3, 4 で定義 → すべて要求しても true
|
||||||
assert!(scope.is_available_in_all(
|
assert!(scope.is_available_in_all(
|
||||||
"i",
|
"i",
|
||||||
&[BasicBlockId::new(2), BasicBlockId::new(3), BasicBlockId::new(4)]
|
&[
|
||||||
|
BasicBlockId::new(2),
|
||||||
|
BasicBlockId::new(3),
|
||||||
|
BasicBlockId::new(4)
|
||||||
|
]
|
||||||
));
|
));
|
||||||
|
|
||||||
// unknown は variable_definitions にない → false
|
// unknown は variable_definitions にない → false
|
||||||
@ -469,7 +477,11 @@ fn test_variable_definitions_partial_availability() {
|
|||||||
// s は全 exit で利用可能
|
// s は全 exit で利用可能
|
||||||
assert!(scope.is_available_in_all(
|
assert!(scope.is_available_in_all(
|
||||||
"s",
|
"s",
|
||||||
&[BasicBlockId::new(20), BasicBlockId::new(21), BasicBlockId::new(22)]
|
&[
|
||||||
|
BasicBlockId::new(20),
|
||||||
|
BasicBlockId::new(21),
|
||||||
|
BasicBlockId::new(22)
|
||||||
|
]
|
||||||
));
|
));
|
||||||
|
|
||||||
// y は exit1 でのみ利用可能
|
// y は exit1 でのみ利用可能
|
||||||
|
|||||||
@ -85,6 +85,10 @@ impl LoopToJoinLowerer {
|
|||||||
loop_form: &LoopForm,
|
loop_form: &LoopForm,
|
||||||
func_name: Option<&str>,
|
func_name: Option<&str>,
|
||||||
) -> Option<JoinModule> {
|
) -> Option<JoinModule> {
|
||||||
|
let strict_on = crate::config::env::joinir_strict_enabled();
|
||||||
|
let is_minimal_target = func_name
|
||||||
|
.map(super::loop_scope_shape::is_case_a_minimal_target)
|
||||||
|
.unwrap_or(false);
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[LoopToJoinLowerer] lower() called for {:?}",
|
"[LoopToJoinLowerer] lower() called for {:?}",
|
||||||
@ -137,6 +141,12 @@ impl LoopToJoinLowerer {
|
|||||||
func_name.unwrap_or("<unknown>")
|
func_name.unwrap_or("<unknown>")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if strict_on && is_minimal_target {
|
||||||
|
panic!(
|
||||||
|
"[joinir/loop] strict mode: view-based check failed for {}",
|
||||||
|
func_name.unwrap_or("<unknown>")
|
||||||
|
);
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +161,12 @@ impl LoopToJoinLowerer {
|
|||||||
func_name.unwrap_or("<unknown>")
|
func_name.unwrap_or("<unknown>")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if strict_on && is_minimal_target {
|
||||||
|
panic!(
|
||||||
|
"[joinir/loop] strict mode: name filter rejected {}",
|
||||||
|
func_name.unwrap_or("<unknown>")
|
||||||
|
);
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
} else if self.debug {
|
} else if self.debug {
|
||||||
@ -161,7 +177,14 @@ impl LoopToJoinLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: パターンに応じた lowering を実行
|
// Step 5: パターンに応じた lowering を実行
|
||||||
self.lower_with_scope(scope, func_name)
|
let out = self.lower_with_scope(scope, func_name);
|
||||||
|
if out.is_none() && strict_on && is_minimal_target {
|
||||||
|
panic!(
|
||||||
|
"[joinir/loop] strict mode: lowering failed for {}",
|
||||||
|
func_name.unwrap_or("<unknown>")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 29 L-5.3: Progress carrier の安全性をチェック
|
/// Phase 29 L-5.3: Progress carrier の安全性をチェック
|
||||||
|
|||||||
@ -20,6 +20,7 @@ pub mod exit_args_resolver;
|
|||||||
pub mod funcscanner_append_defs;
|
pub mod funcscanner_append_defs;
|
||||||
pub mod funcscanner_trim;
|
pub mod funcscanner_trim;
|
||||||
pub mod generic_case_a;
|
pub mod generic_case_a;
|
||||||
|
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
|
||||||
pub mod if_dry_runner; // Phase 33-10.0
|
pub mod if_dry_runner; // Phase 33-10.0
|
||||||
pub mod if_merge; // Phase 33-7
|
pub mod if_merge; // Phase 33-7
|
||||||
pub mod if_phi_context; // Phase 61-1
|
pub mod if_phi_context; // Phase 61-1
|
||||||
@ -35,7 +36,6 @@ pub mod stageb_body;
|
|||||||
pub mod stageb_funcscanner;
|
pub mod stageb_funcscanner;
|
||||||
pub mod type_hint_policy; // Phase 65.5: 型ヒントポリシー箱化
|
pub mod type_hint_policy; // Phase 65.5: 型ヒントポリシー箱化
|
||||||
pub mod type_inference; // Phase 65-2-A
|
pub mod type_inference; // Phase 65-2-A
|
||||||
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
|
|
||||||
pub mod value_id_ranges;
|
pub mod value_id_ranges;
|
||||||
|
|
||||||
// Re-export public lowering functions
|
// Re-export public lowering functions
|
||||||
@ -148,6 +148,8 @@ pub fn try_lower_if_to_joinir(
|
|||||||
if !crate::config::env::joinir_if_select_enabled() {
|
if !crate::config::env::joinir_if_select_enabled() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let core_on = crate::config::env::joinir_core_enabled();
|
||||||
|
let strict_on = crate::config::env::joinir_strict_enabled();
|
||||||
|
|
||||||
// Phase 33-9.1: Loop専任関数の除外(Loop/If責務分離)
|
// Phase 33-9.1: Loop専任関数の除外(Loop/If責務分離)
|
||||||
// Loop lowering対象関数はIf loweringの対象外にすることで、
|
// Loop lowering対象関数はIf loweringの対象外にすることで、
|
||||||
@ -179,15 +181,21 @@ pub fn try_lower_if_to_joinir(
|
|||||||
"Stage1JsonScannerBox.value_start_after_key_pos/2"
|
"Stage1JsonScannerBox.value_start_after_key_pos/2"
|
||||||
);
|
);
|
||||||
|
|
||||||
if !is_allowed {
|
// Phase 80: Core ON のときは許可リストを「JoinIRをまず試す」対象とみなす。
|
||||||
if debug_level >= 2 {
|
// Core OFF のときは従来どおり whitelist + env に頼る。
|
||||||
eprintln!(
|
if !is_allowed || !core_on {
|
||||||
"[try_lower_if_to_joinir] skipping non-allowed function: {}",
|
// Core OFF かつ許可外なら従来のガードでスキップ
|
||||||
func.signature.name
|
if !is_allowed {
|
||||||
);
|
if debug_level >= 2 {
|
||||||
|
eprintln!(
|
||||||
|
"[try_lower_if_to_joinir] skipping non-allowed function: {}",
|
||||||
|
func.signature.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
let strict_allowed = strict_on && core_on && is_allowed;
|
||||||
|
|
||||||
if debug_level >= 1 {
|
if debug_level >= 1 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -232,6 +240,12 @@ pub fn try_lower_if_to_joinir(
|
|||||||
func.signature.name
|
func.signature.name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if strict_allowed {
|
||||||
|
panic!(
|
||||||
|
"[joinir/if] strict mode: pattern not matched for {}",
|
||||||
|
func.signature.name
|
||||||
|
);
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +258,13 @@ pub fn try_lower_if_to_joinir(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result.is_none() && strict_allowed {
|
||||||
|
panic!(
|
||||||
|
"[joinir/if] strict mode: lowering failed for {}",
|
||||||
|
func.signature.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use crate::mir::join_ir_ops::JoinValue;
|
|||||||
use crate::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
use crate::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
||||||
use crate::mir::MirModule;
|
use crate::mir::MirModule;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
use crate::config::env::{joinir_dev_enabled, joinir_strict_enabled};
|
||||||
|
|
||||||
/// Main.skip/1 用 JoinIR ブリッジ(Exec: JoinIR→VM 実行まで対応)
|
/// Main.skip/1 用 JoinIR ブリッジ(Exec: JoinIR→VM 実行まで対応)
|
||||||
///
|
///
|
||||||
@ -21,6 +22,10 @@ pub(crate) fn try_run_skip_ws(module: &MirModule, quiet_pipe: bool) -> bool {
|
|||||||
let input = std::env::var("NYASH_JOINIR_INPUT").unwrap_or_else(|_| " abc".to_string());
|
let input = std::env::var("NYASH_JOINIR_INPUT").unwrap_or_else(|_| " abc".to_string());
|
||||||
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
|
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
|
||||||
|
|
||||||
|
let dev_bridge = joinir_dev_enabled()
|
||||||
|
|| std::env::var("NYASH_EMIT_MIR_TRACE").ok().as_deref() == Some("1");
|
||||||
|
let strict = joinir_strict_enabled();
|
||||||
|
|
||||||
match run_joinir_via_vm(&join_module, JoinFuncId::new(0), &[JoinValue::Str(input)]) {
|
match run_joinir_via_vm(&join_module, JoinFuncId::new(0), &[JoinValue::Str(input)]) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let exit_code = match &result {
|
let exit_code = match &result {
|
||||||
@ -35,10 +40,23 @@ pub(crate) fn try_run_skip_ws(module: &MirModule, quiet_pipe: bool) -> bool {
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
eprintln!("[joinir/vm_bridge] ✅ JoinIR result: {:?}", result);
|
eprintln!("[joinir/vm_bridge] ✅ JoinIR result: {:?}", result);
|
||||||
if !quiet_pipe {
|
if dev_bridge {
|
||||||
println!("RC: {}", exit_code);
|
// Devモード: 結果を出しても exit しない(後続パスを継続して観測する)
|
||||||
|
if !quiet_pipe {
|
||||||
|
println!("RC: {}", exit_code);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if strict {
|
||||||
|
if !quiet_pipe {
|
||||||
|
println!("RC: {}", exit_code);
|
||||||
|
}
|
||||||
|
process::exit(exit_code);
|
||||||
|
} else {
|
||||||
|
if !quiet_pipe {
|
||||||
|
println!("RC: {}", exit_code);
|
||||||
|
}
|
||||||
|
process::exit(exit_code);
|
||||||
}
|
}
|
||||||
process::exit(exit_code);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[joinir/vm_bridge] ❌ JoinIR execution failed: {:?}", e);
|
eprintln!("[joinir/vm_bridge] ❌ JoinIR execution failed: {:?}", e);
|
||||||
@ -64,16 +82,38 @@ pub(crate) fn try_run_trim(module: &MirModule, quiet_pipe: bool) -> bool {
|
|||||||
let input = std::env::var("NYASH_JOINIR_INPUT").unwrap_or_else(|_| " abc ".to_string());
|
let input = std::env::var("NYASH_JOINIR_INPUT").unwrap_or_else(|_| " abc ".to_string());
|
||||||
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
|
eprintln!("[joinir/vm_bridge] Input: {:?}", input);
|
||||||
|
|
||||||
|
let dev_bridge = joinir_dev_enabled()
|
||||||
|
|| std::env::var("NYASH_EMIT_MIR_TRACE").ok().as_deref() == Some("1");
|
||||||
|
let strict = joinir_strict_enabled();
|
||||||
|
|
||||||
match run_joinir_via_vm(&join_module, JoinFuncId::new(0), &[JoinValue::Str(input)]) {
|
match run_joinir_via_vm(&join_module, JoinFuncId::new(0), &[JoinValue::Str(input)]) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
eprintln!("[joinir/vm_bridge] ✅ JoinIR trim result: {:?}", result);
|
eprintln!("[joinir/vm_bridge] ✅ JoinIR trim result: {:?}", result);
|
||||||
if !quiet_pipe {
|
if dev_bridge {
|
||||||
match &result {
|
if !quiet_pipe {
|
||||||
JoinValue::Str(s) => println!("{}", s),
|
match &result {
|
||||||
_ => println!("{:?}", result),
|
JoinValue::Str(s) => println!("{}", s),
|
||||||
|
_ => println!("{:?}", result),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
} else if strict {
|
||||||
|
if !quiet_pipe {
|
||||||
|
match &result {
|
||||||
|
JoinValue::Str(s) => println!("{}", s),
|
||||||
|
_ => println!("{:?}", result),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process::exit(0);
|
||||||
|
} else {
|
||||||
|
if !quiet_pipe {
|
||||||
|
match &result {
|
||||||
|
JoinValue::Str(s) => println!("{}", s),
|
||||||
|
_ => println!("{:?}", result),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process::exit(0);
|
||||||
}
|
}
|
||||||
process::exit(0);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[joinir/vm_bridge] ❌ JoinIR trim failed: {:?}", e);
|
eprintln!("[joinir/vm_bridge] ❌ JoinIR trim failed: {:?}", e);
|
||||||
|
|||||||
@ -40,6 +40,7 @@ use crate::mir::MirModule;
|
|||||||
/// Exec(実行)または LowerOnly(検証のみ)のパスに分岐する。
|
/// Exec(実行)または LowerOnly(検証のみ)のパスに分岐する。
|
||||||
pub fn try_run_joinir_vm_bridge(module: &MirModule, quiet_pipe: bool) -> bool {
|
pub fn try_run_joinir_vm_bridge(module: &MirModule, quiet_pipe: bool) -> bool {
|
||||||
let flags = JoinIrEnvFlags::from_env();
|
let flags = JoinIrEnvFlags::from_env();
|
||||||
|
let strict = crate::config::env::joinir_strict_enabled();
|
||||||
|
|
||||||
// Phase 32 L-4: テーブルから対象関数を探す
|
// Phase 32 L-4: テーブルから対象関数を探す
|
||||||
let Some(target) = find_joinir_target(module) else {
|
let Some(target) = find_joinir_target(module) else {
|
||||||
@ -56,12 +57,25 @@ pub fn try_run_joinir_vm_bridge(module: &MirModule, quiet_pipe: bool) -> bool {
|
|||||||
|
|
||||||
// Phase 32 L-4: テーブル駆動ディスパッチ
|
// Phase 32 L-4: テーブル駆動ディスパッチ
|
||||||
// 関数名でルーティング(将来は lowering テーブルベースに差し替え予定)
|
// 関数名でルーティング(将来は lowering テーブルベースに差し替え予定)
|
||||||
match target.func_name {
|
let handled = match target.func_name {
|
||||||
"Main.skip/1" => try_run_skip_ws(module, quiet_pipe),
|
"Main.skip/1" => try_run_skip_ws(module, quiet_pipe),
|
||||||
"FuncScannerBox.trim/1" => try_run_trim(module, quiet_pipe),
|
"FuncScannerBox.trim/1" => try_run_trim(module, quiet_pipe),
|
||||||
"Stage1UsingResolverBox.resolve_for_source/5" => try_run_stage1_usingresolver(module),
|
"Stage1UsingResolverBox.resolve_for_source/5" => try_run_stage1_usingresolver(module),
|
||||||
"StageBBodyExtractorBox.build_body_src/2" => try_run_stageb_body(module),
|
"StageBBodyExtractorBox.build_body_src/2" => try_run_stageb_body(module),
|
||||||
"StageBFuncScannerBox.scan_all_boxes/1" => try_run_stageb_funcscanner(module),
|
"StageBFuncScannerBox.scan_all_boxes/1" => try_run_stageb_funcscanner(module),
|
||||||
_ => false,
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !handled {
|
||||||
|
if strict {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/bridge] ERROR: target={} lowering/exec failed (strict, no fallback)",
|
||||||
|
target.func_name
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -123,9 +123,7 @@ impl LoopSnapshotMergeBox {
|
|||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[Option C] var '{}': {:?} needs_exit_phi={}",
|
"[Option C] var '{}': {:?} needs_exit_phi={}",
|
||||||
var_name,
|
var_name, class, needs_exit_phi
|
||||||
class,
|
|
||||||
needs_exit_phi
|
|
||||||
);
|
);
|
||||||
if let Some(defining_blocks) = definitions.get(&var_name) {
|
if let Some(defining_blocks) = definitions.get(&var_name) {
|
||||||
eprintln!("[Option C] defining_blocks: {:?}", defining_blocks);
|
eprintln!("[Option C] defining_blocks: {:?}", defining_blocks);
|
||||||
|
|||||||
@ -956,12 +956,7 @@ pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
|||||||
|
|
||||||
// Phase 69-2: LocalScopeInspectorBox 完全削除
|
// Phase 69-2: LocalScopeInspectorBox 完全削除
|
||||||
// variable_definitions は LoopScopeShape に移行済み(Phase 48-4)
|
// variable_definitions は LoopScopeShape に移行済み(Phase 48-4)
|
||||||
loopform.build_exit_phis(
|
loopform.build_exit_phis(ops, exit_id, branch_source_block, exit_snapshots)
|
||||||
ops,
|
|
||||||
exit_id,
|
|
||||||
branch_source_block,
|
|
||||||
exit_snapshots,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -31,6 +31,11 @@ impl MirVerifier {
|
|||||||
pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec<VerificationError>> {
|
pub fn verify_module(&mut self, module: &MirModule) -> Result<(), Vec<VerificationError>> {
|
||||||
self.errors.clear();
|
self.errors.clear();
|
||||||
|
|
||||||
|
// Stage‑B/selfhost 専用: dev verify を一時緩和するためのトグル
|
||||||
|
if !crate::config::env::stageb_dev_verify_enabled() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
for (_name, function) in &module.functions {
|
for (_name, function) in &module.functions {
|
||||||
if let Err(mut func_errors) = self.verify_function(function) {
|
if let Err(mut func_errors) = self.verify_function(function) {
|
||||||
// Dev-only trace: BreakFinderBox / LoopSSA 周辺のSSAバグを詳細に観測する。
|
// Dev-only trace: BreakFinderBox / LoopSSA 周辺のSSAバグを詳細に観測する。
|
||||||
|
|||||||
@ -30,8 +30,10 @@ pub fn apply_core_wrapper_env(cmd: &mut std::process::Command) {
|
|||||||
// Remove noisy or recursive toggles
|
// Remove noisy or recursive toggles
|
||||||
cmd.env_remove("NYASH_USE_NY_COMPILER");
|
cmd.env_remove("NYASH_USE_NY_COMPILER");
|
||||||
cmd.env_remove("NYASH_CLI_VERBOSE");
|
cmd.env_remove("NYASH_CLI_VERBOSE");
|
||||||
// Enforce quiet JSON capture
|
// Enforce quiet JSON capture (allow override for debug)
|
||||||
cmd.env("NYASH_JSON_ONLY", "1");
|
if std::env::var("NYASH_JSON_ONLY").is_err() {
|
||||||
|
cmd.env("NYASH_JSON_ONLY", "1");
|
||||||
|
}
|
||||||
// Restrict environment to avoid plugin/using drift
|
// Restrict environment to avoid plugin/using drift
|
||||||
cmd.env("NYASH_DISABLE_PLUGINS", "1");
|
cmd.env("NYASH_DISABLE_PLUGINS", "1");
|
||||||
cmd.env("NYASH_SKIP_TOML_ENV", "1");
|
cmd.env("NYASH_SKIP_TOML_ENV", "1");
|
||||||
|
|||||||
@ -197,6 +197,16 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Backend selection
|
// Backend selection
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"[dispatch] backend={} file={} path=backend-select",
|
||||||
|
groups.backend.backend, filename
|
||||||
|
);
|
||||||
|
}
|
||||||
match groups.backend.backend.as_str() {
|
match groups.backend.backend.as_str() {
|
||||||
"mir" => {
|
"mir" => {
|
||||||
crate::cli_v!(
|
crate::cli_v!(
|
||||||
|
|||||||
@ -92,6 +92,19 @@ impl NyashRunner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let groups = self.config.as_groups();
|
let groups = self.config.as_groups();
|
||||||
|
|
||||||
|
// CLI mode trace: show backend/file/args when emit-mir trace is enabled
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
let backend = &groups.backend.backend;
|
||||||
|
let file = groups.input.file.as_deref().unwrap_or("<none>");
|
||||||
|
let args = std::env::args().skip(1).collect::<Vec<_>>().join(" ");
|
||||||
|
eprintln!("[cli] mode={} file={} args={}", backend, file, args);
|
||||||
|
}
|
||||||
|
|
||||||
let skip_stage1_stub = groups.emit.hako_emit_program_json || groups.emit.hako_emit_mir_json;
|
let skip_stage1_stub = groups.emit.hako_emit_program_json || groups.emit.hako_emit_mir_json;
|
||||||
if !skip_stage1_stub {
|
if !skip_stage1_stub {
|
||||||
if let Some(code) = self.maybe_run_stage1_cli_stub(&groups) {
|
if let Some(code) = self.maybe_run_stage1_cli_stub(&groups) {
|
||||||
|
|||||||
@ -80,6 +80,7 @@ pub fn check_and_report(strict: bool, quiet_pipe: bool, label: &str) {
|
|||||||
"[plugin/missing] {} providers not loaded: {:?}",
|
"[plugin/missing] {} providers not loaded: {:?}",
|
||||||
label, missing
|
label, missing
|
||||||
);
|
);
|
||||||
|
eprintln!("[plugin/missing] hint: set NYASH_DEBUG_PLUGIN=1 or NYASH_CLI_VERBOSE=1 to see plugin init errors");
|
||||||
emit_hints_for(&missing);
|
emit_hints_for(&missing);
|
||||||
if quiet_pipe {
|
if quiet_pipe {
|
||||||
// In quiet JSON mode, avoid noisy stdout; hints are on stderr already.
|
// In quiet JSON mode, avoid noisy stdout; hints are on stderr already.
|
||||||
|
|||||||
@ -14,6 +14,16 @@ impl NyashRunner {
|
|||||||
|
|
||||||
// Quiet mode for child pipelines (e.g., selfhost compiler JSON emit)
|
// Quiet mode for child pipelines (e.g., selfhost compiler JSON emit)
|
||||||
let quiet_pipe = crate::config::env::env_bool("NYASH_JSON_ONLY");
|
let quiet_pipe = crate::config::env::env_bool("NYASH_JSON_ONLY");
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE").ok().as_deref() == Some("1") {
|
||||||
|
eprintln!(
|
||||||
|
"[runner/vm] entry file={} quiet_pipe={} mode=stage-b?{}",
|
||||||
|
filename,
|
||||||
|
quiet_pipe,
|
||||||
|
std::env::var("HAKO_STAGEB_TRACE")
|
||||||
|
.ok()
|
||||||
|
.unwrap_or_else(|| "0".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Enforce plugin-first policy for VM on this branch (deterministic):
|
// Enforce plugin-first policy for VM on this branch (deterministic):
|
||||||
// - Initialize plugin host if not yet loaded
|
// - Initialize plugin host if not yet loaded
|
||||||
@ -170,7 +180,10 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if trace && crate::config::env::parser_stage3_enabled() {
|
if trace && crate::config::env::parser_stage3_enabled() {
|
||||||
eprintln!("[vm] Stage-3: enabled (NYASH_FEATURES/legacy env) for {}", filename);
|
eprintln!(
|
||||||
|
"[vm] Stage-3: enabled (NYASH_FEATURES/legacy env) for {}",
|
||||||
|
filename
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail‑Fast (opt‑in): Hako 構文を Nyash VM 経路で実行しない
|
// Fail‑Fast (opt‑in): Hako 構文を Nyash VM 経路で実行しない
|
||||||
@ -513,10 +526,25 @@ impl NyashRunner {
|
|||||||
// Routing logic is centralized in join_ir_vm_bridge_dispatch module
|
// Routing logic is centralized in join_ir_vm_bridge_dispatch module
|
||||||
try_run_joinir_vm_bridge(&module_vm, quiet_pipe);
|
try_run_joinir_vm_bridge(&module_vm, quiet_pipe);
|
||||||
|
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
eprintln!("[runner/vm] calling execute_module");
|
||||||
|
}
|
||||||
match vm.execute_module(&module_vm) {
|
match vm.execute_module(&module_vm) {
|
||||||
Ok(ret) => {
|
Ok(ret) => {
|
||||||
use crate::box_trait::{BoolBox, IntegerBox};
|
use crate::box_trait::{BoolBox, IntegerBox};
|
||||||
|
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
eprintln!("[runner/vm] vm_result={}", ret.to_string_box().value);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract exit code from return value
|
// Extract exit code from return value
|
||||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||||
ib.value as i32
|
ib.value as i32
|
||||||
@ -541,11 +569,25 @@ impl NyashRunner {
|
|||||||
if !quiet_pipe {
|
if !quiet_pipe {
|
||||||
println!("RC: {}", exit_code);
|
println!("RC: {}", exit_code);
|
||||||
}
|
}
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
eprintln!("[runner/vm] exit_code={}", exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
// Exit with the return value as exit code
|
// Exit with the return value as exit code
|
||||||
process::exit(exit_code);
|
process::exit(exit_code);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
if std::env::var("NYASH_EMIT_MIR_TRACE")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
== Some("1")
|
||||||
|
{
|
||||||
|
eprintln!("[runner/vm] vm_error={}", e);
|
||||||
|
}
|
||||||
eprintln!("❌ [rust-vm] VM error: {}", e);
|
eprintln!("❌ [rust-vm] VM error: {}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,80 +40,96 @@ pub fn init_bid_plugins() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cfg_path = resolve_plugin_toml();
|
let cfg_path = resolve_plugin_toml();
|
||||||
if let Ok(()) = init_global_plugin_host(&cfg_path) {
|
match init_global_plugin_host(&cfg_path) {
|
||||||
if plugin_debug || cli_verbose {
|
Ok(()) => {
|
||||||
eprintln!("🔌 plugin host initialized from {}", cfg_path);
|
|
||||||
// Show which plugin loader backend compiled in (enabled/stub)
|
|
||||||
println!(
|
|
||||||
"[plugin-loader] backend={}",
|
|
||||||
crate::runtime::plugin_loader_v2::backend_kind()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let host = get_global_plugin_host();
|
|
||||||
let host = host.read().unwrap();
|
|
||||||
if let Some(config) = host.config_ref() {
|
|
||||||
let registry = get_global_registry();
|
|
||||||
for (lib_name, lib_def) in &config.libraries {
|
|
||||||
for box_name in &lib_def.boxes {
|
|
||||||
if plugin_debug {
|
|
||||||
eprintln!(" 📦 Registering plugin provider for {}", box_name);
|
|
||||||
}
|
|
||||||
registry.apply_plugin_config(&PluginConfig {
|
|
||||||
plugins: [(box_name.clone(), lib_name.clone())].into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if plugin_debug || cli_verbose {
|
if plugin_debug || cli_verbose {
|
||||||
eprintln!("✅ plugin host fully configured");
|
eprintln!("[plugin/init] plugin host initialized from {}", cfg_path);
|
||||||
|
// Show which plugin loader backend compiled in (enabled/stub)
|
||||||
|
println!(
|
||||||
|
"[plugin-loader] backend={}",
|
||||||
|
crate::runtime::plugin_loader_v2::backend_kind()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
let host = get_global_plugin_host();
|
||||||
|
let host = host.read().unwrap();
|
||||||
// Optional autoload for [using.*] kind="dylib" packages
|
if let Some(config) = host.config_ref() {
|
||||||
if std::env::var("NYASH_USING_DYLIB_AUTOLOAD").ok().as_deref() == Some("1")
|
let registry = get_global_registry();
|
||||||
&& std::env::var("NYASH_DISABLE_PLUGINS").ok().as_deref() != Some("1")
|
for (lib_name, lib_def) in &config.libraries {
|
||||||
{
|
for box_name in &lib_def.boxes {
|
||||||
if plugin_debug || cli_verbose {
|
if plugin_debug {
|
||||||
eprintln!("[using.dylib/autoload] scanning nyash.toml packages …");
|
eprintln!(" 📦 Registering plugin provider for {}", box_name);
|
||||||
}
|
}
|
||||||
let mut using_paths: Vec<String> = Vec::new();
|
registry.apply_plugin_config(&PluginConfig {
|
||||||
let mut pending_modules: std::vec::Vec<(String, String)> = Vec::new();
|
plugins: [(box_name.clone(), lib_name.clone())].into(),
|
||||||
let mut aliases: std::collections::HashMap<String, String> =
|
});
|
||||||
std::collections::HashMap::new();
|
}
|
||||||
let mut packages: std::collections::HashMap<String, crate::using::spec::UsingPackage> =
|
}
|
||||||
std::collections::HashMap::new();
|
if plugin_debug || cli_verbose {
|
||||||
let _ = crate::using::resolver::populate_from_toml(
|
eprintln!("[plugin/init] ✅ plugin host fully configured");
|
||||||
&mut using_paths,
|
}
|
||||||
&mut pending_modules,
|
}
|
||||||
&mut aliases,
|
|
||||||
&mut packages,
|
// Optional autoload for [using.*] kind="dylib" packages
|
||||||
);
|
if std::env::var("NYASH_USING_DYLIB_AUTOLOAD").ok().as_deref() == Some("1")
|
||||||
for (name, pkg) in packages.iter() {
|
&& std::env::var("NYASH_DISABLE_PLUGINS").ok().as_deref() != Some("1")
|
||||||
if let crate::using::spec::PackageKind::Dylib = pkg.kind {
|
{
|
||||||
// Build library name from file stem (best-effort)
|
if plugin_debug || cli_verbose {
|
||||||
let lib_name = std::path::Path::new(&pkg.path)
|
eprintln!("[using.dylib/autoload] scanning nyash.toml packages …");
|
||||||
.file_name()
|
}
|
||||||
.and_then(|s| s.to_str())
|
let mut using_paths: Vec<String> = Vec::new();
|
||||||
.unwrap_or(name)
|
let mut pending_modules: std::vec::Vec<(String, String)> = Vec::new();
|
||||||
.to_string();
|
let mut aliases: std::collections::HashMap<String, String> =
|
||||||
let host = get_global_plugin_host();
|
std::collections::HashMap::new();
|
||||||
let res = host
|
let mut packages: std::collections::HashMap<
|
||||||
.read()
|
String,
|
||||||
.unwrap()
|
crate::using::spec::UsingPackage,
|
||||||
.load_library_direct(&lib_name, &pkg.path, &[]);
|
> = std::collections::HashMap::new();
|
||||||
if let Err(e) = res {
|
let _ = crate::using::resolver::populate_from_toml(
|
||||||
if plugin_debug || cli_verbose {
|
&mut using_paths,
|
||||||
eprintln!("[using.dylib/autoload] failed '{}': {}", lib_name, e);
|
&mut pending_modules,
|
||||||
|
&mut aliases,
|
||||||
|
&mut packages,
|
||||||
|
);
|
||||||
|
for (name, pkg) in packages.iter() {
|
||||||
|
if let crate::using::spec::PackageKind::Dylib = pkg.kind {
|
||||||
|
// Build library name from file stem (best-effort)
|
||||||
|
let lib_name = std::path::Path::new(&pkg.path)
|
||||||
|
.file_name()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or(name)
|
||||||
|
.to_string();
|
||||||
|
let host = get_global_plugin_host();
|
||||||
|
let res =
|
||||||
|
host.read()
|
||||||
|
.unwrap()
|
||||||
|
.load_library_direct(&lib_name, &pkg.path, &[]);
|
||||||
|
if let Err(e) = res {
|
||||||
|
if plugin_debug || cli_verbose {
|
||||||
|
eprintln!("[using.dylib/autoload] failed '{}': {}", lib_name, e);
|
||||||
|
}
|
||||||
|
} else if plugin_debug || cli_verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[using.dylib/autoload] loaded '{}' from {}",
|
||||||
|
lib_name, pkg.path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if plugin_debug || cli_verbose {
|
|
||||||
eprintln!(
|
|
||||||
"[using.dylib/autoload] loaded '{}' from {}",
|
|
||||||
lib_name, pkg.path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if plugin_debug || cli_verbose {
|
Err(e) => {
|
||||||
eprintln!("⚠️ Failed to load plugin config (hakorune.toml/nyash.toml) - plugins disabled");
|
if plugin_debug || cli_verbose {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] failed to initialize from {}: {}",
|
||||||
|
cfg_path, e
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] plugins disabled (config={}): {}",
|
||||||
|
cfg_path, e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -422,7 +422,40 @@ pub fn get_global_plugin_host() -> Arc<RwLock<PluginHost>> {
|
|||||||
|
|
||||||
pub fn init_global_plugin_host(config_path: &str) -> BidResult<()> {
|
pub fn init_global_plugin_host(config_path: &str) -> BidResult<()> {
|
||||||
let host = get_global_plugin_host();
|
let host = get_global_plugin_host();
|
||||||
host.write().unwrap().load_libraries(config_path)?;
|
{
|
||||||
host.read().unwrap().register_boxes()?;
|
let mut h = host.write().unwrap();
|
||||||
|
let disabled = std::env::var("NYASH_DISABLE_PLUGINS")
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
.map(|v| v == "1" || v.eq_ignore_ascii_case("true") || v.eq_ignore_ascii_case("on"))
|
||||||
|
.unwrap_or(false);
|
||||||
|
if disabled {
|
||||||
|
eprintln!("[plugin/init] plugins disabled by NYASH_DISABLE_PLUGINS=1");
|
||||||
|
return Err(BidError::PluginError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !std::path::Path::new(config_path).exists() {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] plugins disabled (config={}): config file not found",
|
||||||
|
config_path
|
||||||
|
);
|
||||||
|
return Err(BidError::PluginError);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.load_libraries(config_path).map_err(|e| {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] load_libraries({}) failed: {}",
|
||||||
|
config_path, e
|
||||||
|
);
|
||||||
|
BidError::PluginError
|
||||||
|
})?;
|
||||||
|
h.register_boxes().map_err(|e| {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] register_boxes({}) failed: {}",
|
||||||
|
config_path, e
|
||||||
|
);
|
||||||
|
BidError::PluginError
|
||||||
|
})?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,10 @@ pub(super) fn load_config(loader: &mut PluginLoaderV2, config_path: &str) -> Bid
|
|||||||
.unwrap_or_else(|_| config_path.to_string());
|
.unwrap_or_else(|_| config_path.to_string());
|
||||||
loader.config_path = Some(canonical.clone());
|
loader.config_path = Some(canonical.clone());
|
||||||
loader.config = Some(
|
loader.config = Some(
|
||||||
crate::config::nyash_toml_v2::NyashConfigV2::from_file(&canonical)
|
crate::config::nyash_toml_v2::NyashConfigV2::from_file(&canonical).map_err(|e| {
|
||||||
.map_err(|_| BidError::PluginError)?,
|
eprintln!("[plugin/init] failed to parse {}: {}", canonical, e);
|
||||||
|
BidError::PluginError
|
||||||
|
})?,
|
||||||
);
|
);
|
||||||
if let Some(cfg) = loader.config.as_ref() {
|
if let Some(cfg) = loader.config.as_ref() {
|
||||||
let mut labels: Vec<String> = Vec::new();
|
let mut labels: Vec<String> = Vec::new();
|
||||||
|
|||||||
@ -51,7 +51,15 @@ pub(super) fn load_plugin(
|
|||||||
lib_path.display()
|
lib_path.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let lib = unsafe { Library::new(&lib_path) }.map_err(|_| BidError::PluginError)?;
|
let lib = unsafe { Library::new(&lib_path) }.map_err(|e| {
|
||||||
|
eprintln!(
|
||||||
|
"[plugin/init] dlopen failed for {} ({}): {}",
|
||||||
|
lib_name,
|
||||||
|
lib_path.display(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
BidError::PluginError
|
||||||
|
})?;
|
||||||
let lib_arc = Arc::new(lib);
|
let lib_arc = Arc::new(lib);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
21
src/tests/helpers/joinir_env.rs
Normal file
21
src/tests/helpers/joinir_env.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//! JoinIR テスト用の軽量 ENV ヘルパー
|
||||||
|
//!
|
||||||
|
//! Core/Dev のフラグを明示的にセット/クリアすることで、テスト間の競合を避ける。
|
||||||
|
|
||||||
|
/// Core ON (joinir_core_enabled = true) にする。
|
||||||
|
pub fn set_core_on() {
|
||||||
|
std::env::set_var("NYASH_JOINIR_CORE", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core OFF (joinir_core_enabled = false) にする。
|
||||||
|
pub fn set_core_off() {
|
||||||
|
std::env::set_var("NYASH_JOINIR_CORE", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IfSelect/Dev 系のフラグをすべてクリアする。
|
||||||
|
pub fn clear_joinir_flags() {
|
||||||
|
std::env::remove_var("NYASH_JOINIR_CORE");
|
||||||
|
std::env::remove_var("HAKO_JOINIR_IF_SELECT");
|
||||||
|
std::env::remove_var("HAKO_JOINIR_IF_SELECT_DRYRUN");
|
||||||
|
std::env::remove_var("NYASH_JOINIR_EXPERIMENT");
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
//! Phase 34-7.5: テストヘルパーモジュール
|
//! Phase 34-7.5: テストヘルパーモジュール
|
||||||
|
|
||||||
pub mod joinir_frontend;
|
pub mod joinir_frontend;
|
||||||
|
pub mod joinir_env;
|
||||||
|
|||||||
@ -18,11 +18,13 @@
|
|||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::MirCompiler;
|
use crate::mir::MirCompiler;
|
||||||
use crate::parser::NyashParser;
|
use crate::parser::NyashParser;
|
||||||
|
use crate::tests::helpers::joinir_env::clear_joinir_flags;
|
||||||
|
|
||||||
/// Phase 49-3: JoinIR Frontend mainline パイプラインが
|
/// Phase 49-3: JoinIR Frontend mainline パイプラインが
|
||||||
/// print_tokens 関数のコンパイル時にクラッシュしないことを確認
|
/// print_tokens 関数のコンパイル時にクラッシュしないことを確認
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_mainline_pipeline_smoke() {
|
fn phase49_joinir_mainline_pipeline_smoke() {
|
||||||
|
clear_joinir_flags();
|
||||||
// Phase 49 mainline route は dev フラグで制御
|
// Phase 49 mainline route は dev フラグで制御
|
||||||
std::env::set_var("HAKO_JOINIR_PRINT_TOKENS_MAIN", "1");
|
std::env::set_var("HAKO_JOINIR_PRINT_TOKENS_MAIN", "1");
|
||||||
std::env::set_var("NYASH_JOINIR_MAINLINE_DEBUG", "1");
|
std::env::set_var("NYASH_JOINIR_MAINLINE_DEBUG", "1");
|
||||||
@ -74,7 +76,7 @@ static box Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_JOINIR_MAINLINE_DEBUG");
|
std::env::remove_var("NYASH_JOINIR_MAINLINE_DEBUG");
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
@ -83,6 +85,7 @@ static box Main {
|
|||||||
/// Phase 49-3: dev フラグ OFF 時は従来経路を使用することを確認
|
/// Phase 49-3: dev フラグ OFF 時は従来経路を使用することを確認
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_mainline_fallback_without_flag() {
|
fn phase49_joinir_mainline_fallback_without_flag() {
|
||||||
|
clear_joinir_flags();
|
||||||
// dev フラグ OFF
|
// dev フラグ OFF
|
||||||
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
|
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
|
||||||
std::env::set_var("NYASH_FEATURES", "stage3");
|
std::env::set_var("NYASH_FEATURES", "stage3");
|
||||||
@ -125,6 +128,7 @@ static box Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
}
|
}
|
||||||
@ -135,6 +139,7 @@ static box Main {
|
|||||||
/// 両方が正常に完了することを確認する。
|
/// 両方が正常に完了することを確認する。
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_mainline_ab_comparison() {
|
fn phase49_joinir_mainline_ab_comparison() {
|
||||||
|
clear_joinir_flags();
|
||||||
let src = r#"
|
let src = r#"
|
||||||
box JsonTokenizer {
|
box JsonTokenizer {
|
||||||
tokens: ArrayBox
|
tokens: ArrayBox
|
||||||
@ -206,7 +211,7 @@ static box Main {
|
|||||||
// Future: Add execution comparison
|
// Future: Add execution comparison
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
}
|
}
|
||||||
@ -219,6 +224,7 @@ static box Main {
|
|||||||
/// ArrayExtBox.filter 関数のコンパイル時にクラッシュしないことを確認
|
/// ArrayExtBox.filter 関数のコンパイル時にクラッシュしないことを確認
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_array_filter_smoke() {
|
fn phase49_joinir_array_filter_smoke() {
|
||||||
|
clear_joinir_flags();
|
||||||
// Phase 49-4 mainline route は dev フラグで制御
|
// Phase 49-4 mainline route は dev フラグで制御
|
||||||
std::env::set_var("HAKO_JOINIR_ARRAY_FILTER_MAIN", "1");
|
std::env::set_var("HAKO_JOINIR_ARRAY_FILTER_MAIN", "1");
|
||||||
std::env::set_var("NYASH_JOINIR_MAINLINE_DEBUG", "1");
|
std::env::set_var("NYASH_JOINIR_MAINLINE_DEBUG", "1");
|
||||||
@ -264,7 +270,7 @@ static box Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_JOINIR_MAINLINE_DEBUG");
|
std::env::remove_var("NYASH_JOINIR_MAINLINE_DEBUG");
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
@ -273,6 +279,7 @@ static box Main {
|
|||||||
/// Phase 49-4: dev フラグ OFF 時は従来経路を使用することを確認
|
/// Phase 49-4: dev フラグ OFF 時は従来経路を使用することを確認
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_array_filter_fallback() {
|
fn phase49_joinir_array_filter_fallback() {
|
||||||
|
clear_joinir_flags();
|
||||||
// dev フラグ OFF
|
// dev フラグ OFF
|
||||||
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
|
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
|
||||||
std::env::set_var("NYASH_FEATURES", "stage3");
|
std::env::set_var("NYASH_FEATURES", "stage3");
|
||||||
@ -315,6 +322,7 @@ static box Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
}
|
}
|
||||||
@ -323,6 +331,7 @@ static box Main {
|
|||||||
/// ArrayExtBox.filter版
|
/// ArrayExtBox.filter版
|
||||||
#[test]
|
#[test]
|
||||||
fn phase49_joinir_array_filter_ab_comparison() {
|
fn phase49_joinir_array_filter_ab_comparison() {
|
||||||
|
clear_joinir_flags();
|
||||||
let src = r#"
|
let src = r#"
|
||||||
static box ArrayExtBox {
|
static box ArrayExtBox {
|
||||||
filter(arr, pred) {
|
filter(arr, pred) {
|
||||||
@ -388,7 +397,7 @@ static box Main {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// クリーンアップ
|
// クリーンアップ
|
||||||
std::env::remove_var("HAKO_JOINIR_ARRAY_FILTER_MAIN");
|
clear_joinir_flags();
|
||||||
std::env::remove_var("NYASH_FEATURES");
|
std::env::remove_var("NYASH_FEATURES");
|
||||||
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
std::env::remove_var("NYASH_DISABLE_PLUGINS");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,6 +47,24 @@ fn mir_funcscanner_parse_params_trim_min_verify_and_vm() {
|
|||||||
compiled.module.functions.len()
|
compiled.module.functions.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Optional MIR dump for targeted functions when NYASH_MIR_TEST_DUMP=1
|
||||||
|
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||||
|
use crate::mir::MirPrinter;
|
||||||
|
let printer = MirPrinter::new();
|
||||||
|
for name in [
|
||||||
|
"FuncScannerBox.parse_params/1",
|
||||||
|
"FuncScannerBox.trim/1",
|
||||||
|
"main",
|
||||||
|
] {
|
||||||
|
if let Some(func) = compiled.module.functions.get(name) {
|
||||||
|
let dump = printer.print_function(func);
|
||||||
|
eprintln!("----- MIR DUMP: {} -----\n{}", name, dump);
|
||||||
|
} else {
|
||||||
|
eprintln!("[parse-params-trim/min] WARN: function not found: {}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MIR verify: ここで Undefined / dominator エラーが出るかを見る。
|
// MIR verify: ここで Undefined / dominator エラーが出るかを見る。
|
||||||
let mut verifier = MirVerifier::new();
|
let mut verifier = MirVerifier::new();
|
||||||
if let Err(errors) = verifier.verify_module(&compiled.module) {
|
if let Err(errors) = verifier.verify_module(&compiled.module) {
|
||||||
|
|||||||
@ -8,6 +8,20 @@ mod tests {
|
|||||||
use crate::mir::join_ir::JoinInst;
|
use crate::mir::join_ir::JoinInst;
|
||||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, MirModule, ValueId};
|
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, MirModule, ValueId};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn strict_if_env_guard() -> impl Drop {
|
||||||
|
env::set_var("NYASH_JOINIR_CORE", "1");
|
||||||
|
env::set_var("NYASH_JOINIR_STRICT", "1");
|
||||||
|
struct Guard;
|
||||||
|
impl Drop for Guard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = env::remove_var("NYASH_JOINIR_CORE");
|
||||||
|
let _ = env::remove_var("NYASH_JOINIR_STRICT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Guard
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper to create a simple if/else function matching the "simple" pattern
|
/// Helper to create a simple if/else function matching the "simple" pattern
|
||||||
fn create_simple_pattern_mir() -> MirFunction {
|
fn create_simple_pattern_mir() -> MirFunction {
|
||||||
@ -128,6 +142,11 @@ mod tests {
|
|||||||
/// 順番に: simple/local/disabled/wrong_name を確認する。
|
/// 順番に: simple/local/disabled/wrong_name を確認する。
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_select_pattern_matching() {
|
fn test_if_select_pattern_matching() {
|
||||||
|
use crate::tests::helpers::joinir_env::{clear_joinir_flags, set_core_off};
|
||||||
|
|
||||||
|
// 環境を明示的にリセット
|
||||||
|
clear_joinir_flags();
|
||||||
|
|
||||||
// ==== 1. Simple pattern (env ON) ====
|
// ==== 1. Simple pattern (env ON) ====
|
||||||
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
||||||
|
|
||||||
@ -185,13 +204,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==== 3. Disabled by default (env OFF) ====
|
// ==== 3. Disabled by default (env OFF) ====
|
||||||
|
set_core_off();
|
||||||
std::env::remove_var("HAKO_JOINIR_IF_SELECT");
|
std::env::remove_var("HAKO_JOINIR_IF_SELECT");
|
||||||
|
|
||||||
let func = create_simple_pattern_mir();
|
let func = create_simple_pattern_mir();
|
||||||
let entry_block = func.entry_block;
|
let entry_block = func.entry_block;
|
||||||
let result = try_lower_if_to_joinir(&func, entry_block, false, None); // Phase 61-1: Pure If
|
let result = try_lower_if_to_joinir(&func, entry_block, false, None); // Phase 61-1: Pure If
|
||||||
|
|
||||||
assert!(result.is_none(), "Expected None when IfSelect toggle is not set");
|
assert!(
|
||||||
|
result.is_none(),
|
||||||
|
"Expected None when IfSelect toggle is not set"
|
||||||
|
);
|
||||||
|
|
||||||
eprintln!("✅ If/Select lowering correctly disabled by default");
|
eprintln!("✅ If/Select lowering correctly disabled by default");
|
||||||
|
|
||||||
@ -211,7 +234,7 @@ mod tests {
|
|||||||
eprintln!("✅ Function name filter working correctly");
|
eprintln!("✅ Function name filter working correctly");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
std::env::remove_var("HAKO_JOINIR_IF_SELECT");
|
clear_joinir_flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -291,6 +314,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_select_simple_with_verify() {
|
fn test_if_select_simple_with_verify() {
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
use crate::mir::join_ir::verify::verify_select_minimal;
|
use crate::mir::join_ir::verify::verify_select_minimal;
|
||||||
|
|
||||||
// Create simple pattern JoinIR
|
// Create simple pattern JoinIR
|
||||||
@ -309,6 +333,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_select_local_with_verify() {
|
fn test_if_select_local_with_verify() {
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
use crate::mir::join_ir::verify::verify_select_minimal;
|
use crate::mir::join_ir::verify::verify_select_minimal;
|
||||||
|
|
||||||
// Create local pattern JoinIR
|
// Create local pattern JoinIR
|
||||||
@ -327,6 +352,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_select_verify_rejects_multiple_selects() {
|
fn test_if_select_verify_rejects_multiple_selects() {
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
use crate::mir::join_ir::verify::verify_select_minimal;
|
use crate::mir::join_ir::verify::verify_select_minimal;
|
||||||
|
|
||||||
// Create JoinIR with 2 Select instructions (invalid)
|
// Create JoinIR with 2 Select instructions (invalid)
|
||||||
@ -357,6 +383,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_select_verify_checks_invariants() {
|
fn test_if_select_verify_checks_invariants() {
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
use crate::mir::join_ir::verify::verify_select_minimal;
|
use crate::mir::join_ir::verify::verify_select_minimal;
|
||||||
|
|
||||||
// Create valid JoinIR
|
// Create valid JoinIR
|
||||||
@ -517,6 +544,7 @@ mod tests {
|
|||||||
fn test_if_merge_simple_pattern() {
|
fn test_if_merge_simple_pattern() {
|
||||||
use crate::mir::join_ir::JoinInst;
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
||||||
|
|
||||||
let func = create_if_merge_simple_pattern_mir();
|
let func = create_if_merge_simple_pattern_mir();
|
||||||
@ -558,6 +586,7 @@ mod tests {
|
|||||||
fn test_if_merge_multiple_pattern() {
|
fn test_if_merge_multiple_pattern() {
|
||||||
use crate::mir::join_ir::JoinInst;
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
||||||
|
|
||||||
let func = create_if_merge_multiple_pattern_mir();
|
let func = create_if_merge_multiple_pattern_mir();
|
||||||
@ -653,6 +682,7 @@ mod tests {
|
|||||||
fn test_type_hint_propagation_simple() {
|
fn test_type_hint_propagation_simple() {
|
||||||
use crate::mir::MirType;
|
use crate::mir::MirType;
|
||||||
|
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
||||||
|
|
||||||
let func = create_simple_pattern_mir_with_const();
|
let func = create_simple_pattern_mir_with_const();
|
||||||
@ -690,6 +720,7 @@ mod tests {
|
|||||||
fn test_p1_ab_type_inference() {
|
fn test_p1_ab_type_inference() {
|
||||||
use crate::mir::MirType;
|
use crate::mir::MirType;
|
||||||
|
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
std::env::set_var("HAKO_JOINIR_IF_SELECT", "1");
|
||||||
|
|
||||||
// P1 Simple pattern で Select 生成
|
// P1 Simple pattern で Select 生成
|
||||||
@ -729,6 +760,7 @@ mod tests {
|
|||||||
use crate::mir::join_ir::lowering::if_merge::IfMergeLowerer;
|
use crate::mir::join_ir::lowering::if_merge::IfMergeLowerer;
|
||||||
use crate::mir::MirType;
|
use crate::mir::MirType;
|
||||||
|
|
||||||
|
let _env = strict_if_env_guard();
|
||||||
std::env::set_var("NYASH_JOINIR_IF_MERGE", "1");
|
std::env::set_var("NYASH_JOINIR_IF_MERGE", "1");
|
||||||
|
|
||||||
// P2 IfMerge Simple pattern で IfMerge 生成
|
// P2 IfMerge Simple pattern で IfMerge 生成
|
||||||
|
|||||||
@ -27,6 +27,11 @@ use crate::mir::{MirCompiler, ValueId};
|
|||||||
use crate::parser::NyashParser;
|
use crate::parser::NyashParser;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
fn ensure_joinir_strict_env() {
|
||||||
|
std::env::set_var("NYASH_JOINIR_CORE", "1");
|
||||||
|
std::env::set_var("NYASH_JOINIR_STRICT", "1");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] // 手動実行用(Stage-1 minimal)
|
#[ignore] // 手動実行用(Stage-1 minimal)
|
||||||
fn mir_joinir_stage1_using_resolver_auto_lowering() {
|
fn mir_joinir_stage1_using_resolver_auto_lowering() {
|
||||||
@ -108,6 +113,7 @@ fn mir_joinir_stage1_using_resolver_auto_lowering() {
|
|||||||
fn mir_joinir_stage1_using_resolver_type_sanity() {
|
fn mir_joinir_stage1_using_resolver_type_sanity() {
|
||||||
// Phase 27.12: 型定義の基本的なサニティチェック(常時実行)
|
// Phase 27.12: 型定義の基本的なサニティチェック(常時実行)
|
||||||
// stage1_using_resolver 用の JoinFunction が作成できることを確認
|
// stage1_using_resolver 用の JoinFunction が作成できることを確認
|
||||||
|
ensure_joinir_strict_env();
|
||||||
|
|
||||||
let resolve_id = JoinFuncId::new(20);
|
let resolve_id = JoinFuncId::new(20);
|
||||||
let resolve_func = JoinFunction::new(
|
let resolve_func = JoinFunction::new(
|
||||||
@ -126,6 +132,7 @@ fn mir_joinir_stage1_using_resolver_type_sanity() {
|
|||||||
fn mir_joinir_stage1_using_resolver_empty_module_returns_none() {
|
fn mir_joinir_stage1_using_resolver_empty_module_returns_none() {
|
||||||
// Phase 27.13: 空の MIR モジュールでは None を返すことを確認
|
// Phase 27.13: 空の MIR モジュールでは None を返すことを確認
|
||||||
// Stage1UsingResolverBox.resolve_for_source/1 関数が存在しない場合のフォールバック動作
|
// Stage1UsingResolverBox.resolve_for_source/1 関数が存在しない場合のフォールバック動作
|
||||||
|
ensure_joinir_strict_env();
|
||||||
|
|
||||||
// 最小限の MIR モジュールを作成
|
// 最小限の MIR モジュールを作成
|
||||||
use crate::mir::MirModule;
|
use crate::mir::MirModule;
|
||||||
|
|||||||
@ -35,6 +35,11 @@ fn ensure_stage3_env() {
|
|||||||
std::env::set_var("HAKO_ENABLE_USING", "1");
|
std::env::set_var("HAKO_ENABLE_USING", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_joinir_strict_env() {
|
||||||
|
std::env::set_var("NYASH_JOINIR_CORE", "1");
|
||||||
|
std::env::set_var("NYASH_JOINIR_STRICT", "1");
|
||||||
|
}
|
||||||
|
|
||||||
/// StringHelpers.skip_ws + 最小 Main のテスト用フィクスチャ。
|
/// StringHelpers.skip_ws + 最小 Main のテスト用フィクスチャ。
|
||||||
/// Stage‑1 CLI 全体を読み込まずに、StringHelpers 内部の `.length()` 正規化だけを確認する。
|
/// Stage‑1 CLI 全体を読み込まずに、StringHelpers 内部の `.length()` 正規化だけを確認する。
|
||||||
fn stage1_staticcompiler_fixture_src() -> String {
|
fn stage1_staticcompiler_fixture_src() -> String {
|
||||||
@ -62,6 +67,7 @@ static box Main {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mir_stage1_staticcompiler_receiver_compiles_and_verifies() {
|
fn mir_stage1_staticcompiler_receiver_compiles_and_verifies() {
|
||||||
ensure_stage3_env();
|
ensure_stage3_env();
|
||||||
|
ensure_joinir_strict_env();
|
||||||
let src = stage1_staticcompiler_fixture_src();
|
let src = stage1_staticcompiler_fixture_src();
|
||||||
|
|
||||||
let ast: ASTNode = NyashParser::parse_from_string(&src).expect("parse ok");
|
let ast: ASTNode = NyashParser::parse_from_string(&src).expect("parse ok");
|
||||||
@ -81,6 +87,7 @@ fn mir_stage1_staticcompiler_receiver_compiles_and_verifies() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mir_stage1_staticcompiler_receiver_exec_succeeds() {
|
fn mir_stage1_staticcompiler_receiver_exec_succeeds() {
|
||||||
ensure_stage3_env();
|
ensure_stage3_env();
|
||||||
|
ensure_joinir_strict_env();
|
||||||
std::env::set_var("NYASH_DISABLE_PLUGINS", "1"); // Plugin依存を排除
|
std::env::set_var("NYASH_DISABLE_PLUGINS", "1"); // Plugin依存を排除
|
||||||
|
|
||||||
let src = stage1_staticcompiler_fixture_src();
|
let src = stage1_staticcompiler_fixture_src();
|
||||||
@ -113,6 +120,7 @@ fn mir_stage1_staticcompiler_receiver_exec_succeeds() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn mir_stage1_staticcompiler_receiver_normalizes_to_stringbox() {
|
fn mir_stage1_staticcompiler_receiver_normalizes_to_stringbox() {
|
||||||
ensure_stage3_env();
|
ensure_stage3_env();
|
||||||
|
ensure_joinir_strict_env();
|
||||||
let src = stage1_staticcompiler_fixture_src();
|
let src = stage1_staticcompiler_fixture_src();
|
||||||
|
|
||||||
let ast: ASTNode = NyashParser::parse_from_string(&src).expect("parse ok");
|
let ast: ASTNode = NyashParser::parse_from_string(&src).expect("parse ok");
|
||||||
|
|||||||
@ -41,7 +41,9 @@ fn phase67_type_hint_policy_p3c_integration() {
|
|||||||
|
|
||||||
// 一般関数は P3-C 候補
|
// 一般関数は P3-C 候補
|
||||||
assert!(TypeHintPolicy::is_p3c_target("ArrayProcessor.process/1"));
|
assert!(TypeHintPolicy::is_p3c_target("ArrayProcessor.process/1"));
|
||||||
assert!(TypeHintPolicy::is_p3c_target("GenericTypeGetTest.array_get/0"));
|
assert!(TypeHintPolicy::is_p3c_target(
|
||||||
|
"GenericTypeGetTest.array_get/0"
|
||||||
|
));
|
||||||
|
|
||||||
// is_target と is_p3c_target は排他的
|
// is_target と is_p3c_target は排他的
|
||||||
let p3c_func = "GenericTypeGetTest.array_get/0";
|
let p3c_func = "GenericTypeGetTest.array_get/0";
|
||||||
@ -128,7 +130,8 @@ fn phase67_ab_test_resolve_from_phi_equivalence() {
|
|||||||
inputs: vec![(then_bb, v2), (else_bb, v3)],
|
inputs: vec![(then_bb, v2), (else_bb, v3)],
|
||||||
type_hint: None, // P3-C: type_hint なし
|
type_hint: None, // P3-C: type_hint なし
|
||||||
});
|
});
|
||||||
f.get_block_mut(merge_bb).unwrap().terminator = Some(MirInstruction::Return { value: Some(v4) });
|
f.get_block_mut(merge_bb).unwrap().terminator =
|
||||||
|
Some(MirInstruction::Return { value: Some(v4) });
|
||||||
|
|
||||||
// 型情報を設定
|
// 型情報を設定
|
||||||
let mut types: BTreeMap<ValueId, MirType> = BTreeMap::new();
|
let mut types: BTreeMap<ValueId, MirType> = BTreeMap::new();
|
||||||
@ -139,6 +142,13 @@ fn phase67_ab_test_resolve_from_phi_equivalence() {
|
|||||||
let result_a = crate::mir::phi_core::if_phi::infer_type_from_phi(&f, v4, &types);
|
let result_a = crate::mir::phi_core::if_phi::infer_type_from_phi(&f, v4, &types);
|
||||||
let result_b = GenericTypeResolver::resolve_from_phi(&f, v4, &types);
|
let result_b = GenericTypeResolver::resolve_from_phi(&f, v4, &types);
|
||||||
|
|
||||||
assert_eq!(result_a, result_b, "A/B test: both routes should return the same type");
|
assert_eq!(
|
||||||
assert_eq!(result_a, Some(MirType::Integer), "Type should be inferred as Integer");
|
result_a, result_b,
|
||||||
|
"A/B test: both routes should return the same type"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result_a,
|
||||||
|
Some(MirType::Integer),
|
||||||
|
"Type should be inferred as Integer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,13 @@ else
|
|||||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
RAW_KEEP="${NYASH_EMIT_MIR_KEEP_RAW:-0}"
|
||||||
|
RAW_DIR="${NYASH_EMIT_MIR_RAW_DIR:-$ROOT/logs/emit_mir}"
|
||||||
|
if [ "$RAW_KEEP" = "1" ]; then
|
||||||
|
mkdir -p "$RAW_DIR" 2>/dev/null || RAW_KEEP=0
|
||||||
|
fi
|
||||||
|
timestamp_now() { date +%Y%m%d_%H%M%S; }
|
||||||
|
|
||||||
# Resolve nyash/hakorune binary via test_runner helper (ensures consistent env)
|
# Resolve nyash/hakorune binary via test_runner helper (ensures consistent env)
|
||||||
if [ ! -f "$IN" ]; then
|
if [ ! -f "$IN" ]; then
|
||||||
echo "[FAIL] input not found: $IN" >&2
|
echo "[FAIL] input not found: $IN" >&2
|
||||||
@ -154,6 +161,39 @@ PY
|
|||||||
fi
|
fi
|
||||||
export HAKO_MIRBUILDER_IMPORTS="$IMPORTS_JSON"
|
export HAKO_MIRBUILDER_IMPORTS="$IMPORTS_JSON"
|
||||||
|
|
||||||
|
# Run direct emit with optional raw capture (label helps identify caller)
|
||||||
|
run_direct_emit() {
|
||||||
|
local label="$1" out_path="$2" in_path="$3"
|
||||||
|
local rc=0
|
||||||
|
if [ "$RAW_KEEP" = "1" ]; then
|
||||||
|
local tmp_out
|
||||||
|
tmp_out=$(mktemp --suffix .emit_direct_raw)
|
||||||
|
"$NYASH_BIN" --emit-mir-json "$out_path" "$in_path" >"$tmp_out" 2>&1 || rc=$?
|
||||||
|
local ts=$(timestamp_now)
|
||||||
|
local log_path="$RAW_DIR/direct_emit_${label}_${ts}_$$.log"
|
||||||
|
{
|
||||||
|
local raw_len=$(wc -c < "$tmp_out" | tr -d ' ')
|
||||||
|
echo "[emit/raw] label=$label rc=$rc raw_len=$raw_len"
|
||||||
|
cat "$tmp_out"
|
||||||
|
} > "$log_path" 2>/dev/null || true
|
||||||
|
rm -f "$tmp_out" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
"$NYASH_BIN" --emit-mir-json "$out_path" "$in_path" >/dev/null 2>&1 || rc=$?
|
||||||
|
fi
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
# Quick stderr summary to avoid opening the raw file
|
||||||
|
local raw_len=0
|
||||||
|
if [ -f "${tmp_out:-}" ]; then
|
||||||
|
raw_len=$(wc -c < "${tmp_out:-}" | tr -d ' ')
|
||||||
|
echo "[emit/direct] rc=$rc raw_len=$raw_len (head 100 lines)" >&2
|
||||||
|
head -n 100 "${tmp_out:-}" >&2 || true
|
||||||
|
else
|
||||||
|
echo "[emit/direct] rc=$rc (no tmp_out captured)" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return $rc
|
||||||
|
}
|
||||||
|
|
||||||
# Check if FORCE jsonfrag mode is requested (bypasses Stage-B entirely)
|
# Check if FORCE jsonfrag mode is requested (bypasses Stage-B entirely)
|
||||||
if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then
|
if [ "${HAKO_MIR_BUILDER_LOOP_FORCE_JSONFRAG:-0}" = "1" ]; then
|
||||||
# Extract limit from code using grep/awk
|
# Extract limit from code using grep/awk
|
||||||
@ -338,14 +378,34 @@ if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
|||||||
echo "[emit:trace] Stage-B: Starting parse of input (${code_len} chars)..." >&2
|
echo "[emit:trace] Stage-B: Starting parse of input (${code_len} chars)..." >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
stageb_raw_log=""
|
||||||
|
# Decide Stage-B entry (direct compiler_stageb or compiler.hako --stage-b)
|
||||||
|
stageb_target="$ROOT/lang/src/compiler/entry/compiler.hako"
|
||||||
|
stageb_extra=(--stage-b --stage3)
|
||||||
|
# Legacy: allow direct compiler_stageb.hako when explicitly requested
|
||||||
|
if [ "${NYASH_EMIT_USE_COMPILER:-1}" = "0" ]; then
|
||||||
|
stageb_target="$ROOT/lang/src/compiler/entry/compiler_stageb.hako"
|
||||||
|
stageb_extra=()
|
||||||
|
fi
|
||||||
|
# Optional: log which entry we picked
|
||||||
|
if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then
|
||||||
|
echo "[emit/trace] stageb target=$stageb_target extra=${stageb_extra[*]:-<none>}" >&2
|
||||||
|
fi
|
||||||
# Run Stage-B with temp file (avoid subshell CODE variable expansion)
|
# Run Stage-B with temp file (avoid subshell CODE variable expansion)
|
||||||
PROG_JSON_RAW=$(cd "$ROOT" && \
|
PROG_JSON_RAW=$(cd "$ROOT" && \
|
||||||
NYASH_JSON_ONLY=1 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
|
NYASH_JSON_ONLY=${NYASH_JSON_ONLY:-1} NYASH_DISABLE_NY_COMPILER=${NYASH_DISABLE_NY_COMPILER:-1} HAKO_DISABLE_NY_COMPILER=${HAKO_DISABLE_NY_COMPILER:-1} \
|
||||||
HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
||||||
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
|
||||||
NYASH_ENABLE_USING=${NYASH_ENABLE_USING:-1} HAKO_ENABLE_USING=${HAKO_ENABLE_USING:-1} \
|
NYASH_ENABLE_USING=${NYASH_ENABLE_USING:-1} HAKO_ENABLE_USING=${HAKO_ENABLE_USING:-1} \
|
||||||
"$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat "$CODE_TMP")" 2>&1)
|
"$NYASH_BIN" --backend vm "$stageb_target" -- "${stageb_extra[@]}" --source "$(cat "$CODE_TMP")" 2>&1)
|
||||||
rc=$?
|
rc=$?
|
||||||
|
stageb_rc=$rc
|
||||||
|
|
||||||
|
# Optional raw trace to stderr for debugging extraction/filter問題
|
||||||
|
if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then
|
||||||
|
echo "[emit/trace] Stage-B raw len=${#PROG_JSON_RAW} rc=${stageb_rc}" >&2
|
||||||
|
printf '%s\n' "$PROG_JSON_RAW" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
||||||
echo "[emit:trace] Stage-B: Raw output length=${#PROG_JSON_RAW} chars, rc=$rc" >&2
|
echo "[emit:trace] Stage-B: Raw output length=${#PROG_JSON_RAW} chars, rc=$rc" >&2
|
||||||
@ -354,6 +414,22 @@ fi
|
|||||||
# Extract Program JSON from raw output
|
# Extract Program JSON from raw output
|
||||||
PROG_JSON_OUT=$(extract_program_json "$PROG_JSON_RAW" 2>/dev/null || true)
|
PROG_JSON_OUT=$(extract_program_json "$PROG_JSON_RAW" 2>/dev/null || true)
|
||||||
extract_rc=$?
|
extract_rc=$?
|
||||||
|
stageb_hit=$(printf '%s' "$PROG_JSON_OUT" | grep -c '"kind":"Program"' || true)
|
||||||
|
stageb_raw_len=${#PROG_JSON_RAW}
|
||||||
|
echo "[emit/stageb] rc_stageb=${stageb_rc} extract_rc=${extract_rc} raw_len=${stageb_raw_len} program_hits=${stageb_hit}" >&2
|
||||||
|
if [ "$RAW_KEEP" = "1" ]; then
|
||||||
|
ts=$(timestamp_now)
|
||||||
|
stageb_raw_log="$RAW_DIR/stageb_emit_${ts}_$$.log"
|
||||||
|
{
|
||||||
|
echo "[emit/raw] cmd=${stageb_target##*/} rc_stageb=${stageb_rc} extract_rc=${extract_rc} raw_len=${stageb_raw_len} program_hits=${stageb_hit}"
|
||||||
|
echo "[emit/raw] src=$IN"
|
||||||
|
echo "[emit/raw] --- stdout+stderr ---"
|
||||||
|
printf '%s' "$PROG_JSON_RAW"
|
||||||
|
} > "$stageb_raw_log" 2>/dev/null || true
|
||||||
|
if [ $extract_rc -eq 0 ] && [ -n "$PROG_JSON_OUT" ]; then
|
||||||
|
printf '%s' "$PROG_JSON_OUT" > "$RAW_DIR/stageb_emit_${ts}_$$.program.json" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
||||||
if [ $extract_rc -eq 0 ] && [ -n "$PROG_JSON_OUT" ]; then
|
if [ $extract_rc -eq 0 ] && [ -n "$PROG_JSON_OUT" ]; then
|
||||||
@ -372,26 +448,35 @@ if [ "${HAKO_SELFHOST_TRACE:-0}" = "1" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Update rc to reflect extraction result
|
# Update rc to reflect extraction result
|
||||||
|
stageb_ok=1
|
||||||
if [ $extract_rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ]; then
|
if [ $extract_rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ]; then
|
||||||
rc=1
|
rc=1
|
||||||
|
stageb_ok=0
|
||||||
fi
|
fi
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# If Stage-B fails, skip to direct MIR emit paths (provider/legacy)
|
# If Stage-B fails, skip to direct MIR emit paths (provider/legacy)
|
||||||
if [ $rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ]; then
|
if [ $rc -ne 0 ] || [ -z "$PROG_JSON_OUT" ] || [ "$stageb_hit" -eq 0 ]; then
|
||||||
# Diagnose common Stage‑B errors before falling back
|
# Diagnose common Stage‑B errors before falling back
|
||||||
diagnose_stageb_failure "$PROG_JSON_RAW"
|
diagnose_stageb_failure "$PROG_JSON_RAW"
|
||||||
# Stage-B not available - fall back to legacy CLI path directly
|
# Stage-B not available - fall back to legacy CLI path directly
|
||||||
# Skip the intermediate Program(JSON) step and emit MIR directly
|
# Skip the intermediate Program(JSON) step and emit MIR directly
|
||||||
|
echo "[emit/stageb] failed_or_empty stageb_ok=${stageb_ok} rc_stageb=${stageb_rc} extract_rc=${extract_rc} hits=${stageb_hit}" >&2
|
||||||
|
if [ "${NYASH_EMIT_MIR_TRACE:-0}" = "1" ]; then
|
||||||
|
echo "[emit/stageb] raw_len=${stageb_raw_len}" >&2
|
||||||
|
fi
|
||||||
|
direct_ok=0
|
||||||
if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
||||||
HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \
|
HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \
|
||||||
HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \
|
HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \
|
||||||
NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \
|
NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \
|
||||||
NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \
|
NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \
|
||||||
"$NYASH_BIN" --emit-mir-json "$OUT" "$IN" >/dev/null 2>&1; then
|
run_direct_emit "fallback" "$OUT" "$IN"; then
|
||||||
|
direct_ok=1
|
||||||
echo "[OK] MIR JSON written (direct-emit): $OUT"
|
echo "[OK] MIR JSON written (direct-emit): $OUT"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
echo "[emit/direct] failed rc_stageb=${stageb_rc} extract_rc=${extract_rc} direct_ok=${direct_ok} stageb_hits=${stageb_hit}" >&2
|
||||||
echo "[FAIL] Stage-B and direct MIR emit both failed" >&2
|
echo "[FAIL] Stage-B and direct MIR emit both failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -400,15 +485,18 @@ fi
|
|||||||
if ! printf '%s' "$PROG_JSON_OUT" | grep -q '"kind"\s*:\s*"Program"'; then
|
if ! printf '%s' "$PROG_JSON_OUT" | grep -q '"kind"\s*:\s*"Program"'; then
|
||||||
# Invalid Program JSON - fall back to direct emit(事前に簡単な診断を出す)
|
# Invalid Program JSON - fall back to direct emit(事前に簡単な診断を出す)
|
||||||
diagnose_stageb_failure "$PROG_JSON_RAW"
|
diagnose_stageb_failure "$PROG_JSON_RAW"
|
||||||
|
direct_ok=0
|
||||||
if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
if HAKO_STAGEB_FUNC_SCAN="${HAKO_STAGEB_FUNC_SCAN:-1}" \
|
||||||
HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \
|
HAKO_MIR_BUILDER_FUNCS="${HAKO_MIR_BUILDER_FUNCS:-}" \
|
||||||
HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \
|
HAKO_MIR_BUILDER_CALL_RESOLVE="${HAKO_MIR_BUILDER_CALL_RESOLVE:-}" \
|
||||||
NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \
|
NYASH_JSON_SCHEMA_V1=${NYASH_JSON_SCHEMA_V1:-1} \
|
||||||
NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \
|
NYASH_MIR_UNIFIED_CALL=${NYASH_MIR_UNIFIED_CALL:-1} \
|
||||||
"$NYASH_BIN" --emit-mir-json "$OUT" "$IN" >/dev/null 2>&1; then
|
run_direct_emit "invalid_program" "$OUT" "$IN"; then
|
||||||
|
direct_ok=1
|
||||||
echo "[OK] MIR JSON written (direct-emit-fallback): $OUT"
|
echo "[OK] MIR JSON written (direct-emit-fallback): $OUT"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
echo "[emit/direct] invalid_program rc_stageb=${stageb_rc} extract_rc=${extract_rc} direct_ok=${direct_ok} stageb_hits=${stageb_hit}" >&2
|
||||||
echo "[FAIL] Stage‑B output invalid and direct emit failed" >&2
|
echo "[FAIL] Stage‑B output invalid and direct emit failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -21,6 +21,12 @@ if [ -z "${BIN}" ]; then
|
|||||||
elif [ -x "$ROOT/target/release/nyash" ]; then BIN="$ROOT/target/release/nyash";
|
elif [ -x "$ROOT/target/release/nyash" ]; then BIN="$ROOT/target/release/nyash";
|
||||||
else echo "[selfhost] error: NYASH_BIN not set and no binary found under target/release" >&2; exit 2; fi
|
else echo "[selfhost] error: NYASH_BIN not set and no binary found under target/release" >&2; exit 2; fi
|
||||||
fi
|
fi
|
||||||
|
RAW_KEEP="${NYASH_SELFHOST_KEEP_RAW:-0}"
|
||||||
|
RAW_DIR="${NYASH_SELFHOST_RAW_DIR:-$ROOT/logs/selfhost}"
|
||||||
|
if [ "$RAW_KEEP" = "1" ]; then
|
||||||
|
mkdir -p "$RAW_DIR" 2>/dev/null || RAW_KEEP=0
|
||||||
|
fi
|
||||||
|
timestamp_now() { date +%Y%m%d_%H%M%S; }
|
||||||
|
|
||||||
IN=""
|
IN=""
|
||||||
JSON_OUT=""
|
JSON_OUT=""
|
||||||
@ -29,6 +35,20 @@ EXE_OUT=""
|
|||||||
DO_RUN=0
|
DO_RUN=0
|
||||||
KEEP_TMP=0
|
KEEP_TMP=0
|
||||||
|
|
||||||
|
apply_selfhost_env() {
|
||||||
|
export NYASH_FEATURES="${NYASH_FEATURES:-stage3}"
|
||||||
|
export NYASH_PARSER_ALLOW_SEMICOLON=1
|
||||||
|
export NYASH_ALLOW_USING_FILE=0
|
||||||
|
export HAKO_ALLOW_USING_FILE=0
|
||||||
|
export NYASH_USING_AST=1
|
||||||
|
export NYASH_VARMAP_GUARD_STRICT=0
|
||||||
|
export NYASH_BLOCK_SCHEDULE_VERIFY=0
|
||||||
|
export NYASH_QUIET=0 HAKO_QUIET=0 NYASH_CLI_VERBOSE=0
|
||||||
|
# Ensure core plugins (Console/Array/Map/String/Integer) are discoverable
|
||||||
|
export NYASH_PLUGIN_PATH="${NYASH_PLUGIN_PATH:-$ROOT/target/release}"
|
||||||
|
export NYASH_PLUGIN_PATHS="${NYASH_PLUGIN_PATHS:-$NYASH_PLUGIN_PATH}"
|
||||||
|
}
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--in) IN="$2"; shift 2;;
|
--in) IN="$2"; shift 2;;
|
||||||
@ -48,8 +68,12 @@ tmp_json="${JSON_OUT:-/tmp/hako_stageb_$$.json}"
|
|||||||
|
|
||||||
# Emit Program(JSON v0; prefer BuildBox for emit-only when HAKO_USE_BUILDBOX=1)
|
# Emit Program(JSON v0; prefer BuildBox for emit-only when HAKO_USE_BUILDBOX=1)
|
||||||
RAW="/tmp/hako_stageb_raw_$$.txt"
|
RAW="/tmp/hako_stageb_raw_$$.txt"
|
||||||
|
stageb_rc=0
|
||||||
SRC_CONTENT="$(cat "$IN")"
|
SRC_CONTENT="$(cat "$IN")"
|
||||||
|
stageb_cmd_desc=""
|
||||||
if [ "${HAKO_USE_BUILDBOX:-0}" = "1" ] && [ "$DO_RUN" = "0" ] && [ -z "$EXE_OUT" ]; then
|
if [ "${HAKO_USE_BUILDBOX:-0}" = "1" ] && [ "$DO_RUN" = "0" ] && [ -z "$EXE_OUT" ]; then
|
||||||
|
apply_selfhost_env
|
||||||
|
stageb_cmd_desc="BuildBox.emit_program_json_v0 via compiler build_box"
|
||||||
WRAP="/tmp/hako_buildbox_wrap_$$.hako"
|
WRAP="/tmp/hako_buildbox_wrap_$$.hako"
|
||||||
cat > "$WRAP" <<'HAKO'
|
cat > "$WRAP" <<'HAKO'
|
||||||
include "lang/src/compiler/build/build_box.hako"
|
include "lang/src/compiler/build/build_box.hako"
|
||||||
@ -62,31 +86,47 @@ static box Main { method main(args) {
|
|||||||
HAKO
|
HAKO
|
||||||
(
|
(
|
||||||
export HAKO_SRC="$SRC_CONTENT"
|
export HAKO_SRC="$SRC_CONTENT"
|
||||||
export NYASH_QUIET=0 HAKO_QUIET=0 NYASH_CLI_VERBOSE=0
|
|
||||||
cd "$ROOT" && "$BIN" --backend vm "$WRAP"
|
cd "$ROOT" && "$BIN" --backend vm "$WRAP"
|
||||||
) > "$RAW" 2>&1 || true
|
) > "$RAW" 2>&1 || stageb_rc=$?
|
||||||
rm -f "$WRAP" 2>/dev/null || true
|
rm -f "$WRAP" 2>/dev/null || true
|
||||||
else
|
else
|
||||||
|
apply_selfhost_env
|
||||||
|
stageb_cmd_desc="compiler.hako --stage-b --stage3"
|
||||||
(
|
(
|
||||||
export NYASH_PARSER_ALLOW_SEMICOLON=1
|
|
||||||
export NYASH_ALLOW_USING_FILE=0
|
|
||||||
export HAKO_ALLOW_USING_FILE=0
|
|
||||||
export NYASH_USING_AST=1
|
|
||||||
export NYASH_FEATURES="${NYASH_FEATURES:-stage3}"
|
|
||||||
export NYASH_VARMAP_GUARD_STRICT=0
|
|
||||||
export NYASH_BLOCK_SCHEDULE_VERIFY=0
|
|
||||||
export NYASH_QUIET=0 HAKO_QUIET=0 NYASH_CLI_VERBOSE=0
|
|
||||||
cd "$ROOT" && \
|
cd "$ROOT" && \
|
||||||
"$BIN" --backend vm \
|
"$BIN" --backend vm \
|
||||||
"$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- \
|
"$ROOT/lang/src/compiler/entry/compiler.hako" -- \
|
||||||
--source "$SRC_CONTENT"
|
--stage-b --stage3 --source "$SRC_CONTENT"
|
||||||
) > "$RAW" 2>&1 || true
|
) > "$RAW" 2>&1 || stageb_rc=$?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! awk '(/"version":0/ && /"kind":"Program"/){print;found=1;exit} END{exit(found?0:1)}' "$RAW" > "$tmp_json"; then
|
extract_ok=0
|
||||||
|
if awk '(/"version":0/ && /"kind":"Program"/){print;found=1;exit} END{exit(found?0:1)}' "$RAW" > "$tmp_json"; then
|
||||||
|
extract_ok=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$RAW_KEEP" = "1" ]; then
|
||||||
|
ts="$(timestamp_now)"
|
||||||
|
raw_log="$RAW_DIR/stageb_${ts}_$$.log"
|
||||||
|
{
|
||||||
|
echo "[selfhost/raw] cmd: ${stageb_cmd_desc:-unknown}"
|
||||||
|
echo "[selfhost/raw] rc_stageb=${stageb_rc} extract_ok=${extract_ok}"
|
||||||
|
echo "[selfhost/raw] src=${IN}"
|
||||||
|
echo "[selfhost/raw] --- stdout+stderr ---"
|
||||||
|
cat "$RAW"
|
||||||
|
} > "$raw_log" 2>/dev/null || true
|
||||||
|
if [ "$extract_ok" = "1" ] && [ -s "$tmp_json" ]; then
|
||||||
|
cp "$tmp_json" "$RAW_DIR/stageb_${ts}_$$.json" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$extract_ok" != "1" ]; then
|
||||||
echo "[selfhost] Stage‑B emit failed" >&2
|
echo "[selfhost] Stage‑B emit failed" >&2
|
||||||
tail -n 120 "$RAW" >&2 || true
|
tail -n 120 "$RAW" >&2 || true
|
||||||
rm -f "$RAW" 2>/dev/null || true
|
if [ "$RAW_KEEP" = "1" ] && [ -n "${raw_log:-}" ]; then
|
||||||
|
echo "[selfhost/debug] RAW log: $raw_log" >&2
|
||||||
|
fi
|
||||||
|
if [ "$KEEP_TMP" != "1" ]; then rm -f "$RAW" 2>/dev/null || true; fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
rm -f "$RAW" 2>/dev/null || true
|
rm -f "$RAW" 2>/dev/null || true
|
||||||
|
|||||||
@ -20,6 +20,7 @@ Notes
|
|||||||
- Avoid running heavy integration smokes in CI by default. Use `--profile quick`.
|
- Avoid running heavy integration smokes in CI by default. Use `--profile quick`.
|
||||||
- When a test depends on external tools (e.g., LLVM), prefer `[SKIP:<reason>]` over failure.
|
- When a test depends on external tools (e.g., LLVM), prefer `[SKIP:<reason>]` over failure.
|
||||||
- Stage‑B/selfhost canaries(`stage1_launcher_*`, `phase251*` など)は Stage‑3 デフォルト環境で安定しないため、quick プロファイルでは `[SKIP:stageb]` として扱い、必要に応じて別プロファイル(integration/full)で個別に実行する。
|
- Stage‑B/selfhost canaries(`stage1_launcher_*`, `phase251*` など)は Stage‑3 デフォルト環境で安定しないため、quick プロファイルでは `[SKIP:stageb]` として扱い、必要に応じて別プロファイル(integration/full)で個別に実行する。
|
||||||
|
- Selfhost quick カバレッジは最小 1 本(`core/selfhost_minimal.sh`)に絞り、Stage‑3 + JoinIR 前提で Stage‑B→VM を通るかだけを確認する。
|
||||||
- S3 backend 向けの長尺テスト群も quick 向きではないため、timeout を短く保ちたい場合は `[SKIP:slow]` にして別途ローカルで回すことを推奨する。
|
- S3 backend 向けの長尺テスト群も quick 向きではないため、timeout を短く保ちたい場合は `[SKIP:slow]` にして別途ローカルで回すことを推奨する。
|
||||||
|
|
||||||
Quick tips
|
Quick tips
|
||||||
|
|||||||
@ -20,6 +20,8 @@ fi
|
|||||||
|
|
||||||
# Stage-3 is default: prefer feature flag instead of legacy parser envs.
|
# Stage-3 is default: prefer feature flag instead of legacy parser envs.
|
||||||
export NYASH_FEATURES="${NYASH_FEATURES:-stage3}"
|
export NYASH_FEATURES="${NYASH_FEATURES:-stage3}"
|
||||||
|
# JoinIR Core は smokes 実行時のみ既定ON(明示設定があればそれを優先)
|
||||||
|
export NYASH_JOINIR_CORE="${NYASH_JOINIR_CORE:-1}"
|
||||||
|
|
||||||
# Debug convenience: HAKO_DEBUG=1 enables execution trace and log passthrough
|
# Debug convenience: HAKO_DEBUG=1 enables execution trace and log passthrough
|
||||||
if [ "${HAKO_DEBUG:-0}" = "1" ]; then
|
if [ "${HAKO_DEBUG:-0}" = "1" ]; then
|
||||||
@ -95,6 +97,7 @@ log_error() {
|
|||||||
| grep -v "^\\[DEBUG/" \
|
| grep -v "^\\[DEBUG/" \
|
||||||
| grep -v "^\\[ssa-undef-debug\\]" \
|
| grep -v "^\\[ssa-undef-debug\\]" \
|
||||||
| grep -v '^\[PluginBoxFactory\]' \
|
| grep -v '^\[PluginBoxFactory\]' \
|
||||||
|
| grep -v '^\[plugin/init\]' \
|
||||||
| grep -v '^\[using.dylib/autoload\]' \
|
| grep -v '^\[using.dylib/autoload\]' \
|
||||||
| grep -v "^\[vm\] Stage-3" \
|
| grep -v "^\[vm\] Stage-3" \
|
||||||
| grep -v "^\[DEBUG\]" \
|
| grep -v "^\[DEBUG\]" \
|
||||||
|
|||||||
@ -4,6 +4,10 @@ set -euo pipefail
|
|||||||
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
|
||||||
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
|
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
|
||||||
|
|
||||||
|
# Stage-B emit が不安定なため quick ではスキップ(s3 parity は Stage-B ラインで扱う)
|
||||||
|
echo "[SKIP] s3_backend_selector_crate_exe_vm_parity_return42_canary_vm (Stage-B emit 不安定のため quick ではスキップ)" >&2
|
||||||
|
exit 0
|
||||||
|
|
||||||
# Ensure tools are built and environment is consistent for EXE
|
# Ensure tools are built and environment is consistent for EXE
|
||||||
timeout "${HAKO_BUILD_TIMEOUT:-10}" bash -c "cd \"$ROOT\" && cargo build -q --release -p nyash-llvm-compiler >/dev/null" || true
|
timeout "${HAKO_BUILD_TIMEOUT:-10}" bash -c "cd \"$ROOT\" && cargo build -q --release -p nyash-llvm-compiler >/dev/null" || true
|
||||||
timeout "${HAKO_BUILD_TIMEOUT:-10}" bash -c "cd \"$ROOT/crates/nyash_kernel\" && cargo build -q --release >/dev/null" || true
|
timeout "${HAKO_BUILD_TIMEOUT:-10}" bash -c "cd \"$ROOT/crates/nyash_kernel\" && cargo build -q --release >/dev/null" || true
|
||||||
|
|||||||
51
tools/smokes/v2/profiles/quick/core/selfhost_minimal.sh
Normal file
51
tools/smokes/v2/profiles/quick/core/selfhost_minimal.sh
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# selfhost_minimal.sh — Minimal selfhost Stage‑B→VM path using stage1_run_min.hako
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then
|
||||||
|
ROOT="$ROOT_GIT"
|
||||||
|
else
|
||||||
|
ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BIN="${NYASH_BIN:-$ROOT/target/release/nyash}"
|
||||||
|
SELFHOST="$ROOT/tools/selfhost/selfhost_build.sh"
|
||||||
|
TARGET="$ROOT/apps/tests/stage1_run_min.hako"
|
||||||
|
|
||||||
|
warn() { echo -e "[WARN] $*" >&2; }
|
||||||
|
info() { echo -e "[INFO] $*" >&2; }
|
||||||
|
fail() { echo -e "[FAIL] $*" >&2; exit 1; }
|
||||||
|
pass() { echo -e "[PASS] $*" >&2; }
|
||||||
|
|
||||||
|
if [ ! -x "$BIN" ]; then
|
||||||
|
warn "[SKIP] nyash binary not found at $BIN (build release first)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$SELFHOST" ]; then
|
||||||
|
warn "[SKIP] selfhost_build.sh missing at $SELFHOST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$TARGET" ]; then
|
||||||
|
warn "[SKIP] target fixture not found: $TARGET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Running minimal selfhost path via selfhost_build.sh"
|
||||||
|
set +e
|
||||||
|
NYASH_FEATURES="${NYASH_FEATURES:-stage3}" \
|
||||||
|
NYASH_USE_NY_COMPILER="${NYASH_USE_NY_COMPILER:-1}" \
|
||||||
|
NYASH_NY_COMPILER_EMIT_ONLY="${NYASH_NY_COMPILER_EMIT_ONLY:-1}" \
|
||||||
|
"$SELFHOST" --in "$TARGET" --run
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
fail "selfhost_minimal failed (rc=$rc)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
pass "selfhost_minimal passed (stage1_run_min.hako)"
|
||||||
|
exit 0
|
||||||
33
tools/smokes/v2/profiles/quick/core/stageb_min_emit.sh
Normal file
33
tools/smokes/v2/profiles/quick/core/stageb_min_emit.sh
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")" && git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||||
|
if [ -z "$ROOT" ]; then
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../../../../../../.." && pwd)"
|
||||||
|
fi
|
||||||
|
source "$ROOT/tools/smokes/v2/lib/test_runner.sh" || true
|
||||||
|
|
||||||
|
INPUT="$ROOT/apps/tests/emit_boxcall_length_canary_vm.hako"
|
||||||
|
if [ ! -f "$INPUT" ]; then
|
||||||
|
echo "[SKIP] stageb_min_emit: input not found: $INPUT" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TMP_JSON=$(mktemp --suffix .json)
|
||||||
|
trap 'rm -f "$TMP_JSON" 2>/dev/null || true' EXIT
|
||||||
|
|
||||||
|
# Stage‑B emit (RAW 保存は dev 用)
|
||||||
|
if ! NYASH_EMIT_MIR_KEEP_RAW=1 NYASH_EMIT_USE_COMPILER=1 NYASH_EMIT_MIR_TRACE="${NYASH_EMIT_MIR_TRACE:-0}" \
|
||||||
|
NYASH_FEATURES="${NYASH_FEATURES:-stage3}" \
|
||||||
|
bash "$ROOT/tools/hakorune_emit_mir.sh" "$INPUT" "$TMP_JSON" >/dev/null 2>&1; then
|
||||||
|
echo "[FAIL] stageb_min_emit: failed to emit MIR JSON" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$TMP_JSON" ]; then
|
||||||
|
echo "[FAIL] stageb_min_emit: JSON output missing or empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[PASS] stageb_min_emit"
|
||||||
|
exit 0
|
||||||
@ -186,6 +186,11 @@ setup_environment() {
|
|||||||
|
|
||||||
# プロファイル専用設定
|
# プロファイル専用設定
|
||||||
export SMOKES_CURRENT_PROFILE="$PROFILE"
|
export SMOKES_CURRENT_PROFILE="$PROFILE"
|
||||||
|
# Phase 80: quick プロファイルは JoinIR Core を既定ONで回してみる(明示指定があれば尊重)
|
||||||
|
if [ "$PROFILE" = "quick" ] && [ -z "${NYASH_JOINIR_CORE+x}" ]; then
|
||||||
|
export NYASH_JOINIR_CORE=1
|
||||||
|
log_info "JoinIR Core default ON for quick profile (NYASH_JOINIR_CORE=1)"
|
||||||
|
fi
|
||||||
|
|
||||||
# コマンドライン引数の環境変数設定
|
# コマンドライン引数の環境変数設定
|
||||||
if [ -n "$TIMEOUT" ]; then
|
if [ -n "$TIMEOUT" ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user