refactor(mir): Phase 139-P3-B - RoutingDecision を enum 対応 + レガシー削除

- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化)
- error_tags は to_tag() メソッドで自動生成
- 全 callsite を enum variant に修正
- capability_tags モジュール(文字列定数群)を完全削除
- 全テスト PASS(型安全性向上を確認)
- フォーマット適用
This commit is contained in:
nyash-codex
2025-12-16 07:02:14 +09:00
parent 146f14a019
commit e404746612
106 changed files with 1475 additions and 1017 deletions

View File

@ -26,11 +26,11 @@ pub mod stage1;
// New modular organization // New modular organization
mod joinir_flags; mod joinir_flags;
mod mir_flags; mod mir_flags;
mod vm_backend_flags;
mod parser_flags; mod parser_flags;
mod selfhost_flags;
mod using_flags; mod using_flags;
mod verification_flags; mod verification_flags;
mod selfhost_flags; mod vm_backend_flags;
pub use catalog::{env_vars, AppliesTo, EnvVarMeta}; pub use catalog::{env_vars, AppliesTo, EnvVarMeta};
pub use dump::*; pub use dump::*;
@ -42,11 +42,11 @@ pub use stage1::*;
// Backward-compatible re-exports (NO BREAKING CHANGES!) // Backward-compatible re-exports (NO BREAKING CHANGES!)
pub use joinir_flags::*; pub use joinir_flags::*;
pub use mir_flags::*; pub use mir_flags::*;
pub use vm_backend_flags::*;
pub use parser_flags::*; pub use parser_flags::*;
pub use selfhost_flags::*;
pub use using_flags::*; pub use using_flags::*;
pub use verification_flags::*; pub use verification_flags::*;
pub use selfhost_flags::*; pub use vm_backend_flags::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;

View File

@ -181,6 +181,5 @@ pub fn loopform_normalize() -> bool {
/// ///
/// For fine-grained control, use `joinir_debug_level()` which returns 0-3. /// For fine-grained control, use `joinir_debug_level()` which returns 0-3.
pub fn is_joinir_debug() -> bool { pub fn is_joinir_debug() -> bool {
std::env::var("HAKO_JOINIR_DEBUG").is_ok() std::env::var("HAKO_JOINIR_DEBUG").is_ok() || std::env::var("NYASH_JOINIR_DEBUG").is_ok()
|| std::env::var("NYASH_JOINIR_DEBUG").is_ok()
} }

View File

@ -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",
];

View File

@ -15,20 +15,20 @@ use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId; use crate::mir::region::RegionId;
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
mod builder_calls; mod builder_calls;
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
mod calls; // Call system modules (refactored from builder_calls) mod calls; // Call system modules (refactored from builder_calls)
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離 mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction
mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction
mod decls; // declarations lowering split mod decls; // declarations lowering split
mod exprs; // expression lowering split mod exprs; // expression lowering split
mod exprs_call; mod exprs_call;
mod method_call_handlers; // Method call handler separation (Phase 3) // call(expr) mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
// include lowering removed (using is handled in runner) mod method_call_handlers;
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction // Method call handler separation (Phase 3) // call(expr)
// include lowering removed (using is handled in runner)
mod control_flow; // thin wrappers to centralize control-flow entrypoints mod control_flow; // thin wrappers to centralize control-flow entrypoints
mod exprs_lambda; // lambda lowering mod exprs_lambda; // lambda lowering
mod exprs_peek; // peek expression mod exprs_peek; // peek expression
@ -55,9 +55,9 @@ mod receiver; // ReceiverMaterializationBoxMethod recv の pin+LocalSSA 集
mod rewrite; // P1: Known rewrite & special consolidation mod rewrite; // P1: Known rewrite & special consolidation
mod router; // RouterPolicyBoxUnified vs BoxCall mod router; // RouterPolicyBoxUnified vs BoxCall
mod schedule; // BlockScheduleBox物理順序: PHI→materialize→body mod schedule; // BlockScheduleBox物理順序: PHI→materialize→body
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
mod ssa; // LocalSSA helpers (in-block materialization) mod ssa; // LocalSSA helpers (in-block materialization)
mod stmts; mod stmts;
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
mod type_context; // Phase 136 follow-up: TypeContext extraction mod type_context; // Phase 136 follow-up: TypeContext extraction
mod type_facts; // Phase 136 follow-up: Type inference facts box mod type_facts; // Phase 136 follow-up: Type inference facts box
pub(crate) mod type_registry; pub(crate) mod type_registry;
@ -86,8 +86,6 @@ pub struct MirBuilder {
/// Direct field access for backward compatibility (migration in progress). /// Direct field access for backward compatibility (migration in progress).
pub(super) core_ctx: core_context::CoreContext, pub(super) core_ctx: core_context::CoreContext,
/// Phase 136 follow-up: Type information context /// Phase 136 follow-up: Type information context
/// Consolidates value_types, value_kinds, value_origin_newbox for better organization. /// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
/// Direct field access for backward compatibility (migration in progress). /// Direct field access for backward compatibility (migration in progress).
@ -125,7 +123,6 @@ pub struct MirBuilder {
#[allow(dead_code)] #[allow(dead_code)]
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>, pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
// Phase 2-5: binding_map removed - use binding_ctx.binding_map instead // Phase 2-5: binding_map removed - use binding_ctx.binding_map instead
// include guards removed // include guards removed
@ -153,7 +150,6 @@ pub struct MirBuilder {
// ---------------------- // ----------------------
// Debug scope context (dev only; zero-cost when unused) // Debug scope context (dev only; zero-cost when unused)
// ---------------------- // ----------------------
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv) /// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
/// Key: (bb, original ValueId, kind) -> local ValueId /// Key: (bb, original ValueId, kind) -> local ValueId
/// kind: 0=recv, 1+ reserved for future (args etc.) /// kind: 0=recv, 1+ reserved for future (args etc.)
@ -196,7 +192,8 @@ impl MirBuilder {
let core_ctx = core_context::CoreContext::new(); let core_ctx = core_context::CoreContext::new();
// Phase 136 Step 7/7: Compilation context (new SSOT) // Phase 136 Step 7/7: Compilation context (new SSOT)
let comp_ctx = compilation_context::CompilationContext::with_plugin_sigs(plugin_method_sigs.clone()); let comp_ctx =
compilation_context::CompilationContext::with_plugin_sigs(plugin_method_sigs.clone());
// フェーズM: no_phi_mode初期化削除 // フェーズM: no_phi_mode初期化削除
#[allow(deprecated)] #[allow(deprecated)]
@ -384,7 +381,8 @@ impl MirBuilder {
if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) { if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) {
if slash > dot { if slash > dot {
let tail = &name[dot..]; let tail = &name[dot..];
self.comp_ctx.method_tail_index self.comp_ctx
.method_tail_index
.entry(tail.to_string()) .entry(tail.to_string())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(name.clone()); .push(name.clone());
@ -399,7 +397,9 @@ impl MirBuilder {
fn ensure_method_tail_index(&mut self) { fn ensure_method_tail_index(&mut self) {
let need_rebuild = match self.current_module { let need_rebuild = match self.current_module {
Some(ref refmod) => self.comp_ctx.method_tail_index_source_len != refmod.functions.len(), Some(ref refmod) => {
self.comp_ctx.method_tail_index_source_len != refmod.functions.len()
}
None => self.comp_ctx.method_tail_index_source_len != 0, None => self.comp_ctx.method_tail_index_source_len != 0,
}; };
if need_rebuild { if need_rebuild {
@ -410,7 +410,8 @@ impl MirBuilder {
pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> { pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> {
self.ensure_method_tail_index(); self.ensure_method_tail_index();
let tail = format!(".{}{}", method, format!("/{}", arity)); let tail = format!(".{}{}", method, format!("/{}", arity));
self.comp_ctx.method_tail_index self.comp_ctx
.method_tail_index
.get(&tail) .get(&tail)
.cloned() .cloned()
.unwrap_or_default() .unwrap_or_default()
@ -418,7 +419,8 @@ impl MirBuilder {
pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> { pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> {
self.ensure_method_tail_index(); self.ensure_method_tail_index();
self.comp_ctx.method_tail_index self.comp_ctx
.method_tail_index
.get(tail.as_ref()) .get(tail.as_ref())
.cloned() .cloned()
.unwrap_or_default() .unwrap_or_default()
@ -527,7 +529,9 @@ impl MirBuilder {
if !suggest.is_empty() { if !suggest.is_empty() {
msg.push_str("\nHint: symbol appears in using module(s): "); msg.push_str("\nHint: symbol appears in using module(s): ");
msg.push_str(&suggest.join(", ")); msg.push_str(&suggest.join(", "));
msg.push_str("\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using]."); msg.push_str(
"\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].",
);
} }
msg msg
@ -567,7 +571,9 @@ impl MirBuilder {
// Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303). // Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303).
if !var_name.starts_with("__pin$") { if !var_name.starts_with("__pin$") {
// In SSA form, each assignment creates a new value // In SSA form, each assignment creates a new value
self.variable_ctx.variable_map.insert(var_name.clone(), value_id); self.variable_ctx
.variable_map
.insert(var_name.clone(), value_id);
} }
Ok(value_id) Ok(value_id)
@ -582,7 +588,8 @@ impl MirBuilder {
// Precompute debug metadata to avoid borrow conflicts later // Precompute debug metadata to avoid borrow conflicts later
let _dbg_fn_name = self let _dbg_fn_name = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.clone()); .map(|f| f.signature.name.clone());
let _dbg_region_id = self.debug_current_region_id(); let _dbg_region_id = self.debug_current_region_id();
@ -694,7 +701,8 @@ impl MirBuilder {
}) = callee }) = callee
{ {
let names: Vec<String> = self let names: Vec<String> = self
.variable_ctx.variable_map .variable_ctx
.variable_map
.iter() .iter()
.filter(|(_, &vid)| vid == *r) .filter(|(_, &vid)| vid == *r)
.map(|(k, _)| k.clone()) .map(|(k, _)| k.clone())
@ -760,7 +768,10 @@ impl MirBuilder {
); );
} }
// Phase 136 Step 6/7: Use metadata_ctx for span // Phase 136 Step 6/7: Use metadata_ctx for span
block.add_instruction_with_span(instruction.clone(), self.metadata_ctx.current_span()); block.add_instruction_with_span(
instruction.clone(),
self.metadata_ctx.current_span(),
);
// Drop the mutable borrow of `block` before updating other blocks // Drop the mutable borrow of `block` before updating other blocks
} }
// Update predecessor sets for branch/jump immediately so that // Update predecessor sets for branch/jump immediately so that
@ -854,7 +865,8 @@ impl MirBuilder {
effects: EffectMask::PURE, effects: EffectMask::PURE,
})?; })?;
// 型注釈(最小) // 型注釈(最小)
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, super::MirType::Box(class.clone())); .insert(dst, super::MirType::Box(class.clone()));
return Ok(dst); return Ok(dst);
} }
@ -872,7 +884,9 @@ impl MirBuilder {
dst, dst,
value: ConstValue::Integer(n), value: ConstValue::Integer(n),
})?; })?;
self.type_ctx.value_types.insert(dst, super::MirType::Integer); self.type_ctx
.value_types
.insert(dst, super::MirType::Integer);
return Ok(dst); return Ok(dst);
} }
} }
@ -897,7 +911,8 @@ impl MirBuilder {
})?; })?;
// Phase 15.5: Unified box type handling // Phase 15.5: Unified box type handling
// All boxes (including former core boxes) are treated uniformly as Box types // All boxes (including former core boxes) are treated uniformly as Box types
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, super::MirType::Box(class.clone())); .insert(dst, super::MirType::Box(class.clone()));
// Record origin for optimization: dst was created by NewBox of class // Record origin for optimization: dst was created by NewBox of class
@ -954,7 +969,9 @@ impl MirBuilder {
/// Check if the current basic block is terminated /// Check if the current basic block is terminated
fn is_current_block_terminated(&self) -> bool { fn is_current_block_terminated(&self) -> bool {
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.scope_ctx.current_function) { if let (Some(block_id), Some(ref function)) =
(self.current_block, &self.scope_ctx.current_function)
{
if let Some(block) = function.get_block(block_id) { if let Some(block) = function.get_block(block_id) {
return block.is_terminated(); return block.is_terminated();
} }
@ -1018,13 +1035,17 @@ use crate::mir::loop_pattern_detection::BindingMapProvider;
impl BindingMapProvider for MirBuilder { impl BindingMapProvider for MirBuilder {
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
fn get_binding_map(&self) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> { fn get_binding_map(
&self,
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
// Phase 136 Step 4/7: Use binding_ctx (SSOT) // Phase 136 Step 4/7: Use binding_ctx (SSOT)
Some(self.binding_ctx.binding_map()) Some(self.binding_ctx.binding_map())
} }
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
fn get_binding_map(&self) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> { fn get_binding_map(
&self,
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
None None
} }
} }

View File

@ -40,7 +40,8 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1") || std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
{ {
let bx = builder let bx = builder
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&dst) .get(&dst)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();

View File

@ -21,7 +21,8 @@ impl MirBuilder {
// Dev trace // Dev trace
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
let cur_fun = self let cur_fun = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.clone()) .map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string()); .unwrap_or_else(|| "<none>".to_string());
@ -270,7 +271,8 @@ impl MirBuilder {
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) { if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
return Some(Err(e)); return Some(Err(e));
} }
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(math_recv, "MathBox".to_string()); .insert(math_recv, "MathBox".to_string());
// birth() // birth()
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) { if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {

View File

@ -307,8 +307,12 @@ impl MirBuilder {
// me // me
let me_id = f.params[0]; let me_id = f.params[0];
self.variable_ctx.variable_map.insert("me".to_string(), me_id); self.variable_ctx
self.type_ctx.value_origin_newbox.insert(me_id, box_name.to_string()); .variable_map
.insert("me".to_string(), me_id);
self.type_ctx
.value_origin_newbox
.insert(me_id, box_name.to_string());
slot_regs.push(("me".to_string(), None)); slot_regs.push(("me".to_string(), None));
// 通常パラメータ // 通常パラメータ

View File

@ -208,7 +208,8 @@ impl UnifiedCallEmitterBox {
crate::mir::MirType::Box(box_name.to_string()), crate::mir::MirType::Box(box_name.to_string()),
); );
builder builder
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.insert(singleton_id, box_name.to_string()); .insert(singleton_id, box_name.to_string());
// Cache for future use // Cache for future use
builder builder
@ -296,7 +297,8 @@ impl UnifiedCallEmitterBox {
// Try to retrieve origin info for receiver // Try to retrieve origin info for receiver
let recv_meta = receiver.and_then(|r| { let recv_meta = receiver.and_then(|r| {
builder builder
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&r) .get(&r)
.cloned() .cloned()
.map(|cls| (r, cls)) .map(|cls| (r, cls))

View File

@ -394,7 +394,10 @@ mod tests {
let base_id = ValueId::new(10); let base_id = ValueId::new(10);
ctx.set_field_origin_class(base_id, "name".to_string(), "StringBox".to_string()); ctx.set_field_origin_class(base_id, "name".to_string(), "StringBox".to_string());
assert_eq!(ctx.get_field_origin_class(base_id, "name"), Some("StringBox")); assert_eq!(
ctx.get_field_origin_class(base_id, "name"),
Some("StringBox")
);
assert_eq!(ctx.get_field_origin_class(base_id, "other"), None); assert_eq!(ctx.get_field_origin_class(base_id, "other"), None);
} }

View File

@ -13,11 +13,10 @@
/// - **SSOT**: Single function for all CarrierInit → ValueId generation /// - **SSOT**: Single function for all CarrierInit → ValueId generation
/// - **Testability**: Pure function, easy to unit test /// - **Testability**: Pure function, easy to unit test
/// - **Consistency**: Uniform debug output format /// - **Consistency**: Uniform debug output format
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInit; use crate::mir::join_ir::lowering::carrier_info::CarrierInit;
use crate::mir::value_id::ValueId;
use crate::mir::types::ConstValue; use crate::mir::types::ConstValue;
use crate::mir::value_id::ValueId;
use crate::mir::MirInstruction; use crate::mir::MirInstruction;
/// Generate a ValueId for the given CarrierInit policy /// Generate a ValueId for the given CarrierInit policy
@ -120,7 +119,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(999); // Dummy host_id (not used for BoolConst) let host_id = ValueId(999); // Dummy host_id (not used for BoolConst)
let result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "test", false); let result = init_value(
&mut builder,
&CarrierInit::BoolConst(true),
host_id,
"test",
false,
);
assert_ne!(result, host_id, "BoolConst should emit new ValueId"); assert_ne!(result, host_id, "BoolConst should emit new ValueId");
} }
@ -131,7 +136,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(999); let host_id = ValueId(999);
let result = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag", false); let result = init_value(
&mut builder,
&CarrierInit::BoolConst(false),
host_id,
"flag",
false,
);
assert_ne!(result, host_id, "BoolConst should emit new ValueId"); assert_ne!(result, host_id, "BoolConst should emit new ValueId");
} }
@ -142,7 +153,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(999); let host_id = ValueId(999);
let result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "digit", false); let result = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"digit",
false,
);
assert_ne!(result, host_id, "LoopLocalZero should emit new ValueId"); assert_ne!(result, host_id, "LoopLocalZero should emit new ValueId");
} }
@ -153,12 +170,36 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(100); let host_id = ValueId(100);
let result1 = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "flag1", false); let result1 = init_value(
let result2 = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag2", false); &mut builder,
let result3 = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "counter", false); &CarrierInit::BoolConst(true),
host_id,
"flag1",
false,
);
let result2 = init_value(
&mut builder,
&CarrierInit::BoolConst(false),
host_id,
"flag2",
false,
);
let result3 = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"counter",
false,
);
assert_ne!(result1, result2, "Different BoolConst calls should produce different ValueIds"); assert_ne!(
assert_ne!(result2, result3, "BoolConst and LoopLocalZero should produce different ValueIds"); result1, result2,
"Different BoolConst calls should produce different ValueIds"
);
assert_ne!(
result2, result3,
"BoolConst and LoopLocalZero should produce different ValueIds"
);
assert_ne!(result1, result3, "All ValueIds should be unique"); assert_ne!(result1, result3, "All ValueIds should be unique");
} }
@ -168,7 +209,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(42); let host_id = ValueId(42);
let _ = init_value(&mut builder, &CarrierInit::FromHost, host_id, "debug_test", true); let _ = init_value(
&mut builder,
&CarrierInit::FromHost,
host_id,
"debug_test",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_test': FromHost -> ValueId(42)" // Expected stderr output: "[carrier_init_builder] 'debug_test': FromHost -> ValueId(42)"
// (This test just verifies it doesn't crash) // (This test just verifies it doesn't crash)
} }
@ -179,7 +226,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(999); let host_id = ValueId(999);
let _result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "debug_bool", true); let _result = init_value(
&mut builder,
&CarrierInit::BoolConst(true),
host_id,
"debug_bool",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_bool': BoolConst(true) -> ValueId(N)" // Expected stderr output: "[carrier_init_builder] 'debug_bool': BoolConst(true) -> ValueId(N)"
// (This test just verifies it doesn't crash) // (This test just verifies it doesn't crash)
} }
@ -190,7 +243,13 @@ mod tests {
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let host_id = ValueId(999); let host_id = ValueId(999);
let _result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "debug_zero", true); let _result = init_value(
&mut builder,
&CarrierInit::LoopLocalZero,
host_id,
"debug_zero",
true,
);
// Expected stderr output: "[carrier_init_builder] 'debug_zero': LoopLocalZero -> ValueId(N)" // Expected stderr output: "[carrier_init_builder] 'debug_zero': LoopLocalZero -> ValueId(N)"
// (This test just verifies it doesn't crash) // (This test just verifies it doesn't crash)
} }

View File

