# Phase 141: Impure Extension Contract → P1 Known Intrinsic (incremental)

Status: DONE ✅  
Scope: `NormalizedExprLowererBox` の pure/impure 境界を contract（SSOT）として型で固定し、Call/MethodCall を段階投入できる形へ収束させる。  
Related:
- `docs/development/current/main/design/normalized-expr-lowering.md`
- `docs/development/current/main/phases/phase-140/README.md`
- `src/mir/control_tree/normalized_shadow/common/expr_lowering_contract.rs`
- `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`

---

## Goal

- ExprLowerer が “pure only” のままでも、impure（Call/MethodCall）導入の拡張点（契約）を SSOT 化する。
- P0 は contract のみ（Call/MethodCall を lowering しない、既定挙動不変）。
- P1 は “既知 intrinsic だけ” を許可し、impure 導入の第一歩を作る（それ以外は既定挙動不変）。

## Non-Goals (P0)

- Call/MethodCall の lowering（ValueId生成、effects、receiver materialization）
- impure式の順序付け（effects ordering）
- 型解決（dispatch/overload）

## SSOT

- Contract: `src/mir/control_tree/normalized_shadow/common/expr_lowering_contract.rs`
  - `ExprLoweringScope::{PureOnly, WithImpure(..)}`
  - Phase 141 P1: `ImpurePolicy::KnownIntrinsicOnly`, `KnownIntrinsic::{Length0}`
  - `OutOfScopeReason`（Call/MethodCall 等の最小理由）
- ExprLowerer API: `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`
  - `lower_expr_with_scope(scope, ...)` を追加
  - 既存 `lower_expr(...)` は `PureOnly` の thin wrapper（挙動不変）
 - Return lowering entry: `src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs`
   - Phase 141 P1: return lowering は `WithImpure(KnownIntrinsicOnly)` を使う（既知 intrinsic のみ許可）

## Tests

- Unit: `cargo test -p nyash-rust --lib mir::control_tree::normalized_shadow::common`
- Added minimal contract tests:
  - `call_is_out_of_scope_in_pure_only`
  - `methodcall_is_out_of_scope_in_pure_only`
  - `methodcall_length0_is_in_scope_with_known_intrinsic_only`
  - `lower_methodcall_length0_emits_method_call_inst`

## Phase 141 P1: KnownIntrinsicOnly (Length0)

### Scope

- `ExprLoweringScope::WithImpure(ImpurePolicy::KnownIntrinsicOnly)` のときのみ、以下を lowering する:
  - `receiver.length()`（引数 0、receiver は env に存在する Variable のみ）
- それ以外の Call/MethodCall は out-of-scope (`Ok(None)`) のまま（既定挙動不変）。

### Fixture / Smoke

- Fixture: `apps/tests/phase141_p1_if_only_post_k_return_length_min.hako`（expected exit code 3）
- Smoke tests:
  - VM: `tools/smokes/v2/profiles/integration/apps/phase141_p1_if_only_post_k_return_length_vm.sh`
  - LLVM EXE: `tools/smokes/v2/profiles/integration/apps/phase141_p1_if_only_post_k_return_length_llvm_exe.sh`

## Phase 141 P1.5: SSOT Reinforcement (Registry + available_inputs + diagnostics)

### Task A: KnownIntrinsic SSOT (Registry)

- 目的: intrinsic の metadata（method 名/arity/type_hint）を 1 箇所に集約し、文字列直書きの散らばりを止める。
- SSOT:
  - `src/mir/control_tree/normalized_shadow/common/known_intrinsics.rs`
  - `KnownIntrinsicRegistryBox::{lookup,get_spec}`
- 効果:
  - `expr_lowerer_box.rs` の by-name 判定が “registry lookup” に収束

### Task B: available_inputs 3-source merge (Bug fix)

- 目的: suffix 正規化が prefix 側で生成された locals（`local s; s="..."`）を見失わないようにする。
- 変更:
  - `AvailableInputsCollectorBox::collect(builder, captured_env, prefix_variables)` を追加
  - 優先順位: Function params > Prefix variables > CapturedEnv
- Call sites:
  - `src/mir/builder/control_flow/normalization/execute_box.rs`
  - `src/mir/builder/control_flow/joinir/patterns/policies/normalized_shadow_suffix_router_box.rs`

### Task C: Diagnostics

- 追加: `OutOfScopeReason::IntrinsicNotWhitelisted`
  - “methodcall だが known intrinsic allowlist 外” を区別できるようにする

## Next

- Phase 141 P2+: 一般 Call/MethodCall（effects + typing を分離して拡張）
