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
mod joinir_flags;
mod mir_flags;
mod vm_backend_flags;
mod parser_flags;
mod selfhost_flags;
mod using_flags;
mod verification_flags;
mod selfhost_flags;
mod vm_backend_flags;
pub use catalog::{env_vars, AppliesTo, EnvVarMeta};
pub use dump::*;
@ -42,11 +42,11 @@ pub use stage1::*;
// Backward-compatible re-exports (NO BREAKING CHANGES!)
pub use joinir_flags::*;
pub use mir_flags::*;
pub use vm_backend_flags::*;
pub use parser_flags::*;
pub use selfhost_flags::*;
pub use using_flags::*;
pub use verification_flags::*;
pub use selfhost_flags::*;
pub use vm_backend_flags::*;
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.
pub fn is_joinir_debug() -> bool {
std::env::var("HAKO_JOINIR_DEBUG").is_ok()
|| std::env::var("NYASH_JOINIR_DEBUG").is_ok()
std::env::var("HAKO_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> {
for (k, t) in KEYWORDS {
if *k == word { return Some(*t); }
if *k == word {
return Some(*t);
}
}
None
}
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
"box",
"global",
"function",
"static",
"if",
"loop",
"break",
"return",
"print",
"nowait",
"include",
"local",
"outbox",
"try",
"throw",
"using",
"from",
];
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[
"add",
"sub",
"mul",
"div",
"and",
"or",
"eq",
"ne",
"box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait",
"include", "local", "outbox", "try", "throw", "using", "from",
];
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"];

View File

