feat(joinir/dev): Phase 126 wire available_inputs into normalized builder

- AvailableInputsCollectorBox::collect() called in lower_function_body (dev-only)
- try_lower_if_only() signature extended (accepts available_inputs)
- EnvLayout::from_contract() now uses real available_inputs (not empty stub)
- Unit tests updated (empty BTreeMap for backward compat)
- All 23 normalized_shadow tests PASS
This commit is contained in:
nyash-codex
2025-12-18 06:45:23 +09:00
parent 89c2915fa0
commit 72f2c1f64d
2 changed files with 46 additions and 23 deletions

View File

@ -16,6 +16,8 @@
use crate::mir::control_tree::step_tree::StepTree;
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
use crate::mir::join_ir::JoinModule;
use crate::mir::ValueId; // Phase 126
use std::collections::BTreeMap; // Phase 126
use super::contracts::{check_if_only, CapabilityCheckResult};
@ -89,21 +91,27 @@ impl StepTreeNormalizedShadowLowererBox {
/// - Uses contract information only (no AST re-analysis)
/// - Dev-only: caller must check `joinir_dev_enabled()` before calling
///
/// ## Phase 122 Implementation
/// ## Phase 122-126 Implementation
///
/// - Generates Normalized JoinIR (env + continuation)
/// - env layout: writes only (SSOT)
/// - env layout: writes + inputs (Phase 125-126)
/// - merge = join_k(env) tail-call (no PHI)
/// - Minimal node support: If/Return/Assign(Const/Variable/BinOp(Add))
///
/// ## Phase 126: available_inputs
///
/// - available_inputs: function params + CapturedEnv (SSOT)
/// - inputs = reads ∩ available_inputs (deterministic order)
pub fn try_lower_if_only(
step_tree: &StepTree,
available_inputs: &BTreeMap<String, ValueId>,
) -> Result<Option<(JoinModule, JoinFragmentMeta)>, String> {
// Phase 121 P1: Capability check (if-only scope)
let capability = check_if_only(step_tree);
match capability {
CapabilityCheckResult::Supported => {
// Phase 122-123: Generate Normalized JoinModule
Self::lower_if_only_to_normalized(step_tree)
// Phase 122-126: Generate Normalized JoinModule
Self::lower_if_only_to_normalized(step_tree, available_inputs)
}
CapabilityCheckResult::Unsupported(_reason) => {
// Out of scope for Phase 121/122
@ -112,7 +120,7 @@ impl StepTreeNormalizedShadowLowererBox {
}
}
/// Lower if-only StepTree to Normalized JoinModule (Phase 122-125)
/// Lower if-only StepTree to Normalized JoinModule (Phase 122-126)
///
/// ## Design
///
@ -120,31 +128,33 @@ impl StepTreeNormalizedShadowLowererBox {
/// - merge 形式: `join_k(env)` への tail-callPHI 禁止)
/// - 対応ノード: If/Return/Assign(最小セット)
///
/// ## Phase 123-125 Node Support
/// ## Phase 123-126 Node Support
///
/// - Return(Integer literal): `Const + Ret(Some(vid))`
/// - Return(Variable from writes): Phase 124
/// - Return(Variable from inputs): Phase 125 (reads-only)
/// - Return(Variable from inputs): Phase 125-126 (reads-only)
/// - Return(void): `Ret(None)`
/// - If(minimal compare): Compare with Integer literal only
///
/// ## Phase 126: available_inputs
///
/// - available_inputs: function params + CapturedEnv (SSOT)
/// - inputs = reads ∩ available_inputs (deterministic order)
///
/// ## Returns
///
/// - `Ok(Some((module, meta)))`: Normalized JoinModule生成成功
/// - `Ok(None)`: Out of scope for Phase 123-125 (unsupported patterns)
/// - `Ok(None)`: Out of scope for Phase 123-126 (unsupported patterns)
/// - `Err(msg)`: 生成できるはずなのに失敗(内部エラー)
fn lower_if_only_to_normalized(
step_tree: &StepTree,
available_inputs: &BTreeMap<String, ValueId>,
) -> Result<Option<(JoinModule, JoinFragmentMeta)>, String> {
use crate::mir::join_ir::{JoinFunction, JoinFuncId, JoinInst};
use crate::mir::ValueId;
use std::collections::BTreeMap;
// Phase 125 P2: available_inputs (P3 で配線、今は空)
let available_inputs: BTreeMap<String, ValueId> = BTreeMap::new();
// Phase 125 P2: EnvLayout 生成
let env_layout = EnvLayout::from_contract(&step_tree.contract, &available_inputs);
// Phase 126: EnvLayout 生成(available_inputs を使用)
let env_layout = EnvLayout::from_contract(&step_tree.contract, available_inputs);
let main_func_id = JoinFuncId::new(0);
@ -679,7 +689,8 @@ mod tests {
#[test]
fn test_if_only_supported() {
let tree = make_if_only_tree();
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
assert!(result.unwrap().is_some());
}
@ -687,7 +698,8 @@ mod tests {
#[test]
fn test_loop_rejected() {
let tree = make_loop_tree();
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
@ -728,7 +740,8 @@ mod tests {
};
// Lower to JoinModule
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let (module, _meta) = result.unwrap().expect("Should generate JoinModule");
@ -772,7 +785,8 @@ mod tests {
};
// Lower to JoinModule
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let (module, _meta) = result.unwrap().expect("Should generate JoinModule");
@ -818,7 +832,8 @@ mod tests {
// Phase 125 P4: Lower to JoinModule - should return Ok(None)
// because x is in reads but not in available_inputs (not in env)
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let option = result.unwrap();
assert!(option.is_none(), "Should return None when variable is in reads but not available as input");
@ -876,7 +891,8 @@ mod tests {
};
// Lower to JoinModule
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let (module, _meta) = result.unwrap().expect("Should generate JoinModule");
@ -924,7 +940,8 @@ mod tests {
};
// Lower to JoinModule - should succeed
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let option = result.unwrap();
@ -980,7 +997,8 @@ mod tests {
// Phase 125 P2-P4: Lower to JoinModule
// Because available_inputs is empty (P3 not wired), x won't be in inputs
// So this should return Ok(None) (out of scope)
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
let available_inputs = BTreeMap::new(); // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs);
assert!(result.is_ok());
let option = result.unwrap();