@ -143,7 +143,11 @@ impl ExitLineReconnector {
// Update variable_ctx.variable_map with PHI dst // Update variable_ctx.variable_map with PHI dst
if let Some(&phi_value) = phi_dst { if let Some(&phi_value) = phi_dst {
if let Some(var_vid) = builder.variable_ctx.variable_map.get_mut(&binding.carrier_name) { if let Some(var_vid) = builder
.variable_ctx
.variable_map
.get_mut(&binding.carrier_name)
{
// Phase 177-STRUCT: Always log for debugging // Phase 177-STRUCT: Always log for debugging
if verbose { if verbose {
eprintln!( eprintln!(

View File

@ -149,14 +149,15 @@ pub(super) fn merge_and_rewrite(
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering) // Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
// ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new() // ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new()
let mut new_block = if let Some(ref mut current_func) = builder.scope_ctx.current_function { let mut new_block =
current_func if let Some(ref mut current_func) = builder.scope_ctx.current_function {
.blocks current_func
.remove(&new_block_id) .blocks
.unwrap_or_else(|| BasicBlock::new(new_block_id)) .remove(&new_block_id)
} else { .unwrap_or_else(|| BasicBlock::new(new_block_id))
BasicBlock::new(new_block_id) } else {
}; BasicBlock::new(new_block_id)
};
// Phase 33-16: Identify loop entry point // Phase 33-16: Identify loop entry point
// //

View File

@ -96,12 +96,17 @@ impl LoopHeaderPhiBuilder {
// Phase 131-11-H: Set PHI type from entry incoming (init value) only // Phase 131-11-H: Set PHI type from entry incoming (init value) only
// Ignore backedge to avoid circular dependency in type inference // Ignore backedge to avoid circular dependency in type inference
if let Some(init_type) = builder.type_ctx.value_types.get(&loop_var_init).cloned() { if let Some(init_type) = builder.type_ctx.value_types.get(&loop_var_init).cloned() {
builder.type_ctx.value_types.insert(loop_var_phi_dst, init_type.clone()); builder
.type_ctx
.value_types
.insert(loop_var_phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") { if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!( eprintln!(
"[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)", "[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)",
loop_var_name, loop_var_phi_dst.as_u32(), init_type loop_var_name,
loop_var_phi_dst.as_u32(),
init_type
); );
} }
} }
@ -128,13 +133,8 @@ impl LoopHeaderPhiBuilder {
// Allocate PHIs for other carriers // Allocate PHIs for other carriers
for (name, host_id, init, role) in carriers { for (name, host_id, init, role) in carriers {
// Phase 86: Use centralized CarrierInit builder // Phase 86: Use centralized CarrierInit builder
let init_value = super::carrier_init_builder::init_value( let init_value =
builder, super::carrier_init_builder::init_value(builder, &init, *host_id, &name, debug);
&init,
*host_id,
&name,
debug,
);
let phi_dst = builder.next_value_id(); let phi_dst = builder.next_value_id();
@ -145,12 +145,17 @@ impl LoopHeaderPhiBuilder {
// Phase 131-11-H: Set PHI type from entry incoming (init value) only // Phase 131-11-H: Set PHI type from entry incoming (init value) only
// Ignore backedge to avoid circular dependency in type inference // Ignore backedge to avoid circular dependency in type inference
if let Some(init_type) = builder.type_ctx.value_types.get(&init_value).cloned() { if let Some(init_type) = builder.type_ctx.value_types.get(&init_value).cloned() {
builder.type_ctx.value_types.insert(phi_dst, init_type.clone()); builder
.type_ctx
.value_types
.insert(phi_dst, init_type.clone());
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") { if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
eprintln!( eprintln!(
"[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)", "[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)",
name, phi_dst.as_u32(), init_type name,
phi_dst.as_u32(),
init_type
); );
} }
} }
@ -223,7 +228,8 @@ impl LoopHeaderPhiBuilder {
// Get the header block from current function // Get the header block from current function
let current_func = builder let current_func = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_mut() .as_mut()
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?; .ok_or("Phase 33-16: No current function when finalizing header PHIs")?;

View File

@ -141,20 +141,24 @@ mod tests {
#[test] #[test]
fn test_parity_check_skip_whitespace_match() { fn test_parity_check_skip_whitespace_match() {
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features; use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
let loop_ast = build_skip_whitespace_loop(); let loop_ast = build_skip_whitespace_loop();
// Extract condition and body // Extract condition and body
let (condition, body) = match &loop_ast { let (condition, body) = match &loop_ast {
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()), ASTNode::Loop {
condition, body, ..
} => (condition.as_ref(), body.as_slice()),
_ => panic!("Expected loop node"), _ => panic!("Expected loop node"),
}; };
// Run canonicalizer // Run canonicalizer
let (_, canonical_decision) = canonicalize_loop_expr(&loop_ast).unwrap(); let (_, canonical_decision) = canonicalize_loop_expr(&loop_ast).unwrap();
let canonical_pattern = canonical_decision.chosen.expect("Canonicalizer should succeed"); let canonical_pattern = canonical_decision
.chosen
.expect("Canonicalizer should succeed");
// Run router's pattern detection // Run router's pattern detection
let has_continue = ast_features::detect_continue_in_body(body); let has_continue = ast_features::detect_continue_in_body(body);
@ -175,13 +179,16 @@ mod tests {
crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break, crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break,
"Router should classify as Pattern2Break for has_break=true" "Router should classify as Pattern2Break for has_break=true"
); );
assert_eq!(canonical_pattern, actual_pattern, "Phase 137-5: Canonicalizer and router should agree (SSOT policy)"); assert_eq!(
canonical_pattern, actual_pattern,
"Phase 137-5: Canonicalizer and router should agree (SSOT policy)"
);
} }
#[test] #[test]
fn test_parity_check_match_simple_while() { fn test_parity_check_match_simple_while() {
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features; use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
// Simple while loop: no break, no continue, no if // Simple while loop: no break, no continue, no if
let loop_ast = ASTNode::Loop { let loop_ast = ASTNode::Loop {
@ -221,7 +228,9 @@ mod tests {
// Extract condition and body // Extract condition and body
let (condition, body) = match &loop_ast { let (condition, body) = match &loop_ast {
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()), ASTNode::Loop {
condition, body, ..
} => (condition.as_ref(), body.as_slice()),
_ => panic!("Expected loop node"), _ => panic!("Expected loop node"),
}; };
@ -243,6 +252,9 @@ mod tests {
// Canonicalizer should fail (not implemented yet for Pattern1) // Canonicalizer should fail (not implemented yet for Pattern1)
assert!(canonical_result.is_ok()); assert!(canonical_result.is_ok());
let (_, decision) = canonical_result.unwrap(); let (_, decision) = canonical_result.unwrap();
assert!(decision.is_fail_fast(), "Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)"); assert!(
decision.is_fail_fast(),
"Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)"
);
} }
} }

View File

@ -106,7 +106,10 @@ impl<'a> ExitBindingBuilder<'a> {
/// ///
/// Success or error if boundary cannot be updated /// Success or error if boundary cannot be updated
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> { pub(crate) fn apply_to_boundary(
&self,
boundary: &mut JoinInlineBoundary,
) -> Result<(), String> {
// Phase 222.5-C: Delegate to applicator module // Phase 222.5-C: Delegate to applicator module
apply_exit_bindings_to_boundary( apply_exit_bindings_to_boundary(
self.carrier_info, self.carrier_info,

View File

@ -77,14 +77,17 @@ impl MirBuilder {
// //
// Phase 132: Add exit_bindings to enable ExitLineReconnector // Phase 132: Add exit_bindings to enable ExitLineReconnector
// This ensures `return i` after loop returns the final value (3) instead of initial (0) // This ensures `return i` after loop returns the final value (3) instead of initial (0)
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::join_ir::lowering::carrier_info::CarrierRole; use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN; use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN;
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
// Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First) // Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First)
let k_exit_func = join_module.require_function("k_exit", "Pattern 1"); let k_exit_func = join_module.require_function("k_exit", "Pattern 1");
let join_exit_value = k_exit_func.params.first().copied() let join_exit_value = k_exit_func
.params
.first()
.copied()
.expect("k_exit must have parameter for exit value"); .expect("k_exit must have parameter for exit value");
// Phase 132: Create exit binding for loop variable // Phase 132: Create exit binding for loop variable
@ -98,7 +101,7 @@ impl MirBuilder {
let boundary = JoinInlineBoundaryBuilder::new() let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs( .with_inputs(
vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region) vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region)
vec![ctx.loop_var_id], // Host's loop variable vec![ctx.loop_var_id], // Host's loop variable
) )
.with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update .with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness .with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness

View File

@ -9,8 +9,8 @@ use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr; use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::loop_pattern_detection::error_messages; use crate::mir::loop_pattern_detection::error_messages;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -42,7 +42,9 @@ fn prepare_pattern2_inputs(
verbose: bool, verbose: bool,
) -> Result<Pattern2Inputs, String> { ) -> Result<Pattern2Inputs, String> {
use super::condition_env_builder::ConditionEnvBuilder; use super::condition_env_builder::ConditionEnvBuilder;
use crate::mir::loop_pattern_detection::function_scope_capture::{analyze_captured_vars_v2, CapturedEnv}; use crate::mir::loop_pattern_detection::function_scope_capture::{
analyze_captured_vars_v2, CapturedEnv,
};
let loop_var_name = ctx.loop_var_name.clone(); let loop_var_name = ctx.loop_var_name.clone();
let loop_var_id = ctx.loop_var_id; let loop_var_id = ctx.loop_var_id;
@ -88,7 +90,10 @@ fn prepare_pattern2_inputs(
log_pattern2( log_pattern2(
verbose, verbose,
"capture", "capture",
format!("Phase 200-C: Captured {} variables", captured_env.vars.len()), format!(
"Phase 200-C: Captured {} variables",
captured_env.vars.len()
),
); );
for var in &captured_env.vars { for var in &captured_env.vars {
log_pattern2( log_pattern2(
@ -747,16 +752,8 @@ impl MirBuilder {
trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map); trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map);
let mut inputs = let mut inputs = prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?; promote_and_prepare_carriers(self, condition, _body, &mut inputs, debug, verbose)?;
promote_and_prepare_carriers(
self,
condition,
_body,
&mut inputs,
debug,
verbose,
)?;
let (effective_break_condition, normalized_body) = let (effective_break_condition, normalized_body) =
apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?; apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?;
let analysis_body = normalized_body.as_deref().unwrap_or(_body); let analysis_body = normalized_body.as_deref().unwrap_or(_body);
@ -771,10 +768,10 @@ impl MirBuilder {
verbose, verbose,
"updates", "updates",
format!( format!(
"Phase 176-3: Analyzed {} carrier updates", "Phase 176-3: Analyzed {} carrier updates",
carrier_updates.len() carrier_updates.len()
), ),
); );
let original_carrier_count = inputs.carrier_info.carriers.len(); let original_carrier_count = inputs.carrier_info.carriers.len();
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates); filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
@ -977,11 +974,26 @@ mod tests {
use crate::mir::ValueId; use crate::mir::ValueId;
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
builder.variable_ctx.variable_map.insert("i".to_string(), ValueId(1)); builder
builder.variable_ctx.variable_map.insert("len".to_string(), ValueId(2)); .variable_ctx
builder.variable_ctx.variable_map.insert("s".to_string(), ValueId(3)); .variable_map
builder.variable_ctx.variable_map.insert("digits".to_string(), ValueId(4)); .insert("i".to_string(), ValueId(1));
builder.variable_ctx.variable_map.insert("result".to_string(), ValueId(5)); builder
.variable_ctx
.variable_map
.insert("len".to_string(), ValueId(2));
builder
.variable_ctx
.variable_map
.insert("s".to_string(), ValueId(3));
builder
.variable_ctx
.variable_map
.insert("digits".to_string(), ValueId(4));
builder
.variable_ctx
.variable_map
.insert("result".to_string(), ValueId(5));
let condition = bin(BinaryOperator::Less, var("i"), var("len")); let condition = bin(BinaryOperator::Less, var("i"), var("len"));
@ -1008,7 +1020,9 @@ mod tests {
let break_if = ASTNode::If { let break_if = ASTNode::If {
condition: Box::new(bin(BinaryOperator::Less, var("digit_pos"), lit_i(0))), condition: Box::new(bin(BinaryOperator::Less, var("digit_pos"), lit_i(0))),
then_body: vec![ASTNode::Break { span: Span::unknown() }], then_body: vec![ASTNode::Break {
span: Span::unknown(),
}],
else_body: None, else_body: None,
span: Span::unknown(), span: Span::unknown(),
}; };
@ -1035,9 +1049,8 @@ mod tests {
let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2) let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2)
.expect("build_pattern_context"); .expect("build_pattern_context");
let mut inputs = let mut inputs = prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false) .expect("prepare_pattern2_inputs");
.expect("prepare_pattern2_inputs");
promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false) promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false)
.expect("promote_and_prepare_carriers"); .expect("promote_and_prepare_carriers");

View File

@ -183,7 +183,8 @@ impl MirBuilder {
// Collect parent-defined variables from function scope // Collect parent-defined variables from function scope
// For now, use all variables in variable_map except loop_var // For now, use all variables in variable_map except loop_var
let parent_defined: Vec<String> = self let parent_defined: Vec<String> = self
.variable_ctx.variable_map .variable_ctx
.variable_map
.keys() .keys()
.filter(|name| *name != &loop_var_name) .filter(|name| *name != &loop_var_name)
.cloned() .cloned()
@ -196,9 +197,11 @@ impl MirBuilder {
condition_bindings.iter().map(|b| b.name.clone()).collect(); condition_bindings.iter().map(|b| b.name.clone()).collect();
// Run consistency checks // Run consistency checks
if let Err(e) = if let Err(e) = check_ownership_plan_consistency(
check_ownership_plan_consistency(&plan, &ctx.carrier_info, &condition_binding_names) &plan,
{ &ctx.carrier_info,
&condition_binding_names,
) {
eprintln!("[phase64/ownership] Consistency check failed: {}", e); eprintln!("[phase64/ownership] Consistency check failed: {}", e);
return Err(e); return Err(e);
} }

View File

@ -50,7 +50,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
let mut continue_count = 0; let mut continue_count = 0;
let mut has_nested_loop = false; let mut has_nested_loop = false;
fn scan_node(node: &ASTNode, break_count: &mut usize, continue_count: &mut usize, has_nested_loop: &mut bool, depth: usize) { fn scan_node(
node: &ASTNode,
break_count: &mut usize,
continue_count: &mut usize,
has_nested_loop: &mut bool,
depth: usize,
) {
match node { match node {
ASTNode::Break { .. } => { ASTNode::Break { .. } => {
*break_count += 1; *break_count += 1;
@ -61,13 +67,29 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
ASTNode::Loop { .. } if depth > 0 => { ASTNode::Loop { .. } if depth > 0 => {
*has_nested_loop = true; *has_nested_loop = true;
} }
ASTNode::If { then_body, else_body, .. } => { ASTNode::If {
then_body,
else_body,
..
} => {
for stmt in then_body { for stmt in then_body {
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1); scan_node(
stmt,
break_count,
continue_count,
has_nested_loop,
depth + 1,
);
} }
if let Some(else_body) = else_body { if let Some(else_body) = else_body {
for stmt in else_body { for stmt in else_body {
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1); scan_node(
stmt,
break_count,
continue_count,
has_nested_loop,
depth + 1,
);
} }
} }
} }
@ -76,7 +98,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
} }
for stmt in body { for stmt in body {
scan_node(stmt, &mut break_count, &mut continue_count, &mut has_nested_loop, 0); scan_node(
stmt,
&mut break_count,
&mut continue_count,
&mut has_nested_loop,
0,
);
} }
(break_count, continue_count, has_nested_loop) (break_count, continue_count, has_nested_loop)
@ -95,7 +123,12 @@ fn validate_continue_position(body: &[ASTNode]) -> bool {
/// Phase 131-11-D: Validate break is in simple if pattern /// Phase 131-11-D: Validate break is in simple if pattern
fn validate_break_pattern(body: &[ASTNode]) -> bool { fn validate_break_pattern(body: &[ASTNode]) -> bool {
for stmt in body { for stmt in body {
if let ASTNode::If { then_body, else_body, .. } = stmt { if let ASTNode::If {
then_body,
else_body,
..
} = stmt
{
// Check then branch for simple break // Check then branch for simple break
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) { if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// Ensure no else-break pattern // Ensure no else-break pattern
@ -132,13 +165,17 @@ pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool
// Step 2: Shape guard - infinite loop condition (true literal) // Step 2: Shape guard - infinite loop condition (true literal)
if !ctx.features.is_infinite_loop { if !ctx.features.is_infinite_loop {
if debug { if debug {
trace::trace().debug("pattern5/detect", "Not an infinite loop (condition != true)"); trace::trace().debug(
"pattern5/detect",
"Not an infinite loop (condition != true)",
);
} }
return false; return false;
} }
// Phase 131-11-D: Enhanced real count validation // Phase 131-11-D: Enhanced real count validation
let (real_break_count, real_continue_count, has_nested_loop) = count_breaks_and_continues(ctx.body); let (real_break_count, real_continue_count, has_nested_loop) =
count_breaks_and_continues(ctx.body);
// Step 3: Shape guard - exactly 1 break (real count) // Step 3: Shape guard - exactly 1 break (real count)
if real_break_count != 1 { if real_break_count != 1 {
@ -232,11 +269,21 @@ fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
use crate::ast::BinaryOperator; use crate::ast::BinaryOperator;
for stmt in body { for stmt in body {
if let ASTNode::If { condition, then_body, .. } = stmt { if let ASTNode::If {
condition,
then_body,
..
} = stmt
{
// Check if then_body contains just break // Check if then_body contains just break
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) { if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// Extract counter from condition // Extract counter from condition
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, left, .. } = condition.as_ref() { if let ASTNode::BinaryOp {
operator: BinaryOperator::Equal,
left,
..
} = condition.as_ref()
{
if let ASTNode::Variable { name, .. } = left.as_ref() { if let ASTNode::Variable { name, .. } = left.as_ref() {
return Ok(name.clone()); return Ok(name.clone());
} }
@ -256,12 +303,26 @@ fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
use crate::ast::{BinaryOperator, LiteralValue}; use crate::ast::{BinaryOperator, LiteralValue};
for stmt in body { for stmt in body {
if let ASTNode::If { condition, then_body, .. } = stmt { if let ASTNode::If {
condition,
then_body,
..
} = stmt
{
// Check if then_body contains just break // Check if then_body contains just break
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) { if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// Extract limit from condition // Extract limit from condition
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, right, .. } = condition.as_ref() { if let ASTNode::BinaryOp {
if let ASTNode::Literal { value: LiteralValue::Integer(limit), .. } = right.as_ref() { operator: BinaryOperator::Equal,
right,
..
} = condition.as_ref()
{
if let ASTNode::Literal {
value: LiteralValue::Integer(limit),
..
} = right.as_ref()
{
return Ok(*limit); return Ok(*limit);
} }
} }
@ -317,9 +378,17 @@ pub(crate) fn lower(
} }
// Step 2: Get counter ValueId from variable_ctx.variable_map // Step 2: Get counter ValueId from variable_ctx.variable_map
let counter_id = builder.variable_ctx.variable_map.get(&counter_name).copied().ok_or_else(|| { let counter_id = builder
format!("Counter variable '{}' not found in variable_ctx.variable_map", counter_name) .variable_ctx
})?; .variable_map
.get(&counter_name)
.copied()
.ok_or_else(|| {
format!(
"Counter variable '{}' not found in variable_ctx.variable_map",
counter_name
)
})?;
if debug { if debug {
trace::trace().debug( trace::trace().debug(
@ -340,7 +409,8 @@ pub(crate) fn lower(
// Step 4: Generate JoinIR // Step 4: Generate JoinIR
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::{ use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst, BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst,
}; };
let mut join_value_space = JoinValueSpace::new(); let mut join_value_space = JoinValueSpace::new();
@ -356,14 +426,14 @@ pub(crate) fn lower(
// ValueId allocation // ValueId allocation
// main() locals // main() locals
let counter_init = alloc_value(); // ValueId(1000) - initial counter let counter_init = alloc_value(); // ValueId(1000) - initial counter
let loop_result = alloc_value(); // ValueId(1001) - result from loop_step let loop_result = alloc_value(); // ValueId(1001) - result from loop_step
// loop_step locals // loop_step locals
let counter_param = alloc_value(); // ValueId(1002) - parameter let counter_param = alloc_value(); // ValueId(1002) - parameter
let const_1 = alloc_value(); // ValueId(1003) - increment constant let const_1 = alloc_value(); // ValueId(1003) - increment constant
let counter_next = alloc_value(); // ValueId(1004) - counter + 1 let counter_next = alloc_value(); // ValueId(1004) - counter + 1
let const_limit = alloc_value(); // ValueId(1005) - limit constant let const_limit = alloc_value(); // ValueId(1005) - limit constant
let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT
// k_exit locals // k_exit locals
let counter_exit = alloc_value(); // ValueId(1007) - exit parameter let counter_exit = alloc_value(); // ValueId(1007) - exit parameter
@ -397,33 +467,42 @@ pub(crate) fn lower(
// ================================================================== // ==================================================================
// loop_step(counter) function - post-increment pattern // loop_step(counter) function - post-increment pattern
// ================================================================== // ==================================================================
let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]); let mut loop_step_func =
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
// counter_next = counter + 1 // counter_next = counter + 1
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: const_1, .body
value: ConstValue::Integer(1), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: const_1,
value: ConstValue::Integer(1),
}));
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { loop_step_func
dst: counter_next, .body
op: BinOpKind::Add, .push(JoinInst::Compute(MirLikeInst::BinOp {
lhs: counter_param, dst: counter_next,
rhs: const_1, op: BinOpKind::Add,
})); lhs: counter_param,
rhs: const_1,
}));
// break_cond = (counter_next == LIMIT) // break_cond = (counter_next == LIMIT)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: const_limit, .body
value: ConstValue::Integer(limit), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: const_limit,
value: ConstValue::Integer(limit),
}));
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { loop_step_func
dst: break_cond, .body
op: CompareOp::Eq, .push(JoinInst::Compute(MirLikeInst::Compare {
lhs: counter_next, dst: break_cond,
rhs: const_limit, op: CompareOp::Eq,
})); lhs: counter_next,
rhs: const_limit,
}));
// Jump(k_exit, [counter_next], cond=break_cond) // Jump(k_exit, [counter_next], cond=break_cond)
loop_step_func.body.push(JoinInst::Jump { loop_step_func.body.push(JoinInst::Jump {

View File

@ -241,7 +241,8 @@ impl TrimLoopLowerer {
// Step 3: Convert to CarrierInfo and merge // Step 3: Convert to CarrierInfo and merge
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
// Phase 136 Step 4/7: Use binding_ctx for binding_map reference // Phase 136 Step 4/7: Use binding_ctx for binding_map reference
let promoted_carrier = trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map())); let promoted_carrier =
trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map()));
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
let promoted_carrier = trim_info.to_carrier_info(); let promoted_carrier = trim_info.to_carrier_info();
carrier_info.merge_from(&promoted_carrier); carrier_info.merge_from(&promoted_carrier);
@ -352,10 +353,12 @@ impl TrimLoopLowerer {
); );
// Get ValueIds for string and start // Get ValueIds for string and start
let s_id = let s_id = builder
builder.variable_ctx.variable_map.get(&s_name).copied().ok_or_else(|| { .variable_ctx
format!("[TrimLoopLowerer] String variable '{}' not found", s_name) .variable_map
})?; .get(&s_name)
.copied()
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
// Compile start expression to get ValueId // Compile start expression to get ValueId
let start_id = builder.build_expression_impl(*start_expr)?; let start_id = builder.build_expression_impl(*start_expr)?;
@ -403,7 +406,8 @@ impl TrimLoopLowerer {
// Register carrier in variable_ctx.variable_map // Register carrier in variable_ctx.variable_map
builder builder
.variable_ctx.variable_map .variable_ctx
.variable_map
.insert(trim_helper.carrier_name.clone(), is_ch_match0); .insert(trim_helper.carrier_name.clone(), is_ch_match0);
Ok(()) Ok(())

View File

@ -6,7 +6,6 @@ use crate::mir::builder::MirBuilder;
use crate::mir::ValueId; use crate::mir::ValueId;
impl MirBuilder { impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration /// Phase 49: Try JoinIR Frontend for mainline integration
/// ///
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR, /// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
@ -29,7 +28,8 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
// Get current function name // Get current function name
let func_name = self let func_name = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.clone()) .map(|f| f.signature.name.clone())
.unwrap_or_default(); .unwrap_or_default();
@ -138,17 +138,38 @@ impl MirBuilder {
match canonicalize_loop_expr(&loop_ast) { match canonicalize_loop_expr(&loop_ast) {
Ok((skeleton, decision)) => { Ok((skeleton, decision)) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name); eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!("[loop_canonicalizer] Skeleton steps: {}", skeleton.steps.len()); eprintln!(
eprintln!("[loop_canonicalizer] Carriers: {}", skeleton.carriers.len()); "[loop_canonicalizer] Skeleton steps: {}",
eprintln!("[loop_canonicalizer] Has exits: {}", skeleton.exits.has_any_exit()); skeleton.steps.len()
eprintln!("[loop_canonicalizer] Decision: {}", );
if decision.is_success() { "SUCCESS" } else { "FAIL_FAST" }); eprintln!(
"[loop_canonicalizer] Carriers: {}",
skeleton.carriers.len()
);
eprintln!(
"[loop_canonicalizer] Has exits: {}",
skeleton.exits.has_any_exit()
);
eprintln!(
"[loop_canonicalizer] Decision: {}",
if decision.is_success() {
"SUCCESS"
} else {
"FAIL_FAST"
}
);
if let Some(pattern) = decision.chosen { if let Some(pattern) = decision.chosen {
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern); eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
} }
eprintln!("[loop_canonicalizer] Missing caps: {:?}", decision.missing_caps); eprintln!(
"[loop_canonicalizer] Missing caps: {:?}",
decision.missing_caps
);
if decision.is_fail_fast() { if decision.is_fail_fast() {
eprintln!("[loop_canonicalizer] Reason: {}", decision.notes.join("; ")); eprintln!(
"[loop_canonicalizer] Reason: {}",
decision.notes.join("; ")
);
} }
// Phase 137-4: Router parity verification // Phase 137-4: Router parity verification
@ -183,7 +204,11 @@ impl MirBuilder {
func_name, func_name,
&format!( &format!(
"fn_body_ast is {}", "fn_body_ast is {}",
if fn_body_clone.is_some() { "SOME" } else { "NONE" } if fn_body_clone.is_some() {
"SOME"
} else {
"NONE"
}
), ),
); );
let ctx = if let Some(ref fn_body) = fn_body_clone { let ctx = if let Some(ref fn_body) = fn_body_clone {

View File

@ -47,7 +47,10 @@ impl super::MirBuilder {
); );
eprintln!("[DEBUG] params.len() = {}", params.len()); eprintln!("[DEBUG] params.len() = {}", params.len());
eprintln!("[DEBUG] body.len() = {}", body.len()); eprintln!("[DEBUG] body.len() = {}", body.len());
eprintln!("[DEBUG] variable_map = {:?}", self.variable_ctx.variable_map); eprintln!(
"[DEBUG] variable_map = {:?}",
self.variable_ctx.variable_map
);
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論) // Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
// See lifecycle.rs and builder_calls.rs for context swap implementation // See lifecycle.rs and builder_calls.rs for context swap implementation
let _ = self.lower_static_method_as_function( let _ = self.lower_static_method_as_function(
@ -58,7 +61,10 @@ impl super::MirBuilder {
eprintln!( eprintln!(
"[DEBUG] build_static_main_box: After lower_static_method_as_function" "[DEBUG] build_static_main_box: After lower_static_method_as_function"
); );
eprintln!("[DEBUG] variable_map = {:?}", self.variable_ctx.variable_map); eprintln!(
"[DEBUG] variable_map = {:?}",
self.variable_ctx.variable_map
);
} }
// Initialize local variables for Main.main() parameters // Initialize local variables for Main.main() parameters
// Note: These are local variables in the wrapper main() function, NOT parameters // Note: These are local variables in the wrapper main() function, NOT parameters
@ -75,8 +81,11 @@ impl super::MirBuilder {
box_type: "ArrayBox".to_string(), box_type: "ArrayBox".to_string(),
args: vec![], args: vec![],
})?; })?;
self.type_ctx.value_origin_newbox.insert(pid, "ArrayBox".to_string()); self.type_ctx
self.type_ctx.value_types .value_origin_newbox
.insert(pid, "ArrayBox".to_string());
self.type_ctx
.value_types
.insert(pid, super::MirType::Box("ArrayBox".to_string())); .insert(pid, super::MirType::Box("ArrayBox".to_string()));
// Explicitly call birth() to initialize internal state // Explicitly call birth() to initialize internal state
self.emit_instruction(MirInstruction::BoxCall { self.emit_instruction(MirInstruction::BoxCall {
@ -206,7 +215,8 @@ impl super::MirBuilder {
if let Some((k, prop)) = kind_and_prop { if let Some((k, prop)) = kind_and_prop {
use std::collections::HashMap; use std::collections::HashMap;
let entry: &mut HashMap<String, super::PropertyKind> = self let entry: &mut HashMap<String, super::PropertyKind> = self
.comp_ctx.property_getters_by_box .comp_ctx
.property_getters_by_box
.entry(name.clone()) .entry(name.clone())
.or_insert_with(HashMap::new); .or_insert_with(HashMap::new);
entry.insert(prop, k); entry.insert(prop, k);

View File

@ -14,7 +14,9 @@ pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
value: ConstValue::Integer(val), value: ConstValue::Integer(val),
}); });
// Phase 84-1: Integer constant type annotation // Phase 84-1: Integer constant type annotation
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Integer); b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Integer);
dst dst
} }
@ -26,7 +28,9 @@ pub fn emit_bool(b: &mut MirBuilder, val: bool) -> ValueId {
value: ConstValue::Bool(val), value: ConstValue::Bool(val),
}); });
// Phase 84-1: Bool constant type annotation // Phase 84-1: Bool constant type annotation
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Bool); b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Bool);
dst dst
} }
@ -38,7 +42,9 @@ pub fn emit_float(b: &mut MirBuilder, val: f64) -> ValueId {
value: ConstValue::Float(val), value: ConstValue::Float(val),
}); });
// Phase 84-1: Float constant type annotation // Phase 84-1: Float constant type annotation
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Float); b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Float);
dst dst
} }
@ -51,9 +57,12 @@ pub fn emit_string<S: Into<String>>(b: &mut MirBuilder, s: S) -> ValueId {
}); });
// 🎯 Phase 3-A: String constant type annotation // 🎯 Phase 3-A: String constant type annotation
// Ensures string constants have proper Box type for method resolution // Ensures string constants have proper Box type for method resolution
b.type_ctx.value_types b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Box("StringBox".to_string())); .insert(dst, crate::mir::MirType::Box("StringBox".to_string()));
b.type_ctx.value_origin_newbox.insert(dst, "StringBox".to_string()); b.type_ctx
.value_origin_newbox
.insert(dst, "StringBox".to_string());
dst dst
} }
@ -66,7 +75,9 @@ pub fn emit_null(b: &mut MirBuilder) -> ValueId {
}); });
// Phase 84-1: Null constant type annotation // Phase 84-1: Null constant type annotation
// Note: MirType has no Null variant, using Unknown as fallback // Note: MirType has no Null variant, using Unknown as fallback
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Unknown); b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Unknown);
dst dst
} }
@ -78,6 +89,8 @@ pub fn emit_void(b: &mut MirBuilder) -> ValueId {
value: ConstValue::Void, value: ConstValue::Void,
}); });
// Phase 84-1: Void constant type annotation // Phase 84-1: Void constant type annotation
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Void); b.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Void);
dst dst
} }