@ -15,19 +15,19 @@ use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId;
use std::collections::HashSet;
use std::collections::{BTreeMap, HashMap};
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
mod builder_calls;
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
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 context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
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 exprs; // expression lowering split
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
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 exprs_lambda; // lambda lowering
@ -55,9 +55,9 @@ mod receiver; // ReceiverMaterializationBoxMethod recv の pin+LocalSSA 集
mod rewrite; // P1: Known rewrite & special consolidation
mod router; // RouterPolicyBoxUnified vs BoxCall
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 stmts;
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
mod type_context; // Phase 136 follow-up: TypeContext extraction
mod type_facts; // Phase 136 follow-up: Type inference facts box
pub(crate) mod type_registry;
@ -86,8 +86,6 @@ pub struct MirBuilder {
/// Direct field access for backward compatibility (migration in progress).
pub(super) core_ctx: core_context::CoreContext,
/// Phase 136 follow-up: Type information context
/// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
/// Direct field access for backward compatibility (migration in progress).
@ -125,7 +123,6 @@ pub struct MirBuilder {
#[allow(dead_code)]
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
// Phase 2-5: binding_map removed - use binding_ctx.binding_map instead
// include guards removed
@ -153,7 +150,6 @@ pub struct MirBuilder {
// ----------------------
// Debug scope context (dev only; zero-cost when unused)
// ----------------------
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
/// Key: (bb, original ValueId, kind) -> local ValueId
/// kind: 0=recv, 1+ reserved for future (args etc.)
@ -196,7 +192,8 @@ impl MirBuilder {
let core_ctx = core_context::CoreContext::new();
// 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初期化削除
#[allow(deprecated)]
@ -384,7 +381,8 @@ impl MirBuilder {
if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) {
if slash > dot {
let tail = &name[dot..];
self.comp_ctx.method_tail_index
self.comp_ctx
.method_tail_index
.entry(tail.to_string())
.or_insert_with(Vec::new)
.push(name.clone());
@ -399,7 +397,9 @@ impl MirBuilder {
fn ensure_method_tail_index(&mut self) {
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,
};
if need_rebuild {
@ -410,7 +410,8 @@ impl MirBuilder {
pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> {
self.ensure_method_tail_index();
let tail = format!(".{}{}", method, format!("/{}", arity));
self.comp_ctx.method_tail_index
self.comp_ctx
.method_tail_index
.get(&tail)
.cloned()
.unwrap_or_default()
@ -418,7 +419,8 @@ impl MirBuilder {
pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> {
self.ensure_method_tail_index();
self.comp_ctx.method_tail_index
self.comp_ctx
.method_tail_index
.get(tail.as_ref())
.cloned()
.unwrap_or_default()
@ -527,7 +529,9 @@ impl MirBuilder {
if !suggest.is_empty() {
msg.push_str("\nHint: symbol appears in using module(s): ");
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
@ -567,7 +571,9 @@ impl MirBuilder {
// Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303).
if !var_name.starts_with("__pin$") {
// 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)
@ -582,7 +588,8 @@ impl MirBuilder {
// Precompute debug metadata to avoid borrow conflicts later
let _dbg_fn_name = self
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.clone());
let _dbg_region_id = self.debug_current_region_id();
@ -694,7 +701,8 @@ impl MirBuilder {
}) = callee
{
let names: Vec<String> = self
.variable_ctx.variable_map
.variable_ctx
.variable_map
.iter()
.filter(|(_, &vid)| vid == *r)
.map(|(k, _)| k.clone())
@ -760,7 +768,10 @@ impl MirBuilder {
);
}
// 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
}
// Update predecessor sets for branch/jump immediately so that
@ -854,7 +865,8 @@ impl MirBuilder {
effects: EffectMask::PURE,
})?;
// 型注釈(最小)
self.type_ctx.value_types
self.type_ctx
.value_types
.insert(dst, super::MirType::Box(class.clone()));
return Ok(dst);
}
@ -872,7 +884,9 @@ impl MirBuilder {
dst,
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);
}
}
@ -897,7 +911,8 @@ impl MirBuilder {
})?;
// Phase 15.5: Unified box type handling
// 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()));
// 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
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) {
return block.is_terminated();
}
@ -1018,13 +1035,17 @@ use crate::mir::loop_pattern_detection::BindingMapProvider;
impl BindingMapProvider for MirBuilder {
#[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)
Some(self.binding_ctx.binding_map())
}
#[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
}
}

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")
{
let bx = builder
.type_ctx.value_origin_newbox
.type_ctx
.value_origin_newbox
.get(&dst)
.cloned()
.unwrap_or_default();

View File

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

View File

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

View File

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

View File

@ -394,7 +394,10 @@ mod tests {
let base_id = ValueId::new(10);
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);
}

View File

@ -13,11 +13,10 @@
/// - **SSOT**: Single function for all CarrierInit → ValueId generation
/// - **Testability**: Pure function, easy to unit test
/// - **Consistency**: Uniform debug output format
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInit;
use crate::mir::value_id::ValueId;
use crate::mir::types::ConstValue;
use crate::mir::value_id::ValueId;
use crate::mir::MirInstruction;
/// Generate a ValueId for the given CarrierInit policy
@ -120,7 +119,13 @@ mod tests {
let mut builder = MirBuilder::new();
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");
}
@ -131,7 +136,13 @@ mod tests {
let mut builder = MirBuilder::new();
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");
}
@ -142,7 +153,13 @@ mod tests {
let mut builder = MirBuilder::new();
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");
}
@ -153,12 +170,36 @@ mod tests {
let mut builder = MirBuilder::new();
let host_id = ValueId(100);
let result1 = init_value(&mut builder, &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);
let result1 = init_value(
&mut builder,
&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!(result2, result3, "BoolConst and LoopLocalZero should produce different ValueIds");
assert_ne!(
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");
}
@ -168,7 +209,13 @@ mod tests {
let mut builder = MirBuilder::new();
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)"
// (This test just verifies it doesn't crash)
}
@ -179,7 +226,13 @@ mod tests {
let mut builder = MirBuilder::new();
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)"
// (This test just verifies it doesn't crash)
}
@ -190,7 +243,13 @@ mod tests {
let mut builder = MirBuilder::new();
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)"
// (This test just verifies it doesn't crash)
}

View File

@ -143,7 +143,11 @@ impl ExitLineReconnector {
// Update variable_ctx.variable_map with 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
if verbose {
eprintln!(

View File

@ -149,7 +149,8 @@ pub(super) fn merge_and_rewrite(
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
// 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 =
if let Some(ref mut current_func) = builder.scope_ctx.current_function {
current_func
.blocks
.remove(&new_block_id)

View File

@ -96,12 +96,17 @@ impl LoopHeaderPhiBuilder {
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
// Ignore backedge to avoid circular dependency in type inference
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") {
eprintln!(
"[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
for (name, host_id, init, role) in carriers {
// Phase 86: Use centralized CarrierInit builder
let init_value = super::carrier_init_builder::init_value(
builder,
&init,
*host_id,
&name,
debug,
);
let init_value =
super::carrier_init_builder::init_value(builder, &init, *host_id, &name, debug);
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
// Ignore backedge to avoid circular dependency in type inference
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") {
eprintln!(
"[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
let current_func = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_mut()
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;

View File

@ -141,20 +141,24 @@ mod tests {
#[test]
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::loop_canonicalizer::canonicalize_loop_expr;
let loop_ast = build_skip_whitespace_loop();
// Extract condition and body
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"),
};
// Run canonicalizer
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
let has_continue = ast_features::detect_continue_in_body(body);
@ -175,13 +179,16 @@ mod tests {
crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break,
"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]
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::loop_canonicalizer::canonicalize_loop_expr;
// Simple while loop: no break, no continue, no if
let loop_ast = ASTNode::Loop {
@ -221,7 +228,9 @@ mod tests {
// Extract condition and body
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"),
};
@ -243,6 +252,9 @@ mod tests {
// Canonicalizer should fail (not implemented yet for Pattern1)
assert!(canonical_result.is_ok());
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
#[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
apply_exit_bindings_to_boundary(
self.carrier_info,

View File

@ -77,14 +77,17 @@ impl MirBuilder {
//
// Phase 132: Add exit_bindings to enable ExitLineReconnector
// 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::inline_boundary::LoopExitBinding;
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)
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");
// Phase 132: Create exit binding for loop variable

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_scope_shape::LoopScopeShape;
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::function_scope_capture::CapturedEnv;
use crate::mir::ValueId;
use std::collections::BTreeMap;
@ -42,7 +42,9 @@ fn prepare_pattern2_inputs(
verbose: bool,
) -> Result<Pattern2Inputs, String> {
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_id = ctx.loop_var_id;
@ -88,7 +90,10 @@ fn prepare_pattern2_inputs(
log_pattern2(
verbose,
"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 {
log_pattern2(
@ -747,16 +752,8 @@ impl MirBuilder {
trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map);
let mut inputs =
prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
promote_and_prepare_carriers(
self,
condition,
_body,
&mut inputs,
debug,
verbose,
)?;
let mut inputs = prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
promote_and_prepare_carriers(self, condition, _body, &mut inputs, debug, verbose)?;
let (effective_break_condition, normalized_body) =
apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?;
let analysis_body = normalized_body.as_deref().unwrap_or(_body);
@ -977,11 +974,26 @@ mod tests {
use crate::mir::ValueId;
let mut builder = MirBuilder::new();
builder.variable_ctx.variable_map.insert("i".to_string(), ValueId(1));
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));
builder
.variable_ctx
.variable_map
.insert("i".to_string(), ValueId(1));
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"));
@ -1008,7 +1020,9 @@ mod tests {
let break_if = ASTNode::If {
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,
span: Span::unknown(),
};
@ -1035,8 +1049,7 @@ mod tests {
let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2)
.expect("build_pattern_context");
let mut inputs =
prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
let mut inputs = prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
.expect("prepare_pattern2_inputs");
promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false)

View File

@ -183,7 +183,8 @@ impl MirBuilder {
// Collect parent-defined variables from function scope
// For now, use all variables in variable_map except loop_var
let parent_defined: Vec<String> = self
.variable_ctx.variable_map
.variable_ctx
.variable_map
.keys()
.filter(|name| *name != &loop_var_name)
.cloned()
@ -196,9 +197,11 @@ impl MirBuilder {
condition_bindings.iter().map(|b| b.name.clone()).collect();
// Run consistency checks
if let Err(e) =
check_ownership_plan_consistency(&plan, &ctx.carrier_info, &condition_binding_names)
{
if let Err(e) = check_ownership_plan_consistency(
&plan,
&ctx.carrier_info,
&condition_binding_names,
) {
eprintln!("[phase64/ownership] Consistency check failed: {}", 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 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 {
ASTNode::Break { .. } => {
*break_count += 1;
@ -61,13 +67,29 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
ASTNode::Loop { .. } if depth > 0 => {
*has_nested_loop = true;
}
ASTNode::If { then_body, else_body, .. } => {
ASTNode::If {
then_body,
else_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 {
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 {
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)
@ -95,7 +123,12 @@ fn validate_continue_position(body: &[ASTNode]) -> bool {
/// Phase 131-11-D: Validate break is in simple if pattern
fn validate_break_pattern(body: &[ASTNode]) -> bool {
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
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// 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)
if !ctx.features.is_infinite_loop {
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;
}
// 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)
if real_break_count != 1 {
@ -232,11 +269,21 @@ fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
use crate::ast::BinaryOperator;
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
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// 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() {
return Ok(name.clone());
}
@ -256,12 +303,26 @@ fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
use crate::ast::{BinaryOperator, LiteralValue};
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
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
// Extract limit from condition
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, right, .. } = condition.as_ref() {
if let ASTNode::Literal { value: LiteralValue::Integer(limit), .. } = right.as_ref() {
if let ASTNode::BinaryOp {
operator: BinaryOperator::Equal,
right,
..
} = condition.as_ref()
{
if let ASTNode::Literal {
value: LiteralValue::Integer(limit),
..
} = right.as_ref()
{
return Ok(*limit);
}
}
@ -317,8 +378,16 @@ pub(crate) fn lower(
}
// 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(|| {
format!("Counter variable '{}' not found in variable_ctx.variable_map", counter_name)
let counter_id = builder
.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 {
@ -340,7 +409,8 @@ pub(crate) fn lower(
// Step 4: Generate JoinIR
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
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();
@ -397,15 +467,20 @@ pub(crate) fn lower(
// ==================================================================
// 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
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1,
value: ConstValue::Integer(1),
}));
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: counter_next,
op: BinOpKind::Add,
lhs: counter_param,
@ -413,12 +488,16 @@ pub(crate) fn lower(
}));
// break_cond = (counter_next == LIMIT)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_limit,
value: ConstValue::Integer(limit),
}));
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Compare {
dst: break_cond,
op: CompareOp::Eq,
lhs: counter_next,

View File

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

View File

@ -6,7 +6,6 @@ use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
///
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
@ -29,7 +28,8 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> {
// Get current function name
let func_name = self
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_default();
@ -138,17 +138,38 @@ impl MirBuilder {
match canonicalize_loop_expr(&loop_ast) {
Ok((skeleton, decision)) => {
eprintln!("[loop_canonicalizer] Function: {}", func_name);
eprintln!("[loop_canonicalizer] Skeleton steps: {}", skeleton.steps.len());
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" });
eprintln!(
"[loop_canonicalizer] Skeleton steps: {}",
skeleton.steps.len()
);
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 {
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() {
eprintln!("[loop_canonicalizer] Reason: {}", decision.notes.join("; "));
eprintln!(
"[loop_canonicalizer] Reason: {}",
decision.notes.join("; ")
);
}
// Phase 137-4: Router parity verification
@ -183,7 +204,11 @@ impl MirBuilder {
func_name,
&format!(
"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 {

View File

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

View File

@ -14,7 +14,9 @@ pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
value: ConstValue::Integer(val),
});
// 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
}
@ -26,7 +28,9 @@ pub fn emit_bool(b: &mut MirBuilder, val: bool) -> ValueId {
value: ConstValue::Bool(val),
});
// 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
}
@ -38,7 +42,9 @@ pub fn emit_float(b: &mut MirBuilder, val: f64) -> ValueId {
value: ConstValue::Float(val),
});
// 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
}
@ -51,9 +57,12 @@ pub fn emit_string<S: Into<String>>(b: &mut MirBuilder, s: S) -> ValueId {
});
// 🎯 Phase 3-A: String constant type annotation
// 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()));
b.type_ctx.value_origin_newbox.insert(dst, "StringBox".to_string());
b.type_ctx
.value_origin_newbox
.insert(dst, "StringBox".to_string());
dst
}
@ -66,7 +75,9 @@ pub fn emit_null(b: &mut MirBuilder) -> ValueId {
});
// Phase 84-1: Null constant type annotation
// 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
}
@ -78,6 +89,8 @@ pub fn emit_void(b: &mut MirBuilder) -> ValueId {
value: ConstValue::Void,
});
// 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
}

View File

@ -222,7 +222,8 @@ impl super::MirBuilder {
body.clone(),
)?;
// 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())
.or_insert_with(Vec::new)
.push((name.clone(), params.len()));
@ -306,14 +307,18 @@ impl super::MirBuilder {
args: vec![],
effects: super::EffectMask::MUT,
})?;
self.type_ctx.value_origin_newbox
self.type_ctx
.value_origin_newbox
.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()));
// TypeRegistry + trace for deterministic debug
self.comp_ctx.type_registry
self.comp_ctx
.type_registry
.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()));
type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox");
type_trace::ty(
@ -350,13 +355,17 @@ impl super::MirBuilder {
args: vec![],
effects: super::EffectMask::MUT,
})?;
self.type_ctx.value_origin_newbox
self.type_ctx
.value_origin_newbox
.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()));
self.comp_ctx.type_registry
self.comp_ctx
.type_registry
.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()));
type_trace::origin("newbox:MapLiteral", map_id, "MapBox");
type_trace::ty(
@ -404,7 +413,10 @@ impl super::MirBuilder {
if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) {
return Some(cls.clone());
}
self.type_ctx.value_types.get(&target_val).and_then(|ty| match ty {
self.type_ctx
.value_types
.get(&target_val)
.and_then(|ty| match ty {
super::MirType::Box(name) => Some(name.clone()),
super::MirType::String => Some("String".to_string()),
super::MirType::Integer => Some("Integer".to_string()),

View File

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

View File

@ -118,7 +118,9 @@ impl super::MirBuilder {
// Merge and yield result
self.start_new_block(merge_block)?;
// フェーズ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(
func,
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`,
// 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(kind) = map.get(&field) {
let mname = match kind {
@ -53,15 +58,24 @@ impl super::MirBuilder {
// Propagate recorded origin class for this field if any (ValueId-scoped)
if let Some(class_name) = self
.comp_ctx.field_origin_class
.comp_ctx
.field_origin_class
.get(&(object_value, field.clone()))
.cloned()
{
self.type_ctx.value_origin_newbox.insert(field_val, class_name);
} else if let Some(base_cls) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
self.type_ctx
.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
if let Some(fcls) = self
.comp_ctx.field_origin_by_box
.comp_ctx
.field_origin_by_box
.get(&(base_cls.clone(), field.clone()))
.cloned()
{
@ -78,8 +92,11 @@ impl super::MirBuilder {
}
// If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier)
let mut inferred_class: Option<String> =
self.type_ctx.value_origin_newbox.get(&object_value).cloned();
let mut inferred_class: Option<String> = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned();
if inferred_class.is_none() {
if let ASTNode::FieldAccess {
object: inner_obj,
@ -89,7 +106,8 @@ impl super::MirBuilder {
{
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
if let Some(cls) = self
.comp_ctx.field_origin_class
.comp_ctx
.field_origin_class
.get(&(base_id, inner_field))
.cloned()
{
@ -128,7 +146,12 @@ impl super::MirBuilder {
value_result = self.local_arg(value_result);
// 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 weak_set.contains(&field) {
value_result = self.emit_weak_new(value_result)?;
@ -158,7 +181,12 @@ impl super::MirBuilder {
})?;
// 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 weak_set.contains(&field) {
let _ = self.emit_barrier_write(value_result);
@ -167,12 +195,24 @@ impl super::MirBuilder {
}
// Record origin class for this field value if known
if let Some(val_cls) = self.type_ctx.value_origin_newbox.get(&value_result).cloned() {
self.comp_ctx.field_origin_class
if let Some(val_cls) = self
.type_ctx
.value_origin_newbox
.get(&value_result)
.cloned()
{
self.comp_ctx
.field_origin_class
.insert((object_value, field.clone()), val_cls.clone());
// 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() {
self.comp_ctx.field_origin_by_box
if let Some(base_cls) = self
.type_ctx
.value_origin_newbox
.get(&object_value)
.cloned()
{
self.comp_ctx
.field_origin_by_box
.insert((base_cls, field.clone()), val_cls);
}
}

View File

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

View File

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

View File

@ -24,7 +24,8 @@ impl MeCallPolicyBox {
) -> Result<Option<ValueId>, String> {
// Instance box: prefer enclosing box method (lowered function) if存在
let enclosing_cls: Option<String> = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.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
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str());
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
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
let fn_name = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str());
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();
let decided_t = builder
.type_ctx.value_types
.type_ctx
.value_types
.get(&dst)
.cloned()
.map(|tt| format!("{:?}", tt))
.unwrap_or_default();
let decided_o = builder
.type_ctx.value_origin_newbox
.type_ctx
.value_origin_newbox
.get(&dst)
.cloned()
.unwrap_or_default();
@ -35,7 +37,8 @@ pub(crate) fn emit_phi(builder: &MirBuilder, dst: ValueId, inputs: &Vec<(BasicBl
"decided_origin": decided_o,
});
let fn_name = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str());
let region = builder.debug_current_region_id();

View File

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

View File

@ -70,7 +70,8 @@ impl MirBuilder {
if let Some(func) = self.scope_ctx.current_function.as_mut() {
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(
func, cur_bb, &inputs,
@ -94,7 +95,9 @@ impl MirBuilder {
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() {
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(
func, cur_bb, &inputs,
@ -205,7 +209,8 @@ impl MirBuilder {
if let Some(func) = self.scope_ctx.current_function.as_mut() {
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(
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() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.builder.scope_ctx.current_function, self.builder.current_block)
{
if let (Some(func), Some(cur_bb)) = (
&self.builder.scope_ctx.current_function,
self.builder.current_block,
) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &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() {
func.update_cfg();
}
if let (Some(func), Some(cur_bb)) =
(&self.builder.scope_ctx.current_function, self.builder.current_block)
{
if let (Some(func), Some(cur_bb)) = (
&self.builder.scope_ctx.current_function,
self.builder.current_block,
) {
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &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
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
let current_fn = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.clone())
.unwrap_or_else(|| "<none>".to_string());
let bb = builder.current_block;
let names: Vec<String> = builder
.variable_ctx.variable_map
.variable_ctx
.variable_map
.iter()
.filter(|(_, &vid)| vid == r)
.map(|(k, _)| k.clone())

View File

@ -40,7 +40,12 @@ pub(crate) fn try_known_rewrite(
return None;
}
// 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;
}
// Only user-defined boxes (plugin/core boxesは対象外)
@ -54,7 +59,11 @@ pub(crate) fn try_known_rewrite(
.ok()
.as_deref()
== 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 fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity,
@ -107,7 +116,12 @@ pub(crate) fn try_known_rewrite_to_dst(
if !rewrite_enabled() {
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;
}
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
@ -119,7 +133,11 @@ pub(crate) fn try_known_rewrite_to_dst(
.ok()
.as_deref()
== 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 fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
cls, method, arity,
@ -171,7 +189,12 @@ pub(crate) fn try_unique_suffix_rewrite(
return None;
}
// 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;
}
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() {
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;
}
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") {
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 {
return;
};

View File

@ -72,8 +72,7 @@ impl ScopeContext {
/// Push new lexical scope frame
#[inline]
pub(super) fn push_lexical_scope(&mut self) {
self.lexical_scope_stack
.push(LexicalScopeFrame::default());
self.lexical_scope_stack.push(LexicalScopeFrame::default());
}
/// 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 names_for_v: Vec<String> = builder
.variable_ctx.variable_map
.variable_ctx
.variable_map
.iter()
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
.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);
}
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,
// infer MirType::Box from origin
if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() {
builder
.type_ctx.value_types
.type_ctx
.value_types
.insert(loc, crate::mir::MirType::Box(cls));
}
}

View File

@ -181,7 +181,8 @@ impl super::MirBuilder {
idx + 1,
total,
self.current_block,
self.scope_ctx.current_function
self.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("none")
@ -427,9 +428,12 @@ impl super::MirBuilder {
})?;
// 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.
self.type_ctx.value_types
self.type_ctx
.value_types
.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() {
reg.ensure_slot(&variable, None);
}
@ -442,13 +446,17 @@ impl super::MirBuilder {
value: expression_value,
})?;
let inner = self
.type_ctx.value_types
.type_ctx
.value_types
.get(&expression_value)
.cloned()
.unwrap_or(MirType::Unknown);
self.type_ctx.value_types
self.type_ctx
.value_types
.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() {
reg.ensure_slot(&variable, None);
}
@ -487,7 +495,9 @@ impl super::MirBuilder {
"__me__".to_string()
};
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() {
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
if !pin_renames.is_empty() {
let snapshot: Vec<(String, super::ValueId)> = self
.variable_ctx.variable_map
.variable_ctx
.variable_map
.iter()
.filter(|(k, _)| !k.starts_with("__pin$"))
.map(|(k, &v)| (k.clone(), v))
@ -316,7 +317,8 @@ impl super::MirBuilder {
effects,
})?;
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 let Some(t) = self.type_ctx.value_types.get(&box_val) {
match t {
@ -327,7 +329,11 @@ impl super::MirBuilder {
}
}
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());
} else {
// Phase 84-4-B: ビルトイン Box のメソッド戻り値型推論
@ -489,7 +495,8 @@ impl super::MirBuilder {
dst: super::ValueId,
src: super::ValueId,
) -> 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") {
eprintln!(

View File

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

View File

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

View File

@ -12,8 +12,8 @@
//! - `create_entry_function()`: entry 関数生成
//! - `create_k_exit_function()`: k_exit 関数生成
use super::{AstToJoinIrLowerer, JoinModule};
use super::super::stmt_handlers::StatementEffect;
use super::{AstToJoinIrLowerer, JoinModule};
use crate::mir::join_ir::JoinIrPhase;
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
use crate::mir::ValueId;
@ -127,7 +127,10 @@ pub fn process_local_inits(
if let StatementEffect::VarUpdate { name, value_id } = effect {
ctx.register_param(name, value_id);
} 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),

View File

@ -205,7 +205,8 @@ fn create_loop_step_function_continue(
// Phase 89-2: StepCalculator Box に抽出済み(再利用性向上)
let mut i_next_continue = i_next;
let continue_then = continue_if_stmt["then"]
let continue_then =
continue_if_stmt["then"]
.as_array()
.ok_or_else(|| LoweringError::InvalidLoopBody {
message: "Continue pattern If must have 'then' array".to_string(),
@ -283,10 +284,12 @@ fn create_loop_step_function_continue(
// Phase 88: Continue 分岐側でも acc を更新できるようにする(例: acc += 1
let mut acc_then_val = step_acc;
if let Some(then_acc_local) = continue_then.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);
if let Some(then_acc_local) = continue_then
.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);
body.extend(acc_then_insts);
acc_then_val = acc_then;
}

View File

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

View File

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

View File

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

View File

@ -103,7 +103,10 @@ mod tests {
"lhs": {"type": "Var", "name": "i"},
"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]
@ -114,7 +117,10 @@ mod tests {
"lhs": {"type": "Int", "value": 3},
"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]

View File

@ -87,15 +87,13 @@ impl AstToJoinIrLowerer {
.as_str()
.expect("Function must have 'name'");
let route = resolve_function_route(func_name)
.unwrap_or_else(|msg| panic!("{msg}"));
let route = resolve_function_route(func_name).unwrap_or_else(|msg| panic!("{msg}"));
match route {
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name(
self,
program_json,
),
FunctionRoute::LoopFrontend => {
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
}
FunctionRoute::NestedIf => self.lower_nested_if_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))
//! ```
use crate::mir::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::MirBuilder;
#[derive(Debug)]
pub enum CarrierBindingError {
@ -87,9 +87,7 @@ impl CarrierBindingAssigner {
Some(bid) => bid,
None => {
let bid = builder.allocate_binding_id();
builder
.binding_map
.insert(original_name.to_string(), bid);
builder.binding_map.insert(original_name.to_string(), bid);
bid
}
};
@ -189,14 +187,16 @@ mod tests {
#[test]
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
let mut builder = MirBuilder::new();
// Add original binding to builder.binding_map
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
let mut carrier_info = CarrierInfo::with_carriers(
@ -227,7 +227,11 @@ mod tests {
assert!(promoted_bid.is_some());
// 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);
}
@ -238,16 +242,18 @@ mod tests {
// Create MirBuilder WITHOUT adding "digit_pos" to binding_map
let mut builder = MirBuilder::new();
let mut carrier_info = CarrierInfo::with_carriers("test_loop".to_string(), ValueId(0), vec![
CarrierVar {
let mut carrier_info = CarrierInfo::with_carriers(
"test_loop".to_string(),
ValueId(0),
vec![CarrierVar {
name: "is_digit_pos".to_string(),
host_id: ValueId(0),
join_id: None,
role: CarrierRole::ConditionOnly,
init: CarrierInit::BoolConst(false),
binding_id: None,
},
]);
}],
);
// Should succeed (original/promoted BindingId are allocated on demand).
let result = CarrierBindingAssigner::assign_promoted_binding(
@ -270,12 +276,14 @@ mod tests {
#[test]
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();
// Add two original bindings
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();
builder.binding_map.insert("ch".to_string(), ch_bid);
@ -309,24 +317,36 @@ mod tests {
&mut carrier_info,
"digit_pos",
"is_digit_pos",
).unwrap();
)
.unwrap();
CarrierBindingAssigner::assign_promoted_binding(
&mut builder,
&mut carrier_info,
"ch",
"is_ch_match",
).unwrap();
)
.unwrap();
// 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());
// 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());
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());
}
}

View File

@ -649,7 +649,11 @@ impl CarrierInfo {
/// don't have access to binding_map. Actual population happens in a future phase when
/// we integrate BindingId tracking into the promotion pipeline.
#[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;
// Phase 86: Use DebugOutputBox for consistent debug output
@ -663,7 +667,8 @@ impl CarrierInfo {
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")]
pub fn register_carrier_binding(
&mut self,
binding_id: BindingId,
join_value_id: ValueId,
) {
pub fn register_carrier_binding(&mut self, binding_id: BindingId, join_value_id: ValueId) {
self.binding_id_map.insert(binding_id, join_value_id);
}
@ -219,11 +215,7 @@ impl ConditionEnv {
/// }
/// ```
#[cfg(feature = "normalized_dev")]
pub fn register_loop_var_binding(
&mut self,
binding_id: BindingId,
value_id: ValueId,
) {
pub fn register_loop_var_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
self.binding_id_map.insert(binding_id, value_id);
}
@ -250,11 +242,7 @@ impl ConditionEnv {
/// }
/// ```
#[cfg(feature = "normalized_dev")]
pub fn register_condition_binding(
&mut self,
binding_id: BindingId,
value_id: ValueId,
) {
pub fn register_condition_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
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) {
debug.log(
"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);
} else {
@ -330,7 +321,10 @@ impl ConditionEnv {
} else {
// Legacy: no BindingId, use name lookup
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;
}
}

View File

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

View File

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

View File

@ -192,7 +192,11 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
// 2. Build ConditionEnv from ScopeManager
// Phase 79-1: Use BindingId-aware version when available
#[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"))]
let condition_env = scope_resolution::build_condition_env_from_scope(self.scope, ast)?;
@ -279,12 +283,16 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
// Build ConditionEnv from the provided scope (the caller controls the scope + allocator).
#[cfg(feature = "normalized_dev")]
let condition_env =
scope_resolution::build_condition_env_from_scope_with_binding(context.scope, condition, self.builder)
let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(
context.scope,
condition,
self.builder,
)
.map_err(|e| e.to_string())?;
#[cfg(not(feature = "normalized_dev"))]
let condition_env = scope_resolution::build_condition_env_from_scope(context.scope, condition)
let condition_env =
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).

View File

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

View File

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

View File

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

View File

@ -12,8 +12,7 @@
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
mod core;
mod case_a_entrypoints;
mod core;
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={:?}",
i_param, carrier_param_ids
);
if crate::config::env::joinir_dev_enabled()
|| crate::config::env::joinir_test_debug_enabled()
{
if crate::config::env::joinir_dev_enabled() || crate::config::env::joinir_test_debug_enabled() {
eprintln!(
"[joinir/pattern2/debug] loop_var='{}' env.get(loop_var)={:?}, carriers={:?}",
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::Tail => loop_step_func.body.append(&mut tail_block),
// 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");
}
// Phase 48-A: P4 steps not used in P2 lowering (handled in Pattern4 lowerer)

View File

@ -126,7 +126,9 @@ pub(crate) fn lower_loop_with_continue_minimal(
carrier_info: &CarrierInfo,
carrier_updates: &BTreeMap<String, UpdateExpr>, // Phase 222.5-D: HashMap → BTreeMap for determinism
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> {
// 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

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 common; // Internal lowering utilities
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(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
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(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 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 error_tags; // Phase 86: Centralized error message formatting
pub(crate) mod exit_args_resolver; // Internal exit argument resolution
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
pub mod funcscanner_append_defs;
@ -64,11 +64,11 @@ 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_continue_minimal;
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 scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
#[cfg(feature = "normalized_dev")]
pub mod scope_manager_bindingid_poc; // Phase 73: BindingId-based scope PoC (dev-only)
pub mod scope_manager_bindingid_poc;
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 min_loop;

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) {
debug.log(
"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);
}
@ -282,7 +285,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
// Step 2: **NEW (Phase 76)**: Check promoted_bindings map
if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) {
// 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(
"promoted",
&format!(
@ -304,7 +310,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
#[cfg(not(feature = "normalized_dev"))]
debug.log(
"fallback",
&format!("BindingId({}) miss, falling back to name '{}' lookup", bid.0, name),
&format!(
"BindingId({}) miss, falling back to name '{}' lookup",
bid.0, name
),
);
}

View File

@ -225,7 +225,10 @@ mod tests {
let promoted = BindingId(10);
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);
}

View File

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

View File

@ -189,12 +189,7 @@ mod tests {
} else {
CarrierInit::FromHost
};
CarrierVar::with_role_and_init(
"c".to_string(),
ValueId(1),
CarrierRole::LoopState,
init,
)
CarrierVar::with_role_and_init("c".to_string(), ValueId(1), CarrierRole::LoopState, init)
}
fn carrier_info(carriers: Vec<CarrierVar>) -> CarrierInfo {
@ -249,8 +244,7 @@ mod tests {
#[test]
fn loop_local_carrier_triggers_body_first() {
let ctx =
Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
let ctx = Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
let schedule = build_pattern2_schedule(&ctx);
assert_eq!(
schedule.steps(),

View File

@ -15,13 +15,13 @@ use std::collections::HashMap;
#[cfg(feature = "normalized_dev")]
use std::panic::{catch_unwind, AssertUnwindSafe};
#[cfg(feature = "normalized_dev")]
pub mod fixtures;
#[cfg(feature = "normalized_dev")]
pub mod dev_env;
#[cfg(feature = "normalized_dev")]
pub mod dev_fixtures;
#[cfg(feature = "normalized_dev")]
pub mod fixtures;
#[cfg(feature = "normalized_dev")]
pub mod loop_step_inspector;
#[cfg(feature = "normalized_dev")]
pub mod shape_guard;
@ -97,7 +97,10 @@ pub enum JpOp {
BinOp(BinOpKind),
Unary(UnaryOp),
Compare(CompareOp),
BoxCall { box_name: String, method: String },
BoxCall {
box_name: String,
method: String,
},
/// 三項演算子cond ? then : else
Select,
}
@ -462,13 +465,11 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
cond,
then_val,
else_val,
}) => {
body.push(JpInst::Let {
}) => body.push(JpInst::Let {
dst: *dst,
op: JpOp::Select,
args: vec![*cond, *then_val, *else_val],
})
}
}),
JoinInst::Jump { cont, args, cond } => {
if let Some(cond_val) = cond {
body.push(JpInst::If {
@ -497,7 +498,9 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
args: vec![*cond, *then_val, *else_val],
});
}
JoinInst::Call { func, args, k_next, .. } => {
JoinInst::Call {
func, args, k_next, ..
} => {
if k_next.is_none() {
body.push(JpInst::TailCallFn {
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
#[cfg(feature = "normalized_dev")]
pub fn normalize_selfhost_if_sum_p3(
structured: &JoinModule,
) -> Result<NormalizedModule, String> {
pub fn normalize_selfhost_if_sum_p3(structured: &JoinModule) -> Result<NormalizedModule, String> {
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
}
@ -769,9 +770,7 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
op: *op,
operand: args.get(0).copied().unwrap_or(ValueId(0)),
})),
JpOp::Select => func
.body
.push(JoinInst::Compute(MirLikeInst::Select {
JpOp::Select => func.body.push(JoinInst::Compute(MirLikeInst::Select {
dst: *dst,
cond: args.get(0).copied().unwrap_or(ValueId(0)),
then_val: args.get(1).copied().unwrap_or(ValueId(0)),
@ -832,9 +831,7 @@ fn verify_normalized_pattern2(
max_env_fields: usize,
) -> Result<(), String> {
if module.phase != JoinIrPhase::Normalized {
return Err(
"[joinir/normalized-dev] pattern2: phase must be Normalized".to_string(),
);
return Err("[joinir/normalized-dev] pattern2: phase must be Normalized".to_string());
}
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
@ -871,10 +868,8 @@ fn verify_normalized_pattern2(
| JpInst::If { env, .. } => {
if let Some(expected) = expected_env_len {
if env.is_empty() {
return Err(
"[joinir/normalized-dev] pattern2: env must not be empty"
.to_string(),
);
return Err("[joinir/normalized-dev] pattern2: env must not be empty"
.to_string());
}
let _ = expected;
}
@ -885,9 +880,7 @@ fn verify_normalized_pattern2(
if let Some(last) = func.body.last() {
match last {
JpInst::TailCallFn { .. }
| JpInst::TailCallKont { .. }
| JpInst::If { .. } => {}
JpInst::TailCallFn { .. } | JpInst::TailCallKont { .. } | JpInst::If { .. } => {}
_ => {
return Err(format!(
"[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);
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();
@ -1092,15 +1087,15 @@ pub(crate) fn normalized_dev_roundtrip_structured(
| NormalizedDevShape::JsonparserSkipWsReal
| NormalizedDevShape::JsonparserAtoiMini
| NormalizedDevShape::JsonparserAtoiReal
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(
|| {
| NormalizedDevShape::JsonparserParseNumberReal => {
catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
},
)),
}))
}
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
let norm =
normalize_selfhost_token_scan_p2(module).expect("selfhost P2 normalization failed");
let norm = normalize_selfhost_token_scan_p2(module)
.expect("selfhost P2 normalization failed");
normalized_pattern2_to_structured(&norm)
})),
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)
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern3_if_sum_minimal(module)
.expect("P3 normalization failed");
let norm =
normalize_pattern3_if_sum_minimal(module).expect("P3 normalization failed");
normalized_pattern2_to_structured(&norm)
})),
NormalizedDevShape::Pattern3IfSumMulti => catch_unwind(AssertUnwindSafe(|| {
@ -1125,8 +1120,8 @@ pub(crate) fn normalized_dev_roundtrip_structured(
normalized_pattern2_to_structured(&norm)
})),
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_selfhost_if_sum_p3(module)
.expect("selfhost P3 normalization failed");
let norm =
normalize_selfhost_if_sum_p3(module).expect("selfhost P3 normalization failed");
normalized_pattern2_to_structured(&norm)
})),
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)
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern4_continue_minimal(module)
.expect("P4 normalization failed");
let norm =
normalize_pattern4_continue_minimal(module).expect("P4 normalization failed");
normalized_pattern2_to_structured(&norm)
})),
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
catch_unwind(AssertUnwindSafe(|| {
let norm =
normalize_jsonparser_parse_array_continue_skip_ws(module)
let norm = normalize_jsonparser_parse_array_continue_skip_ws(module)
.expect("P4 array normalization failed");
normalized_pattern2_to_structured(&norm)
}))
}
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
catch_unwind(AssertUnwindSafe(|| {
let norm =
normalize_jsonparser_parse_object_continue_skip_ws(module)
let norm = normalize_jsonparser_parse_object_continue_skip_ws(module)
.expect("P4 object normalization failed");
normalized_pattern2_to_structured(&norm)
}))
}
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
NormalizedDevShape::PatternContinueReturnMinimal => catch_unwind(AssertUnwindSafe(|| {
NormalizedDevShape::PatternContinueReturnMinimal => {
catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
})),
}))
}
// Phase 90: Parse String Composite pattern (dev-only, delegates to P2 for now)
NormalizedDevShape::ParseStringCompositeMinimal => catch_unwind(AssertUnwindSafe(|| {
NormalizedDevShape::ParseStringCompositeMinimal => {
catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
})),
}))
}
};
match attempt {

View File

@ -132,8 +132,8 @@ impl NormalizedDevFixture {
use super::super::frontend::ast_lowerer::AstToJoinIrLowerer;
let fixture_json = self.fixture_content();
let program_json: serde_json::Value = serde_json::from_str(fixture_json)
.unwrap_or_else(|e| {
let program_json: serde_json::Value =
serde_json::from_str(fixture_json).unwrap_or_else(|e| {
panic!(
"{} fixture should be valid JSON: {}",
self.function_name(),
@ -165,11 +165,12 @@ mod tests {
#[test]
fn test_all_fixtures_have_unique_names() {
use std::collections::HashSet;
let names: HashSet<_> = ALL_DEV_FIXTURES
.iter()
.map(|f| f.function_name())
.collect();
assert_eq!(names.len(), ALL_DEV_FIXTURES.len(), "Fixture names must be unique");
let names: HashSet<_> = ALL_DEV_FIXTURES.iter().map(|f| f.function_name()).collect();
assert_eq!(
names.len(),
ALL_DEV_FIXTURES.len(),
"Fixture names must be unique"
);
}
#[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) {
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) {
@ -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 {
dst,
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"
);
let program_json: serde_json::Value =
serde_json::from_str(FIXTURE).expect("jsonparser skip_ws real fixture should be valid JSON");
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
.expect("jsonparser skip_ws real fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new();
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"
);
let program_json: serde_json::Value =
serde_json::from_str(FIXTURE).expect("jsonparser parse_number real fixture should be valid JSON");
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
.expect("jsonparser parse_number real fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new();
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 zero_const = alloc();
const_i64(&mut loop_step, zero_const, 0);
compare(
&mut loop_step,
cond_cmp,
CompareOp::Gt,
i_param,
zero_const,
);
compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
// then: sum = sum + 1, count = count + 1
let one_const = alloc();
@ -415,15 +420,14 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
// loop_step params: i, sum
let i_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
let limit_const = alloc();
let cmp_loop = alloc();
let exit_cond = alloc();
loop_step
.body
.push(JoinInst::Compute(MirLikeInst::Const {
loop_step.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: limit_const,
value: ConstValue::Integer(5),
}));
@ -445,13 +449,7 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
let cond_cmp = alloc();
let zero_const = alloc();
const_i64(&mut loop_step, zero_const, 0);
compare(
&mut loop_step,
cond_cmp,
CompareOp::Gt,
i_param,
zero_const,
);
compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
// then: sum = sum + i
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"
);
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
.expect("selfhost if_sum P3 ext fixture should be valid JSON");
let program_json: serde_json::Value =
serde_json::from_str(FIXTURE).expect("selfhost if_sum P3 ext fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new();
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"
);
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
.expect("selfhost args_parse P2 fixture should be valid JSON");
let program_json: serde_json::Value =
serde_json::from_str(FIXTURE).expect("selfhost args_parse P2 fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new();
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"
);
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
.expect("selfhost stmt_count P3 fixture should be valid JSON");
let program_json: serde_json::Value =
serde_json::from_str(FIXTURE).expect("selfhost stmt_count P3 fixture should be valid JSON");
let mut lowerer = AstToJoinIrLowerer::new();
let module = lowerer.lower_program_json(&program_json);
@ -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 if_condition = bin(BinaryOperator::Greater, var("i"), int_lit(0));
let then_update = assignment(
var("sum"),
bin(BinaryOperator::Add, var("sum"), 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 then_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), 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 {
condition: Box::new(if_condition),
@ -707,8 +696,13 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule {
let i_id = join_value_space.alloc_param();
cond_env.insert("i".to_string(), i_id);
let (module, _meta) =
lower_if_sum_pattern(&loop_condition, &if_stmt, &body, &cond_env, &mut join_value_space)
let (module, _meta) = lower_if_sum_pattern(
&loop_condition,
&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() {
@ -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
///
/// 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;
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_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_pattern3_if_sum_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_pattern4_continue_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_structured_for_normalized_dev,
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,

View File

@ -29,12 +29,10 @@ impl LoopStepInspector {
///
/// ループ条件や continue/break 判定で使用される。
pub fn has_compare_instruction(loop_step_func: &JoinFunction) -> bool {
loop_step_func.body.iter().any(|inst| {
matches!(
inst,
JoinInst::Compute(MirLikeInst::Compare { .. })
)
})
loop_step_func
.body
.iter()
.any(|inst| matches!(inst, JoinInst::Compute(MirLikeInst::Compare { .. })))
}
/// 条件付き Jump の個数をカウント
@ -77,10 +75,7 @@ mod tests {
use super::*;
use crate::mir::join_ir::{JoinFuncId, VarId};
fn make_dummy_loop_step(
body: Vec<JoinInst>,
param_count: usize,
) -> JoinFunction {
fn make_dummy_loop_step(body: Vec<JoinInst>, param_count: usize) -> JoinFunction {
let params: Vec<_> = (0..param_count).map(|i| VarId(i as u32)).collect();
JoinFunction {
id: JoinFuncId(1),
@ -105,7 +100,9 @@ mod tests {
assert!(LoopStepInspector::has_select_instruction(&func_with_select));
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]
@ -160,10 +157,20 @@ mod tests {
#[test]
fn test_has_reasonable_param_count() {
assert!(!LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 1)));
assert!(LoopStepInspector::has_reasonable_param_count(&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)));
assert!(!LoopStepInspector::has_reasonable_param_count(
&make_dummy_loop_step(vec![], 1)
));
assert!(LoopStepInspector::has_reasonable_param_count(
&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
SelfhostP3IfSum,
// Future: Other P2 patterns
// P2MidAtOfLoop,
// P2HeavyString,
@ -100,8 +99,14 @@ pub enum NormalizedDevShape {
type Detector = fn(&JoinModule) -> bool;
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,
detectors::is_jsonparser_skip_ws_mini,
@ -552,18 +557,16 @@ mod detectors {
let has_select = loop_func.body.iter().any(|inst| match inst {
JoinInst::Select { .. } => true,
JoinInst::Compute(mir_inst) => matches!(
mir_inst,
crate::mir::join_ir::MirLikeInst::Select { .. }
),
JoinInst::Compute(mir_inst) => {
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
}
_ => false,
});
let has_boxcall = loop_func.body.iter().any(|inst| match inst {
JoinInst::Compute(mir_inst) => matches!(
mir_inst,
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
),
JoinInst::Compute(mir_inst) => {
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
}
_ => false,
});
@ -601,8 +604,7 @@ mod detectors {
.iter()
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
let param_set: std::collections::BTreeSet<_> =
loop_step.params.iter().copied().collect();
let param_set: std::collections::BTreeSet<_> = loop_step.params.iter().copied().collect();
let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match 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 {
JoinInst::Compute(mir_inst) => matches!(
mir_inst,
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
),
JoinInst::Compute(mir_inst) => {
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
}
_ => false,
});
@ -667,10 +668,9 @@ mod detectors {
// Phase 220: Select can be either JoinInst::Select or Compute(MirLikeInst::Select)
let has_select = loop_step.body.iter().any(|inst| match inst {
JoinInst::Select { .. } => true,
JoinInst::Compute(mir_inst) => matches!(
mir_inst,
crate::mir::join_ir::MirLikeInst::Select { .. }
),
JoinInst::Compute(mir_inst) => {
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
}
_ => false,
});
@ -993,8 +993,12 @@ mod detectors {
// 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
loop_step.body.iter().any(|other_inst| match other_inst {
JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { dst, value }) => {
dst == rhs && matches!(value, crate::mir::join_ir::ConstValue::Integer(2))
JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
dst,
value,
}) => {
dst == rhs
&& matches!(value, crate::mir::join_ir::ConstValue::Integer(2))
}
_ => false,
})
@ -1171,7 +1175,9 @@ mod tests {
shapes
);
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: {:?}",
shapes
);
@ -1286,7 +1292,9 @@ mod tests {
k_next: None,
dst: Some(ValueId(2)),
},
JoinInst::Ret { value: Some(ValueId(2)) },
JoinInst::Ret {
value: Some(ValueId(2)),
},
],
exit_cont: None,
};
@ -1348,7 +1356,9 @@ mod tests {
k_next: None,
dst: Some(ValueId(40)),
},
JoinInst::Ret { value: Some(ValueId(40)) },
JoinInst::Ret {
value: Some(ValueId(40)),
},
],
exit_cont: None,
};
@ -1358,7 +1368,9 @@ mod tests {
id: JoinFuncId::new(2),
name: "k_exit".to_string(),
params: vec![ValueId(0)],
body: vec![JoinInst::Ret { value: Some(ValueId(0)) }],
body: vec![JoinInst::Ret {
value: Some(ValueId(0)),
}],
exit_cont: None,
};

View File

@ -77,7 +77,9 @@ impl OwnershipAnalyzer {
return Err("OwnershipAnalyzer: no FunctionDef found in 'defs'".to_string());
}
} 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
@ -87,7 +89,9 @@ impl OwnershipAnalyzer {
fn alloc_scope(&mut self, kind: ScopeKind, parent: Option<ScopeId>) -> ScopeId {
let id = ScopeId(self.next_scope_id);
self.next_scope_id += 1;
self.scopes.insert(id, ScopeInfo {
self.scopes.insert(
id,
ScopeInfo {
id,
kind,
parent,
@ -95,18 +99,27 @@ impl OwnershipAnalyzer {
reads: BTreeSet::new(),
writes: BTreeSet::new(),
condition_reads: BTreeSet::new(),
});
},
);
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);
// Collect function parameters as defined
if let Some(params) = func.get("params").and_then(|p| p.as_array()) {
for param in params {
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" => {
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")) {
self.analyze_expression(value, current_scope, false)?;
@ -234,7 +251,12 @@ impl OwnershipAnalyzer {
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
.get("kind")
.or_else(|| expr.get("type"))
@ -372,8 +394,11 @@ impl OwnershipAnalyzer {
// owned_vars: defined in this scope
for name in &scope.defined {
let is_written = scope.writes.contains(name);
let is_condition_only = scope.condition_reads.contains(name) &&
!scope.writes.iter().any(|w| w == name && !scope.condition_reads.contains(w));
let is_condition_only = scope.condition_reads.contains(name)
&& !scope
.writes
.iter()
.any(|w| w == name && !scope.condition_reads.contains(w));
plan.owned_vars.push(ScopeOwnedVar {
name: name.clone(),
@ -530,10 +555,15 @@ mod tests {
// Find the loop plan
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();
// 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();
// Find loop plan
let loop_plan = plans.iter().find(|p| {
p.owned_vars.iter().any(|v| v.name == "count")
});
let loop_plan = plans
.iter()
.find(|p| p.owned_vars.iter().any(|v| v.name == "count"));
assert!(loop_plan.is_some(), "Loop should own 'count'");
let loop_plan = loop_plan.unwrap();
// 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");
// No relay for count (it's owned)
@ -631,9 +665,9 @@ mod tests {
let plans = analyzer.analyze_json(&json).unwrap();
// Find loop plan
let loop_plan = plans.iter().find(|p| {
p.captures.iter().any(|c| c.name == "limit")
});
let loop_plan = plans
.iter()
.find(|p| p.captures.iter().any(|c| c.name == "limit"));
assert!(loop_plan.is_some(), "Loop should capture 'limit'");
let loop_plan = loop_plan.unwrap();
@ -642,7 +676,10 @@ mod tests {
assert!(loop_plan.captures.iter().any(|c| c.name == "limit"));
// 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)
assert!(!loop_plan.relay_writes.iter().any(|r| r.name == "limit"));
@ -687,9 +724,9 @@ mod tests {
let plans = analyzer.analyze_json(&json).unwrap();
// At least one loop should relay total
let any_relay = plans.iter().any(|p| {
p.relay_writes.iter().any(|r| r.name == "total")
});
let any_relay = plans
.iter()
.any(|p| p.relay_writes.iter().any(|r| r.name == "total"));
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 71-Pre: plan_validator box (reusable validation)
mod types;
mod analyzer;
#[cfg(feature = "normalized_dev")]
mod plan_to_lowering;
#[cfg(feature = "normalized_dev")]
mod ast_analyzer;
#[cfg(feature = "normalized_dev")]
mod plan_validator;
pub use types::*;
pub use analyzer::*;
mod plan_to_lowering;
#[cfg(feature = "normalized_dev")]
pub use plan_to_lowering::*;
mod plan_validator;
mod types;
pub use analyzer::*;
#[cfg(feature = "normalized_dev")]
pub use ast_analyzer::*;
#[cfg(feature = "normalized_dev")]
pub use plan_to_lowering::*;
#[cfg(feature = "normalized_dev")]
pub use plan_validator::*;
pub use types::*;

View File

@ -34,15 +34,15 @@ pub struct P3LoweringInputs {
/// # Errors
/// Returns Err if relay_writes is non-empty (Phase 58 scope limitation).
#[cfg(feature = "normalized_dev")]
pub fn plan_to_p2_inputs(
plan: &OwnershipPlan,
loop_var: &str,
) -> Result<P2LoweringInputs, String> {
pub fn plan_to_p2_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P2LoweringInputs, String> {
// Fail-Fast: relay_writes not supported in Phase 58
if !plan.relay_writes.is_empty() {
return Err(format!(
"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<_>>()
));
}
@ -206,15 +206,15 @@ pub fn plan_to_p2_inputs_with_relay(
/// # Errors
/// Returns Err if relay_writes is non-empty (Phase 59 scope limitation).
#[cfg(feature = "normalized_dev")]
pub fn plan_to_p3_inputs(
plan: &OwnershipPlan,
loop_var: &str,
) -> Result<P3LoweringInputs, String> {
pub fn plan_to_p3_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P3LoweringInputs, String> {
// Fail-Fast: relay_writes not supported in Phase 59
if !plan.relay_writes.is_empty() {
return Err(format!(
"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<_>>()
));
}
@ -382,7 +382,8 @@ mod tests {
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[0].name, "counter");
assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState);
@ -433,7 +434,9 @@ mod tests {
let result = plan_to_p2_inputs_with_relay(&plan, "i");
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]
@ -454,7 +457,9 @@ mod tests {
let result = plan_to_p2_inputs_with_relay(&plan, "i");
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]
@ -546,8 +551,7 @@ mod tests {
fn test_p3_five_plus_carriers() {
// Selfhost P3 pattern: 5+ carriers
let mut plan = OwnershipPlan::new(ScopeId(1));
plan.owned_vars
.push(ScopeOwnedVar {
plan.owned_vars.push(ScopeOwnedVar {
name: "i".to_string(),
is_written: true,
is_condition_only: false,

View File

@ -82,7 +82,9 @@ impl OwnershipPlan {
/// Get condition-only carriers (owned, written, condition-only).
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.

View File

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

View File

@ -32,11 +32,9 @@ use std::collections::HashMap;
#[cfg(feature = "normalized_dev")]
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")]
use crate::mir::join_ir::normalized::{
dev_env, normalized_dev_roundtrip_structured, shape_guard,
};
use crate::mir::join_ir::normalized::{dev_env, normalized_dev_roundtrip_structured, shape_guard};
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
// Phase 27.8: ops box からの再エクスポート
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);
if shapes.is_empty() {
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);
}

View File

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

View File

@ -72,7 +72,10 @@ impl JoinIrBlockConverter {
continue;
}
// 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)?;
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_mir_like_inst; // helper for sub-modules
pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
pub use meta::convert_join_module_to_mir_with_meta;
#[cfg(feature = "normalized_dev")]
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;
/// 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) {
let debug = dev_debug_enabled();
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")]
use super::super::convert_mir_like_inst;
use super::super::join_func_name;
use super::super::JoinIrVmBridgeError;
use super::super::convert_mir_like_inst;
use crate::ast::Span;
use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule};
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_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);
@ -188,7 +190,8 @@ fn lower_normalized_function_direct(
}
JpInst::TailCallFn { target, env } => {
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(
&mut mir_func,
current_block_id,
@ -371,7 +374,12 @@ fn build_tail_call(
effects: EffectMask::PURE,
});
(instructions, MirInstruction::Return { value: Some(result_id) })
(
instructions,
MirInstruction::Return {
value: Some(result_id),
},
)
}
fn build_exit_or_tail_branch(

View File

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

View File

@ -18,8 +18,8 @@ pub struct RoutingDecision {
/// Selected pattern (None = Fail-Fast)
pub chosen: Option<LoopPatternKind>,
/// Missing capabilities that prevented other patterns
pub missing_caps: Vec<&'static str>,
/// Missing capabilities that prevented other patterns (type-safe!)
pub missing_caps: Vec<CapabilityTag>,
/// Selection reasoning (for debugging)
pub notes: Vec<String>,
@ -40,12 +40,17 @@ impl RoutingDecision {
}
/// 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 {
chosen: None,
missing_caps,
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
// ============================================================================
mod skeleton_types;
mod canonicalizer;
mod capability_guard;
mod pattern_recognizer;
mod canonicalizer;
mod skeleton_types;
// ============================================================================
// Public Re-exports
@ -45,20 +45,11 @@ mod canonicalizer;
// Skeleton Types
pub use skeleton_types::{
CarrierRole,
CarrierSlot,
CapturedSlot,
ExitContract,
LoopSkeleton,
SkeletonStep,
UpdateKind,
CapturedSlot, CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
};
// Capability Guard
pub use capability_guard::{
capability_tags,
RoutingDecision,
};
pub use capability_guard::{CapabilityTag, RoutingDecision};
// Canonicalization Entry Point
pub use canonicalizer::canonicalize_loop_expr;
@ -97,13 +88,12 @@ mod tests {
assert!(success.is_success());
assert!(!success.is_fail_fast());
let fail = RoutingDecision::fail_fast(
vec![capability_tags::CAP_MISSING_CONST_STEP],
"Test failure".to_string(),
);
let fail =
RoutingDecision::fail_fast(vec![CapabilityTag::ConstStep], "Test failure".to_string());
assert!(!fail.is_success());
assert!(fail.is_fail_fast());
assert_eq!(fail.missing_caps.len(), 1);
assert_eq!(fail.missing_caps[0], CapabilityTag::ConstStep);
}
#[test]

View File

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

View File

@ -441,11 +441,7 @@ mod tests {
let result = DigitPosDetector::detect(&condition, &loop_body, "p");
assert!(
result.is_some(),
"Expected detection for operator {:?}",
op
);
assert!(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
///
/// Returns Vec<(name, init_expr)> for each variable declared with `local`.
pub(super) fn collect_local_declarations(
stmts: &[ASTNode],
) -> Vec<(String, Option<Box<ASTNode>>)> {
pub(super) fn collect_local_declarations(stmts: &[ASTNode]) -> Vec<(String, Option<Box<ASTNode>>)> {
let mut locals = Vec::new();
for stmt in stmts {

View File

@ -80,8 +80,9 @@ impl TrimPatternInfo {
/// - JoinInlineBoundary will handle the boundary mapping
pub fn to_carrier_info(
&self,
#[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>,
>,
) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
use super::trim_loop_helper::TrimLoopHelper;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
@ -110,11 +111,9 @@ impl TrimPatternInfo {
#[cfg(not(feature = "normalized_dev"))]
let recorder = PromotedBindingRecorder::new();
if let Err(e) = recorder.record_promotion(
&mut carrier_info,
&self.var_name,
&self.carrier_name,
) {
if let Err(e) =
recorder.record_promotion(&mut carrier_info, &self.var_name, &self.carrier_name)
{
log_trim_promotion_error(&e);
}
@ -195,8 +194,7 @@ impl LoopBodyCarrierPromoter {
// 3. 各 LoopBodyLocal に対して TrimDetector で検出を試行
for var_name in &body_locals {
// 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() {
eprintln!(
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",

View File

@ -148,11 +148,8 @@ impl DigitPosPromoter {
}
// Step 3: Use DigitPosDetector for pure detection
let detection = DigitPosDetector::detect(
condition.unwrap(),
req.loop_body,
req.loop_param_name,
);
let detection =
DigitPosDetector::detect(condition.unwrap(), req.loop_body, req.loop_param_name);
if detection.is_none() {
return DigitPosPromotionResult::CannotPromote {
@ -171,9 +168,7 @@ impl DigitPosPromoter {
}
// Step 4: Build CarrierInfo
use crate::mir::join_ir::lowering::carrier_info::{
CarrierInit, CarrierRole, CarrierVar,
};
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
// Boolean carrier (condition-only, for break)
let promoted_carrier_bool = CarrierVar {

View File

@ -424,7 +424,11 @@ mod tests {
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();
assert_eq!(result.match_var, var_name);
assert_eq!(result.carrier_name, format!("is_{}_match", var_name));

View File

@ -37,7 +37,8 @@ pub fn observe_control_form(builder: &mut MirBuilder, form: &ControlForm) {
}
let func_name = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("<unknown>");
@ -60,7 +61,8 @@ pub fn observe_control_form(builder: &mut MirBuilder, form: &ControlForm) {
// 変数スロットは SlotRegistry があればそれを優先し、なければ
// variable_map と value_types から best-effort で推定するよ。
let slots: Vec<SlotMetadata> = if let Some(reg) = builder.comp_ctx.current_slot_registry.as_mut() {
let slots: Vec<SlotMetadata> =
if let Some(reg) = builder.comp_ctx.current_slot_registry.as_mut() {
classify_slots_from_registry(reg)
} else {
classify_slots_from_variable_map(builder)
@ -94,7 +96,8 @@ pub fn observe_function_region(builder: &mut MirBuilder) {
}
let func_name = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.signature.name.as_str())
.unwrap_or("<unknown>");
@ -107,7 +110,8 @@ pub fn observe_function_region(builder: &mut MirBuilder) {
let id = RegionId(NEXT_REGION_ID.fetch_add(1, Ordering::Relaxed));
let entry_block = builder
.scope_ctx.current_function
.scope_ctx
.current_function
.as_ref()
.map(|f| f.entry_block)
.unwrap_or_else(|| crate::mir::BasicBlockId::new(0));

View File

@ -70,7 +70,9 @@ impl MirBuilder {
};
// 統一された挿入ロジック(既存パターンと完全互換)
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)
{
// CFG経由の正規化挿入predecessor順序の正規化を含む
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
@ -136,7 +138,9 @@ impl MirBuilder {
inputs: Vec<(BasicBlockId, ValueId)>,
) -> Result<(), String> {
// 統一された挿入ロジック(既存パターンと完全互換)
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)
{
// CFG経由の正規化挿入predecessor順序の正規化を含む
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,

View File

@ -201,7 +201,8 @@ pub fn emit_mir_json_for_harness(
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
.collect();
// Phase 131-11-F: Add dst_type hint from metadata for all PHI instructions
let mut phi_inst = json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming});
let mut phi_inst =
json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming});
if let Some(dst_type) = f.metadata.value_types.get(dst) {
let type_json = match dst_type {
MirType::Integer => json!("i64"),
@ -227,7 +228,9 @@ pub fn emit_mir_json_for_harness(
continue;
}
I::Copy { dst, src } => {
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
insts.push(
json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}),
);
}
I::UnaryOp { dst, op, operand } => {
let kind = match op {
@ -422,8 +425,7 @@ pub fn emit_mir_json_for_harness(
obj["dst_type"] = t;
}
insts.push(obj);
if let Some(d) = dst.map(|v| v.as_u32()) {
}
if let Some(d) = dst.map(|v| v.as_u32()) {}
}
_ => {
// Other callee types: emit generic call
@ -496,8 +498,7 @@ pub fn emit_mir_json_for_harness(
obj["dst_type"] = t;
}
insts.push(obj);
if let Some(d) = dst.map(|v| v.as_u32()) {
}
if let Some(d) = dst.map(|v| v.as_u32()) {}
}
I::NewBox {
dst,
@ -622,7 +623,8 @@ pub fn emit_mir_json_for_harness_bin(
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
.collect();
// Phase 131-11-F: Add dst_type hint from metadata for all PHI instructions
let mut phi_inst = json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming});
let mut phi_inst =
json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming});
if let Some(dst_type) = f.metadata.value_types.get(dst) {
let type_json = match dst_type {
MirType::Integer => json!("i64"),
@ -648,10 +650,11 @@ pub fn emit_mir_json_for_harness_bin(
continue;
}
I::Copy { dst, src } => {
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
insts.push(
json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}),
);
}
I::Const { dst, value } => {
match value {
I::Const { dst, value } => match value {
crate::mir::ConstValue::Integer(i) => {
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": i}}));
}
@ -674,8 +677,7 @@ pub fn emit_mir_json_for_harness_bin(
crate::mir::ConstValue::Null | crate::mir::ConstValue::Void => {
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
}
}
}
},
I::BinOp { dst, op, lhs, rhs } => {
let op_s = match op {
B::Add => "+",
@ -794,8 +796,7 @@ pub fn emit_mir_json_for_harness_bin(
obj["dst_type"] = t;
}
insts.push(obj);
if let Some(d) = dst.map(|v| v.as_u32()) {
}
if let Some(d) = dst.map(|v| v.as_u32()) {}
}
_ => {
// Other callee types: emit generic call
@ -829,8 +830,7 @@ pub fn emit_mir_json_for_harness_bin(
}
}
insts.push(obj);
if let Some(d) = dst.map(|v| v.as_u32()) {
}
if let Some(d) = dst.map(|v| v.as_u32()) {}
}
I::BoxCall {
dst,
@ -860,8 +860,7 @@ pub fn emit_mir_json_for_harness_bin(
obj["dst_type"] = t;
}
insts.push(obj);
if let Some(d) = dst.map(|v| v.as_u32()) {
}
if let Some(d) = dst.map(|v| v.as_u32()) {}
}
I::NewBox {
dst,

View File

@ -13,15 +13,15 @@ use nyash_rust::mir::join_ir::normalized::fixtures::{
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
build_jsonparser_skip_ws_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_pattern3_if_sum_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_pattern4_continue_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_args_parse_p2_structured_for_normalized_dev,
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
build_selfhost_if_sum_p3_structured_for_normalized_dev,

View File

@ -366,7 +366,8 @@ fn test_phase88_jsonparser_unescape_string_step2_min_rejects_non_const_then_i_up
assert!(res.is_err(), "expected fail-fast panic");
let msg = panic_message(res.err().unwrap());
assert!(
msg.contains("then' branch step increment") || msg.contains("then i update of form (i + const)"),
msg.contains("then' branch step increment")
|| msg.contains("then i update of form (i + const)"),
"unexpected panic message: {}",
msg
);
@ -549,7 +550,10 @@ fn test_normalized_pattern_continue_return_min_vm_bridge_direct_matches_structur
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for pattern continue+return min");
assert_eq!(
base, dev,
"vm bridge mismatch for pattern continue+return min"
);
}
/// Phase 89 P1: Pattern Continue + Return minimal - expected output test
@ -580,7 +584,10 @@ fn test_parse_string_composite_min_vm_bridge_direct_matches_structured() {
let base = run_joinir_vm_bridge(&structured, entry, &input, false);
let dev = run_joinir_vm_bridge(&structured, entry, &input, true);
assert_eq!(base, dev, "vm bridge mismatch for parse_string composite min");
assert_eq!(
base, dev,
"vm bridge mismatch for parse_string composite min"
);
}
/// Phase 90 P0: Parse String Composite minimal - expected output test
@ -614,7 +621,11 @@ fn test_continue_return_multi_min_returns_null_at_first_match() {
// Tests:
// - Refactor-A: Null literal support (returns ConstValue::Null → JoinValue::Unit)
// - Refactor-B: Multiple return-if with same value (i==3, i==7 both return null)
assert_eq!(result, JoinValue::Unit, "Expected Unit (null) from first return-if at i=3");
assert_eq!(
result,
JoinValue::Unit,
"Expected Unit (null) from first return-if at i=3"
);
}
/// Phase Next: Parse Array minimal - vm_bridge direct vs structured

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