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:
@ -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;
|
||||
|
||||
|
||||
3
src/config/env/joinir_flags.rs
vendored
3
src/config/env/joinir_flags.rs
vendored
@ -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()
|
||||
}
|
||||
|
||||
@ -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"];
|
||||
|
||||
@ -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; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集
|
||||
mod rewrite; // P1: Known rewrite & special consolidation
|
||||
mod router; // RouterPolicyBox(Unified 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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![]) {
|
||||
|
||||
@ -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));
|
||||
|
||||
// 通常パラメータ
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")?;
|
||||
|
||||
|
||||
@ -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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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("");
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()));
|
||||
|
||||
|
||||
@ -25,7 +25,8 @@ fn sample_every() -> usize {
|
||||
/// Dev‑only: emit a resolve.try event(candidates 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) {
|
||||
/// Dev‑only: emit a resolve.choose event(decision)。
|
||||
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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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!(
|
||||
|
||||
@ -26,4 +26,3 @@ impl AssignmentResolverBox {
|
||||
Err(msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
})
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
¶m_order,
|
||||
);
|
||||
let entry_func =
|
||||
create_entry_function_if_sum_break(&ctx, &parsed, init_insts, &mut entry_ctx, ¶m_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))
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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),
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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)
|
||||
///
|
||||
|
||||
@ -70,4 +70,3 @@ impl LoopToJoinLowerer {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,8 +12,7 @@
|
||||
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
|
||||
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
|
||||
|
||||
mod core;
|
||||
mod case_a_entrypoints;
|
||||
mod core;
|
||||
|
||||
pub use core::LoopToJoinLowerer;
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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(¤t_scope).unwrap().writes.insert(target.to_string());
|
||||
self.scopes
|
||||
.get_mut(¤t_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");
|
||||
}
|
||||
|
||||
@ -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::*;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 エラー型
|
||||
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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(¶ms, &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(
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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={:?}",
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
Reference in New Issue
Block a user