View File

@ -222,7 +222,8 @@ impl super::MirBuilder {
body.clone(), body.clone(),
)?; )?;
// Index static method for fallback resolution of bare calls // Index static method for fallback resolution of bare calls
self.comp_ctx.static_method_index self.comp_ctx
.static_method_index
.entry(method_name.clone()) .entry(method_name.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push((name.clone(), params.len())); .push((name.clone(), params.len()));
@ -306,14 +307,18 @@ impl super::MirBuilder {
args: vec![], args: vec![],
effects: super::EffectMask::MUT, effects: super::EffectMask::MUT,
})?; })?;
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(arr_id, "ArrayBox".to_string()); .insert(arr_id, "ArrayBox".to_string());
self.type_ctx.value_types self.type_ctx
.value_types
.insert(arr_id, super::MirType::Box("ArrayBox".to_string())); .insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
// TypeRegistry + trace for deterministic debug // TypeRegistry + trace for deterministic debug
self.comp_ctx.type_registry self.comp_ctx
.type_registry
.record_newbox(arr_id, "ArrayBox".to_string()); .record_newbox(arr_id, "ArrayBox".to_string());
self.comp_ctx.type_registry self.comp_ctx
.type_registry
.record_type(arr_id, super::MirType::Box("ArrayBox".to_string())); .record_type(arr_id, super::MirType::Box("ArrayBox".to_string()));
type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox"); type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox");
type_trace::ty( type_trace::ty(
@ -350,13 +355,17 @@ impl super::MirBuilder {
args: vec![], args: vec![],
effects: super::EffectMask::MUT, effects: super::EffectMask::MUT,
})?; })?;
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(map_id, "MapBox".to_string()); .insert(map_id, "MapBox".to_string());
self.type_ctx.value_types self.type_ctx
.value_types
.insert(map_id, super::MirType::Box("MapBox".to_string())); .insert(map_id, super::MirType::Box("MapBox".to_string()));
self.comp_ctx.type_registry self.comp_ctx
.type_registry
.record_newbox(map_id, "MapBox".to_string()); .record_newbox(map_id, "MapBox".to_string());
self.comp_ctx.type_registry self.comp_ctx
.type_registry
.record_type(map_id, super::MirType::Box("MapBox".to_string())); .record_type(map_id, super::MirType::Box("MapBox".to_string()));
type_trace::origin("newbox:MapLiteral", map_id, "MapBox"); type_trace::origin("newbox:MapLiteral", map_id, "MapBox");
type_trace::ty( type_trace::ty(
@ -404,13 +413,16 @@ impl super::MirBuilder {
if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) { if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) {
return Some(cls.clone()); return Some(cls.clone());
} }
self.type_ctx.value_types.get(&target_val).and_then(|ty| match ty { self.type_ctx
super::MirType::Box(name) => Some(name.clone()), .value_types
super::MirType::String => Some("String".to_string()), .get(&target_val)
super::MirType::Integer => Some("Integer".to_string()), .and_then(|ty| match ty {
super::MirType::Float => Some("Float".to_string()), super::MirType::Box(name) => Some(name.clone()),
_ => None, super::MirType::String => Some("String".to_string()),
}) super::MirType::Integer => Some("Integer".to_string()),
super::MirType::Float => Some("Float".to_string()),
_ => None,
})
} }
fn format_index_target_kind(class_hint: Option<&String>) -> String { fn format_index_target_kind(class_hint: Option<&String>) -> String {

View File

@ -170,7 +170,8 @@ impl super::MirBuilder {
captures, captures,
me, me,
})?; })?;
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, crate::mir::MirType::Box("FunctionBox".to_string())); .insert(dst, crate::mir::MirType::Box("FunctionBox".to_string()));
Ok(dst) Ok(dst)
} }

View File

