feat(joinir): Phase 171-fix ConditionEnv/ConditionBinding architecture
Proper HOST↔JoinIR ValueId separation for condition variables: - Add ConditionEnv struct (name → JoinIR-local ValueId mapping) - Add ConditionBinding struct (HOST/JoinIR ValueId pairs) - Modify condition_to_joinir to use ConditionEnv instead of builder.variable_map - Update Pattern2 lowerer to build ConditionEnv and ConditionBindings - Extend JoinInlineBoundary with condition_bindings field - Update BoundaryInjector to inject Copy instructions for condition variables This fixes the undefined ValueId errors where HOST ValueIds were being used directly in JoinIR instructions. Programs now execute (RC: 0), though loop variable exit values still need Phase 172 work. Key invariants established: 1. JoinIR uses ONLY JoinIR-local ValueIds 2. HOST↔JoinIR bridging is ONLY through JoinInlineBoundary 3. condition_to_joinir NEVER accesses builder.variable_map 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -391,12 +391,29 @@ pub(super) fn merge_and_rewrite(
|
||||
|
||||
// Create HashMap from remapper for BoundaryInjector (temporary adapter)
|
||||
let mut value_map_for_injector = HashMap::new();
|
||||
|
||||
// Phase 171-fix: Add join_inputs to value_map
|
||||
for join_in in &boundary.join_inputs {
|
||||
if let Some(remapped) = remapper.get_value(*join_in) {
|
||||
value_map_for_injector.insert(*join_in, remapped);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 171-fix: Add condition_bindings to value_map
|
||||
// Each binding specifies JoinIR ValueId → HOST ValueId mapping
|
||||
// We remap the JoinIR ValueId to a new MIR ValueId
|
||||
for binding in &boundary.condition_bindings {
|
||||
if let Some(remapped) = remapper.get_value(binding.join_value) {
|
||||
value_map_for_injector.insert(binding.join_value, remapped);
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 171-fix: Condition binding '{}': JoinIR {:?} → remapped {:?} (HOST {:?})",
|
||||
binding.name, binding.join_value, remapped, binding.host_value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use BoundaryInjector to inject Copy instructions
|
||||
if let Some(ref mut current_func) = builder.current_function {
|
||||
BoundaryInjector::inject_boundary_copies(
|
||||
|
||||
@ -74,9 +74,22 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
let mut remapper = block_allocator::allocate_blocks(builder, mir_module, debug)?;
|
||||
|
||||
// Phase 2: Collect values from all functions
|
||||
let (used_values, value_to_func_name, function_params) =
|
||||
let (mut used_values, value_to_func_name, function_params) =
|
||||
value_collector::collect_values(mir_module, &remapper, debug)?;
|
||||
|
||||
// Phase 171-fix: Add condition_bindings' join_values to used_values for remapping
|
||||
if let Some(boundary) = boundary {
|
||||
for binding in &boundary.condition_bindings {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values",
|
||||
binding.name, binding.join_value
|
||||
);
|
||||
}
|
||||
used_values.insert(binding.join_value);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Remap ValueIds
|
||||
remap_values(builder, &used_values, &mut remapper, debug)?;
|
||||
|
||||
|
||||
@ -385,6 +385,8 @@ mod tests {
|
||||
join_inputs: vec![],
|
||||
host_outputs: vec![],
|
||||
join_outputs: vec![],
|
||||
exit_bindings: vec![], // Phase 171: Add missing field
|
||||
condition_inputs: vec![], // Phase 171: Add missing field
|
||||
};
|
||||
|
||||
builder.apply_to_boundary(&mut boundary)
|
||||
|
||||
@ -70,6 +70,61 @@ impl MirBuilder {
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().varmap("pattern2_start", &self.variable_map);
|
||||
|
||||
// Phase 171-fix: Build ConditionEnv and ConditionBindings
|
||||
use crate::mir::join_ir::lowering::condition_to_joinir::{
|
||||
extract_condition_variables, ConditionEnv, ConditionBinding,
|
||||
};
|
||||
|
||||
let condition_var_names = extract_condition_variables(condition, &[loop_var_name.clone()]);
|
||||
let mut env = ConditionEnv::new();
|
||||
let mut condition_bindings = Vec::new();
|
||||
|
||||
// Phase 171-fix: Add loop parameter to env (ValueId(0) in JoinIR space)
|
||||
// The loop parameter is NOT a condition binding (it's a join_input instead)
|
||||
env.insert(loop_var_name.clone(), crate::mir::ValueId(0));
|
||||
|
||||
// Create a local allocator for JoinIR-local ValueIds for condition-only variables
|
||||
let mut join_value_counter = 1u32; // Start from 1 (0 is reserved for loop param)
|
||||
let mut alloc_join_value = || {
|
||||
let id = crate::mir::ValueId(join_value_counter);
|
||||
join_value_counter += 1;
|
||||
id
|
||||
};
|
||||
|
||||
// For each condition variable, allocate JoinIR-local ValueId and build binding
|
||||
for var_name in &condition_var_names {
|
||||
let host_id = self.variable_map.get(var_name)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"[cf_loop/pattern2] Condition variable '{}' not found in variable_map. \
|
||||
Loop condition references undefined variable.",
|
||||
var_name
|
||||
)
|
||||
})?;
|
||||
|
||||
let join_id = alloc_join_value(); // Allocate JoinIR-local ValueId
|
||||
|
||||
env.insert(var_name.clone(), join_id);
|
||||
condition_bindings.push(ConditionBinding {
|
||||
name: var_name.clone(),
|
||||
host_value: host_id,
|
||||
join_value: join_id,
|
||||
});
|
||||
}
|
||||
|
||||
// Phase 171-fix Debug: Log condition bindings
|
||||
eprintln!("[cf_loop/pattern2] Phase 171-fix: ConditionEnv contains {} variables:", env.name_to_join.len());
|
||||
eprintln!(" Loop param '{}' → JoinIR ValueId(0)", loop_var_name);
|
||||
if !condition_bindings.is_empty() {
|
||||
eprintln!(" {} condition-only bindings:", condition_bindings.len());
|
||||
for binding in &condition_bindings {
|
||||
eprintln!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value);
|
||||
}
|
||||
} else {
|
||||
eprintln!(" No condition-only variables");
|
||||
}
|
||||
|
||||
// Create a minimal LoopScopeShape (Phase 188: hardcoded for joinir_min_loop.hako)
|
||||
// Pattern 2 lowerer ignores the scope anyway, so this is just a placeholder
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
@ -86,13 +141,13 @@ impl MirBuilder {
|
||||
variable_definitions: BTreeMap::new(),
|
||||
};
|
||||
|
||||
// Call Pattern 2 lowerer
|
||||
let join_module = match lower_loop_with_break_minimal(scope) {
|
||||
Some(module) => module,
|
||||
None => {
|
||||
// Phase 169 / Phase 171-fix: Call Pattern 2 lowerer with ConditionEnv
|
||||
let join_module = match lower_loop_with_break_minimal(scope, condition, &env) {
|
||||
Ok(module) => module,
|
||||
Err(e) => {
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().debug("pattern2", "Pattern 2 lowerer returned None");
|
||||
return Ok(None);
|
||||
trace::trace().debug("pattern2", &format!("Pattern 2 lowerer failed: {}", e));
|
||||
return Err(format!("[cf_loop/pattern2] Lowering failed: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
@ -119,11 +174,19 @@ impl MirBuilder {
|
||||
);
|
||||
|
||||
// Merge JoinIR blocks into current function
|
||||
// Phase 188-Impl-2: Create and pass JoinInlineBoundary for Pattern 2
|
||||
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable init)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
);
|
||||
// Phase 171-fix: Create boundary with ConditionBindings
|
||||
let boundary = if condition_bindings.is_empty() {
|
||||
crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable init)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
)
|
||||
} else {
|
||||
crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_condition_bindings(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable init)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
condition_bindings, // Phase 171-fix: ConditionBindings with JoinIR ValueIds
|
||||
)
|
||||
};
|
||||
// Phase 189: Discard exit PHI result (Pattern 2 returns void)
|
||||
let _ = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;
|
||||
|
||||
|
||||
@ -181,13 +181,13 @@ impl MirBuilder {
|
||||
variable_definitions: BTreeMap::new(),
|
||||
};
|
||||
|
||||
// Call Pattern 4 lowerer (Phase 197: pass carrier_updates for semantic correctness)
|
||||
let (join_module, exit_meta) = match lower_loop_with_continue_minimal(scope, &carrier_info, &carrier_updates) {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
// Phase 169: Call Pattern 4 lowerer with condition AST
|
||||
let (join_module, exit_meta) = match lower_loop_with_continue_minimal(scope, condition, self, &carrier_info, &carrier_updates) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().debug("pattern4", "Pattern 4 lowerer returned None");
|
||||
return Ok(None);
|
||||
trace::trace().debug("pattern4", &format!("Pattern 4 lowerer failed: {}", e));
|
||||
return Err(format!("[cf_loop/pattern4] Lowering failed: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ impl MirBuilder {
|
||||
// - Core OFF では従来通り dev フラグで opt-in
|
||||
// Note: Arity does NOT include implicit `me` receiver
|
||||
// Phase 188: Add "main" routing for loop pattern expansion
|
||||
// Phase 170: Add JsonParserBox methods for selfhost validation
|
||||
let core_on = crate::config::env::joinir_core_enabled();
|
||||
let is_target = match func_name.as_str() {
|
||||
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
|
||||
@ -64,6 +65,16 @@ impl MirBuilder {
|
||||
== Some("1")
|
||||
}
|
||||
}
|
||||
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
|
||||
"JsonParserBox._trim/1" => true,
|
||||
"JsonParserBox._skip_whitespace/2" => true,
|
||||
"JsonParserBox._match_literal/2" => true,
|
||||
"JsonParserBox._parse_string/2" => true,
|
||||
"JsonParserBox._parse_array/2" => true,
|
||||
"JsonParserBox._parse_object/2" => true,
|
||||
// Phase 170-A-1: Test methods (simplified versions)
|
||||
"TrimTest.trim/1" => true,
|
||||
"Main.trim/1" => true, // Phase 171-fix: Main box variant
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user