feat(selfhost): add observation logs for Ny compiler path diagnosis

Add NYASH_CLI_VERBOSE>=2 observation logs to trace the Ny compiler
child process path (NYASH_USE_NY_COMPILER=1):
- Log when spawning child process
- Log when receiving Program(JSON v0)
- Log before/after maybe_dump_mir with dump path info
- Report whether MIR dump file was created

Key finding: apps/selfhost/compiler/compiler.hako doesn't exist,
so the preferred child process route never fires. This explains
why RUST_MIR_DUMP_PATH doesn't create files in Ny compiler path.

Also update environment-variables.md with:
- NYASH_CLI_VERBOSE=2 level documentation
- Ny compiler observation template for Phase 29

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-25 07:08:32 +09:00
parent 8ae5280be2
commit 94b2bd2799
2 changed files with 158 additions and 124 deletions

View File

@ -1,157 +1,153 @@
# 環境変数リファレンス # 環境変数リファレンス
Nyashの動作を制御する環境変数の一覧。 Nyash の主要な環境変数をカテゴリ別に整理するよ。`適用経路` はどのパスで効くかを示す:
- Rust AST: Rust パーサ直通 (`--dump-mir` など)
- JSON v0/Stage-1: selfhost/Stage-1/`--ny-parser-pipe` 経由json_v0_bridge で処理)
- Any: どの経路でも有効
--- ---
## MIR Builder検証系 ## ダンプ / 診断
MIRビルダーの動作検証・デバッグ用環境変数。 | 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| `RUST_MIR_DUMP_PATH=/tmp/out.mir` | OFF | JSON v0/Stage-1 | MIR printer の出力をファイルに書く (`json_v0_bridge::maybe_dump_mir` 経由) |
| `NYASH_CLI_VERBOSE=1` | OFF | Any | 詳細ログ。`maybe_dump_mir` が stdout に MIR を出す |
| `NYASH_CLI_VERBOSE=2` | OFF | Any | さらに詳細なログNy compiler 経路の診断ログ含む) |
| `NYASH_VM_DUMP_MIR=1` | OFF | Any | VM 実行前の MIR を出力 |
| `NYASH_DUMP_JSON_IR=1` | OFF | Any | JSON IR をダンプ |
| `NYASH_DEBUG_STACK_OVERFLOW=1` | OFF | Any | スタックオーバーフロー時に backtrace を有効化 |
| 環境変数 | デフォルト | 用途 | 追加Phase | ### ダンプの使い分け
|---------|----------|-----|-----------| - Rust AST 直通: `./target/release/hakorune --dump-mir apps/tests/minimal.hako`env は不要、stdout のみ)
| `NYASH_ME_CALL_ARITY_STRICT=1` | OFF | me.method呼び出しのarity不一致でエラー返却 | Phase 25.x | - JSON v0 経路/Stage-1: `RUST_MIR_DUMP_PATH=/tmp/out.mir NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_MIR_JSON=1 ./target/release/hakorune --dump-mir`stdout + ファイル)
| `NYASH_STATIC_CALL_TRACE=1` | OFF | Static method呼び出しのトレース出力 | Phase 25.x |
| `NYASH_VERIFY_ALLOW_NO_PHI=1` | OFF | PHI検証スキップ支配・マージ | - |
| `NYASH_VERIFY_ALLOW_LEGACY=1` | OFF | レガシー命令許可 | - |
| `NYASH_VERIFY_BARRIER_STRICT=1` | OFF | Barrier配置厳密チェック | - |
| `NYASH_VERIFY_EDGE_COPY_STRICT=1` | OFF | Edge copy検証 | - |
| `NYASH_VERIFY_RET_PURITY=1` | OFF | Return block純粋性検証 | - |
| `NYASH_BREAKFINDER_SSA_TRACE=1` | OFF | SSA詳細トレースBreakFinderBox等 | - |
### 使用例 ---
## Stage-1 / selfhost CLI
| 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| `NYASH_USE_STAGE1_CLI=1` | OFF | Stage-1 | Stage-1 stub 経由に切替 |
| `NYASH_STAGE1_MODE=emit-mir` | unset | Stage-1 | `emit-program` / `emit-mir` / `run` を明示 |
| `STAGE1_EMIT_PROGRAM_JSON=1` | OFF | Stage-1 | Program(JSON v0) を吐いて終了(レガシー alias |
| `STAGE1_EMIT_MIR_JSON=1` | OFF | Stage-1 | Program(JSON v0)→MIR(JSON) を Rust 側で降ろす(レガシー alias |
| `HAKO_STAGE1_MODE={emit-program\|emit-mir\|run}` | unset | Stage-1 | .hako / Stage-1 ルート専用のモード指定(`--hako-*` が設定) |
| `HAKO_EMIT_PROGRAM_JSON=1` | OFF | Stage-1 | `.hako` stub に Program(JSON v0) emit を指示 |
| `HAKO_EMIT_MIR_JSON=1` | OFF | Stage-1 | `.hako` stub に MIR(JSON) emit を指示json_v0_bridge 経由) |
| `NYASH_STAGE1_INPUT=path` | unset | Stage-1 | 入力ソースalias: `STAGE1_SOURCE`, `STAGE1_INPUT` |
| `HAKO_STAGE1_INPUT=path` | unset | Stage-1 | `.hako` stub 用の入力ソース(`--hako-*` が設定) |
| `NYASH_STAGE1_PROGRAM_JSON=path` | unset | Stage-1 | Program(JSON v0) のパスalias: `STAGE1_PROGRAM_JSON` |
| `HAKO_STAGE1_PROGRAM_JSON=path` | unset | Stage-1 | `.hako` stub 用 Program(JSON v0) パス |
| `NYASH_STAGE1_BACKEND=vm` | `vm` | Stage-1 | Stage-1 実行の backend ヒントalias: `STAGE1_BACKEND` |
| `NYASH_STAGE1_CLI_CHILD=1` | OFF | Stage-1 | 再帰呼び出しガード |
| `STAGE1_CLI_ENTRY=...` | `lang/src/runner/stage1_cli.hako` | Stage-1 | Stage-1 stub のエントリ差し替え |
| `STAGE1_*` alias | legacy | Stage-1 | `NYASH_STAGE1_*` の旧名。互換のため受理するが順次廃止予定 |
### Stage-1 経路の例
```bash ```bash
# Dev環境で厳密検証arity不一致でビルドエラー # Stage-1 で MIR(JSON) を受け取り、Rust 側で dump
NYASH_ME_CALL_ARITY_STRICT=1 cargo test RUST_MIR_DUMP_PATH=/tmp/out.mir \
NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_MIR_JSON=1 \
./target/release/hakorune --dump-mir apps/tests/minimal.hako
# トレースのみwarning表示、エラーにはしない # hako- 前置で Stage-1 stub 経由
NYASH_STATIC_CALL_TRACE=1 ./target/release/hakorune program.hako ./target/release/hakorune --hako-emit-program-json /tmp/out.json apps/tests/minimal.hako
./target/release/hakorune --hako-emit-mir-json /tmp/out.mir apps/tests/minimal.hako --dump-mir
# PHI検証を一時的にスキップ
NYASH_VERIFY_ALLOW_NO_PHI=1 ./target/release/hakorune program.hako
# SSA詳細トレースBreakFinderBox等のデバッグ
NYASH_BREAKFINDER_SSA_TRACE=1 cargo test mir_stage1_using_resolver_verify 2>&1 | grep "breakfinder/ssa"
``` ```
--- ---
## Stage-1 CLI制御系 ## Parser / using
Stage-1 CLINyash selfhosting compilerの制御用環境変数。 | 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| 環境変数 | デフォルト | 用途 | 追加Phase | | `NYASH_PARSER_STAGE3=1` | OFF | Any | Stage-3 構文を許可 |
|---------|----------|-----|-----------| | `HAKO_PARSER_STAGE3=1` | OFF | Any | `.hako` 向け Stage-3 alias |
| `NYASH_USE_STAGE1_CLI=1` | OFF | Stage-1 CLI有効化 | Phase 25.1 | | `NYASH_ENABLE_USING=1` | ON | Any | using 文を有効化 |
| `STAGE1_CLI_DEBUG=1` | OFF | Stage-1 CLI詳細ログ | Phase 25.1 | | `HAKO_ENABLE_USING=1` | ON | Any | using 文 alias (.hako) |
| `STAGE1_EMIT_PROGRAM_JSON=1` | OFF | Program JSON出力モード | Phase 25.1 | | `NYASH_RESOLVE_TRACE=1` | OFF | Any | using/prelude 解決のトレース |
| `STAGE1_EMIT_MIR_JSON=1` | OFF | MIR JSON出力モード | Phase 25.1 | | `NYASH_VM_DUMP_MERGED_HAKO=1` | OFF | Rust AST | using/prelude マージ後の Hako ソースをダンプ |
| `STAGE1_BACKEND={vm\|llvm\|pyvm}` | `vm` | 実行バックエンド指定 | Phase 25.1 |
| `STAGE1_SOURCE=<path>` | - | ソースファイルパス | Phase 25.1 |
| `STAGE1_SOURCE_TEXT=<text>` | - | ソーステキストinline指定 | Phase 25.1 |
| `STAGE1_PROGRAM_JSON=<path>` | - | Program JSONパス | Phase 25.1 |
### 使用例
```bash
# Stage-1 CLI経由で.hakoファイルをコンパイル
NYASH_USE_STAGE1_CLI=1 STAGE1_SOURCE=program.hako \
./target/release/hakorune lang/src/runner/stage1_cli.hako
# Program JSON生成
STAGE1_EMIT_PROGRAM_JSON=1 STAGE1_SOURCE=program.hako \
./target/release/hakorune lang/src/runner/stage1_cli.hako
# デバッグログ付き
STAGE1_CLI_DEBUG=1 NYASH_USE_STAGE1_CLI=1 \
./target/release/hakorune program.hako
```
--- ---
## Parser制御系 ## Runner / backend 選択
パーサーの動作制御用環境変数。 | 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| 環境変数 | デフォルト | 用途 | 追加Phase | | `NYASH_VM_USE_PY=1` | OFF | Any | PyVM を優先実行 |
|---------|----------|-----|-----------| | `NYASH_PIPE_USE_PYVM=1` | OFF | JSON v0/Stage-1 | pipe 実行を PyVM に直送 |
| `NYASH_PARSER_STAGE3=1` | OFF | Stage-3 parser有効化 | Phase 25.3 | | `NYASH_VM_PLUGIN_STRICT=1` | OFF | Any | 必須プラグイン欠如で fail-fast |
| `HAKO_PARSER_STAGE3=1` | OFF | Stage-3 parser有効化.hako側 | Phase 25.3 | | `NYASH_FAIL_FAST=0` | ON | Any | フォールバックを許容(既定は拒否) |
| `NYASH_ENABLE_USING=1` | OFF | using文サポート有効化 | Phase 25.1 |
| `HAKO_ENABLE_USING=1` | OFF | using文サポート有効化.hako側 | Phase 25.1 |
| `HAKO_STAGEB_APPLY_USINGS=1` | OFF | Stage-B using適用 | Phase 25.1 |
### 使用例
```bash
# using文を使ったプログラムをパース
NYASH_PARSER_STAGE3=1 NYASH_ENABLE_USING=1 \
./target/release/hakorune program_with_using.hako
```
--- ---
## デバッグ・観測系 ## Selfhost compiler / Ny compiler
デバッグ・トレース用環境変数。 | 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| `NYASH_USE_NY_COMPILER=1` | OFF | JSON v0 | Ny selfhost コンパイラを使用 |
| `NYASH_NY_COMPILER_STAGE3=1` | OFF | JSON v0 | Ny コンパイラ子プロセスで Stage-3 surface を許可 |
| `NYASH_NY_COMPILER_TIMEOUT_MS=2000` | `2000` | JSON v0 | selfhost 子プロセスのタイムアウト (ms) |
| `NYASH_NY_COMPILER_EMIT_ONLY=1` | ON | JSON v0 | selfhost コンパイラを emit-only で動かす |
| `NYASH_NY_COMPILER_CHILD_ARGS="-- --min-json"` | unset | JSON v0 | 子プロセスへ透過する追加引数 |
| 環境変数 | デフォルト | 用途 | 追加Phase | ### Ny compiler 経路の観測テンプレート (Phase 29)
|---------|----------|-----|-----------|
| `NYASH_CLI_VERBOSE=1` | OFF | 詳細診断情報出力 | - |
| `NYASH_VM_TRACE=1` | OFF | VM実行トレース出力 | - |
| `NYASH_VM_DUMP_MIR=1` | OFF | VM実行前MIR出力 | - |
| `NYASH_DUMP_JSON_IR=1` | OFF | JSON IR出力 | - |
| `NYASH_STAGE1_MIR_DUMP=1` | OFF | Stage-1 MIR出力 | Phase 25.x |
| `NYASH_STAGE1_SCAN_GE=1` | OFF | Compare Ge命令スキャン | Phase 25.x |
| `NYASH_TO_I64_DEBUG=1` | OFF | to_i64変換デバッグ | Phase 25.x |
| `NYASH_FUNCSCANNER_DEBUG=1` | OFF | FuncScanner詳細ログ | Phase 25.3 |
| `NYASH_JOINIR_EXPERIMENT=1` | OFF | JoinIR実験モードMIR→JoinIR変換テストを有効化 | Phase 26-H/27 |
### 使用例
```bash ```bash
# VM実行トレース # Ny compiler 経路で Program(JSON v0)→MIR→dump を観測
NYASH_VM_TRACE=1 ./target/release/hakorune program.hako 2>&1 | grep "vm-trace" NYASH_USE_NY_COMPILER=1 \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 \
# MIR確認複数手法 RUST_MIR_DUMP_PATH=/tmp/ny_selfhost_minimal.mir \
NYASH_VM_DUMP_MIR=1 ./target/release/hakorune program.hako NYASH_CLI_VERBOSE=2 \
./target/release/hakorune --dump-mir program.hako ./target/release/hakorune --dump-mir apps/tests/minimal_ssa_skip_ws.hako \
./target/release/hakorune --emit-mir-json debug.json program.hako 2>/tmp/ny_selfhost_minimal.log
# Stage-1 CLI + MIRダンプ
NYASH_STAGE1_MIR_DUMP=1 cargo test mir_stage1_cli_emit_program_min
# JoinIR実験テスト限定
NYASH_JOINIR_EXPERIMENT=1 cargo test --release mir_joinir_funcscanner_trim_auto_lowering -- --ignored
``` ```
確認項目:
- `/tmp/ny_selfhost_minimal.log` に以下の診断ログが出ているか:
- `[selfhost/ny] spawning Ny compiler child process: ...`
- `[selfhost/ny] received Program(JSON v0), size=... bytes`
- `[selfhost/ny] lowering Program(JSON v0) → MIR via json_v0_bridge`
- `[selfhost/ny] calling maybe_dump_mir (RUST_MIR_DUMP_PATH=..., cli_verbose=...)`
- `[selfhost/ny] ✅ MIR dump file created` または `⚠️ MIR dump file NOT created`
注意: `apps/selfhost/compiler/compiler.hako` が存在しない場合、preferred child process 経路は発火しない。
--- ---
## プラグイン・Box制御系 ## GC / Runtime
プラグインとBox factoryの制御用環境変数。 | 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| 環境変数 | デフォルト | 用途 | 追加Phase | | `NYASH_GC_MODE={auto|rc+cycle|minorgen|stw|rc|off}` | `rc+cycle` | Any | GC モード選択 |
|---------|----------|-----|-----------| | `NYASH_GC_TRACE=1` | OFF | Any | GC トレース出力 (0-3) |
| `NYASH_DISABLE_PLUGINS=1` | OFF | プラグイン無効化 | - | | `NYASH_GC_METRICS=1` | OFF | Any | GC メトリクス (text) |
| `NYASH_BOX_FACTORY_POLICY={builtin_first\|plugin_first}` | `builtin_first` | Box factory優先順位 | Phase 15.5 | | `NYASH_GC_METRICS_JSON=1` | OFF | Any | GC メトリクス (JSON) |
| `NYASH_FILEBOX_MODE={auto\|plugin\|builtin}` | `auto` | FileBox実装選択 | Phase 15.5 | | `NYASH_VM_TRACE=1` | OFF | Any | VM 実行トレース |
### 使用例
```bash
# プラグイン無効で実行コアBox経路のみ
NYASH_DISABLE_PLUGINS=1 ./target/release/hakorune program.hako
# Plugin優先でBox生成
NYASH_BOX_FACTORY_POLICY=plugin_first ./target/release/hakorune program.hako
```
--- ---
## 参考 ## プラグイン / Box
- **MIRログ観測**: [docs/development/architecture/mir-logs-observability.md](../development/architecture/mir-logs-observability.md) | 変数 | デフォルト | 適用経路 | 説明 |
- **Phase 25.4計画**: [docs/private/roadmap2/phases/phase-25.4-naming-cli-cleanup/README.md](../development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md) | --- | --- | --- | --- |
- **MIR検証システム**: [src/mir/verification/](../../src/mir/verification/) | `NYASH_DISABLE_PLUGINS=1` | OFF | Any | プラグイン無効化 |
| `NYASH_BOX_FACTORY_POLICY={builtin_first|plugin_first}` | `builtin_first` | Any | Box factory の優先順位 |
| `NYASH_FILEBOX_MODE={auto|plugin|builtin}` | `auto` | Any | FileBox 実装選択 |
---
## MIR 検証系(代表)
| 変数 | デフォルト | 適用経路 | 説明 |
| --- | --- | --- | --- |
| `NYASH_VERIFY_ALLOW_NO_PHI=1` | OFF | Any | PHI 検証をスキップ |
| `NYASH_VERIFY_EDGE_COPY_STRICT=1` | OFF | Any | Edge copy 検証を厳格化 |
| `NYASH_VERIFY_RET_PURITY=1` | OFF | Any | return ブロックの純粋性検証 |
| `NYASH_ME_CALL_ARITY_STRICT=1` | OFF | Any | me.method の arity 不一致でエラー |
---
参考: [docs/development/architecture/mir-logs-observability.md](../development/architecture/mir-logs-observability.md) / [src/mir/verification/](../../src/mir/verification/)

View File

@ -22,6 +22,16 @@ impl NyashRunner {
if let Some(ext) = path.extension().and_then(|s| s.to_str()) { if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
match ext { match ext {
"ny" | "nyash" => { /* continue */ } "ny" | "nyash" => { /* continue */ }
"hako" => {
// Opt-in: allow .hako to flow through Ny compiler when explicitly requested.
if !crate::config::env::use_ny_compiler() {
crate::cli_v!(
"[ny-compiler] skip selfhost pipeline for .hako (ext={}); enable NYASH_USE_NY_COMPILER=1 to force",
ext
);
return false;
}
}
_ => { _ => {
crate::cli_v!( crate::cli_v!(
"[ny-compiler] skip selfhost pipeline for non-Ny source: {} (ext={})", "[ny-compiler] skip selfhost pipeline for non-Ny source: {} (ext={})",
@ -177,10 +187,15 @@ impl NyashRunner {
// This avoids inline embedding pitfalls and supports Stage-3 gating via args. // This avoids inline embedding pitfalls and supports Stage-3 gating via args.
{ {
use crate::runner::modes::common_util::selfhost::{child, json}; use crate::runner::modes::common_util::selfhost::{child, json};
let verbose_level = crate::config::env::dump::cli_verbose_level();
let exe = std::env::current_exe() let exe = std::env::current_exe()
.unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash")); .unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let parser_prog = std::path::Path::new("apps/selfhost/compiler/compiler.hako"); let parser_prog = std::path::Path::new("apps/selfhost/compiler/compiler.hako");
if parser_prog.exists() { if parser_prog.exists() {
// Phase 28.2: observation log (NYASH_CLI_VERBOSE>=2)
if verbose_level >= 2 {
eprintln!("[selfhost/ny] spawning Ny compiler child process: {}", parser_prog.display());
}
// Build extra args forwarded to child program // Build extra args forwarded to child program
let mut extra_owned: Vec<String> = Vec::new(); let mut extra_owned: Vec<String> = Vec::new();
if crate::config::env::ny_compiler_min_json() { if crate::config::env::ny_compiler_min_json() {
@ -233,11 +248,34 @@ impl NyashRunner {
("HAKO_ALLOW_USING_FILE", "0"), ("HAKO_ALLOW_USING_FILE", "0"),
], ],
) { ) {
// Phase 28.2: observation log - JSON received
if verbose_level >= 2 {
eprintln!("[selfhost/ny] received Program(JSON v0), size={} bytes", line.len());
}
match json::parse_json_v0_line(&line) { match json::parse_json_v0_line(&line) {
Ok(module) => { Ok(module) => {
if crate::config::env::cli_verbose() { // Phase 28.2: observation log - before maybe_dump_mir
if crate::config::env::cli_verbose() { if verbose_level >= 2 {
let dump_path = crate::config::env::dump::rust_mir_dump_path();
eprintln!(
"[selfhost/ny] lowering Program(JSON v0) → MIR via json_v0_bridge (funcs={})",
module.functions.len()
);
eprintln!(
"[selfhost/ny] calling maybe_dump_mir (RUST_MIR_DUMP_PATH={:?}, cli_verbose={})",
dump_path.as_deref().unwrap_or("<unset>"),
crate::config::env::cli_verbose()
);
}
super::json_v0_bridge::maybe_dump_mir(&module); super::json_v0_bridge::maybe_dump_mir(&module);
// Phase 28.2: observation log - after maybe_dump_mir
if verbose_level >= 2 {
if let Some(ref path) = crate::config::env::dump::rust_mir_dump_path() {
if std::path::Path::new(path).exists() {
eprintln!("[selfhost/ny] ✅ MIR dump file created: {}", path);
} else {
eprintln!("[selfhost/ny] ⚠️ MIR dump file NOT created: {}", path);
}
} }
} }
let emit_only = crate::config::env::ny_compiler_emit_only(); let emit_only = crate::config::env::ny_compiler_emit_only();