@ -118,7 +118,9 @@ impl super::MirBuilder {
// Merge and yield result // Merge and yield result
self.start_new_block(merge_block)?; self.start_new_block(merge_block)?;
// フェーズM: PHI はブロック先頭に配置cf_common 統一) // フェーズM: PHI はブロック先頭に配置cf_common 統一)
if let (Some(func), Some(cur_bb)) = (self.scope_ctx.current_function.as_mut(), self.current_block) { if let (Some(func), Some(cur_bb)) =
(self.scope_ctx.current_function.as_mut(), self.current_block)
{
crate::mir::ssot::cf_common::insert_phi_at_head_spanned( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func, func,
cur_bb, cur_bb,

View File

@ -16,7 +16,12 @@ impl super::MirBuilder {
// Unified members: if object class is known and has a synthetic getter for `field`, // Unified members: if object class is known and has a synthetic getter for `field`,
// rewrite to method call `__get_<field>()`. // rewrite to method call `__get_<field>()`.
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() { if let Some(class_name) = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
if let Some(map) = self.comp_ctx.property_getters_by_box.get(&class_name) { if let Some(map) = self.comp_ctx.property_getters_by_box.get(&class_name) {
if let Some(kind) = map.get(&field) { if let Some(kind) = map.get(&field) {
let mname = match kind { let mname = match kind {
@ -53,15 +58,24 @@ impl super::MirBuilder {
// Propagate recorded origin class for this field if any (ValueId-scoped) // Propagate recorded origin class for this field if any (ValueId-scoped)
if let Some(class_name) = self if let Some(class_name) = self
.comp_ctx.field_origin_class .comp_ctx
.field_origin_class
.get(&(object_value, field.clone())) .get(&(object_value, field.clone()))
.cloned() .cloned()
{ {
self.type_ctx.value_origin_newbox.insert(field_val, class_name); self.type_ctx
} else if let Some(base_cls) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() { .value_origin_newbox
.insert(field_val, class_name);
} else if let Some(base_cls) = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
// Cross-function heuristic: use class-level field origin mapping // Cross-function heuristic: use class-level field origin mapping
if let Some(fcls) = self if let Some(fcls) = self
.comp_ctx.field_origin_by_box .comp_ctx
.field_origin_by_box
.get(&(base_cls.clone(), field.clone())) .get(&(base_cls.clone(), field.clone()))
.cloned() .cloned()
{ {
@ -78,8 +92,11 @@ impl super::MirBuilder {
} }
// If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier) // If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier)
let mut inferred_class: Option<String> = let mut inferred_class: Option<String> = self
self.type_ctx.value_origin_newbox.get(&object_value).cloned(); .type_ctx
.value_origin_newbox
.get(&object_value)
.cloned();
if inferred_class.is_none() { if inferred_class.is_none() {
if let ASTNode::FieldAccess { if let ASTNode::FieldAccess {
object: inner_obj, object: inner_obj,
@ -89,7 +106,8 @@ impl super::MirBuilder {
{ {
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) { if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
if let Some(cls) = self if let Some(cls) = self
.comp_ctx.field_origin_class .comp_ctx
.field_origin_class
.get(&(base_id, inner_field)) .get(&(base_id, inner_field))
.cloned() .cloned()
{ {
@ -128,7 +146,12 @@ impl super::MirBuilder {
value_result = self.local_arg(value_result); value_result = self.local_arg(value_result);
// If base is known and field is weak, create WeakRef before store // If base is known and field is weak, create WeakRef before store
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() { if let Some(class_name) = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) { if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) { if weak_set.contains(&field) {
value_result = self.emit_weak_new(value_result)?; value_result = self.emit_weak_new(value_result)?;
@ -158,7 +181,12 @@ impl super::MirBuilder {
})?; })?;
// Write barrier if weak field // Write barrier if weak field
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() { if let Some(class_name) = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) { if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
if weak_set.contains(&field) { if weak_set.contains(&field) {
let _ = self.emit_barrier_write(value_result); let _ = self.emit_barrier_write(value_result);
@ -167,12 +195,24 @@ impl super::MirBuilder {
} }
// Record origin class for this field value if known // Record origin class for this field value if known
if let Some(val_cls) = self.type_ctx.value_origin_newbox.get(&value_result).cloned() { if let Some(val_cls) = self
self.comp_ctx.field_origin_class .type_ctx
.value_origin_newbox
.get(&value_result)
.cloned()
{
self.comp_ctx
.field_origin_class
.insert((object_value, field.clone()), val_cls.clone()); .insert((object_value, field.clone()), val_cls.clone());
// Also record class-level mapping if base object class is known // Also record class-level mapping if base object class is known
if let Some(base_cls) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() { if let Some(base_cls) = self
self.comp_ctx.field_origin_by_box .type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
self.comp_ctx
.field_origin_by_box
.insert((base_cls, field.clone()), val_cls); .insert((base_cls, field.clone()), val_cls);
} }
} }

View File

@ -22,9 +22,10 @@ impl<'a> PhiBuilderOps for ToplevelOps<'a> {
inputs: Vec<(BasicBlockId, ValueId)>, inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> { ) -> Result<(), String> {
// merge ブロックの先頭に PHI を挿入 // merge ブロックの先頭に PHI を挿入
if let (Some(func), Some(_cur_bb)) = if let (Some(func), Some(_cur_bb)) = (
(self.0.scope_ctx.current_function.as_mut(), self.0.current_block) self.0.scope_ctx.current_function.as_mut(),
{ self.0.current_block,
) {
crate::mir::ssot::cf_common::insert_phi_at_head_spanned( crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func, func,
block, block,
@ -170,7 +171,11 @@ impl MirBuilder {
} }
} }
let val = self.build_expression(else_ast.clone())?; let val = self.build_expression(else_ast.clone())?;
(val, Some(else_ast), Some(self.variable_ctx.variable_map.clone())) (
val,
Some(else_ast),
Some(self.variable_ctx.variable_map.clone()),
)
} else { } else {
// No else branch: materialize PHI nodes for the empty else block // No else branch: materialize PHI nodes for the empty else block
self.variable_ctx.variable_map = pre_if_var_map.clone(); self.variable_ctx.variable_map = pre_if_var_map.clone();
@ -222,7 +227,8 @@ impl MirBuilder {
// 関数名ガードチェック // 関数名ガードチェック
let func_name = self let func_name = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.as_str()) .map(|f| f.signature.name.as_str())
.unwrap_or(""); .unwrap_or("");

View File

@ -92,7 +92,8 @@ impl super::MirBuilder {
} else { } else {
for (mname, mast) in methods { for (mname, mast) in methods {
if let ASTNode::FunctionDeclaration { params, .. } = mast { if let ASTNode::FunctionDeclaration { params, .. } = mast {
self.comp_ctx.static_method_index self.comp_ctx
.static_method_index
.entry(mname.clone()) .entry(mname.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push((name.clone(), params.len())); .push((name.clone(), params.len()));
@ -207,7 +208,8 @@ impl super::MirBuilder {
params.clone(), params.clone(),
body.clone(), body.clone(),
)?; )?;
self.comp_ctx.static_method_index self.comp_ctx
.static_method_index
.entry(mname.clone()) .entry(mname.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push((name.clone(), params.len())); .push((name.clone(), params.len()));
@ -447,7 +449,8 @@ impl super::MirBuilder {
// PHI + Copy の小グラフを DFS 探索し、1 種類の型に収束する場合のみ返す。 // PHI + Copy の小グラフを DFS 探索し、1 種類の型に収束する場合のみ返す。
// これにより Loop edge copy / If merge 後の型推論が解決できる。 // これにより Loop edge copy / If merge 後の型推論が解決できる。
if hint.is_none() { if hint.is_none() {
let phi_resolver = PhiTypeResolver::new(&function, &self.type_ctx.value_types); let phi_resolver =
PhiTypeResolver::new(&function, &self.type_ctx.value_types);
if let Some(mt) = phi_resolver.resolve(*v) { if let Some(mt) = phi_resolver.resolve(*v) {
if std::env::var("NYASH_P4_DEBUG").is_ok() { if std::env::var("NYASH_P4_DEBUG").is_ok() {
eprintln!( eprintln!(
@ -461,9 +464,11 @@ 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) = if let Some(mt) = GenericTypeResolver::resolve_from_phi(
GenericTypeResolver::resolve_from_phi(&function, *v, &self.type_ctx.value_types) &function,
{ *v,
&self.type_ctx.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: {:?}",
@ -650,7 +655,9 @@ impl super::MirBuilder {
.unwrap_or(MirType::Unknown), .unwrap_or(MirType::Unknown),
Callee::Constructor { box_type } => { Callee::Constructor { box_type } => {
let ret = MirType::Box(box_type.clone()); let ret = MirType::Box(box_type.clone());
self.type_ctx.value_origin_newbox.insert(*dst, box_type.clone()); self.type_ctx
.value_origin_newbox
.insert(*dst, box_type.clone());
ret ret
} }
_ => MirType::Unknown, _ => MirType::Unknown,

View File

@ -24,7 +24,8 @@ impl MeCallPolicyBox {
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
// Instance box: prefer enclosing box method (lowered function) if存在 // Instance box: prefer enclosing box method (lowered function) if存在
let enclosing_cls: Option<String> = builder let enclosing_cls: Option<String> = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string())); .and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));

View File

@ -25,7 +25,8 @@ fn sample_every() -> usize {
/// Devonly: emit a resolve.try eventcandidates inspection /// Devonly: emit a resolve.try eventcandidates inspection
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) { pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder let fn_name = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.as_str()); .map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id(); let region = builder.debug_current_region_id();
@ -35,7 +36,8 @@ pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
/// Devonly: emit a resolve.choose eventdecision /// Devonly: emit a resolve.choose eventdecision
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) { pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder let fn_name = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.as_str()); .map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id(); let region = builder.debug_current_region_id();

View File

@ -18,13 +18,15 @@ pub(crate) fn emit_phi(builder: &MirBuilder, dst: ValueId, inputs: &Vec<(BasicBl
}) })
.collect(); .collect();
let decided_t = builder let decided_t = builder
.type_ctx.value_types .type_ctx
.value_types
.get(&dst) .get(&dst)
.cloned() .cloned()
.map(|tt| format!("{:?}", tt)) .map(|tt| format!("{:?}", tt))
.unwrap_or_default(); .unwrap_or_default();
let decided_o = builder let decided_o = builder
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&dst) .get(&dst)
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
@ -35,7 +37,8 @@ pub(crate) fn emit_phi(builder: &MirBuilder, dst: ValueId, inputs: &Vec<(BasicBl
"decided_origin": decided_o, "decided_origin": decided_o,
}); });
let fn_name = builder let fn_name = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.as_str()); .map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id(); let region = builder.debug_current_region_id();

View File

@ -48,7 +48,8 @@ impl super::MirBuilder {
BinaryOpType::Arithmetic(op) => { BinaryOpType::Arithmetic(op) => {
// Dev: Lower '+' を演算子ボックス呼び出しに置換既定OFF // Dev: Lower '+' を演算子ボックス呼び出しに置換既定OFF
let in_add_op = self let in_add_op = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.starts_with("AddOperator.apply/")) .map(|f| f.signature.name.starts_with("AddOperator.apply/"))
.unwrap_or(false); .unwrap_or(false);
@ -80,9 +81,11 @@ impl super::MirBuilder {
match (lhs_type, rhs_type) { match (lhs_type, rhs_type) {
(String, String) => { (String, String) => {
// BOTH are strings: result is string // BOTH are strings: result is string
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, MirType::Box("StringBox".to_string())); .insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(dst, "StringBox".to_string()); .insert(dst, "StringBox".to_string());
} }
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => { (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
@ -121,7 +124,8 @@ impl super::MirBuilder {
}; };
if !name.is_empty() { if !name.is_empty() {
let in_guard = self let in_guard = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix)) .map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false); .unwrap_or(false);
@ -172,9 +176,11 @@ impl super::MirBuilder {
match (lhs_type, rhs_type) { match (lhs_type, rhs_type) {
(String, String) => { (String, String) => {
// BOTH are strings: result is definitely a string // BOTH are strings: result is definitely a string
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, MirType::Box("StringBox".to_string())); .insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(dst, "StringBox".to_string()); .insert(dst, "StringBox".to_string());
} }
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => { (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
@ -217,7 +223,8 @@ impl super::MirBuilder {
Some(MirType::String) => true, Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true, Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => self _ => self
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&lhs) .get(&lhs)
.map(|s| s == "StringBox") .map(|s| s == "StringBox")
.unwrap_or(false), .unwrap_or(false),
@ -226,16 +233,19 @@ impl super::MirBuilder {
Some(MirType::String) => true, Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true, Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => self _ => self
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&rhs) .get(&rhs)
.map(|s| s == "StringBox") .map(|s| s == "StringBox")
.unwrap_or(false), .unwrap_or(false),
}; };
if lhs_is_str && rhs_is_str { if lhs_is_str && rhs_is_str {
// BOTH are strings: result is definitely a string // BOTH are strings: result is definitely a string
self.type_ctx.value_types self.type_ctx
.value_types
.insert(dst, MirType::Box("StringBox".to_string())); .insert(dst, MirType::Box("StringBox".to_string()));
self.type_ctx.value_origin_newbox self.type_ctx
.value_origin_newbox
.insert(dst, "StringBox".to_string()); .insert(dst, "StringBox".to_string());
} else if !lhs_is_str && !rhs_is_str { } else if !lhs_is_str && !rhs_is_str {
// NEITHER is a string: numeric addition // NEITHER is a string: numeric addition
@ -252,7 +262,8 @@ impl super::MirBuilder {
BinaryOpType::Comparison(op) => { BinaryOpType::Comparison(op) => {
// Dev: Lower 比較 を演算子ボックス呼び出しに置換既定OFF // Dev: Lower 比較 を演算子ボックス呼び出しに置換既定OFF
let in_cmp_op = self let in_cmp_op = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.starts_with("CompareOperator.apply/")) .map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
.unwrap_or(false); .unwrap_or(false);
@ -285,12 +296,14 @@ impl super::MirBuilder {
} else { } else {
// 既存の比較経路(安全のための型注釈/slot化含む // 既存の比較経路(安全のための型注釈/slot化含む
let (lhs2_raw, rhs2_raw) = if self let (lhs2_raw, rhs2_raw) = if self
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&lhs) .get(&lhs)
.map(|s| s == "IntegerBox") .map(|s| s == "IntegerBox")
.unwrap_or(false) .unwrap_or(false)
&& self && self
.type_ctx.value_origin_newbox .type_ctx
.value_origin_newbox
.get(&rhs) .get(&rhs)
.map(|s| s == "IntegerBox") .map(|s| s == "IntegerBox")
.unwrap_or(false) .unwrap_or(false)
@ -541,7 +554,8 @@ impl super::MirBuilder {
}; };
if !name.is_empty() { if !name.is_empty() {
let in_guard = self let in_guard = self
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.starts_with(guard_prefix)) .map(|f| f.signature.name.starts_with(guard_prefix))
.unwrap_or(false); .unwrap_or(false);

View File

@ -23,7 +23,10 @@ pub(crate) fn annotate_me_origin(builder: &mut MirBuilder, me_id: ValueId) {
} }
if let Some(c) = cls { if let Some(c) = cls {
// Record both origin class and a Box type hint for downstream passes観測用 // Record both origin class and a Box type hint for downstream passes観測用
builder.type_ctx.value_origin_newbox.insert(me_id, c.clone()); builder
.type_ctx
.value_origin_newbox
.insert(me_id, c.clone());
builder.type_ctx.value_types.insert(me_id, MirType::Box(c)); builder.type_ctx.value_types.insert(me_id, MirType::Box(c));
} }
} }

View File

@ -70,7 +70,8 @@ impl MirBuilder {
if let Some(func) = self.scope_ctx.current_function.as_mut() { if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg(); func.update_cfg();
} }
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block) if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{ {
crate::mir::phi_core::common::debug_verify_phi_inputs( crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs, func, cur_bb, &inputs,
@ -94,7 +95,9 @@ impl MirBuilder {
type_hint: None, // Phase 63-6: Legacy path, no type hint type_hint: None, // Phase 63-6: Legacy path, no type hint
})?; })?;
} }
self.variable_ctx.variable_map.insert(pin_name.clone(), merged); self.variable_ctx
.variable_map
.insert(pin_name.clone(), merged);
} }
} }
} }
@ -172,7 +175,8 @@ impl MirBuilder {
if let Some(func) = self.scope_ctx.current_function.as_mut() { if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg(); func.update_cfg();
} }
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block) if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{ {
crate::mir::phi_core::common::debug_verify_phi_inputs( crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs, func, cur_bb, &inputs,
@ -205,7 +209,8 @@ impl MirBuilder {
if let Some(func) = self.scope_ctx.current_function.as_mut() { if let Some(func) = self.scope_ctx.current_function.as_mut() {
func.update_cfg(); func.update_cfg();
} }
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block) if let (Some(func), Some(cur_bb)) =
(&self.scope_ctx.current_function, self.current_block)
{ {
crate::mir::phi_core::common::debug_verify_phi_inputs( crate::mir::phi_core::common::debug_verify_phi_inputs(
func, cur_bb, &inputs, func, cur_bb, &inputs,

View File

@ -89,9 +89,10 @@ impl<'a> PhiMergeHelper<'a> {
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() { if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
func.update_cfg(); func.update_cfg();
} }
if let (Some(func), Some(cur_bb)) = if let (Some(func), Some(cur_bb)) = (
(&self.builder.scope_ctx.current_function, self.builder.current_block) &self.builder.scope_ctx.current_function,
{ self.builder.current_block,
) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
} }
let merged = self.builder.insert_phi(inputs)?; let merged = self.builder.insert_phi(inputs)?;
@ -135,9 +136,10 @@ impl<'a> PhiMergeHelper<'a> {
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() { if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
func.update_cfg(); func.update_cfg();
} }
if let (Some(func), Some(cur_bb)) = if let (Some(func), Some(cur_bb)) = (
(&self.builder.scope_ctx.current_function, self.builder.current_block) &self.builder.scope_ctx.current_function,
{ self.builder.current_block,
) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
} }
self.builder.insert_phi_with_dst(dst, inputs)?; self.builder.insert_phi_with_dst(dst, inputs)?;

View File

@ -23,13 +23,15 @@ pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
// Optional dev trace for receiver aliases // Optional dev trace for receiver aliases
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") { if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
let current_fn = builder let current_fn = builder
.scope_ctx.current_function .scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.clone()) .map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string()); .unwrap_or_else(|| "<none>".to_string());
let bb = builder.current_block; let bb = builder.current_block;
let names: Vec<String> = builder let names: Vec<String> = builder
.variable_ctx.variable_map .variable_ctx
.variable_map
.iter() .iter()
.filter(|(_, &vid)| vid == r) .filter(|(_, &vid)| vid == r)
.map(|(k, _)| k.clone()) .map(|(k, _)| k.clone())

View File

@ -40,7 +40,12 @@ pub(crate) fn try_known_rewrite(
return None; return None;
} }
// Receiver must be Known (origin 由来) // Receiver must be Known (origin 由来)
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() { if builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_none()
{
return None; return None;
} }
// Only user-defined boxes (plugin/core boxesは対象外) // Only user-defined boxes (plugin/core boxesは対象外)
@ -54,7 +59,11 @@ pub(crate) fn try_known_rewrite(
.ok() .ok()
.as_deref() .as_deref()
== Some("1"); == Some("1");
let from_new_origin = builder.type_ctx.value_origin_newbox.get(&object_value).is_some(); let from_new_origin = builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_some();
let arity = arg_values.len(); let arity = arg_values.len();
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name( let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity, cls, method, arity,
@ -107,7 +116,12 @@ pub(crate) fn try_known_rewrite_to_dst(
if !rewrite_enabled() { if !rewrite_enabled() {
return None; return None;
} }
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() { if builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_none()
{
return None; return None;
} }
if !builder.comp_ctx.user_defined_boxes.contains(cls) { if !builder.comp_ctx.user_defined_boxes.contains(cls) {
@ -119,7 +133,11 @@ pub(crate) fn try_known_rewrite_to_dst(
.ok() .ok()
.as_deref() .as_deref()
== Some("1"); == Some("1");
let from_new_origin = builder.type_ctx.value_origin_newbox.get(&object_value).is_some(); let from_new_origin = builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_some();
let arity = arg_values.len(); let arity = arg_values.len();
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name( let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity, cls, method, arity,
@ -171,7 +189,12 @@ pub(crate) fn try_unique_suffix_rewrite(
return None; return None;
} }
// Only attempt if receiver is Known (keeps behavior stable and avoids surprises) // Only attempt if receiver is Known (keeps behavior stable and avoids surprises)
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() { if builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_none()
{
return None; return None;
} }
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len()); let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
@ -222,7 +245,12 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
if !rewrite_enabled() { if !rewrite_enabled() {
return None; return None;
} }
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() { if builder
.type_ctx
.value_origin_newbox
.get(&object_value)
.is_none()
{
return None; return None;
} }
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len()); let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());

View File

@ -64,7 +64,10 @@ impl BlockScheduleBox {
if std::env::var("NYASH_BLOCK_SCHEDULE_VERIFY").ok().as_deref() != Some("1") { if std::env::var("NYASH_BLOCK_SCHEDULE_VERIFY").ok().as_deref() != Some("1") {
return; return;
} }
let (f_opt, bb_opt) = (builder.scope_ctx.current_function.as_ref(), builder.current_block); let (f_opt, bb_opt) = (
builder.scope_ctx.current_function.as_ref(),
builder.current_block,
);
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else { let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else {
return; return;
}; };

View File

@ -72,8 +72,7 @@ impl ScopeContext {
/// Push new lexical scope frame /// Push new lexical scope frame
#[inline] #[inline]
pub(super) fn push_lexical_scope(&mut self) { pub(super) fn push_lexical_scope(&mut self) {
self.lexical_scope_stack self.lexical_scope_stack.push(LexicalScopeFrame::default());
.push(LexicalScopeFrame::default());
} }
/// Pop lexical scope frame (returns frame for restoration) /// Pop lexical scope frame (returns frame for restoration)

View File

@ -63,7 +63,8 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
let mut slot_name_opt: Option<String> = None; let mut slot_name_opt: Option<String> = None;
let names_for_v: Vec<String> = builder let names_for_v: Vec<String> = builder
.variable_ctx.variable_map .variable_ctx
.variable_map
.iter() .iter()
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$")) .filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
.map(|(k, _)| k.clone()) .map(|(k, _)| k.clone())
@ -116,13 +117,17 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
builder.type_ctx.value_types.insert(loc, t); builder.type_ctx.value_types.insert(loc, t);
} }
if let Some(cls) = builder.type_ctx.value_origin_newbox.get(&v).cloned() { if let Some(cls) = builder.type_ctx.value_origin_newbox.get(&v).cloned() {
builder.type_ctx.value_origin_newbox.insert(loc, cls.clone()); builder
.type_ctx
.value_origin_newbox
.insert(loc, cls.clone());
// CRITICAL FIX: For receiver kind, if type is missing but origin exists, // CRITICAL FIX: For receiver kind, if type is missing but origin exists,
// infer MirType::Box from origin // infer MirType::Box from origin
if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() { if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() {
builder builder
.type_ctx.value_types .type_ctx
.value_types
.insert(loc, crate::mir::MirType::Box(cls)); .insert(loc, crate::mir::MirType::Box(cls));
} }
} }

View File

@ -181,7 +181,8 @@ impl super::MirBuilder {
idx + 1, idx + 1,
total, total,
self.current_block, self.current_block,
self.scope_ctx.current_function self.scope_ctx
.current_function
.as_ref() .as_ref()
.map(|f| f.signature.name.as_str()) .map(|f| f.signature.name.as_str())
.unwrap_or("none") .unwrap_or("none")
@ -427,9 +428,12 @@ impl super::MirBuilder {
})?; })?;
// Future spawn returns a Future<T>; the inner type is not statically known here. // Future spawn returns a Future<T>; the inner type is not statically known here.
// Register at least Future<Unknown> to avoid later fail-fast type inference panics. // Register at least Future<Unknown> to avoid later fail-fast type inference panics.
self.type_ctx.value_types self.type_ctx
.value_types
.insert(future_id, MirType::Future(Box::new(MirType::Unknown))); .insert(future_id, MirType::Future(Box::new(MirType::Unknown)));
self.variable_ctx.variable_map.insert(variable.clone(), future_id); self.variable_ctx
.variable_map
.insert(variable.clone(), future_id);
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() { if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
reg.ensure_slot(&variable, None); reg.ensure_slot(&variable, None);
} }
@ -442,13 +446,17 @@ impl super::MirBuilder {
value: expression_value, value: expression_value,
})?; })?;
let inner = self let inner = self
.type_ctx.value_types .type_ctx
.value_types
.get(&expression_value) .get(&expression_value)
.cloned() .cloned()
.unwrap_or(MirType::Unknown); .unwrap_or(MirType::Unknown);
self.type_ctx.value_types self.type_ctx
.value_types
.insert(future_id, MirType::Future(Box::new(inner))); .insert(future_id, MirType::Future(Box::new(inner)));
self.variable_ctx.variable_map.insert(variable.clone(), future_id); self.variable_ctx
.variable_map
.insert(variable.clone(), future_id);
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() { if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
reg.ensure_slot(&variable, None); reg.ensure_slot(&variable, None);
} }
@ -487,7 +495,9 @@ impl super::MirBuilder {
"__me__".to_string() "__me__".to_string()
}; };
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag); let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
self.variable_ctx.variable_map.insert("me".to_string(), me_value); self.variable_ctx
.variable_map
.insert("me".to_string(), me_value);
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() { if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
reg.ensure_slot("me", None); reg.ensure_slot("me", None);
} }

View File

@ -149,7 +149,8 @@ impl super::MirBuilder {
// Second pass: update any user variables that pointed to old pin ids to the new ones // Second pass: update any user variables that pointed to old pin ids to the new ones
if !pin_renames.is_empty() { if !pin_renames.is_empty() {
let snapshot: Vec<(String, super::ValueId)> = self let snapshot: Vec<(String, super::ValueId)> = self
.variable_ctx.variable_map .variable_ctx
.variable_map
.iter() .iter()
.filter(|(k, _)| !k.starts_with("__pin$")) .filter(|(k, _)| !k.starts_with("__pin$"))
.map(|(k, &v)| (k.clone(), v)) .map(|(k, &v)| (k.clone(), v))
@ -316,7 +317,8 @@ impl super::MirBuilder {
effects, effects,
})?; })?;
if let Some(d) = dst { if let Some(d) = dst {
let mut recv_box: Option<String> = self.type_ctx.value_origin_newbox.get(&box_val).cloned(); let mut recv_box: Option<String> =
self.type_ctx.value_origin_newbox.get(&box_val).cloned();
if recv_box.is_none() { if recv_box.is_none() {
if let Some(t) = self.type_ctx.value_types.get(&box_val) { if let Some(t) = self.type_ctx.value_types.get(&box_val) {
match t { match t {
@ -327,7 +329,11 @@ impl super::MirBuilder {
} }
} }
if let Some(bt) = recv_box { if let Some(bt) = recv_box {
if let Some(mt) = self.comp_ctx.plugin_method_sigs.get(&(bt.clone(), method.clone())) { if let Some(mt) = self
.comp_ctx
.plugin_method_sigs
.get(&(bt.clone(), method.clone()))
{
self.type_ctx.value_types.insert(d, mt.clone()); self.type_ctx.value_types.insert(d, mt.clone());
} else { } else {
// Phase 84-4-B: ビルトイン Box のメソッド戻り値型推論 // Phase 84-4-B: ビルトイン Box のメソッド戻り値型推論
@ -489,7 +495,8 @@ impl super::MirBuilder {
dst: super::ValueId, dst: super::ValueId,
src: super::ValueId, src: super::ValueId,
) -> Result<(), String> { ) -> Result<(), String> {
if let (Some(ref mut function), Some(bb)) = (&mut self.scope_ctx.current_function, self.current_block) if let (Some(ref mut function), Some(bb)) =
(&mut self.scope_ctx.current_function, self.current_block)
{ {
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") { if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
eprintln!( eprintln!(

View File

@ -26,4 +26,3 @@ impl AssignmentResolverBox {
Err(msg) Err(msg)
} }
} }

View File

@ -97,7 +97,9 @@ impl super::super::MirBuilder {
} }
// Update both ValueId and BindingId mappings // Update both ValueId and BindingId mappings
self.variable_ctx.variable_map.insert(name.to_string(), value); self.variable_ctx
.variable_map
.insert(name.to_string(), value);
// Phase 74: Allocate and register new BindingId for this binding // Phase 74: Allocate and register new BindingId for this binding
let binding_id = self.allocate_binding_id(); let binding_id = self.allocate_binding_id();

View File

@ -24,17 +24,15 @@ use super::common::{
build_join_module, create_k_exit_function, create_loop_context, parse_program_json, build_join_module, create_k_exit_function, create_loop_context, parse_program_json,
process_local_inits, process_local_inits,
}; };
use super::param_guess::{build_param_order, compute_param_guess};
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use super::if_sum_break_pattern; use super::if_sum_break_pattern;
use super::param_guess::{build_param_order, compute_param_guess};
use super::{AstToJoinIrLowerer, JoinModule, LoweringError}; use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
use crate::mir::join_ir::{JoinFunction, JoinInst}; use crate::mir::join_ir::{JoinFunction, JoinInst};
use crate::mir::ValueId; use crate::mir::ValueId;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir::ownership::{ use crate::mir::join_ir::ownership::{plan_to_p2_inputs_with_relay, OwnershipAnalyzer};
plan_to_p2_inputs_with_relay, OwnershipAnalyzer,
};
/// Break パターンを JoinModule に変換 /// Break パターンを JoinModule に変換
/// ///
@ -217,7 +215,11 @@ fn compute_param_order_from_ownership(
// Fallback: any loop plan with relay_writes // Fallback: any loop plan with relay_writes
.or_else(|| plans.iter().find(|p| !p.relay_writes.is_empty())) .or_else(|| plans.iter().find(|p| !p.relay_writes.is_empty()))
// Last resort: any plan that owns loop_var_name (loop-local case) // Last resort: any plan that owns loop_var_name (loop-local case)
.or_else(|| plans.iter().find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name)))?; .or_else(|| {
plans
.iter()
.find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name))
})?;
let inputs = plan_to_p2_inputs_with_relay(loop_plan, loop_var_name).ok()?; let inputs = plan_to_p2_inputs_with_relay(loop_plan, loop_var_name).ok()?;
@ -316,18 +318,19 @@ fn create_loop_step_function_break(
let mut body = Vec::new(); let mut body = Vec::new();
let (loop_cond_var, loop_cond_insts) = let (loop_cond_var, loop_cond_insts) = lowerer.extract_value(loop_cond_expr, &mut step_ctx);
lowerer.extract_value(loop_cond_expr, &mut step_ctx);
body.extend(loop_cond_insts); body.extend(loop_cond_insts);
let acc_current = step_ctx let acc_current = step_ctx
.get_var(acc_name) .get_var(acc_name)
.unwrap_or_else(|| panic!("{} must be initialized", acc_name)); .unwrap_or_else(|| panic!("{} must be initialized", acc_name));
let header_exit_flag = step_ctx.alloc_var(); let header_exit_flag = step_ctx.alloc_var();
body.push(JoinInst::Compute(crate::mir::join_ir::MirLikeInst::UnaryOp { body.push(JoinInst::Compute(
dst: header_exit_flag, crate::mir::join_ir::MirLikeInst::UnaryOp {
op: crate::mir::join_ir::UnaryOp::Not, dst: header_exit_flag,
operand: loop_cond_var, op: crate::mir::join_ir::UnaryOp::Not,
})); operand: loop_cond_var,
},
));
body.push(JoinInst::Jump { body.push(JoinInst::Jump {
cont: ctx.k_exit_id.as_cont(), cont: ctx.k_exit_id.as_cont(),
args: vec![acc_current], args: vec![acc_current],
@ -398,9 +401,7 @@ fn create_loop_step_function_break(
Ok(JoinFunction { Ok(JoinFunction {
id: ctx.loop_step_id, id: ctx.loop_step_id,
name: format!("{}_loop_step", func_name), name: format!("{}_loop_step", func_name),
params: (0..param_names.len()) params: (0..param_names.len()).map(|i| ValueId(i as u32)).collect(),
.map(|i| ValueId(i as u32))
.collect(),
body, body,
exit_cont: None, exit_cont: None,
}) })

View File

@ -12,8 +12,8 @@
//! - `create_entry_function()`: entry 関数生成 //! - `create_entry_function()`: entry 関数生成
//! - `create_k_exit_function()`: k_exit 関数生成 //! - `create_k_exit_function()`: k_exit 関数生成
use super::{AstToJoinIrLowerer, JoinModule};
use super::super::stmt_handlers::StatementEffect; use super::super::stmt_handlers::StatementEffect;
use super::{AstToJoinIrLowerer, JoinModule};
use crate::mir::join_ir::JoinIrPhase; use crate::mir::join_ir::JoinIrPhase;
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst}; use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
use crate::mir::ValueId; use crate::mir::ValueId;
@ -127,7 +127,10 @@ pub fn process_local_inits(
if let StatementEffect::VarUpdate { name, value_id } = effect { if let StatementEffect::VarUpdate { name, value_id } = effect {
ctx.register_param(name, value_id); ctx.register_param(name, value_id);
} else if matches!(effect, StatementEffect::SideEffect) { } else if matches!(effect, StatementEffect::SideEffect) {
panic!("Unexpected side-effecting statement before Loop: {}", stmt_type); panic!(
"Unexpected side-effecting statement before Loop: {}",
stmt_type
);
} }
} }
_ => panic!("Unexpected statement type before Loop: {}", stmt_type), _ => panic!("Unexpected statement type before Loop: {}", stmt_type),

View File

@ -205,11 +205,12 @@ fn create_loop_step_function_continue(
// Phase 89-2: StepCalculator Box に抽出済み(再利用性向上) // Phase 89-2: StepCalculator Box に抽出済み(再利用性向上)
let mut i_next_continue = i_next; let mut i_next_continue = i_next;
let continue_then = continue_if_stmt["then"] let continue_then =
.as_array() continue_if_stmt["then"]
.ok_or_else(|| LoweringError::InvalidLoopBody { .as_array()
message: "Continue pattern If must have 'then' array".to_string(), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: "Continue pattern If must have 'then' array".to_string(),
})?;
if let Some(then_i_local) = continue_then if let Some(then_i_local) = continue_then
.iter() .iter()
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i")) .find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i"))
@ -283,10 +284,12 @@ fn create_loop_step_function_continue(
// Phase 88: Continue 分岐側でも acc を更新できるようにする(例: acc += 1 // Phase 88: Continue 分岐側でも acc を更新できるようにする(例: acc += 1
let mut acc_then_val = step_acc; let mut acc_then_val = step_acc;
if let Some(then_acc_local) = continue_then.iter().find(|stmt| { if let Some(then_acc_local) = continue_then
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc") .iter()
}) { .find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
let (acc_then, acc_then_insts) = lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx); {
let (acc_then, acc_then_insts) =
lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
body.extend(acc_then_insts); body.extend(acc_then_insts);
acc_then_val = acc_then; acc_then_val = acc_then;
} }
@ -298,7 +301,7 @@ fn create_loop_step_function_continue(
cond: continue_cond_var, cond: continue_cond_var,
then_val: acc_then_val, then_val: acc_then_val,
else_val: acc_increment, else_val: acc_increment,
type_hint: None, // Phase 63-3 type_hint: None, // Phase 63-3
}); });
// Phase 88: Continue/通常 で次 i を切り替える // Phase 88: Continue/通常 で次 i を切り替える

View File

@ -128,11 +128,12 @@ pub fn lower(
let return_if_stmt = return_if_stmts[0]; let return_if_stmt = return_if_stmts[0];
// 5-2. Return が then にあることを確認Fail-Fast // 5-2. Return が then にあることを確認Fail-Fast
let return_then = return_if_stmt["then"] let return_then =
.as_array() return_if_stmt["then"]
.ok_or_else(|| LoweringError::InvalidLoopBody { .as_array()
message: "Return If must have 'then' array".to_string(), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: "Return If must have 'then' array".to_string(),
})?;
let return_stmt = return_then let return_stmt = return_then
.iter() .iter()
@ -318,11 +319,12 @@ fn create_loop_step_function_continue_return(
// 3. Continue pattern: i のインクリメント処理 // 3. Continue pattern: i のインクリメント処理
// Continue If の then 内に i の更新がある場合、それを使う(例: i = i + 1 // Continue If の then 内に i の更新がある場合、それを使う(例: i = i + 1
let continue_then = continue_if_stmt["then"] let continue_then =
.as_array() continue_if_stmt["then"]
.ok_or_else(|| LoweringError::InvalidLoopBody { .as_array()
message: "Continue If must have 'then' array".to_string(), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: "Continue If must have 'then' array".to_string(),
})?;
let i_update_in_continue = continue_then let i_update_in_continue = continue_then
.iter() .iter()
@ -411,9 +413,7 @@ fn create_loop_step_function_continue_return(
// 5. acc の更新値を計算(通常パス) // 5. acc の更新値を計算(通常パス)
let acc_update_local = loop_body let acc_update_local = loop_body
.iter() .iter()
.find(|stmt| { .find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc")
})
.ok_or_else(|| LoweringError::InvalidLoopBody { .ok_or_else(|| LoweringError::InvalidLoopBody {
message: format!( message: format!(
"ContinueReturn pattern validation failed: missing accumulator update.\n\ "ContinueReturn pattern validation failed: missing accumulator update.\n\
@ -429,9 +429,10 @@ fn create_loop_step_function_continue_return(
// Continue 分岐側でも acc を更新できる場合(例: acc += 1 // Continue 分岐側でも acc を更新できる場合(例: acc += 1
let mut acc_then_val = step_acc; let mut acc_then_val = step_acc;
if let Some(then_acc_local) = continue_then.iter().find(|stmt| { if let Some(then_acc_local) = continue_then
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc") .iter()
}) { .find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
{
let (acc_then, acc_then_insts) = let (acc_then, acc_then_insts) =
lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx); lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
body.extend(acc_then_insts); body.extend(acc_then_insts);

View File

@ -17,9 +17,9 @@ use super::common::{
process_local_inits, process_local_inits,
}; };
use super::{AstToJoinIrLowerer, JoinModule, LoweringError}; use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
use crate::mir::join_ir::ownership::{plan_to_p3_inputs_with_relay, OwnershipAnalyzer};
use crate::mir::join_ir::{BinOpKind, JoinFunction, JoinInst, MirLikeInst}; use crate::mir::join_ir::{BinOpKind, JoinFunction, JoinInst, MirLikeInst};
use crate::mir::ValueId; use crate::mir::ValueId;
use crate::mir::join_ir::ownership::{plan_to_p3_inputs_with_relay, OwnershipAnalyzer};
pub fn try_lower_if_sum_break( pub fn try_lower_if_sum_break(
lowerer: &mut AstToJoinIrLowerer, lowerer: &mut AstToJoinIrLowerer,
@ -65,10 +65,7 @@ pub fn try_lower_if_sum_break(
None => return Ok(None), None => return Ok(None),
}; };
let counter_update_stmt = loop_body let counter_update_stmt = loop_body.last().expect("loop_body len checked").clone();
.last()
.expect("loop_body len checked")
.clone();
let loop_var_name = detect_counter_update_loop_var(loop_body).ok_or_else(|| { let loop_var_name = detect_counter_update_loop_var(loop_body).ok_or_else(|| {
LoweringError::InvalidLoopBody { LoweringError::InvalidLoopBody {
@ -92,14 +89,17 @@ pub fn try_lower_if_sum_break(
let loop_plan = plans let loop_plan = plans
.iter() .iter()
.find(|p| p.relay_writes.iter().any(|r| r.name == loop_var_name)) .find(|p| p.relay_writes.iter().any(|r| r.name == loop_var_name))
.or_else(|| plans.iter().find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name))) .or_else(|| {
plans
.iter()
.find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name))
})
.ok_or_else(|| LoweringError::InvalidLoopBody { .ok_or_else(|| LoweringError::InvalidLoopBody {
message: "if-sum-break: failed to find loop ownership plan".to_string(), message: "if-sum-break: failed to find loop ownership plan".to_string(),
})?; })?;
let inputs = plan_to_p3_inputs_with_relay(loop_plan, &loop_var_name).map_err(|e| { let inputs = plan_to_p3_inputs_with_relay(loop_plan, &loop_var_name)
LoweringError::JsonParseError { message: e } .map_err(|e| LoweringError::JsonParseError { message: e })?;
})?;
// Ensure carriers are exactly the return vars (fail-fast mixing protection). // Ensure carriers are exactly the return vars (fail-fast mixing protection).
let carrier_names: std::collections::BTreeSet<String> = let carrier_names: std::collections::BTreeSet<String> =
@ -119,20 +119,25 @@ pub fn try_lower_if_sum_break(
let mut param_order: Vec<(String, ValueId)> = Vec::new(); let mut param_order: Vec<(String, ValueId)> = Vec::new();
let mut seen = std::collections::BTreeSet::<String>::new(); let mut seen = std::collections::BTreeSet::<String>::new();
let loop_var_id = entry_ctx let loop_var_id =
.get_var(&loop_var_name) entry_ctx
.ok_or_else(|| LoweringError::InvalidLoopBody { .get_var(&loop_var_name)
message: format!("loop var '{}' must be initialized before loop", loop_var_name), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: format!(
"loop var '{}' must be initialized before loop",
loop_var_name
),
})?;
param_order.push((loop_var_name.clone(), loop_var_id)); param_order.push((loop_var_name.clone(), loop_var_id));
seen.insert(loop_var_name.clone()); seen.insert(loop_var_name.clone());
for carrier in &inputs.carriers { for carrier in &inputs.carriers {
let id = entry_ctx let id =
.get_var(&carrier.name) entry_ctx
.ok_or_else(|| LoweringError::InvalidLoopBody { .get_var(&carrier.name)
message: format!("carrier '{}' must be initialized before loop", carrier.name), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: format!("carrier '{}' must be initialized before loop", carrier.name),
})?;
param_order.push((carrier.name.clone(), id)); param_order.push((carrier.name.clone(), id));
seen.insert(carrier.name.clone()); seen.insert(carrier.name.clone());
} }
@ -155,13 +160,8 @@ pub fn try_lower_if_sum_break(
} }
} }
let entry_func = create_entry_function_if_sum_break( let entry_func =
&ctx, create_entry_function_if_sum_break(&ctx, &parsed, init_insts, &mut entry_ctx, &param_order);
&parsed,
init_insts,
&mut entry_ctx,
&param_order,
);
let loop_step_func = create_loop_step_function_if_sum_break( let loop_step_func = create_loop_step_function_if_sum_break(
lowerer, lowerer,
@ -179,12 +179,14 @@ pub fn try_lower_if_sum_break(
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name); let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
Ok(Some(build_join_module(entry_func, loop_step_func, k_exit_func))) Ok(Some(build_join_module(
entry_func,
loop_step_func,
k_exit_func,
)))
} }
fn parse_return_var_plus_var( fn parse_return_var_plus_var(expr: Option<&serde_json::Value>) -> Option<(String, String)> {
expr: Option<&serde_json::Value>,
) -> Option<(String, String)> {
let expr = expr?; let expr = expr?;
if expr["type"].as_str()? != "Binary" { if expr["type"].as_str()? != "Binary" {
return None; return None;
@ -300,11 +302,12 @@ fn create_loop_step_function_if_sum_break(
.ok_or_else(|| LoweringError::InvalidLoopBody { .ok_or_else(|| LoweringError::InvalidLoopBody {
message: format!("{} must exist", sum_name), message: format!("{} must exist", sum_name),
})?; })?;
let count_before = step_ctx let count_before =
.get_var(count_name) step_ctx
.ok_or_else(|| LoweringError::InvalidLoopBody { .get_var(count_name)
message: format!("{} must exist", count_name), .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: format!("{} must exist", count_name),
})?;
let acc_before = step_ctx.alloc_var(); let acc_before = step_ctx.alloc_var();
body.push(JoinInst::Compute(MirLikeInst::BinOp { body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: acc_before, dst: acc_before,
@ -333,19 +336,14 @@ fn create_loop_step_function_if_sum_break(
lowerer.extract_value(update_cond_expr, &mut step_ctx); lowerer.extract_value(update_cond_expr, &mut step_ctx);
body.extend(update_cond_insts); body.extend(update_cond_insts);
let (sum_then_expr, sum_else_expr) = let (sum_then_expr, sum_else_expr) = extract_if_branch_assignment(update_if_stmt, sum_name)?;
extract_if_branch_assignment(update_if_stmt, sum_name)?;
let (count_then_expr, count_else_expr) = let (count_then_expr, count_else_expr) =
extract_if_branch_assignment(update_if_stmt, count_name)?; extract_if_branch_assignment(update_if_stmt, count_name)?;
let (sum_then_val, sum_then_insts) = let (sum_then_val, sum_then_insts) = lowerer.extract_value(&sum_then_expr, &mut step_ctx);
lowerer.extract_value(&sum_then_expr, &mut step_ctx); let (sum_else_val, sum_else_insts) = lowerer.extract_value(&sum_else_expr, &mut step_ctx);
let (sum_else_val, sum_else_insts) = let (count_then_val, count_then_insts) = lowerer.extract_value(&count_then_expr, &mut step_ctx);
lowerer.extract_value(&sum_else_expr, &mut step_ctx); let (count_else_val, count_else_insts) = lowerer.extract_value(&count_else_expr, &mut step_ctx);
let (count_then_val, count_then_insts) =
lowerer.extract_value(&count_then_expr, &mut step_ctx);
let (count_else_val, count_else_insts) =
lowerer.extract_value(&count_else_expr, &mut step_ctx);
body.extend(sum_then_insts); body.extend(sum_then_insts);
body.extend(sum_else_insts); body.extend(sum_else_insts);
body.extend(count_then_insts); body.extend(count_then_insts);
@ -370,11 +368,12 @@ fn create_loop_step_function_if_sum_break(
step_ctx.register_param(count_name.to_string(), count_next); step_ctx.register_param(count_name.to_string(), count_next);
// Counter update (must update loop var) // Counter update (must update loop var)
let counter_expr = counter_update_stmt.get("expr").ok_or_else(|| { let counter_expr =
LoweringError::InvalidLoopBody { counter_update_stmt
message: "counter update must have 'expr'".to_string(), .get("expr")
} .ok_or_else(|| LoweringError::InvalidLoopBody {
})?; message: "counter update must have 'expr'".to_string(),
})?;
let (i_next, i_insts) = lowerer.extract_value(counter_expr, &mut step_ctx); let (i_next, i_insts) = lowerer.extract_value(counter_expr, &mut step_ctx);
body.extend(i_insts); body.extend(i_insts);
step_ctx.register_param(loop_var_name.to_string(), i_next); step_ctx.register_param(loop_var_name.to_string(), i_next);
@ -401,9 +400,7 @@ fn create_loop_step_function_if_sum_break(
Ok(JoinFunction { Ok(JoinFunction {
id: ctx.loop_step_id, id: ctx.loop_step_id,
name: format!("{}_loop_step", func_name), name: format!("{}_loop_step", func_name),
params: (0..param_names.len()) params: (0..param_names.len()).map(|i| ValueId(i as u32)).collect(),
.map(|i| ValueId(i as u32))
.collect(),
body, body,
exit_cont: None, exit_cont: None,
}) })
@ -432,14 +429,20 @@ fn extract_if_branch_assignment(
if_stmt: &serde_json::Value, if_stmt: &serde_json::Value,
target: &str, target: &str,
) -> Result<(serde_json::Value, serde_json::Value), LoweringError> { ) -> Result<(serde_json::Value, serde_json::Value), LoweringError> {
fn find_assignment_expr(branch: &[serde_json::Value], target: &str) -> Result<Option<serde_json::Value>, LoweringError> { fn find_assignment_expr(
branch: &[serde_json::Value],
target: &str,
) -> Result<Option<serde_json::Value>, LoweringError> {
let mut found: Option<serde_json::Value> = None; let mut found: Option<serde_json::Value> = None;
for stmt in branch { for stmt in branch {
match stmt["type"].as_str() { match stmt["type"].as_str() {
Some("Local") => { Some("Local") => {
let name = stmt["name"].as_str().ok_or_else(|| LoweringError::InvalidLoopBody { let name =
message: "Local must have 'name'".to_string(), stmt["name"]
})?; .as_str()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Local must have 'name'".to_string(),
})?;
if name != target { if name != target {
continue; continue;
} }
@ -448,15 +451,20 @@ fn extract_if_branch_assignment(
message: format!("if-sum-break: multiple assignments to '{}'", target), message: format!("if-sum-break: multiple assignments to '{}'", target),
}); });
} }
let expr = stmt.get("expr").ok_or_else(|| LoweringError::InvalidLoopBody { let expr = stmt
message: "Local must have 'expr'".to_string(), .get("expr")
})?; .ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Local must have 'expr'".to_string(),
})?;
found = Some(expr.clone()); found = Some(expr.clone());
} }
Some("Assignment") | Some("Assign") => { Some("Assignment") | Some("Assign") => {
let name = stmt["target"].as_str().ok_or_else(|| LoweringError::InvalidLoopBody { let name =
message: "Assignment must have 'target'".to_string(), stmt["target"]
})?; .as_str()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Assignment must have 'target'".to_string(),
})?;
if name != target { if name != target {
continue; continue;
} }
@ -465,11 +473,12 @@ fn extract_if_branch_assignment(
message: format!("if-sum-break: multiple assignments to '{}'", target), message: format!("if-sum-break: multiple assignments to '{}'", target),
}); });
} }
let expr = stmt.get("expr").or_else(|| stmt.get("value")).ok_or_else(|| { let expr = stmt
LoweringError::InvalidLoopBody { .get("expr")
.or_else(|| stmt.get("value"))
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Assignment must have 'expr' or 'value'".to_string(), message: "Assignment must have 'expr' or 'value'".to_string(),
} })?;
})?;
found = Some(expr.clone()); found = Some(expr.clone());
} }
_ => { _ => {
@ -493,11 +502,9 @@ fn extract_if_branch_assignment(
message: "If must have 'else' array".to_string(), message: "If must have 'else' array".to_string(),
})?; })?;
let then_expr = find_assignment_expr(then_branch, target)?.unwrap_or_else(|| { let then_expr = find_assignment_expr(then_branch, target)?
serde_json::json!({"type":"Var","name":target}) .unwrap_or_else(|| serde_json::json!({"type":"Var","name":target}));
}); let else_expr = find_assignment_expr(else_branch, target)?
let else_expr = find_assignment_expr(else_branch, target)?.unwrap_or_else(|| { .unwrap_or_else(|| serde_json::json!({"type":"Var","name":target}));
serde_json::json!({"type":"Var","name":target})
});
Ok((then_expr, else_expr)) Ok((then_expr, else_expr))
} }

View File

@ -22,9 +22,9 @@ pub mod continue_return_pattern;
pub mod filter; pub mod filter;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod if_sum_break_pattern; pub mod if_sum_break_pattern;
pub mod param_guess;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod parse_string_composite_pattern; pub mod parse_string_composite_pattern;
pub mod param_guess;
pub mod print_tokens; pub mod print_tokens;
pub mod simple; pub mod simple;
pub mod step_calculator; pub mod step_calculator;

View File

@ -103,7 +103,10 @@ mod tests {
"lhs": {"type": "Var", "name": "i"}, "lhs": {"type": "Var", "name": "i"},
"rhs": {"type": "Int", "value": 2} "rhs": {"type": "Int", "value": 2}
}); });
assert_eq!(StepCalculator::extract_linear_increment(&expr, "i"), Some(2)); assert_eq!(
StepCalculator::extract_linear_increment(&expr, "i"),
Some(2)
);
} }
#[test] #[test]
@ -114,7 +117,10 @@ mod tests {
"lhs": {"type": "Int", "value": 3}, "lhs": {"type": "Int", "value": 3},
"rhs": {"type": "Var", "name": "i"} "rhs": {"type": "Var", "name": "i"}
}); });
assert_eq!(StepCalculator::extract_linear_increment(&expr, "i"), Some(3)); assert_eq!(
StepCalculator::extract_linear_increment(&expr, "i"),
Some(3)
);
} }
#[test] #[test]

View File

@ -87,15 +87,13 @@ impl AstToJoinIrLowerer {
.as_str() .as_str()
.expect("Function must have 'name'"); .expect("Function must have 'name'");
let route = resolve_function_route(func_name) let route = resolve_function_route(func_name).unwrap_or_else(|msg| panic!("{msg}"));
.unwrap_or_else(|msg| panic!("{msg}"));
match route { match route {
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json), FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name( FunctionRoute::LoopFrontend => {
self, loop_frontend_binding::lower_loop_by_function_name(self, program_json)
program_json, }
),
FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json), FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json), FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
} }

View File

@ -57,8 +57,8 @@
//! // - carrier_info.carriers["is_digit_pos"].binding_id = Some(BindingId(10)) //! // - carrier_info.carriers["is_digit_pos"].binding_id = Some(BindingId(10))
//! ``` //! ```
use crate::mir::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::MirBuilder;
#[derive(Debug)] #[derive(Debug)]
pub enum CarrierBindingError { pub enum CarrierBindingError {
@ -87,9 +87,7 @@ impl CarrierBindingAssigner {
Some(bid) => bid, Some(bid) => bid,
None => { None => {
let bid = builder.allocate_binding_id(); let bid = builder.allocate_binding_id();
builder builder.binding_map.insert(original_name.to_string(), bid);
.binding_map
.insert(original_name.to_string(), bid);
bid bid
} }
}; };
@ -189,14 +187,16 @@ mod tests {
#[test] #[test]
fn test_assign_promoted_binding_success() { fn test_assign_promoted_binding_success() {
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
// Create a MirBuilder with binding_map // Create a MirBuilder with binding_map
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
// Add original binding to builder.binding_map // Add original binding to builder.binding_map
let original_bid = builder.allocate_binding_id(); let original_bid = builder.allocate_binding_id();
builder.binding_map.insert("digit_pos".to_string(), original_bid); builder
.binding_map
.insert("digit_pos".to_string(), original_bid);
// Create CarrierInfo with promoted carrier // Create CarrierInfo with promoted carrier
let mut carrier_info = CarrierInfo::with_carriers( let mut carrier_info = CarrierInfo::with_carriers(
@ -227,7 +227,11 @@ mod tests {
assert!(promoted_bid.is_some()); assert!(promoted_bid.is_some());
// Verify: carrier.binding_id was set // Verify: carrier.binding_id was set
let carrier = carrier_info.carriers.iter().find(|c| c.name == "is_digit_pos").unwrap(); let carrier = carrier_info
.carriers
.iter()
.find(|c| c.name == "is_digit_pos")
.unwrap();
assert_eq!(carrier.binding_id, promoted_bid); assert_eq!(carrier.binding_id, promoted_bid);
} }
@ -238,16 +242,18 @@ mod tests {
// Create MirBuilder WITHOUT adding "digit_pos" to binding_map // Create MirBuilder WITHOUT adding "digit_pos" to binding_map
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
let mut carrier_info = CarrierInfo::with_carriers("test_loop".to_string(), ValueId(0), vec![ let mut carrier_info = CarrierInfo::with_carriers(
CarrierVar { "test_loop".to_string(),
ValueId(0),
vec![CarrierVar {
name: "is_digit_pos".to_string(), name: "is_digit_pos".to_string(),
host_id: ValueId(0), host_id: ValueId(0),
join_id: None, join_id: None,
role: CarrierRole::ConditionOnly, role: CarrierRole::ConditionOnly,
init: CarrierInit::BoolConst(false), init: CarrierInit::BoolConst(false),
binding_id: None, binding_id: None,
}, }],
]); );
// Should succeed (original/promoted BindingId are allocated on demand). // Should succeed (original/promoted BindingId are allocated on demand).
let result = CarrierBindingAssigner::assign_promoted_binding( let result = CarrierBindingAssigner::assign_promoted_binding(
@ -270,12 +276,14 @@ mod tests {
#[test] #[test]
fn test_assign_promoted_binding_multiple_carriers() { fn test_assign_promoted_binding_multiple_carriers() {
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
let mut builder = MirBuilder::new(); let mut builder = MirBuilder::new();
// Add two original bindings // Add two original bindings
let digit_pos_bid = builder.allocate_binding_id(); let digit_pos_bid = builder.allocate_binding_id();
builder.binding_map.insert("digit_pos".to_string(), digit_pos_bid); builder
.binding_map
.insert("digit_pos".to_string(), digit_pos_bid);
let ch_bid = builder.allocate_binding_id(); let ch_bid = builder.allocate_binding_id();
builder.binding_map.insert("ch".to_string(), ch_bid); builder.binding_map.insert("ch".to_string(), ch_bid);
@ -309,24 +317,36 @@ mod tests {
&mut carrier_info, &mut carrier_info,
"digit_pos", "digit_pos",
"is_digit_pos", "is_digit_pos",
).unwrap(); )
.unwrap();
CarrierBindingAssigner::assign_promoted_binding( CarrierBindingAssigner::assign_promoted_binding(
&mut builder, &mut builder,
&mut carrier_info, &mut carrier_info,
"ch", "ch",
"is_ch_match", "is_ch_match",
).unwrap(); )
.unwrap();
// Verify both mappings exist // Verify both mappings exist
assert!(carrier_info.resolve_promoted_with_binding(digit_pos_bid).is_some()); assert!(carrier_info
.resolve_promoted_with_binding(digit_pos_bid)
.is_some());
assert!(carrier_info.resolve_promoted_with_binding(ch_bid).is_some()); assert!(carrier_info.resolve_promoted_with_binding(ch_bid).is_some());
// Verify both carriers have binding_id set // Verify both carriers have binding_id set
let is_digit_pos = carrier_info.carriers.iter().find(|c| c.name == "is_digit_pos").unwrap(); let is_digit_pos = carrier_info
.carriers
.iter()
.find(|c| c.name == "is_digit_pos")
.unwrap();
assert!(is_digit_pos.binding_id.is_some()); assert!(is_digit_pos.binding_id.is_some());
let is_ch_match = carrier_info.carriers.iter().find(|c| c.name == "is_ch_match").unwrap(); let is_ch_match = carrier_info
.carriers
.iter()
.find(|c| c.name == "is_ch_match")
.unwrap();
assert!(is_ch_match.binding_id.is_some()); assert!(is_ch_match.binding_id.is_some());
} }
} }

View File

@ -649,7 +649,11 @@ impl CarrierInfo {
/// don't have access to binding_map. Actual population happens in a future phase when /// don't have access to binding_map. Actual population happens in a future phase when
/// we integrate BindingId tracking into the promotion pipeline. /// we integrate BindingId tracking into the promotion pipeline.
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn record_promoted_binding(&mut self, original_binding: BindingId, promoted_binding: BindingId) { pub fn record_promoted_binding(
&mut self,
original_binding: BindingId,
promoted_binding: BindingId,
) {
use super::debug_output_box::DebugOutputBox; use super::debug_output_box::DebugOutputBox;
// Phase 86: Use DebugOutputBox for consistent debug output // Phase 86: Use DebugOutputBox for consistent debug output
@ -663,7 +667,8 @@ impl CarrierInfo {
original_binding, promoted_binding original_binding, promoted_binding
); );
} }
self.promoted_bindings.insert(original_binding, promoted_binding); self.promoted_bindings
.insert(original_binding, promoted_binding);
} }
} }

View File

@ -191,11 +191,7 @@ impl ConditionEnv {
/// } /// }
/// ``` /// ```
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn register_carrier_binding( pub fn register_carrier_binding(&mut self, binding_id: BindingId, join_value_id: ValueId) {
&mut self,
binding_id: BindingId,
join_value_id: ValueId,
) {
self.binding_id_map.insert(binding_id, join_value_id); self.binding_id_map.insert(binding_id, join_value_id);
} }
@ -219,11 +215,7 @@ impl ConditionEnv {
/// } /// }
/// ``` /// ```
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn register_loop_var_binding( pub fn register_loop_var_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
&mut self,
binding_id: BindingId,
value_id: ValueId,
) {
self.binding_id_map.insert(binding_id, value_id); self.binding_id_map.insert(binding_id, value_id);
} }
@ -250,11 +242,7 @@ impl ConditionEnv {
/// } /// }
/// ``` /// ```
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn register_condition_binding( pub fn register_condition_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
&mut self,
binding_id: BindingId,
value_id: ValueId,
) {
self.binding_id_map.insert(binding_id, value_id); self.binding_id_map.insert(binding_id, value_id);
} }
@ -315,7 +303,10 @@ impl ConditionEnv {
if let Some(&value_id) = self.binding_id_map.get(&bid) { if let Some(&value_id) = self.binding_id_map.get(&bid) {
debug.log( debug.log(
"hit", "hit",
&format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name), &format!(
"BindingId({}) -> ValueId({}) for '{}'",
bid.0, value_id.0, name
),
); );
return Some(value_id); return Some(value_id);
} else { } else {
@ -330,7 +321,10 @@ impl ConditionEnv {
} else { } else {
// Legacy: no BindingId, use name lookup // Legacy: no BindingId, use name lookup
let result = self.get(name); let result = self.get(name);
debug.log("legacy", &format!("No BindingId, name '{}' -> {:?}", name, result)); debug.log(
"legacy",
&format!("No BindingId, name '{}' -> {:?}", name, result),
);
return result; return result;
} }
} }

View File

@ -129,8 +129,7 @@ fn lower_comparison(
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv, env: &ConditionEnv,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
// Lower left and right sides // Lower left and right sides
let lhs = lower_value_expression(left, alloc_value, env, instructions)?; let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, instructions)?; let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
@ -164,8 +163,7 @@ fn lower_logical_and(
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv, env: &ConditionEnv,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
// Logical AND: evaluate both sides and combine // Logical AND: evaluate both sides and combine
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?; let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?; let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
@ -189,8 +187,7 @@ fn lower_logical_or(
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv, env: &ConditionEnv,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
// Logical OR: evaluate both sides and combine // Logical OR: evaluate both sides and combine
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?; let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?; let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
@ -213,8 +210,7 @@ fn lower_not_operator(
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv, env: &ConditionEnv,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
let operand_val = lower_condition_recursive(operand, alloc_value, env, instructions)?; let operand_val = lower_condition_recursive(operand, alloc_value, env, instructions)?;
let dst = alloc_value(); let dst = alloc_value();
@ -233,8 +229,7 @@ fn lower_literal(
value: &LiteralValue, value: &LiteralValue,
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
let dst = alloc_value(); let dst = alloc_value();
let const_value = match value { let const_value = match value {
LiteralValue::Integer(n) => ConstValue::Integer(*n), LiteralValue::Integer(n) => ConstValue::Integer(*n),
@ -322,8 +317,7 @@ fn lower_arithmetic_binop(
alloc_value: &mut dyn FnMut() -> ValueId, alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv, env: &ConditionEnv,
instructions: &mut Vec<JoinInst>, instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> ) -> Result<ValueId, String> {
{
let lhs = lower_value_expression(left, alloc_value, env, instructions)?; let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, instructions)?; let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
let dst = alloc_value(); let dst = alloc_value();

View File

@ -206,7 +206,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -262,7 +262,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };

View File

@ -49,16 +49,21 @@ fn is_if_sum_value_expr(expr: &ASTNode) -> bool {
match expr { match expr {
ASTNode::Variable { .. } | ASTNode::Literal { .. } => true, ASTNode::Variable { .. } | ASTNode::Literal { .. } => true,
ASTNode::BinaryOp { ASTNode::BinaryOp {
operator, left, right, ..
} => matches!(
operator, operator,
BinaryOperator::Add left,
| BinaryOperator::Subtract right,
| BinaryOperator::Multiply ..
| BinaryOperator::Divide } => {
| BinaryOperator::Modulo matches!(
) && is_if_sum_value_expr(left.as_ref()) operator,
&& is_if_sum_value_expr(right.as_ref()), BinaryOperator::Add
| BinaryOperator::Subtract
| BinaryOperator::Multiply
| BinaryOperator::Divide
| BinaryOperator::Modulo
) && is_if_sum_value_expr(left.as_ref())
&& is_if_sum_value_expr(right.as_ref())
}
_ => false, _ => false,
} }
} }

View File

@ -192,7 +192,11 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
// 2. Build ConditionEnv from ScopeManager // 2. Build ConditionEnv from ScopeManager
// Phase 79-1: Use BindingId-aware version when available // Phase 79-1: Use BindingId-aware version when available
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(self.scope, ast, self.builder)?; let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(
self.scope,
ast,
self.builder,
)?;
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
let condition_env = scope_resolution::build_condition_env_from_scope(self.scope, ast)?; let condition_env = scope_resolution::build_condition_env_from_scope(self.scope, ast)?;
@ -279,13 +283,17 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
// Build ConditionEnv from the provided scope (the caller controls the scope + allocator). // Build ConditionEnv from the provided scope (the caller controls the scope + allocator).
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
let condition_env = let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(
scope_resolution::build_condition_env_from_scope_with_binding(context.scope, condition, self.builder) context.scope,
.map_err(|e| e.to_string())?; condition,
self.builder,
)
.map_err(|e| e.to_string())?;
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
let condition_env = scope_resolution::build_condition_env_from_scope(context.scope, condition) let condition_env =
.map_err(|e| e.to_string())?; scope_resolution::build_condition_env_from_scope(context.scope, condition)
.map_err(|e| e.to_string())?;
// Delegate to the well-tested lowerer, but use the caller-provided allocator (SSOT). // Delegate to the well-tested lowerer, but use the caller-provided allocator (SSOT).
let (result_value, instructions) = let (result_value, instructions) =
@ -347,7 +355,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -393,7 +401,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -439,7 +447,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -521,7 +529,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info); let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
@ -550,7 +558,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info); let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
@ -701,7 +709,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };

View File

@ -1,8 +1,8 @@
use super::{ExprLoweringError, ScopeManager}; use super::{ExprLoweringError, ScopeManager};
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
/// Phase 79-1: Build ConditionEnv with BindingId support (dev-only) /// Phase 79-1: Build ConditionEnv with BindingId support (dev-only)
/// ///

View File

@ -116,9 +116,9 @@ pub struct LoopExitBinding {
pub struct JoinInlineBoundary { pub struct JoinInlineBoundary {
/// JoinIR-local ValueIds that act as "input slots" /// JoinIR-local ValueIds that act as "input slots"
/// ///
/// These are the ValueIds used **inside** the JoinIR fragment to refer /// These are the ValueIds used **inside** the JoinIR fragment to refer
/// to values that come from the host. They should be in the JoinValueSpace /// to values that come from the host. They should be in the JoinValueSpace
/// Param region (100-999). (They are typically allocated sequentially.) /// Param region (100-999). (They are typically allocated sequentially.)
/// ///
/// Example: For a loop variable `i`, JoinIR uses ValueId(100) as the parameter. /// Example: For a loop variable `i`, JoinIR uses ValueId(100) as the parameter.
pub join_inputs: Vec<ValueId>, pub join_inputs: Vec<ValueId>,

View File

@ -70,4 +70,3 @@ impl LoopToJoinLowerer {
) )
} }
} }

View File

@ -108,9 +108,10 @@ impl LoopToJoinLowerer {
} }
if !generic_case_a_enabled() { if !generic_case_a_enabled() {
if !func_name if !func_name.map_or(
.map_or(false, super::super::loop_scope_shape::is_case_a_minimal_target) false,
{ super::super::loop_scope_shape::is_case_a_minimal_target,
) {
if self.debug { if self.debug {
eprintln!( eprintln!(
"[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}", "[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}",
@ -163,4 +164,3 @@ mod tests {
assert!(!lowerer.debug || lowerer.debug); assert!(!lowerer.debug || lowerer.debug);
} }
} }

View File

@ -12,8 +12,7 @@
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。 //! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。 //! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
mod core;
mod case_a_entrypoints; mod case_a_entrypoints;
mod core;
pub use core::LoopToJoinLowerer; pub use core::LoopToJoinLowerer;

View File

@ -261,9 +261,7 @@ pub(crate) fn lower_loop_with_break_minimal(
"[joinir/pattern2] Phase 201: loop_step params - i_param={:?}, carrier_params={:?}", "[joinir/pattern2] Phase 201: loop_step params - i_param={:?}, carrier_params={:?}",
i_param, carrier_param_ids i_param, carrier_param_ids
); );
if crate::config::env::joinir_dev_enabled() if crate::config::env::joinir_dev_enabled() || crate::config::env::joinir_test_debug_enabled() {
|| crate::config::env::joinir_test_debug_enabled()
{
eprintln!( eprintln!(
"[joinir/pattern2/debug] loop_var='{}' env.get(loop_var)={:?}, carriers={:?}", "[joinir/pattern2/debug] loop_var='{}' env.get(loop_var)={:?}, carriers={:?}",
loop_var_name, loop_var_name,
@ -668,7 +666,9 @@ pub(crate) fn lower_loop_with_break_minimal(
Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block), Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block),
Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block), Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block),
// Phase 47-A: P3 steps not used in P2 lowering (handled in Pattern3 lowerer) // Phase 47-A: P3 steps not used in P2 lowering (handled in Pattern3 lowerer)
Pattern2StepKind::IfCond | Pattern2StepKind::ThenUpdates | Pattern2StepKind::ElseUpdates => { Pattern2StepKind::IfCond
| Pattern2StepKind::ThenUpdates
| Pattern2StepKind::ElseUpdates => {
panic!("Pattern3 step kinds should not appear in Pattern2 lowering"); panic!("Pattern3 step kinds should not appear in Pattern2 lowering");
} }
// Phase 48-A: P4 steps not used in P2 lowering (handled in Pattern4 lowerer) // Phase 48-A: P4 steps not used in P2 lowering (handled in Pattern4 lowerer)

View File

@ -127,8 +127,8 @@ fn test_pattern2_header_condition_via_exprlowerer() {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
let carrier_updates = BTreeMap::new(); let carrier_updates = BTreeMap::new();

View File

@ -126,7 +126,9 @@ pub(crate) fn lower_loop_with_continue_minimal(
carrier_info: &CarrierInfo, carrier_info: &CarrierInfo,
carrier_updates: &BTreeMap<String, UpdateExpr>, // Phase 222.5-D: HashMap → BTreeMap for determinism carrier_updates: &BTreeMap<String, UpdateExpr>, // Phase 222.5-D: HashMap → BTreeMap for determinism
join_value_space: &mut JoinValueSpace, join_value_space: &mut JoinValueSpace,
#[cfg(feature = "normalized_dev")] binding_map: Option<&std::collections::BTreeMap<String, crate::mir::BindingId>>, #[cfg(feature = "normalized_dev")] binding_map: Option<
&std::collections::BTreeMap<String, crate::mir::BindingId>,
>,
) -> Result<(JoinModule, ExitMeta), String> { ) -> Result<(JoinModule, ExitMeta), String> {
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes // Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables // LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables

View File

@ -25,8 +25,6 @@ pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
pub(crate) mod common; // Internal lowering utilities pub(crate) mod common; // Internal lowering utilities
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing) pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
pub mod debug_output_box; // Phase 85: Centralized debug output management
pub mod error_tags; // Phase 86: Centralized error message formatting
pub mod condition_env; // Phase 171-fix: Condition expression environment pub mod condition_env; // Phase 171-fix: Condition expression environment
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based) pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
@ -34,7 +32,9 @@ pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (sim
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored) pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
pub mod debug_output_box; // Phase 85: Centralized debug output management
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos) pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
pub mod error_tags; // Phase 86: Centralized error message formatting
pub(crate) mod exit_args_resolver; // Internal exit argument resolution pub(crate) mod exit_args_resolver; // Internal exit argument resolution
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
pub mod funcscanner_append_defs; pub mod funcscanner_append_defs;
@ -64,12 +64,12 @@ pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
pub mod loop_with_continue_minimal; pub mod loop_with_continue_minimal;
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven) pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
pub(crate) mod step_schedule; // Phase 47-A: Generic step scheduler for P2/P3 (renamed from pattern2_step_schedule)
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱 pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod scope_manager_bindingid_poc; // Phase 73: BindingId-based scope PoC (dev-only) pub mod scope_manager_bindingid_poc;
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum pub(crate) mod step_schedule; // Phase 47-A: Generic step scheduler for P2/P3 (renamed from pattern2_step_schedule) // Phase 73: BindingId-based scope PoC (dev-only)
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions) pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
pub mod min_loop; pub mod min_loop;
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer

View File

@ -274,7 +274,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) { if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) {
debug.log( debug.log(
"direct", "direct",
&format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name), &format!(
"BindingId({}) -> ValueId({}) for '{}'",
bid.0, value_id.0, name
),
); );
return Some(value_id); return Some(value_id);
} }
@ -282,7 +285,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
// Step 2: **NEW (Phase 76)**: Check promoted_bindings map // Step 2: **NEW (Phase 76)**: Check promoted_bindings map
if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) { if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) {
// Promoted BindingId found, lookup in ConditionEnv // Promoted BindingId found, lookup in ConditionEnv
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(promoted_bid), name) { if let Some(value_id) = self
.condition_env
.resolve_var_with_binding(Some(promoted_bid), name)
{
debug.log( debug.log(
"promoted", "promoted",
&format!( &format!(
@ -304,7 +310,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
debug.log( debug.log(
"fallback", "fallback",
&format!("BindingId({}) miss, falling back to name '{}' lookup", bid.0, name), &format!(
"BindingId({}) miss, falling back to name '{}' lookup",
bid.0, name
),
); );
} }
@ -331,7 +340,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -429,7 +438,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };
@ -464,7 +473,7 @@ mod tests {
carriers: vec![], carriers: vec![],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };

View File

@ -29,8 +29,8 @@ pub struct ConditionEnvV2 {
name_to_join: BTreeMap<String, ValueId>, name_to_join: BTreeMap<String, ValueId>,
// Phase 73+: NEW - BindingId-based tracking // Phase 73+: NEW - BindingId-based tracking
binding_to_join: BTreeMap<BindingId, ValueId>, // BindingId → JoinIR ValueId binding_to_join: BTreeMap<BindingId, ValueId>, // BindingId → JoinIR ValueId
name_to_binding: BTreeMap<String, BindingId>, // Name → current BindingId (shadowing) name_to_binding: BTreeMap<String, BindingId>, // Name → current BindingId (shadowing)
} }
impl ConditionEnvV2 { impl ConditionEnvV2 {
@ -93,7 +93,7 @@ pub struct CarrierVarV2 {
pub join_id: Option<ValueId>, pub join_id: Option<ValueId>,
// Phase 73+: NEW - BindingId tracking // Phase 73+: NEW - BindingId tracking
pub host_binding: Option<BindingId>, // HOST function's BindingId pub host_binding: Option<BindingId>, // HOST function's BindingId
} }
/// Phase 73 PoC: CarrierInfo with BindingId-based promotion /// Phase 73 PoC: CarrierInfo with BindingId-based promotion
@ -104,7 +104,7 @@ pub struct CarrierInfoV2 {
pub carriers: Vec<CarrierVarV2>, pub carriers: Vec<CarrierVarV2>,
// Phase 73+: Replace string list with BindingId map // Phase 73+: Replace string list with BindingId map
pub promoted_bindings: BTreeMap<BindingId, BindingId>, // Original → Promoted pub promoted_bindings: BTreeMap<BindingId, BindingId>, // Original → Promoted
} }
impl CarrierInfoV2 { impl CarrierInfoV2 {
@ -225,7 +225,10 @@ mod tests {
let promoted = BindingId(10); let promoted = BindingId(10);
carrier_info.add_promoted_binding(original, promoted); carrier_info.add_promoted_binding(original, promoted);
assert_eq!(carrier_info.resolve_promoted_binding(original), Some(promoted)); assert_eq!(
carrier_info.resolve_promoted_binding(original),
Some(promoted)
);
assert_eq!(carrier_info.resolve_promoted_binding(BindingId(99)), None); assert_eq!(carrier_info.resolve_promoted_binding(BindingId(99)), None);
} }
@ -308,7 +311,7 @@ mod tests {
env.insert_by_binding("new_var".to_string(), binding, ValueId(888)); env.insert_by_binding("new_var".to_string(), binding, ValueId(888));
// Unified lookup should find both // Unified lookup should find both
assert_eq!(env.lookup("legacy_var"), Some(ValueId(999))); // Fallback to name assert_eq!(env.lookup("legacy_var"), Some(ValueId(999))); // Fallback to name
assert_eq!(env.lookup("new_var"), Some(ValueId(888))); // BindingId first assert_eq!(env.lookup("new_var"), Some(ValueId(888))); // BindingId first
} }
} }

View File

@ -193,7 +193,9 @@ pub(crate) fn lower_simple_while_minimal(
// Phase 188-Impl-1-E: Use Print instruction // Phase 188-Impl-1-E: Use Print instruction
loop_step_func loop_step_func
.body .body
.push(JoinInst::Compute(MirLikeInst::Print { value: i_step_param })); .push(JoinInst::Compute(MirLikeInst::Print {
value: i_step_param,
}));
// i_next = i + 1 // i_next = i + 1
// Step 1: const 1 // Step 1: const 1

View File

@ -17,16 +17,16 @@ use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Pattern2StepKind { pub(crate) enum Pattern2StepKind {
// P2 (Pattern2 Break) steps // P2 (Pattern2 Break) steps
HeaderCond, // loop(cond) HeaderCond, // loop(cond)
BodyInit, // local ch = ... BodyInit, // local ch = ...
BreakCheck, // if (cond) break BreakCheck, // if (cond) break
Updates, // sum = sum + 1 Updates, // sum = sum + 1
Tail, // i = i + 1 Tail, // i = i + 1
// Phase 47-A: P3 (Pattern3 If-Sum) steps // Phase 47-A: P3 (Pattern3 If-Sum) steps
IfCond, // if (cond) in body IfCond, // if (cond) in body
ThenUpdates, // carrier updates in then branch ThenUpdates, // carrier updates in then branch
ElseUpdates, // carrier updates in else branch (if any) ElseUpdates, // carrier updates in else branch (if any)
// Phase 48-A: P4 (Pattern4 Continue) steps // Phase 48-A: P4 (Pattern4 Continue) steps
ContinueCheck, // if (cond) continue ContinueCheck, // if (cond) continue
@ -158,11 +158,11 @@ fn log_schedule(ctx: &Pattern2ScheduleContext, schedule: &Pattern2StepSchedule)
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn pattern3_if_sum_schedule() -> Vec<Pattern2StepKind> { pub(crate) fn pattern3_if_sum_schedule() -> Vec<Pattern2StepKind> {
vec![ vec![
Pattern2StepKind::HeaderCond, // loop(i < n) Pattern2StepKind::HeaderCond, // loop(i < n)
Pattern2StepKind::IfCond, // if (i > 0) Pattern2StepKind::IfCond, // if (i > 0)
Pattern2StepKind::ThenUpdates, // sum = sum + i Pattern2StepKind::ThenUpdates, // sum = sum + i
// ElseUpdates omitted for minimal (no else branch) // ElseUpdates omitted for minimal (no else branch)
Pattern2StepKind::Tail, // i = i + 1 Pattern2StepKind::Tail, // i = i + 1
] ]
} }
@ -189,12 +189,7 @@ mod tests {
} else { } else {
CarrierInit::FromHost CarrierInit::FromHost
}; };
CarrierVar::with_role_and_init( CarrierVar::with_role_and_init("c".to_string(), ValueId(1), CarrierRole::LoopState, init)
"c".to_string(),
ValueId(1),
CarrierRole::LoopState,
init,
)
} }
fn carrier_info(carriers: Vec<CarrierVar>) -> CarrierInfo { fn carrier_info(carriers: Vec<CarrierVar>) -> CarrierInfo {
@ -204,7 +199,7 @@ mod tests {
carriers, carriers,
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
} }
} }
@ -249,8 +244,7 @@ mod tests {
#[test] #[test]
fn loop_local_carrier_triggers_body_first() { fn loop_local_carrier_triggers_body_first() {
let ctx = let ctx = Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
let schedule = build_pattern2_schedule(&ctx); let schedule = build_pattern2_schedule(&ctx);
assert_eq!( assert_eq!(
schedule.steps(), schedule.steps(),

View File

@ -15,13 +15,13 @@ use std::collections::HashMap;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use std::panic::{catch_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, AssertUnwindSafe};
#[cfg(feature = "normalized_dev")]
pub mod fixtures;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod dev_env; pub mod dev_env;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod dev_fixtures; pub mod dev_fixtures;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod fixtures;
#[cfg(feature = "normalized_dev")]
pub mod loop_step_inspector; pub mod loop_step_inspector;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub mod shape_guard; pub mod shape_guard;
@ -97,7 +97,10 @@ pub enum JpOp {
BinOp(BinOpKind), BinOp(BinOpKind),
Unary(UnaryOp), Unary(UnaryOp),
Compare(CompareOp), Compare(CompareOp),
BoxCall { box_name: String, method: String }, BoxCall {
box_name: String,
method: String,
},
/// 三項演算子cond ? then : else /// 三項演算子cond ? then : else
Select, Select,
} }
@ -462,13 +465,11 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
cond, cond,
then_val, then_val,
else_val, else_val,
}) => { }) => body.push(JpInst::Let {
body.push(JpInst::Let { dst: *dst,
dst: *dst, op: JpOp::Select,
op: JpOp::Select, args: vec![*cond, *then_val, *else_val],
args: vec![*cond, *then_val, *else_val], }),
})
}
JoinInst::Jump { cont, args, cond } => { JoinInst::Jump { cont, args, cond } => {
if let Some(cond_val) = cond { if let Some(cond_val) = cond {
body.push(JpInst::If { body.push(JpInst::If {
@ -497,7 +498,9 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
args: vec![*cond, *then_val, *else_val], args: vec![*cond, *then_val, *else_val],
}); });
} }
JoinInst::Call { func, args, k_next, .. } => { JoinInst::Call {
func, args, k_next, ..
} => {
if k_next.is_none() { if k_next.is_none() {
body.push(JpInst::TailCallFn { body.push(JpInst::TailCallFn {
target: JpFuncId(func.0), target: JpFuncId(func.0),
@ -620,9 +623,7 @@ pub fn normalize_pattern3_if_sum_json_minimal(
/// Phase 50: selfhost if-sum P3 を Normalized に載せるdev-only /// Phase 50: selfhost if-sum P3 を Normalized に載せるdev-only
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn normalize_selfhost_if_sum_p3( pub fn normalize_selfhost_if_sum_p3(structured: &JoinModule) -> Result<NormalizedModule, String> {
structured: &JoinModule,
) -> Result<NormalizedModule, String> {
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3) normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
} }
@ -769,14 +770,12 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
op: *op, op: *op,
operand: args.get(0).copied().unwrap_or(ValueId(0)), operand: args.get(0).copied().unwrap_or(ValueId(0)),
})), })),
JpOp::Select => func JpOp::Select => func.body.push(JoinInst::Compute(MirLikeInst::Select {
.body dst: *dst,
.push(JoinInst::Compute(MirLikeInst::Select { cond: args.get(0).copied().unwrap_or(ValueId(0)),
dst: *dst, then_val: args.get(1).copied().unwrap_or(ValueId(0)),
cond: args.get(0).copied().unwrap_or(ValueId(0)), else_val: args.get(2).copied().unwrap_or(ValueId(0)),
then_val: args.get(1).copied().unwrap_or(ValueId(0)), })),
else_val: args.get(2).copied().unwrap_or(ValueId(0)),
})),
JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare { JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst: *dst, dst: *dst,
op: *op, op: *op,
@ -832,9 +831,7 @@ fn verify_normalized_pattern2(
max_env_fields: usize, max_env_fields: usize,
) -> Result<(), String> { ) -> Result<(), String> {
if module.phase != JoinIrPhase::Normalized { if module.phase != JoinIrPhase::Normalized {
return Err( return Err("[joinir/normalized-dev] pattern2: phase must be Normalized".to_string());
"[joinir/normalized-dev] pattern2: phase must be Normalized".to_string(),
);
} }
let mut layout_sizes: HashMap<u32, usize> = HashMap::new(); let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
@ -871,10 +868,8 @@ fn verify_normalized_pattern2(
| JpInst::If { env, .. } => { | JpInst::If { env, .. } => {
if let Some(expected) = expected_env_len { if let Some(expected) = expected_env_len {
if env.is_empty() { if env.is_empty() {
return Err( return Err("[joinir/normalized-dev] pattern2: env must not be empty"
"[joinir/normalized-dev] pattern2: env must not be empty" .to_string());
.to_string(),
);
} }
let _ = expected; let _ = expected;
} }
@ -885,9 +880,7 @@ fn verify_normalized_pattern2(
if let Some(last) = func.body.last() { if let Some(last) = func.body.last() {
match last { match last {
JpInst::TailCallFn { .. } JpInst::TailCallFn { .. } | JpInst::TailCallKont { .. } | JpInst::If { .. } => {}
| JpInst::TailCallKont { .. }
| JpInst::If { .. } => {}
_ => { _ => {
return Err(format!( return Err(format!(
"[joinir/normalized-dev] pattern2: function '{}' does not end with tail call/if", "[joinir/normalized-dev] pattern2: function '{}' does not end with tail call/if",
@ -1069,7 +1062,9 @@ pub(crate) fn normalized_dev_roundtrip_structured(
let shapes = shape_guard::supported_shapes(module); let shapes = shape_guard::supported_shapes(module);
if shapes.is_empty() { if shapes.is_empty() {
return Err("[joinir/normalized-dev] module shape is not supported by normalized_dev".into()); return Err(
"[joinir/normalized-dev] module shape is not supported by normalized_dev".into(),
);
} }
let debug = dev_env::normalized_dev_logs_enabled() && crate::config::env::joinir_dev_enabled(); let debug = dev_env::normalized_dev_logs_enabled() && crate::config::env::joinir_dev_enabled();
@ -1092,15 +1087,15 @@ pub(crate) fn normalized_dev_roundtrip_structured(
| NormalizedDevShape::JsonparserSkipWsReal | NormalizedDevShape::JsonparserSkipWsReal
| NormalizedDevShape::JsonparserAtoiMini | NormalizedDevShape::JsonparserAtoiMini
| NormalizedDevShape::JsonparserAtoiReal | NormalizedDevShape::JsonparserAtoiReal
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe( | NormalizedDevShape::JsonparserParseNumberReal => {
|| { catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module); let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
}, }))
)), }
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
let norm = let norm = normalize_selfhost_token_scan_p2(module)
normalize_selfhost_token_scan_p2(module).expect("selfhost P2 normalization failed"); .expect("selfhost P2 normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
@ -1110,8 +1105,8 @@ pub(crate) fn normalized_dev_roundtrip_structured(
})), })),
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard) // Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern3_if_sum_minimal(module) let norm =
.expect("P3 normalization failed"); normalize_pattern3_if_sum_minimal(module).expect("P3 normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
NormalizedDevShape::Pattern3IfSumMulti => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::Pattern3IfSumMulti => catch_unwind(AssertUnwindSafe(|| {
@ -1125,8 +1120,8 @@ pub(crate) fn normalized_dev_roundtrip_structured(
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_selfhost_if_sum_p3(module) let norm =
.expect("selfhost P3 normalization failed"); normalize_selfhost_if_sum_p3(module).expect("selfhost P3 normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
@ -1156,36 +1151,38 @@ pub(crate) fn normalized_dev_roundtrip_structured(
})), })),
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard) // Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern4_continue_minimal(module) let norm =
.expect("P4 normalization failed"); normalize_pattern4_continue_minimal(module).expect("P4 normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => { NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
catch_unwind(AssertUnwindSafe(|| { catch_unwind(AssertUnwindSafe(|| {
let norm = let norm = normalize_jsonparser_parse_array_continue_skip_ws(module)
normalize_jsonparser_parse_array_continue_skip_ws(module) .expect("P4 array normalization failed");
.expect("P4 array normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})) }))
} }
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => { NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
catch_unwind(AssertUnwindSafe(|| { catch_unwind(AssertUnwindSafe(|| {
let norm = let norm = normalize_jsonparser_parse_object_continue_skip_ws(module)
normalize_jsonparser_parse_object_continue_skip_ws(module) .expect("P4 object normalization failed");
.expect("P4 object normalization failed");
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})) }))
} }
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now) // Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
NormalizedDevShape::PatternContinueReturnMinimal => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::PatternContinueReturnMinimal => {
let norm = normalize_pattern2_minimal(module); catch_unwind(AssertUnwindSafe(|| {
normalized_pattern2_to_structured(&norm) let norm = normalize_pattern2_minimal(module);
})), normalized_pattern2_to_structured(&norm)
}))
}
// Phase 90: Parse String Composite pattern (dev-only, delegates to P2 for now) // Phase 90: Parse String Composite pattern (dev-only, delegates to P2 for now)
NormalizedDevShape::ParseStringCompositeMinimal => catch_unwind(AssertUnwindSafe(|| { NormalizedDevShape::ParseStringCompositeMinimal => {
let norm = normalize_pattern2_minimal(module); catch_unwind(AssertUnwindSafe(|| {
normalized_pattern2_to_structured(&norm) let norm = normalize_pattern2_minimal(module);
})), normalized_pattern2_to_structured(&norm)
}))
}
}; };
match attempt { match attempt {

View File

@ -132,8 +132,8 @@ impl NormalizedDevFixture {
use super::super::frontend::ast_lowerer::AstToJoinIrLowerer; use super::super::frontend::ast_lowerer::AstToJoinIrLowerer;
let fixture_json = self.fixture_content(); let fixture_json = self.fixture_content();
let program_json: serde_json::Value = serde_json::from_str(fixture_json) let program_json: serde_json::Value =
.unwrap_or_else(|e| { serde_json::from_str(fixture_json).unwrap_or_else(|e| {
panic!( panic!(
"{} fixture should be valid JSON: {}", "{} fixture should be valid JSON: {}",
self.function_name(), self.function_name(),
@ -165,11 +165,12 @@ mod tests {
#[test] #[test]
fn test_all_fixtures_have_unique_names() { fn test_all_fixtures_have_unique_names() {
use std::collections::HashSet; use std::collections::HashSet;
let names: HashSet<_> = ALL_DEV_FIXTURES let names: HashSet<_> = ALL_DEV_FIXTURES.iter().map(|f| f.function_name()).collect();
.iter() assert_eq!(
.map(|f| f.function_name()) names.len(),
.collect(); ALL_DEV_FIXTURES.len(),
assert_eq!(names.len(), ALL_DEV_FIXTURES.len(), "Fixture names must be unique"); "Fixture names must be unique"
);
} }
#[test] #[test]

View File

@ -24,7 +24,12 @@ fn const_i64(func: &mut JoinFunction, dst: ValueId, value: i64) {
} }
fn compare(func: &mut JoinFunction, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) { fn compare(func: &mut JoinFunction, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) {
func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst, op, lhs, rhs })); func.body.push(JoinInst::Compute(MirLikeInst::Compare {
dst,
op,
lhs,
rhs,
}));
} }
fn unary_not(func: &mut JoinFunction, dst: ValueId, operand: ValueId) { fn unary_not(func: &mut JoinFunction, dst: ValueId, operand: ValueId) {
@ -44,7 +49,13 @@ fn bin_add(func: &mut JoinFunction, dst: ValueId, lhs: ValueId, rhs: ValueId) {
})); }));
} }
fn select(func: &mut JoinFunction, dst: ValueId, cond: ValueId, then_val: ValueId, else_val: ValueId) { fn select(
func: &mut JoinFunction,
dst: ValueId,
cond: ValueId,
then_val: ValueId,
else_val: ValueId,
) {
func.body.push(JoinInst::Compute(MirLikeInst::Select { func.body.push(JoinInst::Compute(MirLikeInst::Select {
dst, dst,
cond, cond,
@ -165,8 +176,8 @@ pub fn build_jsonparser_skip_ws_real_structured_for_normalized_dev() -> JoinModu
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json" "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json"
); );
let program_json: serde_json::Value = let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
serde_json::from_str(FIXTURE).expect("jsonparser skip_ws real fixture should be valid JSON"); .expect("jsonparser skip_ws real fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new(); let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json); let module = lowerer.lower_program_json(&program_json);
@ -189,8 +200,8 @@ pub fn build_jsonparser_parse_number_real_structured_for_normalized_dev() -> Joi
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json" "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json"
); );
let program_json: serde_json::Value = let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
serde_json::from_str(FIXTURE).expect("jsonparser parse_number real fixture should be valid JSON"); .expect("jsonparser parse_number real fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new(); let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json); let module = lowerer.lower_program_json(&program_json);
@ -319,13 +330,7 @@ pub fn build_pattern3_if_sum_multi_min_structured_for_normalized_dev() -> JoinMo
let cond_cmp = alloc(); let cond_cmp = alloc();
let zero_const = alloc(); let zero_const = alloc();
const_i64(&mut loop_step, zero_const, 0); const_i64(&mut loop_step, zero_const, 0);
compare( compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
&mut loop_step,
cond_cmp,
CompareOp::Gt,
i_param,
zero_const,
);
// then: sum = sum + 1, count = count + 1 // then: sum = sum + 1, count = count + 1
let one_const = alloc(); let one_const = alloc();
@ -415,18 +420,17 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
// loop_step params: i, sum // loop_step params: i, sum
let i_param = alloc(); let i_param = alloc();
let sum_param = alloc(); let sum_param = alloc();
let mut loop_step = JoinFunction::new(loop_id, "loop_step".to_string(), vec![i_param, sum_param]); let mut loop_step =
JoinFunction::new(loop_id, "loop_step".to_string(), vec![i_param, sum_param]);
// loop condition: i < 5 // loop condition: i < 5
let limit_const = alloc(); let limit_const = alloc();
let cmp_loop = alloc(); let cmp_loop = alloc();
let exit_cond = alloc(); let exit_cond = alloc();
loop_step loop_step.body.push(JoinInst::Compute(MirLikeInst::Const {
.body dst: limit_const,
.push(JoinInst::Compute(MirLikeInst::Const { value: ConstValue::Integer(5),
dst: limit_const, }));
value: ConstValue::Integer(5),
}));
compare( compare(
&mut loop_step, &mut loop_step,
cmp_loop, cmp_loop,
@ -445,13 +449,7 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
let cond_cmp = alloc(); let cond_cmp = alloc();
let zero_const = alloc(); let zero_const = alloc();
const_i64(&mut loop_step, zero_const, 0); const_i64(&mut loop_step, zero_const, 0);
compare( compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
&mut loop_step,
cond_cmp,
CompareOp::Gt,
i_param,
zero_const,
);
// then: sum = sum + i // then: sum = sum + i
let sum_then = alloc(); let sum_then = alloc();
@ -524,8 +522,8 @@ pub fn build_selfhost_if_sum_p3_ext_structured_for_normalized_dev() -> JoinModul
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json" "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json"
); );
let program_json: serde_json::Value = serde_json::from_str(FIXTURE) let program_json: serde_json::Value =
.expect("selfhost if_sum P3 ext fixture should be valid JSON"); serde_json::from_str(FIXTURE).expect("selfhost if_sum P3 ext fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new(); let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json); let module = lowerer.lower_program_json(&program_json);
@ -548,8 +546,8 @@ pub fn build_selfhost_args_parse_p2_structured_for_normalized_dev() -> JoinModul
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json" "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json"
); );
let program_json: serde_json::Value = serde_json::from_str(FIXTURE) let program_json: serde_json::Value =
.expect("selfhost args_parse P2 fixture should be valid JSON"); serde_json::from_str(FIXTURE).expect("selfhost args_parse P2 fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new(); let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json); let module = lowerer.lower_program_json(&program_json);
@ -572,8 +570,8 @@ pub fn build_selfhost_stmt_count_p3_structured_for_normalized_dev() -> JoinModul
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json" "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json"
); );
let program_json: serde_json::Value = serde_json::from_str(FIXTURE) let program_json: serde_json::Value =
.expect("selfhost stmt_count P3 fixture should be valid JSON"); serde_json::from_str(FIXTURE).expect("selfhost stmt_count P3 fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new(); let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json); let module = lowerer.lower_program_json(&program_json);
@ -604,7 +602,7 @@ pub fn build_jsonparser_atoi_structured_for_normalized_dev() -> JoinModule {
if joinir_dev_enabled() && joinir_test_debug_enabled() { if joinir_dev_enabled() && joinir_test_debug_enabled() {
eprintln!( eprintln!(
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}", "[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
module module
); );
} }
@ -679,18 +677,9 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule {
let loop_condition = bin(BinaryOperator::Less, var("i"), int_lit(3)); let loop_condition = bin(BinaryOperator::Less, var("i"), int_lit(3));
let if_condition = bin(BinaryOperator::Greater, var("i"), int_lit(0)); let if_condition = bin(BinaryOperator::Greater, var("i"), int_lit(0));
let then_update = assignment( let then_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(1)));
var("sum"), let else_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(0)));
bin(BinaryOperator::Add, var("sum"), int_lit(1)), let counter_update = assignment(var("i"), bin(BinaryOperator::Add, var("i"), int_lit(1)));
);
let else_update = assignment(
var("sum"),
bin(BinaryOperator::Add, var("sum"), int_lit(0)),
);
let counter_update = assignment(
var("i"),
bin(BinaryOperator::Add, var("i"), int_lit(1)),
);
let if_stmt = ASTNode::If { let if_stmt = ASTNode::If {
condition: Box::new(if_condition), condition: Box::new(if_condition),
@ -707,9 +696,14 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule {
let i_id = join_value_space.alloc_param(); let i_id = join_value_space.alloc_param();
cond_env.insert("i".to_string(), i_id); cond_env.insert("i".to_string(), i_id);
let (module, _meta) = let (module, _meta) = lower_if_sum_pattern(
lower_if_sum_pattern(&loop_condition, &if_stmt, &body, &cond_env, &mut join_value_space) &loop_condition,
.expect("if-sum lowering should succeed for minimal pattern3"); &if_stmt,
&body,
&cond_env,
&mut join_value_space,
)
.expect("if-sum lowering should succeed for minimal pattern3");
if joinir_dev_enabled() && joinir_test_debug_enabled() { if joinir_dev_enabled() && joinir_test_debug_enabled() {
eprintln!( eprintln!(
@ -743,7 +737,8 @@ pub fn build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_d
/// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパーdev-only /// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパーdev-only
/// ///
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json /// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json
pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule { pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule
{
use super::dev_fixtures::NormalizedDevFixture; use super::dev_fixtures::NormalizedDevFixture;
NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs.load_and_lower() NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs.load_and_lower()
} }
@ -818,15 +813,15 @@ pub mod prelude {
build_jsonparser_skip_ws_real_structured_for_normalized_dev, build_jsonparser_skip_ws_real_structured_for_normalized_dev,
build_jsonparser_skip_ws_structured_for_normalized_dev, build_jsonparser_skip_ws_structured_for_normalized_dev,
build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev, build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev,
build_parse_array_min_structured_for_normalized_dev,
build_parse_object_min_structured_for_normalized_dev,
build_parse_string_composite_min_structured_for_normalized_dev,
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured, build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
build_pattern3_if_sum_min_structured_for_normalized_dev, build_pattern3_if_sum_min_structured_for_normalized_dev,
build_pattern3_if_sum_multi_min_structured_for_normalized_dev, build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
build_pattern3_json_if_sum_min_structured_for_normalized_dev, build_pattern3_json_if_sum_min_structured_for_normalized_dev,
build_pattern4_continue_min_structured_for_normalized_dev, build_pattern4_continue_min_structured_for_normalized_dev,
build_pattern_continue_return_min_structured_for_normalized_dev, build_pattern_continue_return_min_structured_for_normalized_dev,
build_parse_string_composite_min_structured_for_normalized_dev,
build_parse_array_min_structured_for_normalized_dev,
build_parse_object_min_structured_for_normalized_dev,
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev, build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
build_selfhost_if_sum_p3_structured_for_normalized_dev, build_selfhost_if_sum_p3_structured_for_normalized_dev,
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev, build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,

View File

@ -29,12 +29,10 @@ impl LoopStepInspector {
/// ///
/// ループ条件や continue/break 判定で使用される。 /// ループ条件や continue/break 判定で使用される。
pub fn has_compare_instruction(loop_step_func: &JoinFunction) -> bool { pub fn has_compare_instruction(loop_step_func: &JoinFunction) -> bool {
loop_step_func.body.iter().any(|inst| { loop_step_func
matches!( .body
inst, .iter()
JoinInst::Compute(MirLikeInst::Compare { .. }) .any(|inst| matches!(inst, JoinInst::Compute(MirLikeInst::Compare { .. })))
)
})
} }
/// 条件付き Jump の個数をカウント /// 条件付き Jump の個数をカウント
@ -77,10 +75,7 @@ mod tests {
use super::*; use super::*;
use crate::mir::join_ir::{JoinFuncId, VarId}; use crate::mir::join_ir::{JoinFuncId, VarId};
fn make_dummy_loop_step( fn make_dummy_loop_step(body: Vec<JoinInst>, param_count: usize) -> JoinFunction {
body: Vec<JoinInst>,
param_count: usize,
) -> JoinFunction {
let params: Vec<_> = (0..param_count).map(|i| VarId(i as u32)).collect(); let params: Vec<_> = (0..param_count).map(|i| VarId(i as u32)).collect();
JoinFunction { JoinFunction {
id: JoinFuncId(1), id: JoinFuncId(1),
@ -105,7 +100,9 @@ mod tests {
assert!(LoopStepInspector::has_select_instruction(&func_with_select)); assert!(LoopStepInspector::has_select_instruction(&func_with_select));
let func_without_select = make_dummy_loop_step(vec![], 2); let func_without_select = make_dummy_loop_step(vec![], 2);
assert!(!LoopStepInspector::has_select_instruction(&func_without_select)); assert!(!LoopStepInspector::has_select_instruction(
&func_without_select
));
} }
#[test] #[test]
@ -124,7 +121,7 @@ mod tests {
}, },
JoinInst::Jump { JoinInst::Jump {
target: JoinFuncId(2), target: JoinFuncId(2),
cond: None, // 無条件は含まれない cond: None, // 無条件は含まれない
args: vec![], args: vec![],
}, },
], ],
@ -139,7 +136,7 @@ mod tests {
vec![JoinInst::Call { vec![JoinInst::Call {
target: JoinFuncId(1), target: JoinFuncId(1),
args: vec![], args: vec![],
k_next: None, // tail call k_next: None, // tail call
results: vec![], results: vec![],
}], }],
2, 2,
@ -150,7 +147,7 @@ mod tests {
vec![JoinInst::Call { vec![JoinInst::Call {
target: JoinFuncId(1), target: JoinFuncId(1),
args: vec![], args: vec![],
k_next: Some(JoinFuncId(3)), // not tail call k_next: Some(JoinFuncId(3)), // not tail call
results: vec![], results: vec![],
}], }],
2, 2,
@ -160,10 +157,20 @@ mod tests {
#[test] #[test]
fn test_has_reasonable_param_count() { fn test_has_reasonable_param_count() {
assert!(!LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 1))); assert!(!LoopStepInspector::has_reasonable_param_count(
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 2))); &make_dummy_loop_step(vec![], 1)
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 3))); ));
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 4))); assert!(LoopStepInspector::has_reasonable_param_count(
assert!(!LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 5))); &make_dummy_loop_step(vec![], 2)
));
assert!(LoopStepInspector::has_reasonable_param_count(
&make_dummy_loop_step(vec![], 3)
));
assert!(LoopStepInspector::has_reasonable_param_count(
&make_dummy_loop_step(vec![], 4)
));
assert!(!LoopStepInspector::has_reasonable_param_count(
&make_dummy_loop_step(vec![], 5)
));
} }
} }

View File

@ -37,7 +37,6 @@ pub enum ShapeCapabilityKind {
/// Selfhost P3 if-sum family /// Selfhost P3 if-sum family
SelfhostP3IfSum, SelfhostP3IfSum,
// Future: Other P2 patterns // Future: Other P2 patterns
// P2MidAtOfLoop, // P2MidAtOfLoop,
// P2HeavyString, // P2HeavyString,
@ -100,8 +99,14 @@ pub enum NormalizedDevShape {
type Detector = fn(&JoinModule) -> bool; type Detector = fn(&JoinModule) -> bool;
const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[ const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
(NormalizedDevShape::Pattern1Mini, detectors::is_pattern1_mini), (
(NormalizedDevShape::Pattern2Mini, detectors::is_pattern2_mini), NormalizedDevShape::Pattern1Mini,
detectors::is_pattern1_mini,
),
(
NormalizedDevShape::Pattern2Mini,
detectors::is_pattern2_mini,
),
( (
NormalizedDevShape::JsonparserSkipWsMini, NormalizedDevShape::JsonparserSkipWsMini,
detectors::is_jsonparser_skip_ws_mini, detectors::is_jsonparser_skip_ws_mini,
@ -552,18 +557,16 @@ mod detectors {
let has_select = loop_func.body.iter().any(|inst| match inst { let has_select = loop_func.body.iter().any(|inst| match inst {
JoinInst::Select { .. } => true, JoinInst::Select { .. } => true,
JoinInst::Compute(mir_inst) => matches!( JoinInst::Compute(mir_inst) => {
mir_inst, matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
crate::mir::join_ir::MirLikeInst::Select { .. } }
),
_ => false, _ => false,
}); });
let has_boxcall = loop_func.body.iter().any(|inst| match inst { let has_boxcall = loop_func.body.iter().any(|inst| match inst {
JoinInst::Compute(mir_inst) => matches!( JoinInst::Compute(mir_inst) => {
mir_inst, matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
crate::mir::join_ir::MirLikeInst::BoxCall { .. } }
),
_ => false, _ => false,
}); });
@ -601,8 +604,7 @@ mod detectors {
.iter() .iter()
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. })); .any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
let param_set: std::collections::BTreeSet<_> = let param_set: std::collections::BTreeSet<_> = loop_step.params.iter().copied().collect();
loop_step.params.iter().copied().collect();
let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match inst { let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match inst {
JoinInst::Compute(mir_inst) => match mir_inst { JoinInst::Compute(mir_inst) => match mir_inst {
@ -617,10 +619,9 @@ mod detectors {
}); });
let has_boxcall = loop_step.body.iter().any(|inst| match inst { let has_boxcall = loop_step.body.iter().any(|inst| match inst {
JoinInst::Compute(mir_inst) => matches!( JoinInst::Compute(mir_inst) => {
mir_inst, matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
crate::mir::join_ir::MirLikeInst::BoxCall { .. } }
),
_ => false, _ => false,
}); });
@ -667,10 +668,9 @@ mod detectors {
// Phase 220: Select can be either JoinInst::Select or Compute(MirLikeInst::Select) // Phase 220: Select can be either JoinInst::Select or Compute(MirLikeInst::Select)
let has_select = loop_step.body.iter().any(|inst| match inst { let has_select = loop_step.body.iter().any(|inst| match inst {
JoinInst::Select { .. } => true, JoinInst::Select { .. } => true,
JoinInst::Compute(mir_inst) => matches!( JoinInst::Compute(mir_inst) => {
mir_inst, matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
crate::mir::join_ir::MirLikeInst::Select { .. } }
),
_ => false, _ => false,
}); });
@ -896,7 +896,7 @@ mod detectors {
&& has_select && has_select
&& has_tail_call && has_tail_call
&& reasonable_param_count && reasonable_param_count
&& k_exit_jumps_count == 1 // Exactly one loop break (not early return) && k_exit_jumps_count == 1 // Exactly one loop break (not early return)
} }
pub(crate) fn is_jsonparser_parse_array_continue_skip_ws(module: &JoinModule) -> bool { pub(crate) fn is_jsonparser_parse_array_continue_skip_ws(module: &JoinModule) -> bool {
@ -952,7 +952,7 @@ mod detectors {
&& has_select && has_select
&& has_tail_call && has_tail_call
&& reasonable_param_count && reasonable_param_count
&& k_exit_jumps_count >= 2 // At least 2: loop break + early return && k_exit_jumps_count >= 2 // At least 2: loop break + early return
} }
/// Phase 90: Check if module matches Parse String Composite pattern /// Phase 90: Check if module matches Parse String Composite pattern
@ -993,8 +993,12 @@ mod detectors {
// Check if rhs is a const value 2 (indicating i+=2 for escape) // Check if rhs is a const value 2 (indicating i+=2 for escape)
// We need to check if rhs points to a Const instruction with value 2 // We need to check if rhs points to a Const instruction with value 2
loop_step.body.iter().any(|other_inst| match other_inst { loop_step.body.iter().any(|other_inst| match other_inst {
JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { dst, value }) => { JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
dst == rhs && matches!(value, crate::mir::join_ir::ConstValue::Integer(2)) dst,
value,
}) => {
dst == rhs
&& matches!(value, crate::mir::join_ir::ConstValue::Integer(2))
} }
_ => false, _ => false,
}) })
@ -1171,7 +1175,9 @@ mod tests {
shapes shapes
); );
assert!( assert!(
!shapes.iter().any(|s| matches!(s, NormalizedDevShape::Pattern3IfSumMinimal)), !shapes
.iter()
.any(|s| matches!(s, NormalizedDevShape::Pattern3IfSumMinimal)),
"selfhost_if_sum_p3 should not rely on canonical P3 minimal detection: {:?}", "selfhost_if_sum_p3 should not rely on canonical P3 minimal detection: {:?}",
shapes shapes
); );
@ -1286,7 +1292,9 @@ mod tests {
k_next: None, k_next: None,
dst: Some(ValueId(2)), dst: Some(ValueId(2)),
}, },
JoinInst::Ret { value: Some(ValueId(2)) }, JoinInst::Ret {
value: Some(ValueId(2)),
},
], ],
exit_cont: None, exit_cont: None,
}; };
@ -1348,7 +1356,9 @@ mod tests {
k_next: None, k_next: None,
dst: Some(ValueId(40)), dst: Some(ValueId(40)),
}, },
JoinInst::Ret { value: Some(ValueId(40)) }, JoinInst::Ret {
value: Some(ValueId(40)),
},
], ],
exit_cont: None, exit_cont: None,
}; };
@ -1358,7 +1368,9 @@ mod tests {
id: JoinFuncId::new(2), id: JoinFuncId::new(2),
name: "k_exit".to_string(), name: "k_exit".to_string(),
params: vec![ValueId(0)], params: vec![ValueId(0)],
body: vec![JoinInst::Ret { value: Some(ValueId(0)) }], body: vec![JoinInst::Ret {
value: Some(ValueId(0)),
}],
exit_cont: None, exit_cont: None,
}; };

View File

@ -77,7 +77,9 @@ impl OwnershipAnalyzer {
return Err("OwnershipAnalyzer: no FunctionDef found in 'defs'".to_string()); return Err("OwnershipAnalyzer: no FunctionDef found in 'defs'".to_string());
} }
} else { } else {
return Err("OwnershipAnalyzer: expected top-level 'functions' or 'defs' array".to_string()); return Err(
"OwnershipAnalyzer: expected top-level 'functions' or 'defs' array".to_string(),
);
} }
// Convert ScopeInfo to OwnershipPlan // Convert ScopeInfo to OwnershipPlan
@ -87,26 +89,37 @@ impl OwnershipAnalyzer {
fn alloc_scope(&mut self, kind: ScopeKind, parent: Option<ScopeId>) -> ScopeId { fn alloc_scope(&mut self, kind: ScopeKind, parent: Option<ScopeId>) -> ScopeId {
let id = ScopeId(self.next_scope_id); let id = ScopeId(self.next_scope_id);
self.next_scope_id += 1; self.next_scope_id += 1;
self.scopes.insert(id, ScopeInfo { self.scopes.insert(
id, id,
kind, ScopeInfo {
parent, id,
defined: BTreeSet::new(), kind,
reads: BTreeSet::new(), parent,
writes: BTreeSet::new(), defined: BTreeSet::new(),
condition_reads: BTreeSet::new(), reads: BTreeSet::new(),
}); writes: BTreeSet::new(),
condition_reads: BTreeSet::new(),
},
);
id id
} }
fn analyze_function(&mut self, func: &Value, parent: Option<ScopeId>) -> Result<ScopeId, String> { fn analyze_function(
&mut self,
func: &Value,
parent: Option<ScopeId>,
) -> Result<ScopeId, String> {
let scope_id = self.alloc_scope(ScopeKind::Function, parent); let scope_id = self.alloc_scope(ScopeKind::Function, parent);
// Collect function parameters as defined // Collect function parameters as defined
if let Some(params) = func.get("params").and_then(|p| p.as_array()) { if let Some(params) = func.get("params").and_then(|p| p.as_array()) {
for param in params { for param in params {
if let Some(name) = param.as_str() { if let Some(name) = param.as_str() {
self.scopes.get_mut(&scope_id).unwrap().defined.insert(name.to_string()); self.scopes
.get_mut(&scope_id)
.unwrap()
.defined
.insert(name.to_string());
} }
} }
} }
@ -156,7 +169,11 @@ impl OwnershipAnalyzer {
} }
"Assign" | "Assignment" => { "Assign" | "Assignment" => {
if let Some(target) = stmt.get("target").and_then(|t| t.as_str()) { if let Some(target) = stmt.get("target").and_then(|t| t.as_str()) {
self.scopes.get_mut(&current_scope).unwrap().writes.insert(target.to_string()); self.scopes
.get_mut(&current_scope)
.unwrap()
.writes
.insert(target.to_string());
} }
if let Some(value) = stmt.get("value").or_else(|| stmt.get("expr")) { if let Some(value) = stmt.get("value").or_else(|| stmt.get("expr")) {
self.analyze_expression(value, current_scope, false)?; self.analyze_expression(value, current_scope, false)?;
@ -234,7 +251,12 @@ impl OwnershipAnalyzer {
Ok(()) Ok(())
} }
fn analyze_expression(&mut self, expr: &Value, current_scope: ScopeId, is_condition: bool) -> Result<(), String> { fn analyze_expression(
&mut self,
expr: &Value,
current_scope: ScopeId,
is_condition: bool,
) -> Result<(), String> {
let kind = expr let kind = expr
.get("kind") .get("kind")
.or_else(|| expr.get("type")) .or_else(|| expr.get("type"))
@ -372,8 +394,11 @@ impl OwnershipAnalyzer {
// owned_vars: defined in this scope // owned_vars: defined in this scope
for name in &scope.defined { for name in &scope.defined {
let is_written = scope.writes.contains(name); let is_written = scope.writes.contains(name);
let is_condition_only = scope.condition_reads.contains(name) && let is_condition_only = scope.condition_reads.contains(name)
!scope.writes.iter().any(|w| w == name && !scope.condition_reads.contains(w)); && !scope
.writes
.iter()
.any(|w| w == name && !scope.condition_reads.contains(w));
plan.owned_vars.push(ScopeOwnedVar { plan.owned_vars.push(ScopeOwnedVar {
name: name.clone(), name: name.clone(),
@ -530,10 +555,15 @@ mod tests {
// Find the loop plan // Find the loop plan
let loop_plan = plans.iter().find(|p| { let loop_plan = plans.iter().find(|p| {
p.relay_writes.iter().any(|r| r.name == "sum" || r.name == "i") p.relay_writes
.iter()
.any(|r| r.name == "sum" || r.name == "i")
}); });
assert!(loop_plan.is_some(), "Should have a loop plan with relay writes"); assert!(
loop_plan.is_some(),
"Should have a loop plan with relay writes"
);
let loop_plan = loop_plan.unwrap(); let loop_plan = loop_plan.unwrap();
// sum and i are written in loop but owned by function -> relay_writes // sum and i are written in loop but owned by function -> relay_writes
@ -576,15 +606,19 @@ mod tests {
let plans = analyzer.analyze_json(&json).unwrap(); let plans = analyzer.analyze_json(&json).unwrap();
// Find loop plan // Find loop plan
let loop_plan = plans.iter().find(|p| { let loop_plan = plans
p.owned_vars.iter().any(|v| v.name == "count") .iter()
}); .find(|p| p.owned_vars.iter().any(|v| v.name == "count"));
assert!(loop_plan.is_some(), "Loop should own 'count'"); assert!(loop_plan.is_some(), "Loop should own 'count'");
let loop_plan = loop_plan.unwrap(); let loop_plan = loop_plan.unwrap();
// count is owned AND written -> carrier // count is owned AND written -> carrier
let count_var = loop_plan.owned_vars.iter().find(|v| v.name == "count").unwrap(); let count_var = loop_plan
.owned_vars
.iter()
.find(|v| v.name == "count")
.unwrap();
assert!(count_var.is_written, "count should be marked as written"); assert!(count_var.is_written, "count should be marked as written");
// No relay for count (it's owned) // No relay for count (it's owned)
@ -631,9 +665,9 @@ mod tests {
let plans = analyzer.analyze_json(&json).unwrap(); let plans = analyzer.analyze_json(&json).unwrap();
// Find loop plan // Find loop plan
let loop_plan = plans.iter().find(|p| { let loop_plan = plans
p.captures.iter().any(|c| c.name == "limit") .iter()
}); .find(|p| p.captures.iter().any(|c| c.name == "limit"));
assert!(loop_plan.is_some(), "Loop should capture 'limit'"); assert!(loop_plan.is_some(), "Loop should capture 'limit'");
let loop_plan = loop_plan.unwrap(); let loop_plan = loop_plan.unwrap();
@ -642,7 +676,10 @@ mod tests {
assert!(loop_plan.captures.iter().any(|c| c.name == "limit")); assert!(loop_plan.captures.iter().any(|c| c.name == "limit"));
// limit should also be in condition_captures // limit should also be in condition_captures
assert!(loop_plan.condition_captures.iter().any(|c| c.name == "limit")); assert!(loop_plan
.condition_captures
.iter()
.any(|c| c.name == "limit"));
// limit is NOT in relay_writes (not written) // limit is NOT in relay_writes (not written)
assert!(!loop_plan.relay_writes.iter().any(|r| r.name == "limit")); assert!(!loop_plan.relay_writes.iter().any(|r| r.name == "limit"));
@ -687,9 +724,9 @@ mod tests {
let plans = analyzer.analyze_json(&json).unwrap(); let plans = analyzer.analyze_json(&json).unwrap();
// At least one loop should relay total // At least one loop should relay total
let any_relay = plans.iter().any(|p| { let any_relay = plans
p.relay_writes.iter().any(|r| r.name == "total") .iter()
}); .any(|p| p.relay_writes.iter().any(|r| r.name == "total"));
assert!(any_relay, "Some loop should relay 'total' to function"); assert!(any_relay, "Some loop should relay 'total' to function");
} }

View File

@ -26,20 +26,20 @@
//! - Phase 59: plan_to_lowering helper for P3 (if-sum patterns) //! - Phase 59: plan_to_lowering helper for P3 (if-sum patterns)
//! - Phase 71-Pre: plan_validator box (reusable validation) //! - Phase 71-Pre: plan_validator box (reusable validation)
mod types;
mod analyzer; mod analyzer;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
mod plan_to_lowering;
#[cfg(feature = "normalized_dev")]
mod ast_analyzer; mod ast_analyzer;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
mod plan_validator; mod plan_to_lowering;
pub use types::*;
pub use analyzer::*;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub use plan_to_lowering::*; mod plan_validator;
mod types;
pub use analyzer::*;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub use ast_analyzer::*; pub use ast_analyzer::*;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub use plan_to_lowering::*;
#[cfg(feature = "normalized_dev")]
pub use plan_validator::*; pub use plan_validator::*;
pub use types::*;

View File

@ -34,15 +34,15 @@ pub struct P3LoweringInputs {
/// # Errors /// # Errors
/// Returns Err if relay_writes is non-empty (Phase 58 scope limitation). /// Returns Err if relay_writes is non-empty (Phase 58 scope limitation).
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn plan_to_p2_inputs( pub fn plan_to_p2_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P2LoweringInputs, String> {
plan: &OwnershipPlan,
loop_var: &str,
) -> Result<P2LoweringInputs, String> {
// Fail-Fast: relay_writes not supported in Phase 58 // Fail-Fast: relay_writes not supported in Phase 58
if !plan.relay_writes.is_empty() { if !plan.relay_writes.is_empty() {
return Err(format!( return Err(format!(
"Phase 58 limitation: relay_writes not yet supported. Found: {:?}", "Phase 58 limitation: relay_writes not yet supported. Found: {:?}",
plan.relay_writes.iter().map(|r| &r.name).collect::<Vec<_>>() plan.relay_writes
.iter()
.map(|r| &r.name)
.collect::<Vec<_>>()
)); ));
} }
@ -68,7 +68,7 @@ pub fn plan_to_p2_inputs(
carriers.push(CarrierVar { carriers.push(CarrierVar {
name: var.name.clone(), name: var.name.clone(),
role, role,
init: CarrierInit::FromHost, // Default (Phase 228) init: CarrierInit::FromHost, // Default (Phase 228)
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
join_id: None, join_id: None,
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
@ -206,15 +206,15 @@ pub fn plan_to_p2_inputs_with_relay(
/// # Errors /// # Errors
/// Returns Err if relay_writes is non-empty (Phase 59 scope limitation). /// Returns Err if relay_writes is non-empty (Phase 59 scope limitation).
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub fn plan_to_p3_inputs( pub fn plan_to_p3_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P3LoweringInputs, String> {
plan: &OwnershipPlan,
loop_var: &str,
) -> Result<P3LoweringInputs, String> {
// Fail-Fast: relay_writes not supported in Phase 59 // Fail-Fast: relay_writes not supported in Phase 59
if !plan.relay_writes.is_empty() { if !plan.relay_writes.is_empty() {
return Err(format!( return Err(format!(
"Phase 59 limitation: relay_writes not yet supported for P3. Found: {:?}", "Phase 59 limitation: relay_writes not yet supported for P3. Found: {:?}",
plan.relay_writes.iter().map(|r| &r.name).collect::<Vec<_>>() plan.relay_writes
.iter()
.map(|r| &r.name)
.collect::<Vec<_>>()
)); ));
} }
@ -240,7 +240,7 @@ pub fn plan_to_p3_inputs(
carriers.push(CarrierVar { carriers.push(CarrierVar {
name: var.name.clone(), name: var.name.clone(),
role, role,
init: CarrierInit::FromHost, // Default (Phase 228) init: CarrierInit::FromHost, // Default (Phase 228)
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
join_id: None, join_id: None,
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
@ -378,11 +378,12 @@ mod tests {
let mut plan = OwnershipPlan::new(ScopeId(1)); let mut plan = OwnershipPlan::new(ScopeId(1));
plan.relay_writes.push(RelayVar { plan.relay_writes.push(RelayVar {
name: "counter".to_string(), name: "counter".to_string(),
owner_scope: ScopeId(0), // L1 owns owner_scope: ScopeId(0), // L1 owns
relay_path: vec![ScopeId(1), ScopeId(2)], // L3 → L2 → L1 relay_path: vec![ScopeId(1), ScopeId(2)], // L3 → L2 → L1
}); });
let inputs = plan_to_p2_inputs_with_relay(&plan, "i").expect("Phase 66: multihop should be accepted"); let inputs = plan_to_p2_inputs_with_relay(&plan, "i")
.expect("Phase 66: multihop should be accepted");
assert_eq!(inputs.carriers.len(), 1); assert_eq!(inputs.carriers.len(), 1);
assert_eq!(inputs.carriers[0].name, "counter"); assert_eq!(inputs.carriers[0].name, "counter");
assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState); assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState);
@ -433,7 +434,9 @@ mod tests {
let result = plan_to_p2_inputs_with_relay(&plan, "i"); let result = plan_to_p2_inputs_with_relay(&plan, "i");
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("relay cannot be owned by same scope")); assert!(result
.unwrap_err()
.contains("relay cannot be owned by same scope"));
} }
#[test] #[test]
@ -454,7 +457,9 @@ mod tests {
let result = plan_to_p2_inputs_with_relay(&plan, "i"); let result = plan_to_p2_inputs_with_relay(&plan, "i");
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("appears in both owned_vars and relay_writes")); assert!(result
.unwrap_err()
.contains("appears in both owned_vars and relay_writes"));
} }
#[test] #[test]
@ -546,12 +551,11 @@ mod tests {
fn test_p3_five_plus_carriers() { fn test_p3_five_plus_carriers() {
// Selfhost P3 pattern: 5+ carriers // Selfhost P3 pattern: 5+ carriers
let mut plan = OwnershipPlan::new(ScopeId(1)); let mut plan = OwnershipPlan::new(ScopeId(1));
plan.owned_vars plan.owned_vars.push(ScopeOwnedVar {
.push(ScopeOwnedVar { name: "i".to_string(),
name: "i".to_string(), is_written: true,
is_written: true, is_condition_only: false,
is_condition_only: false, });
});
plan.owned_vars.push(ScopeOwnedVar { plan.owned_vars.push(ScopeOwnedVar {
name: "total".to_string(), name: "total".to_string(),
is_written: true, is_written: true,

View File

@ -297,7 +297,7 @@ mod tests {
)], )],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
promoted_bindings: std::collections::BTreeMap::new(), promoted_bindings: std::collections::BTreeMap::new(),
}; };

View File

@ -82,7 +82,9 @@ impl OwnershipPlan {
/// Get condition-only carriers (owned, written, condition-only). /// Get condition-only carriers (owned, written, condition-only).
pub fn condition_only_carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> { pub fn condition_only_carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> {
self.owned_vars.iter().filter(|v| v.is_written && v.is_condition_only) self.owned_vars
.iter()
.filter(|v| v.is_written && v.is_condition_only)
} }
/// Check invariant: no variable appears in multiple categories. /// Check invariant: no variable appears in multiple categories.
@ -134,7 +136,7 @@ mod tests {
}); });
plan.owned_vars.push(ScopeOwnedVar { plan.owned_vars.push(ScopeOwnedVar {
name: "limit".to_string(), name: "limit".to_string(),
is_written: false, // read-only owned is_written: false, // read-only owned
is_condition_only: false, is_condition_only: false,
}); });

View File

@ -59,9 +59,7 @@ pub fn disable_observation() {
/// Analyze PHI dst distribution /// Analyze PHI dst distribution
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub fn analyze_distribution(observations: &[u32]) -> PhiDistributionReport { pub fn analyze_distribution(observations: &[u32]) -> PhiDistributionReport {
use crate::mir::join_ir::lowering::join_value_space::{ use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MIN, PARAM_MIN, PHI_RESERVED_MAX};
PHI_RESERVED_MAX, PARAM_MIN, LOCAL_MIN,
};
let mut in_reserved = 0; let mut in_reserved = 0;
let mut in_param = 0; let mut in_param = 0;

View File

@ -32,11 +32,9 @@ use std::collections::HashMap;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode}; use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir::normalized::{ use crate::mir::join_ir::normalized::{dev_env, normalized_dev_roundtrip_structured, shape_guard};
dev_env, normalized_dev_roundtrip_structured, shape_guard, use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
};
// Phase 27.8: ops box からの再エクスポート // Phase 27.8: ops box からの再エクスポート
pub use crate::mir::join_ir_ops::{JoinIrOpError, JoinValue}; pub use crate::mir::join_ir_ops::{JoinIrOpError, JoinValue};
@ -104,7 +102,9 @@ fn run_joinir_function_normalized_dev(
let shapes = shape_guard::supported_shapes(module); let shapes = shape_guard::supported_shapes(module);
if shapes.is_empty() { if shapes.is_empty() {
if debug { if debug {
eprintln!("[joinir/normalized-dev/runner] shape unsupported; staying on Structured path"); eprintln!(
"[joinir/normalized-dev/runner] shape unsupported; staying on Structured path"
);
} }
return execute_function(vm, module, entry, args_vec); return execute_function(vm, module, entry, args_vec);
} }

View File

@ -7,13 +7,13 @@ use std::collections::BTreeMap;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode}; use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir::JoinIrPhase; use crate::mir::join_ir::normalized::shape_guard::{self, NormalizedDevShape};
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir::normalized::{ use crate::mir::join_ir::normalized::{
normalize_pattern1_minimal, normalize_pattern2_minimal, NormalizedModule, normalize_pattern1_minimal, normalize_pattern2_minimal, NormalizedModule,
}; };
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir::normalized::shape_guard::{self, NormalizedDevShape}; use crate::mir::join_ir::JoinIrPhase;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
use crate::mir::join_ir_vm_bridge::lower_normalized_to_mir_minimal; use crate::mir::join_ir_vm_bridge::lower_normalized_to_mir_minimal;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
@ -124,22 +124,22 @@ fn normalize_for_shape(
.expect("P4 normalization failed") .expect("P4 normalization failed")
})), })),
// Phase 48-B: JsonParser continue skip_ws (array/object) // Phase 48-B: JsonParser continue skip_ws (array/object)
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => catch_unwind(AssertUnwindSafe( NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|| { catch_unwind(AssertUnwindSafe(|| {
crate::mir::join_ir::normalized::normalize_jsonparser_parse_array_continue_skip_ws( crate::mir::join_ir::normalized::normalize_jsonparser_parse_array_continue_skip_ws(
module, module,
) )
.expect("P4 array normalization failed") .expect("P4 array normalization failed")
}, }))
)), }
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => catch_unwind(AssertUnwindSafe( NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|| { catch_unwind(AssertUnwindSafe(|| {
crate::mir::join_ir::normalized::normalize_jsonparser_parse_object_continue_skip_ws( crate::mir::join_ir::normalized::normalize_jsonparser_parse_object_continue_skip_ws(
module, module,
) )
.expect("P4 object normalization failed") .expect("P4 object normalization failed")
}, }))
)), }
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now) // Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
NormalizedDevShape::PatternContinueReturnMinimal => { NormalizedDevShape::PatternContinueReturnMinimal => {
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module))) catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))

View File

@ -72,7 +72,10 @@ impl JoinIrBlockConverter {
continue; continue;
} }
// Debug: show what instruction we're processing // Debug: show what instruction we're processing
log_dbg(format!("[joinir_block] Compute instruction: {:?}", mir_like)); log_dbg(format!(
"[joinir_block] Compute instruction: {:?}",
mir_like
));
let mir_inst = convert_mir_like_inst(mir_like)?; let mir_inst = convert_mir_like_inst(mir_like)?;
self.current_instructions.push(mir_inst); self.current_instructions.push(mir_inst);
} }

View File

@ -55,9 +55,9 @@ pub(crate) use bridge::{bridge_joinir_to_mir, bridge_joinir_to_mir_with_meta};
pub(crate) use convert::convert_joinir_to_mir; pub(crate) use convert::convert_joinir_to_mir;
pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules
pub(crate) use joinir_function_converter::JoinIrFunctionConverter; pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
pub use meta::convert_join_module_to_mir_with_meta;
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
pub(crate) use normalized_bridge::lower_normalized_to_mir_minimal; pub(crate) use normalized_bridge::lower_normalized_to_mir_minimal;
pub use meta::convert_join_module_to_mir_with_meta;
pub use runner::run_joinir_via_vm; pub use runner::run_joinir_via_vm;
/// Phase 27-shortterm S-4 エラー型 /// Phase 27-shortterm S-4 エラー型

View File

@ -19,7 +19,11 @@ fn dev_debug_enabled() -> bool {
pub(super) fn log_dev(category: &str, message: impl AsRef<str>, important: bool) { pub(super) fn log_dev(category: &str, message: impl AsRef<str>, important: bool) {
let debug = dev_debug_enabled(); let debug = dev_debug_enabled();
if debug || important { if debug || important {
eprintln!("[joinir/normalized-dev/bridge/{}] {}", category, message.as_ref()); eprintln!(
"[joinir/normalized-dev/bridge/{}] {}",
category,
message.as_ref()
);
} }
} }

View File

@ -1,8 +1,8 @@
#![cfg(feature = "normalized_dev")] #![cfg(feature = "normalized_dev")]
use super::super::convert_mir_like_inst;
use super::super::join_func_name; use super::super::join_func_name;
use super::super::JoinIrVmBridgeError; use super::super::JoinIrVmBridgeError;
use super::super::convert_mir_like_inst;
use crate::ast::Span; use crate::ast::Span;
use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule}; use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule};
use crate::mir::join_ir::{JoinFuncId, JoinIrPhase, MirLikeInst}; use crate::mir::join_ir::{JoinFuncId, JoinIrPhase, MirLikeInst};
@ -83,7 +83,9 @@ fn lower_normalized_function_direct(
let remap = |id: ValueId, map: &mut BTreeMap<ValueId, ValueId>| remap_value(id, map); let remap = |id: ValueId, map: &mut BTreeMap<ValueId, ValueId>| remap_value(id, map);
let remap_vec = |ids: &[ValueId], map: &mut BTreeMap<ValueId, ValueId>| { let remap_vec = |ids: &[ValueId], map: &mut BTreeMap<ValueId, ValueId>| {
ids.iter().map(|id| remap_value(*id, map)).collect::<Vec<_>>() ids.iter()
.map(|id| remap_value(*id, map))
.collect::<Vec<_>>()
}; };
let remapped_params = remap_vec(&params, &mut value_map); let remapped_params = remap_vec(&params, &mut value_map);
@ -188,7 +190,8 @@ fn lower_normalized_function_direct(
} }
JpInst::TailCallFn { target, env } => { JpInst::TailCallFn { target, env } => {
let env_remapped = remap_vec(env, &mut value_map); let env_remapped = remap_vec(env, &mut value_map);
let (instructions, terminator) = build_tail_call(&mut mir_func, target, &env_remapped); let (instructions, terminator) =
build_tail_call(&mut mir_func, target, &env_remapped);
finalize_block( finalize_block(
&mut mir_func, &mut mir_func,
current_block_id, current_block_id,
@ -371,7 +374,12 @@ fn build_tail_call(
effects: EffectMask::PURE, effects: EffectMask::PURE,
}); });
(instructions, MirInstruction::Return { value: Some(result_id) }) (
instructions,
MirInstruction::Return {
value: Some(result_id),
},
)
} }
fn build_exit_or_tail_branch( fn build_exit_or_tail_branch(

View File

@ -6,7 +6,7 @@
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::loop_pattern_detection::LoopPatternKind; use crate::mir::loop_pattern_detection::LoopPatternKind;
use super::capability_guard::{capability_tags, RoutingDecision}; use super::capability_guard::{CapabilityTag, RoutingDecision};
use super::pattern_recognizer::try_extract_skip_whitespace_pattern; use super::pattern_recognizer::try_extract_skip_whitespace_pattern;
use super::skeleton_types::{ use super::skeleton_types::{
CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind, CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
@ -63,9 +63,9 @@ pub fn canonicalize_loop_expr(
// Step 2: Body statements (if any) // Step 2: Body statements (if any)
if !body_stmts.is_empty() { if !body_stmts.is_empty() {
skeleton.steps.push(SkeletonStep::Body { skeleton
stmts: body_stmts, .steps
}); .push(SkeletonStep::Body { stmts: body_stmts });
} }
// Step 3: Update step // Step 3: Update step
@ -100,7 +100,7 @@ pub fn canonicalize_loop_expr(
Ok(( Ok((
LoopSkeleton::new(span), LoopSkeleton::new(span),
RoutingDecision::fail_fast( RoutingDecision::fail_fast(
vec![capability_tags::CAP_MISSING_CONST_STEP], vec![CapabilityTag::ConstStep],
"Phase 3: Loop does not match skip_whitespace pattern".to_string(), "Phase 3: Loop does not match skip_whitespace pattern".to_string(),
), ),
)) ))
@ -192,14 +192,8 @@ mod tests {
// Verify skeleton structure // Verify skeleton structure
assert_eq!(skeleton.steps.len(), 2); // HeaderCond + Update assert_eq!(skeleton.steps.len(), 2); // HeaderCond + Update
assert!(matches!( assert!(matches!(skeleton.steps[0], SkeletonStep::HeaderCond { .. }));
skeleton.steps[0], assert!(matches!(skeleton.steps[1], SkeletonStep::Update { .. }));
SkeletonStep::HeaderCond { .. }
));
assert!(matches!(
skeleton.steps[1],
SkeletonStep::Update { .. }
));
// Verify carrier // Verify carrier
assert_eq!(skeleton.carriers.len(), 1); assert_eq!(skeleton.carriers.len(), 1);
@ -300,15 +294,9 @@ mod tests {
// Verify skeleton has Body step // Verify skeleton has Body step
assert_eq!(skeleton.steps.len(), 3); // HeaderCond + Body + Update assert_eq!(skeleton.steps.len(), 3); // HeaderCond + Body + Update
assert!(matches!( assert!(matches!(skeleton.steps[0], SkeletonStep::HeaderCond { .. }));
skeleton.steps[0],
SkeletonStep::HeaderCond { .. }
));
assert!(matches!(skeleton.steps[1], SkeletonStep::Body { .. })); assert!(matches!(skeleton.steps[1], SkeletonStep::Body { .. }));
assert!(matches!( assert!(matches!(skeleton.steps[2], SkeletonStep::Update { .. }));
skeleton.steps[2],
SkeletonStep::Update { .. }
));
// Verify body contains 1 statement // Verify body contains 1 statement
match &skeleton.steps[1] { match &skeleton.steps[1] {
@ -360,9 +348,7 @@ mod tests {
let (_, decision) = result.unwrap(); let (_, decision) = result.unwrap();
assert!(decision.is_fail_fast()); assert!(decision.is_fail_fast());
assert!(decision assert!(decision.notes[0].contains("does not match skip_whitespace pattern"));
.notes[0]
.contains("does not match skip_whitespace pattern"));
} }
#[test] #[test]

View File

@ -18,8 +18,8 @@ pub struct RoutingDecision {
/// Selected pattern (None = Fail-Fast) /// Selected pattern (None = Fail-Fast)
pub chosen: Option<LoopPatternKind>, pub chosen: Option<LoopPatternKind>,
/// Missing capabilities that prevented other patterns /// Missing capabilities that prevented other patterns (type-safe!)
pub missing_caps: Vec<&'static str>, pub missing_caps: Vec<CapabilityTag>,
/// Selection reasoning (for debugging) /// Selection reasoning (for debugging)
pub notes: Vec<String>, pub notes: Vec<String>,
@ -40,12 +40,17 @@ impl RoutingDecision {
} }
/// Create a failed routing decision (Fail-Fast) /// Create a failed routing decision (Fail-Fast)
pub fn fail_fast(missing_caps: Vec<&'static str>, reason: String) -> Self { pub fn fail_fast(missing_caps: Vec<CapabilityTag>, reason: String) -> Self {
let error_tags = missing_caps
.iter()
.map(|cap| cap.to_tag().to_string())
.collect();
Self { Self {
chosen: None, chosen: None,
missing_caps, missing_caps,
notes: vec![reason.clone()], notes: vec![reason.clone()],
error_tags: vec![format!("[loop_canonicalizer/fail_fast] {}", reason)], error_tags,
} }
} }
@ -125,38 +130,3 @@ impl CapabilityTag {
} }
} }
} }
// ============================================================================
// Legacy Capability Tags (Deprecated - will be removed in Phase 139-P3-B)
// ============================================================================
/// Legacy capability tags - Standardized vocabulary for Fail-Fast reasons
///
/// ⚠️ DEPRECATED: Use CapabilityTag enum instead for type safety.
/// This module will be removed in Phase 139-P3-B.
#[deprecated(since = "Phase 139-P3-A", note = "Use CapabilityTag enum instead")]
pub mod capability_tags {
/// Requires: Carrier update is constant step (`i = i + const`)
pub const CAP_MISSING_CONST_STEP: &str = "CAP_MISSING_CONST_STEP";
/// Requires: Single break point only
pub const CAP_MISSING_SINGLE_BREAK: &str = "CAP_MISSING_SINGLE_BREAK";
/// Requires: Single continue point only
pub const CAP_MISSING_SINGLE_CONTINUE: &str = "CAP_MISSING_SINGLE_CONTINUE";
/// Requires: Loop header condition has no side effects
pub const CAP_MISSING_PURE_HEADER: &str = "CAP_MISSING_PURE_HEADER";
/// Requires: Condition variable defined in outer local scope
pub const CAP_MISSING_OUTER_LOCAL_COND: &str = "CAP_MISSING_OUTER_LOCAL_COND";
/// Requires: All exit bindings are complete (no missing values)
pub const CAP_MISSING_EXIT_BINDINGS: &str = "CAP_MISSING_EXIT_BINDINGS";
/// Requires: LoopBodyLocal can be promoted to carrier
pub const CAP_MISSING_CARRIER_PROMOTION: &str = "CAP_MISSING_CARRIER_PROMOTION";
/// Requires: Break value types are consistent across all break points
pub const CAP_MISSING_BREAK_VALUE_TYPE: &str = "CAP_MISSING_BREAK_VALUE_TYPE";
}

View File

@ -34,10 +34,10 @@
// Module Declarations // Module Declarations
// ============================================================================ // ============================================================================
mod skeleton_types; mod canonicalizer;
mod capability_guard; mod capability_guard;
mod pattern_recognizer; mod pattern_recognizer;
mod canonicalizer; mod skeleton_types;
// ============================================================================ // ============================================================================
// Public Re-exports // Public Re-exports
@ -45,20 +45,11 @@ mod canonicalizer;
// Skeleton Types // Skeleton Types
pub use skeleton_types::{ pub use skeleton_types::{
CarrierRole, CapturedSlot, CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
CarrierSlot,
CapturedSlot,
ExitContract,
LoopSkeleton,
SkeletonStep,
UpdateKind,
}; };
// Capability Guard // Capability Guard
pub use capability_guard::{ pub use capability_guard::{CapabilityTag, RoutingDecision};
capability_tags,
RoutingDecision,
};
// Canonicalization Entry Point // Canonicalization Entry Point
pub use canonicalizer::canonicalize_loop_expr; pub use canonicalizer::canonicalize_loop_expr;
@ -97,13 +88,12 @@ mod tests {
assert!(success.is_success()); assert!(success.is_success());
assert!(!success.is_fail_fast()); assert!(!success.is_fail_fast());
let fail = RoutingDecision::fail_fast( let fail =
vec![capability_tags::CAP_MISSING_CONST_STEP], RoutingDecision::fail_fast(vec![CapabilityTag::ConstStep], "Test failure".to_string());
"Test failure".to_string(),
);
assert!(!fail.is_success()); assert!(!fail.is_success());
assert!(fail.is_fail_fast()); assert!(fail.is_fail_fast());
assert_eq!(fail.missing_caps.len(), 1); assert_eq!(fail.missing_caps.len(), 1);
assert_eq!(fail.missing_caps[0], CapabilityTag::ConstStep);
} }
#[test] #[test]

View File

@ -39,20 +39,13 @@ pub struct LoopSkeleton {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SkeletonStep { pub enum SkeletonStep {
/// Loop continuation condition (the `cond` in `loop(cond)`) /// Loop continuation condition (the `cond` in `loop(cond)`)
HeaderCond { HeaderCond { expr: Box<ASTNode> },
expr: Box<ASTNode>,
},
/// Early exit check (`if cond { break }`) /// Early exit check (`if cond { break }`)
BreakCheck { BreakCheck { cond: Box<ASTNode>, has_value: bool },
cond: Box<ASTNode>,
has_value: bool,
},
/// Skip check (`if cond { continue }`) /// Skip check (`if cond { continue }`)
ContinueCheck { ContinueCheck { cond: Box<ASTNode> },
cond: Box<ASTNode>,
},
/// Carrier update (`i = i + 1`, etc.) /// Carrier update (`i = i + 1`, etc.)
Update { Update {
@ -61,9 +54,7 @@ pub enum SkeletonStep {
}, },
/// Loop body (all other statements) /// Loop body (all other statements)
Body { Body { stmts: Vec<ASTNode> },
stmts: Vec<ASTNode>,
},
} }
/// Update kind - How a carrier variable is updated /// Update kind - How a carrier variable is updated
@ -72,9 +63,7 @@ pub enum SkeletonStep {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum UpdateKind { pub enum UpdateKind {
/// Constant step (`i = i + const`) /// Constant step (`i = i + const`)
ConstStep { ConstStep { delta: i64 },
delta: i64,
},
/// Conditional update (`if cond { x = a } else { x = b }`) /// Conditional update (`if cond { x = a } else { x = b }`)
Conditional { Conditional {

View File

@ -441,11 +441,7 @@ mod tests {
let result = DigitPosDetector::detect(&condition, &loop_body, "p"); let result = DigitPosDetector::detect(&condition, &loop_body, "p");
assert!( assert!(result.is_some(), "Expected detection for operator {:?}", op);
result.is_some(),
"Expected detection for operator {:?}",
op
);
} }
} }

View File

@ -56,9 +56,7 @@ pub(super) fn body_matches(a: &[ASTNode], b: &[ASTNode]) -> bool {
/// Collect local variable declarations from statements /// Collect local variable declarations from statements
/// ///
/// Returns Vec<(name, init_expr)> for each variable declared with `local`. /// Returns Vec<(name, init_expr)> for each variable declared with `local`.
pub(super) fn collect_local_declarations( pub(super) fn collect_local_declarations(stmts: &[ASTNode]) -> Vec<(String, Option<Box<ASTNode>>)> {
stmts: &[ASTNode],
) -> Vec<(String, Option<Box<ASTNode>>)> {
let mut locals = Vec::new(); let mut locals = Vec::new();
for stmt in stmts { for stmt in stmts {

View File

@ -80,8 +80,9 @@ impl TrimPatternInfo {
/// - JoinInlineBoundary will handle the boundary mapping /// - JoinInlineBoundary will handle the boundary mapping
pub fn to_carrier_info( pub fn to_carrier_info(
&self, &self,
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")] binding_map: Option<
binding_map: Option<&std::collections::BTreeMap<String, crate::mir::BindingId>>, &std::collections::BTreeMap<String, crate::mir::BindingId>,
>,
) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo { ) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
use super::trim_loop_helper::TrimLoopHelper; use super::trim_loop_helper::TrimLoopHelper;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
@ -110,11 +111,9 @@ impl TrimPatternInfo {
#[cfg(not(feature = "normalized_dev"))] #[cfg(not(feature = "normalized_dev"))]
let recorder = PromotedBindingRecorder::new(); let recorder = PromotedBindingRecorder::new();
if let Err(e) = recorder.record_promotion( if let Err(e) =
&mut carrier_info, recorder.record_promotion(&mut carrier_info, &self.var_name, &self.carrier_name)
&self.var_name, {
&self.carrier_name,
) {
log_trim_promotion_error(&e); log_trim_promotion_error(&e);
} }
@ -195,8 +194,7 @@ impl LoopBodyCarrierPromoter {
// 3. 各 LoopBodyLocal に対して TrimDetector で検出を試行 // 3. 各 LoopBodyLocal に対して TrimDetector で検出を試行
for var_name in &body_locals { for var_name in &body_locals {
// Phase 79: Use TrimDetector for pure detection logic // Phase 79: Use TrimDetector for pure detection logic
if let Some(detection) = TrimDetector::detect(break_cond, request.loop_body, var_name) if let Some(detection) = TrimDetector::detect(break_cond, request.loop_body, var_name) {
{
if is_joinir_debug() || std::env::var("JOINIR_TEST_DEBUG").is_ok() { if is_joinir_debug() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!( eprintln!(
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}", "[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",

View File

@ -148,11 +148,8 @@ impl DigitPosPromoter {
} }
// Step 3: Use DigitPosDetector for pure detection // Step 3: Use DigitPosDetector for pure detection
let detection = DigitPosDetector::detect( let detection =
condition.unwrap(), DigitPosDetector::detect(condition.unwrap(), req.loop_body, req.loop_param_name);
req.loop_body,
req.loop_param_name,
);
if detection.is_none() { if detection.is_none() {
return DigitPosPromotionResult::CannotPromote { return DigitPosPromotionResult::CannotPromote {
@ -171,16 +168,14 @@ impl DigitPosPromoter {
} }
// Step 4: Build CarrierInfo // Step 4: Build CarrierInfo
use crate::mir::join_ir::lowering::carrier_info::{ use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
CarrierInit, CarrierRole, CarrierVar,
};
// Boolean carrier (condition-only, for break) // Boolean carrier (condition-only, for break)
let promoted_carrier_bool = CarrierVar { let promoted_carrier_bool = CarrierVar {
name: detection.bool_carrier_name.clone(), name: detection.bool_carrier_name.clone(),
host_id: ValueId(0), // Placeholder (will be remapped) host_id: ValueId(0), // Placeholder (will be remapped)
join_id: None, // Will be allocated later join_id: None, // Will be allocated later
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false
#[cfg(feature = "normalized_dev")] #[cfg(feature = "normalized_dev")]
binding_id: None, // Phase 78: Set by CarrierBindingAssigner binding_id: None, // Phase 78: Set by CarrierBindingAssigner

View File

@ -116,7 +116,7 @@ fn classify_body(body: &[ASTNode]) -> LoopPatternKind {
carrier_count: carrier_count(body), carrier_count: carrier_count(body),
break_count: if has_break_flag { 1 } else { 0 }, break_count: if has_break_flag { 1 } else { 0 },
continue_count: if has_continue_flag { 1 } else { 0 }, continue_count: if has_continue_flag { 1 } else { 0 },
is_infinite_loop: false, // テストでは通常ループを想定 is_infinite_loop: false, // テストでは通常ループを想定
update_summary: None, update_summary: None,
}; };
classify(&features) classify(&features)

View File

@ -424,7 +424,11 @@ mod tests {
let result = TrimDetector::detect(&condition, &loop_body, var_name); let result = TrimDetector::detect(&condition, &loop_body, var_name);
assert!(result.is_some(), "Expected detection for var '{}'", var_name); assert!(
result.is_some(),
"Expected detection for var '{}'",
var_name
);
let result = result.unwrap(); let result = result.unwrap();
assert_eq!(result.match_var, var_name); assert_eq!(result.match_var, var_name);
assert_eq!(result.carrier_name, format!("is_{}_match", var_name)); assert_eq!(result.carrier_name, format!("is_{}_match", var_name));

Some files were not shown because too many files have changed in this diff Show More