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
|
// New modular organization
|
||||||
mod joinir_flags;
|
mod joinir_flags;
|
||||||
mod mir_flags;
|
mod mir_flags;
|
||||||
mod vm_backend_flags;
|
|
||||||
mod parser_flags;
|
mod parser_flags;
|
||||||
|
mod selfhost_flags;
|
||||||
mod using_flags;
|
mod using_flags;
|
||||||
mod verification_flags;
|
mod verification_flags;
|
||||||
mod selfhost_flags;
|
mod vm_backend_flags;
|
||||||
|
|
||||||
pub use catalog::{env_vars, AppliesTo, EnvVarMeta};
|
pub use catalog::{env_vars, AppliesTo, EnvVarMeta};
|
||||||
pub use dump::*;
|
pub use dump::*;
|
||||||
@ -42,11 +42,11 @@ pub use stage1::*;
|
|||||||
// Backward-compatible re-exports (NO BREAKING CHANGES!)
|
// Backward-compatible re-exports (NO BREAKING CHANGES!)
|
||||||
pub use joinir_flags::*;
|
pub use joinir_flags::*;
|
||||||
pub use mir_flags::*;
|
pub use mir_flags::*;
|
||||||
pub use vm_backend_flags::*;
|
|
||||||
pub use parser_flags::*;
|
pub use parser_flags::*;
|
||||||
|
pub use selfhost_flags::*;
|
||||||
pub use using_flags::*;
|
pub use using_flags::*;
|
||||||
pub use verification_flags::*;
|
pub use verification_flags::*;
|
||||||
pub use selfhost_flags::*;
|
pub use vm_backend_flags::*;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
|||||||
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.
|
/// For fine-grained control, use `joinir_debug_level()` which returns 0-3.
|
||||||
pub fn is_joinir_debug() -> bool {
|
pub fn is_joinir_debug() -> bool {
|
||||||
std::env::var("HAKO_JOINIR_DEBUG").is_ok()
|
std::env::var("HAKO_JOINIR_DEBUG").is_ok() || std::env::var("NYASH_JOINIR_DEBUG").is_ok()
|
||||||
|| std::env::var("NYASH_JOINIR_DEBUG").is_ok()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,37 +36,15 @@ pub static OPERATORS_DIV_RULES: &[(&str, &str, &str, &str)] = &[
|
|||||||
];
|
];
|
||||||
pub fn lookup_keyword(word: &str) -> Option<&'static str> {
|
pub fn lookup_keyword(word: &str) -> Option<&'static str> {
|
||||||
for (k, t) in KEYWORDS {
|
for (k, t) in KEYWORDS {
|
||||||
if *k == word { return Some(*t); }
|
if *k == word {
|
||||||
|
return Some(*t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
|
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
|
||||||
"box",
|
"box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait",
|
||||||
"global",
|
"include", "local", "outbox", "try", "throw", "using", "from",
|
||||||
"function",
|
|
||||||
"static",
|
|
||||||
"if",
|
|
||||||
"loop",
|
|
||||||
"break",
|
|
||||||
"return",
|
|
||||||
"print",
|
|
||||||
"nowait",
|
|
||||||
"include",
|
|
||||||
"local",
|
|
||||||
"outbox",
|
|
||||||
"try",
|
|
||||||
"throw",
|
|
||||||
"using",
|
|
||||||
"from",
|
|
||||||
];
|
];
|
||||||
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[
|
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"];
|
||||||
"add",
|
|
||||||
"sub",
|
|
||||||
"mul",
|
|
||||||
"div",
|
|
||||||
"and",
|
|
||||||
"or",
|
|
||||||
"eq",
|
|
||||||
"ne",
|
|
||||||
];
|
|
||||||
|
|||||||
@ -15,20 +15,20 @@ use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
|
|||||||
use crate::mir::region::RegionId;
|
use crate::mir::region::RegionId;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
|
||||||
mod builder_calls;
|
mod builder_calls;
|
||||||
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
|
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
|
||||||
mod calls; // Call system modules (refactored from builder_calls)
|
mod calls; // Call system modules (refactored from builder_calls)
|
||||||
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
|
|
||||||
mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction
|
mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction
|
||||||
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
|
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
|
||||||
mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction
|
mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction
|
||||||
mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
|
|
||||||
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction
|
|
||||||
mod decls; // declarations lowering split
|
mod decls; // declarations lowering split
|
||||||
mod exprs; // expression lowering split
|
mod exprs; // expression lowering split
|
||||||
mod exprs_call;
|
mod exprs_call;
|
||||||
mod method_call_handlers; // Method call handler separation (Phase 3) // call(expr)
|
mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
|
||||||
// include lowering removed (using is handled in runner)
|
mod method_call_handlers;
|
||||||
|
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction // Method call handler separation (Phase 3) // call(expr)
|
||||||
|
// include lowering removed (using is handled in runner)
|
||||||
mod control_flow; // thin wrappers to centralize control-flow entrypoints
|
mod control_flow; // thin wrappers to centralize control-flow entrypoints
|
||||||
mod exprs_lambda; // lambda lowering
|
mod exprs_lambda; // lambda lowering
|
||||||
mod exprs_peek; // peek expression
|
mod exprs_peek; // peek expression
|
||||||
@ -55,9 +55,9 @@ mod receiver; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集
|
|||||||
mod rewrite; // P1: Known rewrite & special consolidation
|
mod rewrite; // P1: Known rewrite & special consolidation
|
||||||
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||||||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||||||
|
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
|
||||||
mod ssa; // LocalSSA helpers (in-block materialization)
|
mod ssa; // LocalSSA helpers (in-block materialization)
|
||||||
mod stmts;
|
mod stmts;
|
||||||
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
|
|
||||||
mod type_context; // Phase 136 follow-up: TypeContext extraction
|
mod type_context; // Phase 136 follow-up: TypeContext extraction
|
||||||
mod type_facts; // Phase 136 follow-up: Type inference facts box
|
mod type_facts; // Phase 136 follow-up: Type inference facts box
|
||||||
pub(crate) mod type_registry;
|
pub(crate) mod type_registry;
|
||||||
@ -86,8 +86,6 @@ pub struct MirBuilder {
|
|||||||
/// Direct field access for backward compatibility (migration in progress).
|
/// Direct field access for backward compatibility (migration in progress).
|
||||||
pub(super) core_ctx: core_context::CoreContext,
|
pub(super) core_ctx: core_context::CoreContext,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Phase 136 follow-up: Type information context
|
/// Phase 136 follow-up: Type information context
|
||||||
/// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
|
/// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
|
||||||
/// Direct field access for backward compatibility (migration in progress).
|
/// Direct field access for backward compatibility (migration in progress).
|
||||||
@ -125,7 +123,6 @@ pub struct MirBuilder {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
|
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
|
||||||
|
|
||||||
|
|
||||||
// Phase 2-5: binding_map removed - use binding_ctx.binding_map instead
|
// Phase 2-5: binding_map removed - use binding_ctx.binding_map instead
|
||||||
|
|
||||||
// include guards removed
|
// include guards removed
|
||||||
@ -153,7 +150,6 @@ pub struct MirBuilder {
|
|||||||
// ----------------------
|
// ----------------------
|
||||||
// Debug scope context (dev only; zero-cost when unused)
|
// Debug scope context (dev only; zero-cost when unused)
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
|
||||||
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
|
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
|
||||||
/// Key: (bb, original ValueId, kind) -> local ValueId
|
/// Key: (bb, original ValueId, kind) -> local ValueId
|
||||||
/// kind: 0=recv, 1+ reserved for future (args etc.)
|
/// kind: 0=recv, 1+ reserved for future (args etc.)
|
||||||
@ -196,7 +192,8 @@ impl MirBuilder {
|
|||||||
let core_ctx = core_context::CoreContext::new();
|
let core_ctx = core_context::CoreContext::new();
|
||||||
|
|
||||||
// Phase 136 Step 7/7: Compilation context (new SSOT)
|
// Phase 136 Step 7/7: Compilation context (new SSOT)
|
||||||
let comp_ctx = compilation_context::CompilationContext::with_plugin_sigs(plugin_method_sigs.clone());
|
let comp_ctx =
|
||||||
|
compilation_context::CompilationContext::with_plugin_sigs(plugin_method_sigs.clone());
|
||||||
|
|
||||||
// フェーズM: no_phi_mode初期化削除
|
// フェーズM: no_phi_mode初期化削除
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@ -384,7 +381,8 @@ impl MirBuilder {
|
|||||||
if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) {
|
if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) {
|
||||||
if slash > dot {
|
if slash > dot {
|
||||||
let tail = &name[dot..];
|
let tail = &name[dot..];
|
||||||
self.comp_ctx.method_tail_index
|
self.comp_ctx
|
||||||
|
.method_tail_index
|
||||||
.entry(tail.to_string())
|
.entry(tail.to_string())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(name.clone());
|
.push(name.clone());
|
||||||
@ -399,7 +397,9 @@ impl MirBuilder {
|
|||||||
|
|
||||||
fn ensure_method_tail_index(&mut self) {
|
fn ensure_method_tail_index(&mut self) {
|
||||||
let need_rebuild = match self.current_module {
|
let need_rebuild = match self.current_module {
|
||||||
Some(ref refmod) => self.comp_ctx.method_tail_index_source_len != refmod.functions.len(),
|
Some(ref refmod) => {
|
||||||
|
self.comp_ctx.method_tail_index_source_len != refmod.functions.len()
|
||||||
|
}
|
||||||
None => self.comp_ctx.method_tail_index_source_len != 0,
|
None => self.comp_ctx.method_tail_index_source_len != 0,
|
||||||
};
|
};
|
||||||
if need_rebuild {
|
if need_rebuild {
|
||||||
@ -410,7 +410,8 @@ impl MirBuilder {
|
|||||||
pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> {
|
pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> {
|
||||||
self.ensure_method_tail_index();
|
self.ensure_method_tail_index();
|
||||||
let tail = format!(".{}{}", method, format!("/{}", arity));
|
let tail = format!(".{}{}", method, format!("/{}", arity));
|
||||||
self.comp_ctx.method_tail_index
|
self.comp_ctx
|
||||||
|
.method_tail_index
|
||||||
.get(&tail)
|
.get(&tail)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@ -418,7 +419,8 @@ impl MirBuilder {
|
|||||||
|
|
||||||
pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> {
|
pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> {
|
||||||
self.ensure_method_tail_index();
|
self.ensure_method_tail_index();
|
||||||
self.comp_ctx.method_tail_index
|
self.comp_ctx
|
||||||
|
.method_tail_index
|
||||||
.get(tail.as_ref())
|
.get(tail.as_ref())
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@ -527,7 +529,9 @@ impl MirBuilder {
|
|||||||
if !suggest.is_empty() {
|
if !suggest.is_empty() {
|
||||||
msg.push_str("\nHint: symbol appears in using module(s): ");
|
msg.push_str("\nHint: symbol appears in using module(s): ");
|
||||||
msg.push_str(&suggest.join(", "));
|
msg.push_str(&suggest.join(", "));
|
||||||
msg.push_str("\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].");
|
msg.push_str(
|
||||||
|
"\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg
|
msg
|
||||||
@ -567,7 +571,9 @@ impl MirBuilder {
|
|||||||
// Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303).
|
// Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303).
|
||||||
if !var_name.starts_with("__pin$") {
|
if !var_name.starts_with("__pin$") {
|
||||||
// In SSA form, each assignment creates a new value
|
// In SSA form, each assignment creates a new value
|
||||||
self.variable_ctx.variable_map.insert(var_name.clone(), value_id);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert(var_name.clone(), value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(value_id)
|
Ok(value_id)
|
||||||
@ -582,7 +588,8 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// Precompute debug metadata to avoid borrow conflicts later
|
// Precompute debug metadata to avoid borrow conflicts later
|
||||||
let _dbg_fn_name = self
|
let _dbg_fn_name = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.clone());
|
.map(|f| f.signature.name.clone());
|
||||||
let _dbg_region_id = self.debug_current_region_id();
|
let _dbg_region_id = self.debug_current_region_id();
|
||||||
@ -694,7 +701,8 @@ impl MirBuilder {
|
|||||||
}) = callee
|
}) = callee
|
||||||
{
|
{
|
||||||
let names: Vec<String> = self
|
let names: Vec<String> = self
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, &vid)| vid == *r)
|
.filter(|(_, &vid)| vid == *r)
|
||||||
.map(|(k, _)| k.clone())
|
.map(|(k, _)| k.clone())
|
||||||
@ -760,7 +768,10 @@ impl MirBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Phase 136 Step 6/7: Use metadata_ctx for span
|
// Phase 136 Step 6/7: Use metadata_ctx for span
|
||||||
block.add_instruction_with_span(instruction.clone(), self.metadata_ctx.current_span());
|
block.add_instruction_with_span(
|
||||||
|
instruction.clone(),
|
||||||
|
self.metadata_ctx.current_span(),
|
||||||
|
);
|
||||||
// Drop the mutable borrow of `block` before updating other blocks
|
// Drop the mutable borrow of `block` before updating other blocks
|
||||||
}
|
}
|
||||||
// Update predecessor sets for branch/jump immediately so that
|
// Update predecessor sets for branch/jump immediately so that
|
||||||
@ -854,7 +865,8 @@ impl MirBuilder {
|
|||||||
effects: EffectMask::PURE,
|
effects: EffectMask::PURE,
|
||||||
})?;
|
})?;
|
||||||
// 型注釈(最小)
|
// 型注釈(最小)
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, super::MirType::Box(class.clone()));
|
.insert(dst, super::MirType::Box(class.clone()));
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
@ -872,7 +884,9 @@ impl MirBuilder {
|
|||||||
dst,
|
dst,
|
||||||
value: ConstValue::Integer(n),
|
value: ConstValue::Integer(n),
|
||||||
})?;
|
})?;
|
||||||
self.type_ctx.value_types.insert(dst, super::MirType::Integer);
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, super::MirType::Integer);
|
||||||
return Ok(dst);
|
return Ok(dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -897,7 +911,8 @@ impl MirBuilder {
|
|||||||
})?;
|
})?;
|
||||||
// Phase 15.5: Unified box type handling
|
// Phase 15.5: Unified box type handling
|
||||||
// All boxes (including former core boxes) are treated uniformly as Box types
|
// All boxes (including former core boxes) are treated uniformly as Box types
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, super::MirType::Box(class.clone()));
|
.insert(dst, super::MirType::Box(class.clone()));
|
||||||
|
|
||||||
// Record origin for optimization: dst was created by NewBox of class
|
// Record origin for optimization: dst was created by NewBox of class
|
||||||
@ -954,7 +969,9 @@ impl MirBuilder {
|
|||||||
|
|
||||||
/// Check if the current basic block is terminated
|
/// Check if the current basic block is terminated
|
||||||
fn is_current_block_terminated(&self) -> bool {
|
fn is_current_block_terminated(&self) -> bool {
|
||||||
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.scope_ctx.current_function) {
|
if let (Some(block_id), Some(ref function)) =
|
||||||
|
(self.current_block, &self.scope_ctx.current_function)
|
||||||
|
{
|
||||||
if let Some(block) = function.get_block(block_id) {
|
if let Some(block) = function.get_block(block_id) {
|
||||||
return block.is_terminated();
|
return block.is_terminated();
|
||||||
}
|
}
|
||||||
@ -1018,13 +1035,17 @@ use crate::mir::loop_pattern_detection::BindingMapProvider;
|
|||||||
|
|
||||||
impl BindingMapProvider for MirBuilder {
|
impl BindingMapProvider for MirBuilder {
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
fn get_binding_map(&self) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
fn get_binding_map(
|
||||||
|
&self,
|
||||||
|
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
||||||
// Phase 136 Step 4/7: Use binding_ctx (SSOT)
|
// Phase 136 Step 4/7: Use binding_ctx (SSOT)
|
||||||
Some(self.binding_ctx.binding_map())
|
Some(self.binding_ctx.binding_map())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
fn get_binding_map(&self) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
fn get_binding_map(
|
||||||
|
&self,
|
||||||
|
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,8 @@ pub(in super::super) fn annotate_call_result_from_func_name<S: AsRef<str>>(
|
|||||||
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
|| std::env::var("NYASH_BUILDER_DEBUG").ok().as_deref() == Some("1")
|
||||||
{
|
{
|
||||||
let bx = builder
|
let bx = builder
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&dst)
|
.get(&dst)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|||||||
@ -21,7 +21,8 @@ impl MirBuilder {
|
|||||||
// Dev trace
|
// Dev trace
|
||||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||||
let cur_fun = self
|
let cur_fun = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.clone())
|
.map(|f| f.signature.name.clone())
|
||||||
.unwrap_or_else(|| "<none>".to_string());
|
.unwrap_or_else(|| "<none>".to_string());
|
||||||
@ -270,7 +271,8 @@ impl MirBuilder {
|
|||||||
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
|
if let Err(e) = self.emit_constructor_call(math_recv, "MathBox".to_string(), vec![]) {
|
||||||
return Some(Err(e));
|
return Some(Err(e));
|
||||||
}
|
}
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(math_recv, "MathBox".to_string());
|
.insert(math_recv, "MathBox".to_string());
|
||||||
// birth()
|
// birth()
|
||||||
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
|
if let Err(e) = self.emit_method_call(None, math_recv, "birth".to_string(), vec![]) {
|
||||||
|
|||||||
@ -307,8 +307,12 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// me
|
// me
|
||||||
let me_id = f.params[0];
|
let me_id = f.params[0];
|
||||||
self.variable_ctx.variable_map.insert("me".to_string(), me_id);
|
self.variable_ctx
|
||||||
self.type_ctx.value_origin_newbox.insert(me_id, box_name.to_string());
|
.variable_map
|
||||||
|
.insert("me".to_string(), me_id);
|
||||||
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.insert(me_id, box_name.to_string());
|
||||||
slot_regs.push(("me".to_string(), None));
|
slot_regs.push(("me".to_string(), None));
|
||||||
|
|
||||||
// 通常パラメータ
|
// 通常パラメータ
|
||||||
|
|||||||
@ -208,7 +208,8 @@ impl UnifiedCallEmitterBox {
|
|||||||
crate::mir::MirType::Box(box_name.to_string()),
|
crate::mir::MirType::Box(box_name.to_string()),
|
||||||
);
|
);
|
||||||
builder
|
builder
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(singleton_id, box_name.to_string());
|
.insert(singleton_id, box_name.to_string());
|
||||||
// Cache for future use
|
// Cache for future use
|
||||||
builder
|
builder
|
||||||
@ -296,7 +297,8 @@ impl UnifiedCallEmitterBox {
|
|||||||
// Try to retrieve origin info for receiver
|
// Try to retrieve origin info for receiver
|
||||||
let recv_meta = receiver.and_then(|r| {
|
let recv_meta = receiver.and_then(|r| {
|
||||||
builder
|
builder
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&r)
|
.get(&r)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|cls| (r, cls))
|
.map(|cls| (r, cls))
|
||||||
|
|||||||
@ -394,7 +394,10 @@ mod tests {
|
|||||||
let base_id = ValueId::new(10);
|
let base_id = ValueId::new(10);
|
||||||
|
|
||||||
ctx.set_field_origin_class(base_id, "name".to_string(), "StringBox".to_string());
|
ctx.set_field_origin_class(base_id, "name".to_string(), "StringBox".to_string());
|
||||||
assert_eq!(ctx.get_field_origin_class(base_id, "name"), Some("StringBox"));
|
assert_eq!(
|
||||||
|
ctx.get_field_origin_class(base_id, "name"),
|
||||||
|
Some("StringBox")
|
||||||
|
);
|
||||||
assert_eq!(ctx.get_field_origin_class(base_id, "other"), None);
|
assert_eq!(ctx.get_field_origin_class(base_id, "other"), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,11 +13,10 @@
|
|||||||
/// - **SSOT**: Single function for all CarrierInit → ValueId generation
|
/// - **SSOT**: Single function for all CarrierInit → ValueId generation
|
||||||
/// - **Testability**: Pure function, easy to unit test
|
/// - **Testability**: Pure function, easy to unit test
|
||||||
/// - **Consistency**: Uniform debug output format
|
/// - **Consistency**: Uniform debug output format
|
||||||
|
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInit;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInit;
|
||||||
use crate::mir::value_id::ValueId;
|
|
||||||
use crate::mir::types::ConstValue;
|
use crate::mir::types::ConstValue;
|
||||||
|
use crate::mir::value_id::ValueId;
|
||||||
use crate::mir::MirInstruction;
|
use crate::mir::MirInstruction;
|
||||||
|
|
||||||
/// Generate a ValueId for the given CarrierInit policy
|
/// Generate a ValueId for the given CarrierInit policy
|
||||||
@ -120,7 +119,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(999); // Dummy host_id (not used for BoolConst)
|
let host_id = ValueId(999); // Dummy host_id (not used for BoolConst)
|
||||||
|
|
||||||
let result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "test", false);
|
let result = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::BoolConst(true),
|
||||||
|
host_id,
|
||||||
|
"test",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
|
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
|
||||||
}
|
}
|
||||||
@ -131,7 +136,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(999);
|
let host_id = ValueId(999);
|
||||||
|
|
||||||
let result = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag", false);
|
let result = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::BoolConst(false),
|
||||||
|
host_id,
|
||||||
|
"flag",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
|
assert_ne!(result, host_id, "BoolConst should emit new ValueId");
|
||||||
}
|
}
|
||||||
@ -142,7 +153,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(999);
|
let host_id = ValueId(999);
|
||||||
|
|
||||||
let result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "digit", false);
|
let result = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::LoopLocalZero,
|
||||||
|
host_id,
|
||||||
|
"digit",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
assert_ne!(result, host_id, "LoopLocalZero should emit new ValueId");
|
assert_ne!(result, host_id, "LoopLocalZero should emit new ValueId");
|
||||||
}
|
}
|
||||||
@ -153,12 +170,36 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(100);
|
let host_id = ValueId(100);
|
||||||
|
|
||||||
let result1 = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "flag1", false);
|
let result1 = init_value(
|
||||||
let result2 = init_value(&mut builder, &CarrierInit::BoolConst(false), host_id, "flag2", false);
|
&mut builder,
|
||||||
let result3 = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "counter", false);
|
&CarrierInit::BoolConst(true),
|
||||||
|
host_id,
|
||||||
|
"flag1",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let result2 = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::BoolConst(false),
|
||||||
|
host_id,
|
||||||
|
"flag2",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let result3 = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::LoopLocalZero,
|
||||||
|
host_id,
|
||||||
|
"counter",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
assert_ne!(result1, result2, "Different BoolConst calls should produce different ValueIds");
|
assert_ne!(
|
||||||
assert_ne!(result2, result3, "BoolConst and LoopLocalZero should produce different ValueIds");
|
result1, result2,
|
||||||
|
"Different BoolConst calls should produce different ValueIds"
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
result2, result3,
|
||||||
|
"BoolConst and LoopLocalZero should produce different ValueIds"
|
||||||
|
);
|
||||||
assert_ne!(result1, result3, "All ValueIds should be unique");
|
assert_ne!(result1, result3, "All ValueIds should be unique");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +209,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(42);
|
let host_id = ValueId(42);
|
||||||
|
|
||||||
let _ = init_value(&mut builder, &CarrierInit::FromHost, host_id, "debug_test", true);
|
let _ = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::FromHost,
|
||||||
|
host_id,
|
||||||
|
"debug_test",
|
||||||
|
true,
|
||||||
|
);
|
||||||
// Expected stderr output: "[carrier_init_builder] 'debug_test': FromHost -> ValueId(42)"
|
// Expected stderr output: "[carrier_init_builder] 'debug_test': FromHost -> ValueId(42)"
|
||||||
// (This test just verifies it doesn't crash)
|
// (This test just verifies it doesn't crash)
|
||||||
}
|
}
|
||||||
@ -179,7 +226,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(999);
|
let host_id = ValueId(999);
|
||||||
|
|
||||||
let _result = init_value(&mut builder, &CarrierInit::BoolConst(true), host_id, "debug_bool", true);
|
let _result = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::BoolConst(true),
|
||||||
|
host_id,
|
||||||
|
"debug_bool",
|
||||||
|
true,
|
||||||
|
);
|
||||||
// Expected stderr output: "[carrier_init_builder] 'debug_bool': BoolConst(true) -> ValueId(N)"
|
// Expected stderr output: "[carrier_init_builder] 'debug_bool': BoolConst(true) -> ValueId(N)"
|
||||||
// (This test just verifies it doesn't crash)
|
// (This test just verifies it doesn't crash)
|
||||||
}
|
}
|
||||||
@ -190,7 +243,13 @@ mod tests {
|
|||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
let host_id = ValueId(999);
|
let host_id = ValueId(999);
|
||||||
|
|
||||||
let _result = init_value(&mut builder, &CarrierInit::LoopLocalZero, host_id, "debug_zero", true);
|
let _result = init_value(
|
||||||
|
&mut builder,
|
||||||
|
&CarrierInit::LoopLocalZero,
|
||||||
|
host_id,
|
||||||
|
"debug_zero",
|
||||||
|
true,
|
||||||
|
);
|
||||||
// Expected stderr output: "[carrier_init_builder] 'debug_zero': LoopLocalZero -> ValueId(N)"
|
// Expected stderr output: "[carrier_init_builder] 'debug_zero': LoopLocalZero -> ValueId(N)"
|
||||||
// (This test just verifies it doesn't crash)
|
// (This test just verifies it doesn't crash)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,7 +143,11 @@ impl ExitLineReconnector {
|
|||||||
|
|
||||||
// Update variable_ctx.variable_map with PHI dst
|
// Update variable_ctx.variable_map with PHI dst
|
||||||
if let Some(&phi_value) = phi_dst {
|
if let Some(&phi_value) = phi_dst {
|
||||||
if let Some(var_vid) = builder.variable_ctx.variable_map.get_mut(&binding.carrier_name) {
|
if let Some(var_vid) = builder
|
||||||
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.get_mut(&binding.carrier_name)
|
||||||
|
{
|
||||||
// Phase 177-STRUCT: Always log for debugging
|
// Phase 177-STRUCT: Always log for debugging
|
||||||
if verbose {
|
if verbose {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
@ -149,14 +149,15 @@ pub(super) fn merge_and_rewrite(
|
|||||||
|
|
||||||
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
|
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
|
||||||
// ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new()
|
// ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new()
|
||||||
let mut new_block = if let Some(ref mut current_func) = builder.scope_ctx.current_function {
|
let mut new_block =
|
||||||
current_func
|
if let Some(ref mut current_func) = builder.scope_ctx.current_function {
|
||||||
.blocks
|
current_func
|
||||||
.remove(&new_block_id)
|
.blocks
|
||||||
.unwrap_or_else(|| BasicBlock::new(new_block_id))
|
.remove(&new_block_id)
|
||||||
} else {
|
.unwrap_or_else(|| BasicBlock::new(new_block_id))
|
||||||
BasicBlock::new(new_block_id)
|
} else {
|
||||||
};
|
BasicBlock::new(new_block_id)
|
||||||
|
};
|
||||||
|
|
||||||
// Phase 33-16: Identify loop entry point
|
// Phase 33-16: Identify loop entry point
|
||||||
//
|
//
|
||||||
|
|||||||
@ -96,12 +96,17 @@ impl LoopHeaderPhiBuilder {
|
|||||||
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
|
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
|
||||||
// Ignore backedge to avoid circular dependency in type inference
|
// Ignore backedge to avoid circular dependency in type inference
|
||||||
if let Some(init_type) = builder.type_ctx.value_types.get(&loop_var_init).cloned() {
|
if let Some(init_type) = builder.type_ctx.value_types.get(&loop_var_init).cloned() {
|
||||||
builder.type_ctx.value_types.insert(loop_var_phi_dst, init_type.clone());
|
builder
|
||||||
|
.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(loop_var_phi_dst, init_type.clone());
|
||||||
|
|
||||||
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
|
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)",
|
"[carrier/phi] Loop var '{}': dst=%{} entry_type={:?} (backedge ignored)",
|
||||||
loop_var_name, loop_var_phi_dst.as_u32(), init_type
|
loop_var_name,
|
||||||
|
loop_var_phi_dst.as_u32(),
|
||||||
|
init_type
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,13 +133,8 @@ impl LoopHeaderPhiBuilder {
|
|||||||
// Allocate PHIs for other carriers
|
// Allocate PHIs for other carriers
|
||||||
for (name, host_id, init, role) in carriers {
|
for (name, host_id, init, role) in carriers {
|
||||||
// Phase 86: Use centralized CarrierInit builder
|
// Phase 86: Use centralized CarrierInit builder
|
||||||
let init_value = super::carrier_init_builder::init_value(
|
let init_value =
|
||||||
builder,
|
super::carrier_init_builder::init_value(builder, &init, *host_id, &name, debug);
|
||||||
&init,
|
|
||||||
*host_id,
|
|
||||||
&name,
|
|
||||||
debug,
|
|
||||||
);
|
|
||||||
|
|
||||||
let phi_dst = builder.next_value_id();
|
let phi_dst = builder.next_value_id();
|
||||||
|
|
||||||
@ -145,12 +145,17 @@ impl LoopHeaderPhiBuilder {
|
|||||||
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
|
// Phase 131-11-H: Set PHI type from entry incoming (init value) only
|
||||||
// Ignore backedge to avoid circular dependency in type inference
|
// Ignore backedge to avoid circular dependency in type inference
|
||||||
if let Some(init_type) = builder.type_ctx.value_types.get(&init_value).cloned() {
|
if let Some(init_type) = builder.type_ctx.value_types.get(&init_value).cloned() {
|
||||||
builder.type_ctx.value_types.insert(phi_dst, init_type.clone());
|
builder
|
||||||
|
.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(phi_dst, init_type.clone());
|
||||||
|
|
||||||
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
|
if debug || std::env::var("NYASH_CARRIER_PHI_DEBUG").ok().as_deref() == Some("1") {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)",
|
"[carrier/phi] Carrier '{}': dst=%{} entry_type={:?} (backedge ignored)",
|
||||||
name, phi_dst.as_u32(), init_type
|
name,
|
||||||
|
phi_dst.as_u32(),
|
||||||
|
init_type
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,7 +228,8 @@ impl LoopHeaderPhiBuilder {
|
|||||||
|
|
||||||
// Get the header block from current function
|
// Get the header block from current function
|
||||||
let current_func = builder
|
let current_func = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
|
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
|
||||||
|
|
||||||
|
|||||||
@ -141,20 +141,24 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parity_check_skip_whitespace_match() {
|
fn test_parity_check_skip_whitespace_match() {
|
||||||
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
|
||||||
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
|
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
|
||||||
|
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
||||||
|
|
||||||
let loop_ast = build_skip_whitespace_loop();
|
let loop_ast = build_skip_whitespace_loop();
|
||||||
|
|
||||||
// Extract condition and body
|
// Extract condition and body
|
||||||
let (condition, body) = match &loop_ast {
|
let (condition, body) = match &loop_ast {
|
||||||
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()),
|
ASTNode::Loop {
|
||||||
|
condition, body, ..
|
||||||
|
} => (condition.as_ref(), body.as_slice()),
|
||||||
_ => panic!("Expected loop node"),
|
_ => panic!("Expected loop node"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run canonicalizer
|
// Run canonicalizer
|
||||||
let (_, canonical_decision) = canonicalize_loop_expr(&loop_ast).unwrap();
|
let (_, canonical_decision) = canonicalize_loop_expr(&loop_ast).unwrap();
|
||||||
let canonical_pattern = canonical_decision.chosen.expect("Canonicalizer should succeed");
|
let canonical_pattern = canonical_decision
|
||||||
|
.chosen
|
||||||
|
.expect("Canonicalizer should succeed");
|
||||||
|
|
||||||
// Run router's pattern detection
|
// Run router's pattern detection
|
||||||
let has_continue = ast_features::detect_continue_in_body(body);
|
let has_continue = ast_features::detect_continue_in_body(body);
|
||||||
@ -175,13 +179,16 @@ mod tests {
|
|||||||
crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break,
|
crate::mir::loop_pattern_detection::LoopPatternKind::Pattern2Break,
|
||||||
"Router should classify as Pattern2Break for has_break=true"
|
"Router should classify as Pattern2Break for has_break=true"
|
||||||
);
|
);
|
||||||
assert_eq!(canonical_pattern, actual_pattern, "Phase 137-5: Canonicalizer and router should agree (SSOT policy)");
|
assert_eq!(
|
||||||
|
canonical_pattern, actual_pattern,
|
||||||
|
"Phase 137-5: Canonicalizer and router should agree (SSOT policy)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parity_check_match_simple_while() {
|
fn test_parity_check_match_simple_while() {
|
||||||
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
|
||||||
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
|
use crate::mir::builder::control_flow::joinir::patterns::ast_feature_extractor as ast_features;
|
||||||
|
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
|
||||||
|
|
||||||
// Simple while loop: no break, no continue, no if
|
// Simple while loop: no break, no continue, no if
|
||||||
let loop_ast = ASTNode::Loop {
|
let loop_ast = ASTNode::Loop {
|
||||||
@ -221,7 +228,9 @@ mod tests {
|
|||||||
|
|
||||||
// Extract condition and body
|
// Extract condition and body
|
||||||
let (condition, body) = match &loop_ast {
|
let (condition, body) = match &loop_ast {
|
||||||
ASTNode::Loop { condition, body, .. } => (condition.as_ref(), body.as_slice()),
|
ASTNode::Loop {
|
||||||
|
condition, body, ..
|
||||||
|
} => (condition.as_ref(), body.as_slice()),
|
||||||
_ => panic!("Expected loop node"),
|
_ => panic!("Expected loop node"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -243,6 +252,9 @@ mod tests {
|
|||||||
// Canonicalizer should fail (not implemented yet for Pattern1)
|
// Canonicalizer should fail (not implemented yet for Pattern1)
|
||||||
assert!(canonical_result.is_ok());
|
assert!(canonical_result.is_ok());
|
||||||
let (_, decision) = canonical_result.unwrap();
|
let (_, decision) = canonical_result.unwrap();
|
||||||
assert!(decision.is_fail_fast(), "Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)");
|
assert!(
|
||||||
|
decision.is_fail_fast(),
|
||||||
|
"Canonicalizer should fail for simple patterns (Phase 3 only supports skip_whitespace)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,10 @@ impl<'a> ExitBindingBuilder<'a> {
|
|||||||
///
|
///
|
||||||
/// Success or error if boundary cannot be updated
|
/// Success or error if boundary cannot be updated
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
|
pub(crate) fn apply_to_boundary(
|
||||||
|
&self,
|
||||||
|
boundary: &mut JoinInlineBoundary,
|
||||||
|
) -> Result<(), String> {
|
||||||
// Phase 222.5-C: Delegate to applicator module
|
// Phase 222.5-C: Delegate to applicator module
|
||||||
apply_exit_bindings_to_boundary(
|
apply_exit_bindings_to_boundary(
|
||||||
self.carrier_info,
|
self.carrier_info,
|
||||||
|
|||||||
@ -77,14 +77,17 @@ impl MirBuilder {
|
|||||||
//
|
//
|
||||||
// Phase 132: Add exit_bindings to enable ExitLineReconnector
|
// Phase 132: Add exit_bindings to enable ExitLineReconnector
|
||||||
// This ensures `return i` after loop returns the final value (3) instead of initial (0)
|
// This ensures `return i` after loop returns the final value (3) instead of initial (0)
|
||||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
|
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||||
use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN;
|
use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN;
|
||||||
|
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||||
|
|
||||||
// Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First)
|
// Phase 132-Post: Extract k_exit's parameter ValueId from join_module (Box-First)
|
||||||
let k_exit_func = join_module.require_function("k_exit", "Pattern 1");
|
let k_exit_func = join_module.require_function("k_exit", "Pattern 1");
|
||||||
let join_exit_value = k_exit_func.params.first().copied()
|
let join_exit_value = k_exit_func
|
||||||
|
.params
|
||||||
|
.first()
|
||||||
|
.copied()
|
||||||
.expect("k_exit must have parameter for exit value");
|
.expect("k_exit must have parameter for exit value");
|
||||||
|
|
||||||
// Phase 132: Create exit binding for loop variable
|
// Phase 132: Create exit binding for loop variable
|
||||||
@ -98,7 +101,7 @@ impl MirBuilder {
|
|||||||
let boundary = JoinInlineBoundaryBuilder::new()
|
let boundary = JoinInlineBoundaryBuilder::new()
|
||||||
.with_inputs(
|
.with_inputs(
|
||||||
vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region)
|
vec![ValueId(PARAM_MIN)], // JoinIR's main() parameter (loop variable, Param region)
|
||||||
vec![ctx.loop_var_id], // Host's loop variable
|
vec![ctx.loop_var_id], // Host's loop variable
|
||||||
)
|
)
|
||||||
.with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update
|
.with_exit_bindings(vec![exit_binding]) // Phase 132: Enable exit PHI & variable_map update
|
||||||
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||||
|
|||||||
@ -9,8 +9,8 @@ use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
|||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
|
||||||
use crate::mir::loop_pattern_detection::error_messages;
|
use crate::mir::loop_pattern_detection::error_messages;
|
||||||
|
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
@ -42,7 +42,9 @@ fn prepare_pattern2_inputs(
|
|||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<Pattern2Inputs, String> {
|
) -> Result<Pattern2Inputs, String> {
|
||||||
use super::condition_env_builder::ConditionEnvBuilder;
|
use super::condition_env_builder::ConditionEnvBuilder;
|
||||||
use crate::mir::loop_pattern_detection::function_scope_capture::{analyze_captured_vars_v2, CapturedEnv};
|
use crate::mir::loop_pattern_detection::function_scope_capture::{
|
||||||
|
analyze_captured_vars_v2, CapturedEnv,
|
||||||
|
};
|
||||||
|
|
||||||
let loop_var_name = ctx.loop_var_name.clone();
|
let loop_var_name = ctx.loop_var_name.clone();
|
||||||
let loop_var_id = ctx.loop_var_id;
|
let loop_var_id = ctx.loop_var_id;
|
||||||
@ -88,7 +90,10 @@ fn prepare_pattern2_inputs(
|
|||||||
log_pattern2(
|
log_pattern2(
|
||||||
verbose,
|
verbose,
|
||||||
"capture",
|
"capture",
|
||||||
format!("Phase 200-C: Captured {} variables", captured_env.vars.len()),
|
format!(
|
||||||
|
"Phase 200-C: Captured {} variables",
|
||||||
|
captured_env.vars.len()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
for var in &captured_env.vars {
|
for var in &captured_env.vars {
|
||||||
log_pattern2(
|
log_pattern2(
|
||||||
@ -747,16 +752,8 @@ impl MirBuilder {
|
|||||||
|
|
||||||
trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map);
|
trace::trace().varmap("pattern2_start", &self.variable_ctx.variable_map);
|
||||||
|
|
||||||
let mut inputs =
|
let mut inputs = prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
|
||||||
prepare_pattern2_inputs(self, condition, _body, fn_body, &ctx, verbose)?;
|
promote_and_prepare_carriers(self, condition, _body, &mut inputs, debug, verbose)?;
|
||||||
promote_and_prepare_carriers(
|
|
||||||
self,
|
|
||||||
condition,
|
|
||||||
_body,
|
|
||||||
&mut inputs,
|
|
||||||
debug,
|
|
||||||
verbose,
|
|
||||||
)?;
|
|
||||||
let (effective_break_condition, normalized_body) =
|
let (effective_break_condition, normalized_body) =
|
||||||
apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?;
|
apply_trim_and_normalize(self, condition, _body, &mut inputs, verbose)?;
|
||||||
let analysis_body = normalized_body.as_deref().unwrap_or(_body);
|
let analysis_body = normalized_body.as_deref().unwrap_or(_body);
|
||||||
@ -771,10 +768,10 @@ impl MirBuilder {
|
|||||||
verbose,
|
verbose,
|
||||||
"updates",
|
"updates",
|
||||||
format!(
|
format!(
|
||||||
"Phase 176-3: Analyzed {} carrier updates",
|
"Phase 176-3: Analyzed {} carrier updates",
|
||||||
carrier_updates.len()
|
carrier_updates.len()
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let original_carrier_count = inputs.carrier_info.carriers.len();
|
let original_carrier_count = inputs.carrier_info.carriers.len();
|
||||||
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
|
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
|
||||||
@ -977,11 +974,26 @@ mod tests {
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
builder.variable_ctx.variable_map.insert("i".to_string(), ValueId(1));
|
builder
|
||||||
builder.variable_ctx.variable_map.insert("len".to_string(), ValueId(2));
|
.variable_ctx
|
||||||
builder.variable_ctx.variable_map.insert("s".to_string(), ValueId(3));
|
.variable_map
|
||||||
builder.variable_ctx.variable_map.insert("digits".to_string(), ValueId(4));
|
.insert("i".to_string(), ValueId(1));
|
||||||
builder.variable_ctx.variable_map.insert("result".to_string(), ValueId(5));
|
builder
|
||||||
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert("len".to_string(), ValueId(2));
|
||||||
|
builder
|
||||||
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert("s".to_string(), ValueId(3));
|
||||||
|
builder
|
||||||
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert("digits".to_string(), ValueId(4));
|
||||||
|
builder
|
||||||
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert("result".to_string(), ValueId(5));
|
||||||
|
|
||||||
let condition = bin(BinaryOperator::Less, var("i"), var("len"));
|
let condition = bin(BinaryOperator::Less, var("i"), var("len"));
|
||||||
|
|
||||||
@ -1008,7 +1020,9 @@ mod tests {
|
|||||||
|
|
||||||
let break_if = ASTNode::If {
|
let break_if = ASTNode::If {
|
||||||
condition: Box::new(bin(BinaryOperator::Less, var("digit_pos"), lit_i(0))),
|
condition: Box::new(bin(BinaryOperator::Less, var("digit_pos"), lit_i(0))),
|
||||||
then_body: vec![ASTNode::Break { span: Span::unknown() }],
|
then_body: vec![ASTNode::Break {
|
||||||
|
span: Span::unknown(),
|
||||||
|
}],
|
||||||
else_body: None,
|
else_body: None,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
@ -1035,9 +1049,8 @@ mod tests {
|
|||||||
|
|
||||||
let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2)
|
let ctx = build_pattern_context(&mut builder, &condition, &body, PatternVariant::Pattern2)
|
||||||
.expect("build_pattern_context");
|
.expect("build_pattern_context");
|
||||||
let mut inputs =
|
let mut inputs = prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
|
||||||
prepare_pattern2_inputs(&builder, &condition, &body, None, &ctx, false)
|
.expect("prepare_pattern2_inputs");
|
||||||
.expect("prepare_pattern2_inputs");
|
|
||||||
|
|
||||||
promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false)
|
promote_and_prepare_carriers(&mut builder, &condition, &body, &mut inputs, false, false)
|
||||||
.expect("promote_and_prepare_carriers");
|
.expect("promote_and_prepare_carriers");
|
||||||
|
|||||||
@ -183,7 +183,8 @@ impl MirBuilder {
|
|||||||
// Collect parent-defined variables from function scope
|
// Collect parent-defined variables from function scope
|
||||||
// For now, use all variables in variable_map except loop_var
|
// For now, use all variables in variable_map except loop_var
|
||||||
let parent_defined: Vec<String> = self
|
let parent_defined: Vec<String> = self
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.keys()
|
.keys()
|
||||||
.filter(|name| *name != &loop_var_name)
|
.filter(|name| *name != &loop_var_name)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -196,9 +197,11 @@ impl MirBuilder {
|
|||||||
condition_bindings.iter().map(|b| b.name.clone()).collect();
|
condition_bindings.iter().map(|b| b.name.clone()).collect();
|
||||||
|
|
||||||
// Run consistency checks
|
// Run consistency checks
|
||||||
if let Err(e) =
|
if let Err(e) = check_ownership_plan_consistency(
|
||||||
check_ownership_plan_consistency(&plan, &ctx.carrier_info, &condition_binding_names)
|
&plan,
|
||||||
{
|
&ctx.carrier_info,
|
||||||
|
&condition_binding_names,
|
||||||
|
) {
|
||||||
eprintln!("[phase64/ownership] Consistency check failed: {}", e);
|
eprintln!("[phase64/ownership] Consistency check failed: {}", e);
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,7 +50,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
|||||||
let mut continue_count = 0;
|
let mut continue_count = 0;
|
||||||
let mut has_nested_loop = false;
|
let mut has_nested_loop = false;
|
||||||
|
|
||||||
fn scan_node(node: &ASTNode, break_count: &mut usize, continue_count: &mut usize, has_nested_loop: &mut bool, depth: usize) {
|
fn scan_node(
|
||||||
|
node: &ASTNode,
|
||||||
|
break_count: &mut usize,
|
||||||
|
continue_count: &mut usize,
|
||||||
|
has_nested_loop: &mut bool,
|
||||||
|
depth: usize,
|
||||||
|
) {
|
||||||
match node {
|
match node {
|
||||||
ASTNode::Break { .. } => {
|
ASTNode::Break { .. } => {
|
||||||
*break_count += 1;
|
*break_count += 1;
|
||||||
@ -61,13 +67,29 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
|||||||
ASTNode::Loop { .. } if depth > 0 => {
|
ASTNode::Loop { .. } if depth > 0 => {
|
||||||
*has_nested_loop = true;
|
*has_nested_loop = true;
|
||||||
}
|
}
|
||||||
ASTNode::If { then_body, else_body, .. } => {
|
ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
for stmt in then_body {
|
for stmt in then_body {
|
||||||
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1);
|
scan_node(
|
||||||
|
stmt,
|
||||||
|
break_count,
|
||||||
|
continue_count,
|
||||||
|
has_nested_loop,
|
||||||
|
depth + 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(else_body) = else_body {
|
if let Some(else_body) = else_body {
|
||||||
for stmt in else_body {
|
for stmt in else_body {
|
||||||
scan_node(stmt, break_count, continue_count, has_nested_loop, depth + 1);
|
scan_node(
|
||||||
|
stmt,
|
||||||
|
break_count,
|
||||||
|
continue_count,
|
||||||
|
has_nested_loop,
|
||||||
|
depth + 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +98,13 @@ fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
scan_node(stmt, &mut break_count, &mut continue_count, &mut has_nested_loop, 0);
|
scan_node(
|
||||||
|
stmt,
|
||||||
|
&mut break_count,
|
||||||
|
&mut continue_count,
|
||||||
|
&mut has_nested_loop,
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(break_count, continue_count, has_nested_loop)
|
(break_count, continue_count, has_nested_loop)
|
||||||
@ -95,7 +123,12 @@ fn validate_continue_position(body: &[ASTNode]) -> bool {
|
|||||||
/// Phase 131-11-D: Validate break is in simple if pattern
|
/// Phase 131-11-D: Validate break is in simple if pattern
|
||||||
fn validate_break_pattern(body: &[ASTNode]) -> bool {
|
fn validate_break_pattern(body: &[ASTNode]) -> bool {
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if let ASTNode::If { then_body, else_body, .. } = stmt {
|
if let ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
..
|
||||||
|
} = stmt
|
||||||
|
{
|
||||||
// Check then branch for simple break
|
// Check then branch for simple break
|
||||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||||
// Ensure no else-break pattern
|
// Ensure no else-break pattern
|
||||||
@ -132,13 +165,17 @@ pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool
|
|||||||
// Step 2: Shape guard - infinite loop condition (true literal)
|
// Step 2: Shape guard - infinite loop condition (true literal)
|
||||||
if !ctx.features.is_infinite_loop {
|
if !ctx.features.is_infinite_loop {
|
||||||
if debug {
|
if debug {
|
||||||
trace::trace().debug("pattern5/detect", "Not an infinite loop (condition != true)");
|
trace::trace().debug(
|
||||||
|
"pattern5/detect",
|
||||||
|
"Not an infinite loop (condition != true)",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 131-11-D: Enhanced real count validation
|
// Phase 131-11-D: Enhanced real count validation
|
||||||
let (real_break_count, real_continue_count, has_nested_loop) = count_breaks_and_continues(ctx.body);
|
let (real_break_count, real_continue_count, has_nested_loop) =
|
||||||
|
count_breaks_and_continues(ctx.body);
|
||||||
|
|
||||||
// Step 3: Shape guard - exactly 1 break (real count)
|
// Step 3: Shape guard - exactly 1 break (real count)
|
||||||
if real_break_count != 1 {
|
if real_break_count != 1 {
|
||||||
@ -232,11 +269,21 @@ fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
|
|||||||
use crate::ast::BinaryOperator;
|
use crate::ast::BinaryOperator;
|
||||||
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if let ASTNode::If { condition, then_body, .. } = stmt {
|
if let ASTNode::If {
|
||||||
|
condition,
|
||||||
|
then_body,
|
||||||
|
..
|
||||||
|
} = stmt
|
||||||
|
{
|
||||||
// Check if then_body contains just break
|
// Check if then_body contains just break
|
||||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||||
// Extract counter from condition
|
// Extract counter from condition
|
||||||
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, left, .. } = condition.as_ref() {
|
if let ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::Equal,
|
||||||
|
left,
|
||||||
|
..
|
||||||
|
} = condition.as_ref()
|
||||||
|
{
|
||||||
if let ASTNode::Variable { name, .. } = left.as_ref() {
|
if let ASTNode::Variable { name, .. } = left.as_ref() {
|
||||||
return Ok(name.clone());
|
return Ok(name.clone());
|
||||||
}
|
}
|
||||||
@ -256,12 +303,26 @@ fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
|
|||||||
use crate::ast::{BinaryOperator, LiteralValue};
|
use crate::ast::{BinaryOperator, LiteralValue};
|
||||||
|
|
||||||
for stmt in body {
|
for stmt in body {
|
||||||
if let ASTNode::If { condition, then_body, .. } = stmt {
|
if let ASTNode::If {
|
||||||
|
condition,
|
||||||
|
then_body,
|
||||||
|
..
|
||||||
|
} = stmt
|
||||||
|
{
|
||||||
// Check if then_body contains just break
|
// Check if then_body contains just break
|
||||||
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
|
||||||
// Extract limit from condition
|
// Extract limit from condition
|
||||||
if let ASTNode::BinaryOp { operator: BinaryOperator::Equal, right, .. } = condition.as_ref() {
|
if let ASTNode::BinaryOp {
|
||||||
if let ASTNode::Literal { value: LiteralValue::Integer(limit), .. } = right.as_ref() {
|
operator: BinaryOperator::Equal,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} = condition.as_ref()
|
||||||
|
{
|
||||||
|
if let ASTNode::Literal {
|
||||||
|
value: LiteralValue::Integer(limit),
|
||||||
|
..
|
||||||
|
} = right.as_ref()
|
||||||
|
{
|
||||||
return Ok(*limit);
|
return Ok(*limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,9 +378,17 @@ pub(crate) fn lower(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Get counter ValueId from variable_ctx.variable_map
|
// Step 2: Get counter ValueId from variable_ctx.variable_map
|
||||||
let counter_id = builder.variable_ctx.variable_map.get(&counter_name).copied().ok_or_else(|| {
|
let counter_id = builder
|
||||||
format!("Counter variable '{}' not found in variable_ctx.variable_map", counter_name)
|
.variable_ctx
|
||||||
})?;
|
.variable_map
|
||||||
|
.get(&counter_name)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"Counter variable '{}' not found in variable_ctx.variable_map",
|
||||||
|
counter_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
trace::trace().debug(
|
trace::trace().debug(
|
||||||
@ -340,7 +409,8 @@ pub(crate) fn lower(
|
|||||||
// Step 4: Generate JoinIR
|
// Step 4: Generate JoinIR
|
||||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
use crate::mir::join_ir::{
|
use crate::mir::join_ir::{
|
||||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||||
|
MirLikeInst,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut join_value_space = JoinValueSpace::new();
|
let mut join_value_space = JoinValueSpace::new();
|
||||||
@ -356,14 +426,14 @@ pub(crate) fn lower(
|
|||||||
// ValueId allocation
|
// ValueId allocation
|
||||||
// main() locals
|
// main() locals
|
||||||
let counter_init = alloc_value(); // ValueId(1000) - initial counter
|
let counter_init = alloc_value(); // ValueId(1000) - initial counter
|
||||||
let loop_result = alloc_value(); // ValueId(1001) - result from loop_step
|
let loop_result = alloc_value(); // ValueId(1001) - result from loop_step
|
||||||
|
|
||||||
// loop_step locals
|
// loop_step locals
|
||||||
let counter_param = alloc_value(); // ValueId(1002) - parameter
|
let counter_param = alloc_value(); // ValueId(1002) - parameter
|
||||||
let const_1 = alloc_value(); // ValueId(1003) - increment constant
|
let const_1 = alloc_value(); // ValueId(1003) - increment constant
|
||||||
let counter_next = alloc_value(); // ValueId(1004) - counter + 1
|
let counter_next = alloc_value(); // ValueId(1004) - counter + 1
|
||||||
let const_limit = alloc_value(); // ValueId(1005) - limit constant
|
let const_limit = alloc_value(); // ValueId(1005) - limit constant
|
||||||
let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT
|
let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT
|
||||||
|
|
||||||
// k_exit locals
|
// k_exit locals
|
||||||
let counter_exit = alloc_value(); // ValueId(1007) - exit parameter
|
let counter_exit = alloc_value(); // ValueId(1007) - exit parameter
|
||||||
@ -397,33 +467,42 @@ pub(crate) fn lower(
|
|||||||
// ==================================================================
|
// ==================================================================
|
||||||
// loop_step(counter) function - post-increment pattern
|
// loop_step(counter) function - post-increment pattern
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
let mut loop_step_func = JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
|
let mut loop_step_func =
|
||||||
|
JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);
|
||||||
|
|
||||||
// counter_next = counter + 1
|
// counter_next = counter + 1
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: const_1,
|
.body
|
||||||
value: ConstValue::Integer(1),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: const_1,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
loop_step_func
|
||||||
dst: counter_next,
|
.body
|
||||||
op: BinOpKind::Add,
|
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
lhs: counter_param,
|
dst: counter_next,
|
||||||
rhs: const_1,
|
op: BinOpKind::Add,
|
||||||
}));
|
lhs: counter_param,
|
||||||
|
rhs: const_1,
|
||||||
|
}));
|
||||||
|
|
||||||
// break_cond = (counter_next == LIMIT)
|
// break_cond = (counter_next == LIMIT)
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
loop_step_func
|
||||||
dst: const_limit,
|
.body
|
||||||
value: ConstValue::Integer(limit),
|
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
}));
|
dst: const_limit,
|
||||||
|
value: ConstValue::Integer(limit),
|
||||||
|
}));
|
||||||
|
|
||||||
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
loop_step_func
|
||||||
dst: break_cond,
|
.body
|
||||||
op: CompareOp::Eq,
|
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
lhs: counter_next,
|
dst: break_cond,
|
||||||
rhs: const_limit,
|
op: CompareOp::Eq,
|
||||||
}));
|
lhs: counter_next,
|
||||||
|
rhs: const_limit,
|
||||||
|
}));
|
||||||
|
|
||||||
// Jump(k_exit, [counter_next], cond=break_cond)
|
// Jump(k_exit, [counter_next], cond=break_cond)
|
||||||
loop_step_func.body.push(JoinInst::Jump {
|
loop_step_func.body.push(JoinInst::Jump {
|
||||||
|
|||||||
@ -241,7 +241,8 @@ impl TrimLoopLowerer {
|
|||||||
// Step 3: Convert to CarrierInfo and merge
|
// Step 3: Convert to CarrierInfo and merge
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
// Phase 136 Step 4/7: Use binding_ctx for binding_map reference
|
// Phase 136 Step 4/7: Use binding_ctx for binding_map reference
|
||||||
let promoted_carrier = trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map()));
|
let promoted_carrier =
|
||||||
|
trim_info.to_carrier_info(Some(builder.binding_ctx.binding_map()));
|
||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
let promoted_carrier = trim_info.to_carrier_info();
|
let promoted_carrier = trim_info.to_carrier_info();
|
||||||
carrier_info.merge_from(&promoted_carrier);
|
carrier_info.merge_from(&promoted_carrier);
|
||||||
@ -352,10 +353,12 @@ impl TrimLoopLowerer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Get ValueIds for string and start
|
// Get ValueIds for string and start
|
||||||
let s_id =
|
let s_id = builder
|
||||||
builder.variable_ctx.variable_map.get(&s_name).copied().ok_or_else(|| {
|
.variable_ctx
|
||||||
format!("[TrimLoopLowerer] String variable '{}' not found", s_name)
|
.variable_map
|
||||||
})?;
|
.get(&s_name)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
|
||||||
|
|
||||||
// Compile start expression to get ValueId
|
// Compile start expression to get ValueId
|
||||||
let start_id = builder.build_expression_impl(*start_expr)?;
|
let start_id = builder.build_expression_impl(*start_expr)?;
|
||||||
@ -403,7 +406,8 @@ impl TrimLoopLowerer {
|
|||||||
|
|
||||||
// Register carrier in variable_ctx.variable_map
|
// Register carrier in variable_ctx.variable_map
|
||||||
builder
|
builder
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.insert(trim_helper.carrier_name.clone(), is_ch_match0);
|
.insert(trim_helper.carrier_name.clone(), is_ch_match0);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -6,7 +6,6 @@ use crate::mir::builder::MirBuilder;
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
impl MirBuilder {
|
impl MirBuilder {
|
||||||
|
|
||||||
/// Phase 49: Try JoinIR Frontend for mainline integration
|
/// Phase 49: Try JoinIR Frontend for mainline integration
|
||||||
///
|
///
|
||||||
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
|
/// Returns `Ok(Some(value))` if the loop is successfully lowered via JoinIR,
|
||||||
@ -29,7 +28,8 @@ impl MirBuilder {
|
|||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
// Get current function name
|
// Get current function name
|
||||||
let func_name = self
|
let func_name = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.clone())
|
.map(|f| f.signature.name.clone())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@ -138,17 +138,38 @@ impl MirBuilder {
|
|||||||
match canonicalize_loop_expr(&loop_ast) {
|
match canonicalize_loop_expr(&loop_ast) {
|
||||||
Ok((skeleton, decision)) => {
|
Ok((skeleton, decision)) => {
|
||||||
eprintln!("[loop_canonicalizer] Function: {}", func_name);
|
eprintln!("[loop_canonicalizer] Function: {}", func_name);
|
||||||
eprintln!("[loop_canonicalizer] Skeleton steps: {}", skeleton.steps.len());
|
eprintln!(
|
||||||
eprintln!("[loop_canonicalizer] Carriers: {}", skeleton.carriers.len());
|
"[loop_canonicalizer] Skeleton steps: {}",
|
||||||
eprintln!("[loop_canonicalizer] Has exits: {}", skeleton.exits.has_any_exit());
|
skeleton.steps.len()
|
||||||
eprintln!("[loop_canonicalizer] Decision: {}",
|
);
|
||||||
if decision.is_success() { "SUCCESS" } else { "FAIL_FAST" });
|
eprintln!(
|
||||||
|
"[loop_canonicalizer] Carriers: {}",
|
||||||
|
skeleton.carriers.len()
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"[loop_canonicalizer] Has exits: {}",
|
||||||
|
skeleton.exits.has_any_exit()
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"[loop_canonicalizer] Decision: {}",
|
||||||
|
if decision.is_success() {
|
||||||
|
"SUCCESS"
|
||||||
|
} else {
|
||||||
|
"FAIL_FAST"
|
||||||
|
}
|
||||||
|
);
|
||||||
if let Some(pattern) = decision.chosen {
|
if let Some(pattern) = decision.chosen {
|
||||||
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
|
eprintln!("[loop_canonicalizer] Chosen pattern: {:?}", pattern);
|
||||||
}
|
}
|
||||||
eprintln!("[loop_canonicalizer] Missing caps: {:?}", decision.missing_caps);
|
eprintln!(
|
||||||
|
"[loop_canonicalizer] Missing caps: {:?}",
|
||||||
|
decision.missing_caps
|
||||||
|
);
|
||||||
if decision.is_fail_fast() {
|
if decision.is_fail_fast() {
|
||||||
eprintln!("[loop_canonicalizer] Reason: {}", decision.notes.join("; "));
|
eprintln!(
|
||||||
|
"[loop_canonicalizer] Reason: {}",
|
||||||
|
decision.notes.join("; ")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 137-4: Router parity verification
|
// Phase 137-4: Router parity verification
|
||||||
@ -183,7 +204,11 @@ impl MirBuilder {
|
|||||||
func_name,
|
func_name,
|
||||||
&format!(
|
&format!(
|
||||||
"fn_body_ast is {}",
|
"fn_body_ast is {}",
|
||||||
if fn_body_clone.is_some() { "SOME" } else { "NONE" }
|
if fn_body_clone.is_some() {
|
||||||
|
"SOME"
|
||||||
|
} else {
|
||||||
|
"NONE"
|
||||||
|
}
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
||||||
|
|||||||
@ -47,7 +47,10 @@ impl super::MirBuilder {
|
|||||||
);
|
);
|
||||||
eprintln!("[DEBUG] params.len() = {}", params.len());
|
eprintln!("[DEBUG] params.len() = {}", params.len());
|
||||||
eprintln!("[DEBUG] body.len() = {}", body.len());
|
eprintln!("[DEBUG] body.len() = {}", body.len());
|
||||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_ctx.variable_map);
|
eprintln!(
|
||||||
|
"[DEBUG] variable_map = {:?}",
|
||||||
|
self.variable_ctx.variable_map
|
||||||
|
);
|
||||||
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
|
// Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
|
||||||
// See lifecycle.rs and builder_calls.rs for context swap implementation
|
// See lifecycle.rs and builder_calls.rs for context swap implementation
|
||||||
let _ = self.lower_static_method_as_function(
|
let _ = self.lower_static_method_as_function(
|
||||||
@ -58,7 +61,10 @@ impl super::MirBuilder {
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"[DEBUG] build_static_main_box: After lower_static_method_as_function"
|
"[DEBUG] build_static_main_box: After lower_static_method_as_function"
|
||||||
);
|
);
|
||||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_ctx.variable_map);
|
eprintln!(
|
||||||
|
"[DEBUG] variable_map = {:?}",
|
||||||
|
self.variable_ctx.variable_map
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Initialize local variables for Main.main() parameters
|
// Initialize local variables for Main.main() parameters
|
||||||
// Note: These are local variables in the wrapper main() function, NOT parameters
|
// Note: These are local variables in the wrapper main() function, NOT parameters
|
||||||
@ -75,8 +81,11 @@ impl super::MirBuilder {
|
|||||||
box_type: "ArrayBox".to_string(),
|
box_type: "ArrayBox".to_string(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
})?;
|
})?;
|
||||||
self.type_ctx.value_origin_newbox.insert(pid, "ArrayBox".to_string());
|
self.type_ctx
|
||||||
self.type_ctx.value_types
|
.value_origin_newbox
|
||||||
|
.insert(pid, "ArrayBox".to_string());
|
||||||
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(pid, super::MirType::Box("ArrayBox".to_string()));
|
.insert(pid, super::MirType::Box("ArrayBox".to_string()));
|
||||||
// Explicitly call birth() to initialize internal state
|
// Explicitly call birth() to initialize internal state
|
||||||
self.emit_instruction(MirInstruction::BoxCall {
|
self.emit_instruction(MirInstruction::BoxCall {
|
||||||
@ -206,7 +215,8 @@ impl super::MirBuilder {
|
|||||||
if let Some((k, prop)) = kind_and_prop {
|
if let Some((k, prop)) = kind_and_prop {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
let entry: &mut HashMap<String, super::PropertyKind> = self
|
let entry: &mut HashMap<String, super::PropertyKind> = self
|
||||||
.comp_ctx.property_getters_by_box
|
.comp_ctx
|
||||||
|
.property_getters_by_box
|
||||||
.entry(name.clone())
|
.entry(name.clone())
|
||||||
.or_insert_with(HashMap::new);
|
.or_insert_with(HashMap::new);
|
||||||
entry.insert(prop, k);
|
entry.insert(prop, k);
|
||||||
|
|||||||
@ -14,7 +14,9 @@ pub fn emit_integer(b: &mut MirBuilder, val: i64) -> ValueId {
|
|||||||
value: ConstValue::Integer(val),
|
value: ConstValue::Integer(val),
|
||||||
});
|
});
|
||||||
// Phase 84-1: Integer constant type annotation
|
// Phase 84-1: Integer constant type annotation
|
||||||
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Integer);
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, crate::mir::MirType::Integer);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +28,9 @@ pub fn emit_bool(b: &mut MirBuilder, val: bool) -> ValueId {
|
|||||||
value: ConstValue::Bool(val),
|
value: ConstValue::Bool(val),
|
||||||
});
|
});
|
||||||
// Phase 84-1: Bool constant type annotation
|
// Phase 84-1: Bool constant type annotation
|
||||||
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Bool);
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, crate::mir::MirType::Bool);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +42,9 @@ pub fn emit_float(b: &mut MirBuilder, val: f64) -> ValueId {
|
|||||||
value: ConstValue::Float(val),
|
value: ConstValue::Float(val),
|
||||||
});
|
});
|
||||||
// Phase 84-1: Float constant type annotation
|
// Phase 84-1: Float constant type annotation
|
||||||
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Float);
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, crate::mir::MirType::Float);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,9 +57,12 @@ pub fn emit_string<S: Into<String>>(b: &mut MirBuilder, s: S) -> ValueId {
|
|||||||
});
|
});
|
||||||
// 🎯 Phase 3-A: String constant type annotation
|
// 🎯 Phase 3-A: String constant type annotation
|
||||||
// Ensures string constants have proper Box type for method resolution
|
// Ensures string constants have proper Box type for method resolution
|
||||||
b.type_ctx.value_types
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, crate::mir::MirType::Box("StringBox".to_string()));
|
.insert(dst, crate::mir::MirType::Box("StringBox".to_string()));
|
||||||
b.type_ctx.value_origin_newbox.insert(dst, "StringBox".to_string());
|
b.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.insert(dst, "StringBox".to_string());
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +75,9 @@ pub fn emit_null(b: &mut MirBuilder) -> ValueId {
|
|||||||
});
|
});
|
||||||
// Phase 84-1: Null constant type annotation
|
// Phase 84-1: Null constant type annotation
|
||||||
// Note: MirType has no Null variant, using Unknown as fallback
|
// Note: MirType has no Null variant, using Unknown as fallback
|
||||||
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Unknown);
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, crate::mir::MirType::Unknown);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +89,8 @@ pub fn emit_void(b: &mut MirBuilder) -> ValueId {
|
|||||||
value: ConstValue::Void,
|
value: ConstValue::Void,
|
||||||
});
|
});
|
||||||
// Phase 84-1: Void constant type annotation
|
// Phase 84-1: Void constant type annotation
|
||||||
b.type_ctx.value_types.insert(dst, crate::mir::MirType::Void);
|
b.type_ctx
|
||||||
|
.value_types
|
||||||
|
.insert(dst, crate::mir::MirType::Void);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
|
|||||||
@ -222,7 +222,8 @@ impl super::MirBuilder {
|
|||||||
body.clone(),
|
body.clone(),
|
||||||
)?;
|
)?;
|
||||||
// Index static method for fallback resolution of bare calls
|
// Index static method for fallback resolution of bare calls
|
||||||
self.comp_ctx.static_method_index
|
self.comp_ctx
|
||||||
|
.static_method_index
|
||||||
.entry(method_name.clone())
|
.entry(method_name.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((name.clone(), params.len()));
|
.push((name.clone(), params.len()));
|
||||||
@ -306,14 +307,18 @@ impl super::MirBuilder {
|
|||||||
args: vec![],
|
args: vec![],
|
||||||
effects: super::EffectMask::MUT,
|
effects: super::EffectMask::MUT,
|
||||||
})?;
|
})?;
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(arr_id, "ArrayBox".to_string());
|
.insert(arr_id, "ArrayBox".to_string());
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
|
.insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
|
||||||
// TypeRegistry + trace for deterministic debug
|
// TypeRegistry + trace for deterministic debug
|
||||||
self.comp_ctx.type_registry
|
self.comp_ctx
|
||||||
|
.type_registry
|
||||||
.record_newbox(arr_id, "ArrayBox".to_string());
|
.record_newbox(arr_id, "ArrayBox".to_string());
|
||||||
self.comp_ctx.type_registry
|
self.comp_ctx
|
||||||
|
.type_registry
|
||||||
.record_type(arr_id, super::MirType::Box("ArrayBox".to_string()));
|
.record_type(arr_id, super::MirType::Box("ArrayBox".to_string()));
|
||||||
type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox");
|
type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox");
|
||||||
type_trace::ty(
|
type_trace::ty(
|
||||||
@ -350,13 +355,17 @@ impl super::MirBuilder {
|
|||||||
args: vec![],
|
args: vec![],
|
||||||
effects: super::EffectMask::MUT,
|
effects: super::EffectMask::MUT,
|
||||||
})?;
|
})?;
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(map_id, "MapBox".to_string());
|
.insert(map_id, "MapBox".to_string());
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(map_id, super::MirType::Box("MapBox".to_string()));
|
.insert(map_id, super::MirType::Box("MapBox".to_string()));
|
||||||
self.comp_ctx.type_registry
|
self.comp_ctx
|
||||||
|
.type_registry
|
||||||
.record_newbox(map_id, "MapBox".to_string());
|
.record_newbox(map_id, "MapBox".to_string());
|
||||||
self.comp_ctx.type_registry
|
self.comp_ctx
|
||||||
|
.type_registry
|
||||||
.record_type(map_id, super::MirType::Box("MapBox".to_string()));
|
.record_type(map_id, super::MirType::Box("MapBox".to_string()));
|
||||||
type_trace::origin("newbox:MapLiteral", map_id, "MapBox");
|
type_trace::origin("newbox:MapLiteral", map_id, "MapBox");
|
||||||
type_trace::ty(
|
type_trace::ty(
|
||||||
@ -404,13 +413,16 @@ impl super::MirBuilder {
|
|||||||
if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) {
|
if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) {
|
||||||
return Some(cls.clone());
|
return Some(cls.clone());
|
||||||
}
|
}
|
||||||
self.type_ctx.value_types.get(&target_val).and_then(|ty| match ty {
|
self.type_ctx
|
||||||
super::MirType::Box(name) => Some(name.clone()),
|
.value_types
|
||||||
super::MirType::String => Some("String".to_string()),
|
.get(&target_val)
|
||||||
super::MirType::Integer => Some("Integer".to_string()),
|
.and_then(|ty| match ty {
|
||||||
super::MirType::Float => Some("Float".to_string()),
|
super::MirType::Box(name) => Some(name.clone()),
|
||||||
_ => None,
|
super::MirType::String => Some("String".to_string()),
|
||||||
})
|
super::MirType::Integer => Some("Integer".to_string()),
|
||||||
|
super::MirType::Float => Some("Float".to_string()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_index_target_kind(class_hint: Option<&String>) -> String {
|
fn format_index_target_kind(class_hint: Option<&String>) -> String {
|
||||||
|
|||||||
@ -170,7 +170,8 @@ impl super::MirBuilder {
|
|||||||
captures,
|
captures,
|
||||||
me,
|
me,
|
||||||
})?;
|
})?;
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, crate::mir::MirType::Box("FunctionBox".to_string()));
|
.insert(dst, crate::mir::MirType::Box("FunctionBox".to_string()));
|
||||||
Ok(dst)
|
Ok(dst)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,7 +118,9 @@ impl super::MirBuilder {
|
|||||||
// Merge and yield result
|
// Merge and yield result
|
||||||
self.start_new_block(merge_block)?;
|
self.start_new_block(merge_block)?;
|
||||||
// フェーズM: PHI はブロック先頭に配置(cf_common 統一)
|
// フェーズM: PHI はブロック先頭に配置(cf_common 統一)
|
||||||
if let (Some(func), Some(cur_bb)) = (self.scope_ctx.current_function.as_mut(), self.current_block) {
|
if let (Some(func), Some(cur_bb)) =
|
||||||
|
(self.scope_ctx.current_function.as_mut(), self.current_block)
|
||||||
|
{
|
||||||
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
|
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
|
||||||
func,
|
func,
|
||||||
cur_bb,
|
cur_bb,
|
||||||
|
|||||||
@ -16,7 +16,12 @@ impl super::MirBuilder {
|
|||||||
|
|
||||||
// Unified members: if object class is known and has a synthetic getter for `field`,
|
// Unified members: if object class is known and has a synthetic getter for `field`,
|
||||||
// rewrite to method call `__get_<field>()`.
|
// rewrite to method call `__get_<field>()`.
|
||||||
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
|
if let Some(class_name) = self
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
if let Some(map) = self.comp_ctx.property_getters_by_box.get(&class_name) {
|
if let Some(map) = self.comp_ctx.property_getters_by_box.get(&class_name) {
|
||||||
if let Some(kind) = map.get(&field) {
|
if let Some(kind) = map.get(&field) {
|
||||||
let mname = match kind {
|
let mname = match kind {
|
||||||
@ -53,15 +58,24 @@ impl super::MirBuilder {
|
|||||||
|
|
||||||
// Propagate recorded origin class for this field if any (ValueId-scoped)
|
// Propagate recorded origin class for this field if any (ValueId-scoped)
|
||||||
if let Some(class_name) = self
|
if let Some(class_name) = self
|
||||||
.comp_ctx.field_origin_class
|
.comp_ctx
|
||||||
|
.field_origin_class
|
||||||
.get(&(object_value, field.clone()))
|
.get(&(object_value, field.clone()))
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
self.type_ctx.value_origin_newbox.insert(field_val, class_name);
|
self.type_ctx
|
||||||
} else if let Some(base_cls) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
|
.value_origin_newbox
|
||||||
|
.insert(field_val, class_name);
|
||||||
|
} else if let Some(base_cls) = self
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
// Cross-function heuristic: use class-level field origin mapping
|
// Cross-function heuristic: use class-level field origin mapping
|
||||||
if let Some(fcls) = self
|
if let Some(fcls) = self
|
||||||
.comp_ctx.field_origin_by_box
|
.comp_ctx
|
||||||
|
.field_origin_by_box
|
||||||
.get(&(base_cls.clone(), field.clone()))
|
.get(&(base_cls.clone(), field.clone()))
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
@ -78,8 +92,11 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier)
|
// If base is a known newbox and field is weak, emit WeakLoad (+ optional barrier)
|
||||||
let mut inferred_class: Option<String> =
|
let mut inferred_class: Option<String> = self
|
||||||
self.type_ctx.value_origin_newbox.get(&object_value).cloned();
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned();
|
||||||
if inferred_class.is_none() {
|
if inferred_class.is_none() {
|
||||||
if let ASTNode::FieldAccess {
|
if let ASTNode::FieldAccess {
|
||||||
object: inner_obj,
|
object: inner_obj,
|
||||||
@ -89,7 +106,8 @@ impl super::MirBuilder {
|
|||||||
{
|
{
|
||||||
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
|
if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
|
||||||
if let Some(cls) = self
|
if let Some(cls) = self
|
||||||
.comp_ctx.field_origin_class
|
.comp_ctx
|
||||||
|
.field_origin_class
|
||||||
.get(&(base_id, inner_field))
|
.get(&(base_id, inner_field))
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
@ -128,7 +146,12 @@ impl super::MirBuilder {
|
|||||||
value_result = self.local_arg(value_result);
|
value_result = self.local_arg(value_result);
|
||||||
|
|
||||||
// If base is known and field is weak, create WeakRef before store
|
// If base is known and field is weak, create WeakRef before store
|
||||||
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
|
if let Some(class_name) = self
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
|
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
|
||||||
if weak_set.contains(&field) {
|
if weak_set.contains(&field) {
|
||||||
value_result = self.emit_weak_new(value_result)?;
|
value_result = self.emit_weak_new(value_result)?;
|
||||||
@ -158,7 +181,12 @@ impl super::MirBuilder {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Write barrier if weak field
|
// Write barrier if weak field
|
||||||
if let Some(class_name) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
|
if let Some(class_name) = self
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
|
if let Some(weak_set) = self.comp_ctx.weak_fields_by_box.get(&class_name) {
|
||||||
if weak_set.contains(&field) {
|
if weak_set.contains(&field) {
|
||||||
let _ = self.emit_barrier_write(value_result);
|
let _ = self.emit_barrier_write(value_result);
|
||||||
@ -167,12 +195,24 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Record origin class for this field value if known
|
// Record origin class for this field value if known
|
||||||
if let Some(val_cls) = self.type_ctx.value_origin_newbox.get(&value_result).cloned() {
|
if let Some(val_cls) = self
|
||||||
self.comp_ctx.field_origin_class
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&value_result)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
self.comp_ctx
|
||||||
|
.field_origin_class
|
||||||
.insert((object_value, field.clone()), val_cls.clone());
|
.insert((object_value, field.clone()), val_cls.clone());
|
||||||
// Also record class-level mapping if base object class is known
|
// Also record class-level mapping if base object class is known
|
||||||
if let Some(base_cls) = self.type_ctx.value_origin_newbox.get(&object_value).cloned() {
|
if let Some(base_cls) = self
|
||||||
self.comp_ctx.field_origin_by_box
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
self.comp_ctx
|
||||||
|
.field_origin_by_box
|
||||||
.insert((base_cls, field.clone()), val_cls);
|
.insert((base_cls, field.clone()), val_cls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,10 @@ impl<'a> PhiBuilderOps for ToplevelOps<'a> {
|
|||||||
inputs: Vec<(BasicBlockId, ValueId)>,
|
inputs: Vec<(BasicBlockId, ValueId)>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// merge ブロックの先頭に PHI を挿入
|
// merge ブロックの先頭に PHI を挿入
|
||||||
if let (Some(func), Some(_cur_bb)) =
|
if let (Some(func), Some(_cur_bb)) = (
|
||||||
(self.0.scope_ctx.current_function.as_mut(), self.0.current_block)
|
self.0.scope_ctx.current_function.as_mut(),
|
||||||
{
|
self.0.current_block,
|
||||||
|
) {
|
||||||
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
|
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
|
||||||
func,
|
func,
|
||||||
block,
|
block,
|
||||||
@ -170,7 +171,11 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let val = self.build_expression(else_ast.clone())?;
|
let val = self.build_expression(else_ast.clone())?;
|
||||||
(val, Some(else_ast), Some(self.variable_ctx.variable_map.clone()))
|
(
|
||||||
|
val,
|
||||||
|
Some(else_ast),
|
||||||
|
Some(self.variable_ctx.variable_map.clone()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// No else branch: materialize PHI nodes for the empty else block
|
// No else branch: materialize PHI nodes for the empty else block
|
||||||
self.variable_ctx.variable_map = pre_if_var_map.clone();
|
self.variable_ctx.variable_map = pre_if_var_map.clone();
|
||||||
@ -222,7 +227,8 @@ impl MirBuilder {
|
|||||||
|
|
||||||
// 関数名ガードチェック
|
// 関数名ガードチェック
|
||||||
let func_name = self
|
let func_name = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.as_str())
|
.map(|f| f.signature.name.as_str())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
|
|||||||
@ -92,7 +92,8 @@ impl super::MirBuilder {
|
|||||||
} else {
|
} else {
|
||||||
for (mname, mast) in methods {
|
for (mname, mast) in methods {
|
||||||
if let ASTNode::FunctionDeclaration { params, .. } = mast {
|
if let ASTNode::FunctionDeclaration { params, .. } = mast {
|
||||||
self.comp_ctx.static_method_index
|
self.comp_ctx
|
||||||
|
.static_method_index
|
||||||
.entry(mname.clone())
|
.entry(mname.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((name.clone(), params.len()));
|
.push((name.clone(), params.len()));
|
||||||
@ -207,7 +208,8 @@ impl super::MirBuilder {
|
|||||||
params.clone(),
|
params.clone(),
|
||||||
body.clone(),
|
body.clone(),
|
||||||
)?;
|
)?;
|
||||||
self.comp_ctx.static_method_index
|
self.comp_ctx
|
||||||
|
.static_method_index
|
||||||
.entry(mname.clone())
|
.entry(mname.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((name.clone(), params.len()));
|
.push((name.clone(), params.len()));
|
||||||
@ -447,7 +449,8 @@ impl super::MirBuilder {
|
|||||||
// PHI + Copy の小グラフを DFS 探索し、1 種類の型に収束する場合のみ返す。
|
// PHI + Copy の小グラフを DFS 探索し、1 種類の型に収束する場合のみ返す。
|
||||||
// これにより Loop edge copy / If merge 後の型推論が解決できる。
|
// これにより Loop edge copy / If merge 後の型推論が解決できる。
|
||||||
if hint.is_none() {
|
if hint.is_none() {
|
||||||
let phi_resolver = PhiTypeResolver::new(&function, &self.type_ctx.value_types);
|
let phi_resolver =
|
||||||
|
PhiTypeResolver::new(&function, &self.type_ctx.value_types);
|
||||||
if let Some(mt) = phi_resolver.resolve(*v) {
|
if let Some(mt) = phi_resolver.resolve(*v) {
|
||||||
if std::env::var("NYASH_P4_DEBUG").is_ok() {
|
if std::env::var("NYASH_P4_DEBUG").is_ok() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -461,9 +464,11 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
// Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
|
||||||
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
|
if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
|
||||||
if let Some(mt) =
|
if let Some(mt) = GenericTypeResolver::resolve_from_phi(
|
||||||
GenericTypeResolver::resolve_from_phi(&function, *v, &self.type_ctx.value_types)
|
&function,
|
||||||
{
|
*v,
|
||||||
|
&self.type_ctx.value_types,
|
||||||
|
) {
|
||||||
if std::env::var("NYASH_P3C_DEBUG").is_ok() {
|
if std::env::var("NYASH_P3C_DEBUG").is_ok() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[lifecycle/p3c] {} type inferred via GenericTypeResolver: {:?}",
|
"[lifecycle/p3c] {} type inferred via GenericTypeResolver: {:?}",
|
||||||
@ -650,7 +655,9 @@ impl super::MirBuilder {
|
|||||||
.unwrap_or(MirType::Unknown),
|
.unwrap_or(MirType::Unknown),
|
||||||
Callee::Constructor { box_type } => {
|
Callee::Constructor { box_type } => {
|
||||||
let ret = MirType::Box(box_type.clone());
|
let ret = MirType::Box(box_type.clone());
|
||||||
self.type_ctx.value_origin_newbox.insert(*dst, box_type.clone());
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.insert(*dst, box_type.clone());
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
_ => MirType::Unknown,
|
_ => MirType::Unknown,
|
||||||
|
|||||||
@ -24,7 +24,8 @@ impl MeCallPolicyBox {
|
|||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
// Instance box: prefer enclosing box method (lowered function) if存在
|
// Instance box: prefer enclosing box method (lowered function) if存在
|
||||||
let enclosing_cls: Option<String> = builder
|
let enclosing_cls: Option<String> = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));
|
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,8 @@ fn sample_every() -> usize {
|
|||||||
/// Dev‑only: emit a resolve.try event(candidates inspection)。
|
/// Dev‑only: emit a resolve.try event(candidates inspection)。
|
||||||
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
|
pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
|
||||||
let fn_name = builder
|
let fn_name = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.as_str());
|
.map(|f| f.signature.name.as_str());
|
||||||
let region = builder.debug_current_region_id();
|
let region = builder.debug_current_region_id();
|
||||||
@ -35,7 +36,8 @@ pub(crate) fn emit_try(builder: &MirBuilder, meta: serde_json::Value) {
|
|||||||
/// Dev‑only: emit a resolve.choose event(decision)。
|
/// Dev‑only: emit a resolve.choose event(decision)。
|
||||||
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
|
pub(crate) fn emit_choose(builder: &MirBuilder, meta: serde_json::Value) {
|
||||||
let fn_name = builder
|
let fn_name = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.as_str());
|
.map(|f| f.signature.name.as_str());
|
||||||
let region = builder.debug_current_region_id();
|
let region = builder.debug_current_region_id();
|
||||||
|
|||||||
@ -18,13 +18,15 @@ pub(crate) fn emit_phi(builder: &MirBuilder, dst: ValueId, inputs: &Vec<(BasicBl
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let decided_t = builder
|
let decided_t = builder
|
||||||
.type_ctx.value_types
|
.type_ctx
|
||||||
|
.value_types
|
||||||
.get(&dst)
|
.get(&dst)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|tt| format!("{:?}", tt))
|
.map(|tt| format!("{:?}", tt))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let decided_o = builder
|
let decided_o = builder
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&dst)
|
.get(&dst)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@ -35,7 +37,8 @@ pub(crate) fn emit_phi(builder: &MirBuilder, dst: ValueId, inputs: &Vec<(BasicBl
|
|||||||
"decided_origin": decided_o,
|
"decided_origin": decided_o,
|
||||||
});
|
});
|
||||||
let fn_name = builder
|
let fn_name = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.as_str());
|
.map(|f| f.signature.name.as_str());
|
||||||
let region = builder.debug_current_region_id();
|
let region = builder.debug_current_region_id();
|
||||||
|
|||||||
@ -48,7 +48,8 @@ impl super::MirBuilder {
|
|||||||
BinaryOpType::Arithmetic(op) => {
|
BinaryOpType::Arithmetic(op) => {
|
||||||
// Dev: Lower '+' を演算子ボックス呼び出しに置換(既定OFF)
|
// Dev: Lower '+' を演算子ボックス呼び出しに置換(既定OFF)
|
||||||
let in_add_op = self
|
let in_add_op = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.starts_with("AddOperator.apply/"))
|
.map(|f| f.signature.name.starts_with("AddOperator.apply/"))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
@ -80,9 +81,11 @@ impl super::MirBuilder {
|
|||||||
match (lhs_type, rhs_type) {
|
match (lhs_type, rhs_type) {
|
||||||
(String, String) => {
|
(String, String) => {
|
||||||
// BOTH are strings: result is string
|
// BOTH are strings: result is string
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, MirType::Box("StringBox".to_string()));
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(dst, "StringBox".to_string());
|
.insert(dst, "StringBox".to_string());
|
||||||
}
|
}
|
||||||
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
|
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
|
||||||
@ -121,7 +124,8 @@ impl super::MirBuilder {
|
|||||||
};
|
};
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
let in_guard = self
|
let in_guard = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.starts_with(guard_prefix))
|
.map(|f| f.signature.name.starts_with(guard_prefix))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
@ -172,9 +176,11 @@ impl super::MirBuilder {
|
|||||||
match (lhs_type, rhs_type) {
|
match (lhs_type, rhs_type) {
|
||||||
(String, String) => {
|
(String, String) => {
|
||||||
// BOTH are strings: result is definitely a string
|
// BOTH are strings: result is definitely a string
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, MirType::Box("StringBox".to_string()));
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(dst, "StringBox".to_string());
|
.insert(dst, "StringBox".to_string());
|
||||||
}
|
}
|
||||||
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
|
(Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
|
||||||
@ -217,7 +223,8 @@ impl super::MirBuilder {
|
|||||||
Some(MirType::String) => true,
|
Some(MirType::String) => true,
|
||||||
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
||||||
_ => self
|
_ => self
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&lhs)
|
.get(&lhs)
|
||||||
.map(|s| s == "StringBox")
|
.map(|s| s == "StringBox")
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
@ -226,16 +233,19 @@ impl super::MirBuilder {
|
|||||||
Some(MirType::String) => true,
|
Some(MirType::String) => true,
|
||||||
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
Some(MirType::Box(bt)) if bt == "StringBox" => true,
|
||||||
_ => self
|
_ => self
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&rhs)
|
.get(&rhs)
|
||||||
.map(|s| s == "StringBox")
|
.map(|s| s == "StringBox")
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
};
|
};
|
||||||
if lhs_is_str && rhs_is_str {
|
if lhs_is_str && rhs_is_str {
|
||||||
// BOTH are strings: result is definitely a string
|
// BOTH are strings: result is definitely a string
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(dst, MirType::Box("StringBox".to_string()));
|
.insert(dst, MirType::Box("StringBox".to_string()));
|
||||||
self.type_ctx.value_origin_newbox
|
self.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.insert(dst, "StringBox".to_string());
|
.insert(dst, "StringBox".to_string());
|
||||||
} else if !lhs_is_str && !rhs_is_str {
|
} else if !lhs_is_str && !rhs_is_str {
|
||||||
// NEITHER is a string: numeric addition
|
// NEITHER is a string: numeric addition
|
||||||
@ -252,7 +262,8 @@ impl super::MirBuilder {
|
|||||||
BinaryOpType::Comparison(op) => {
|
BinaryOpType::Comparison(op) => {
|
||||||
// Dev: Lower 比較 を演算子ボックス呼び出しに置換(既定OFF)
|
// Dev: Lower 比較 を演算子ボックス呼び出しに置換(既定OFF)
|
||||||
let in_cmp_op = self
|
let in_cmp_op = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
|
.map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
@ -285,12 +296,14 @@ impl super::MirBuilder {
|
|||||||
} else {
|
} else {
|
||||||
// 既存の比較経路(安全のための型注釈/slot化含む)
|
// 既存の比較経路(安全のための型注釈/slot化含む)
|
||||||
let (lhs2_raw, rhs2_raw) = if self
|
let (lhs2_raw, rhs2_raw) = if self
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&lhs)
|
.get(&lhs)
|
||||||
.map(|s| s == "IntegerBox")
|
.map(|s| s == "IntegerBox")
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
&& self
|
&& self
|
||||||
.type_ctx.value_origin_newbox
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
.get(&rhs)
|
.get(&rhs)
|
||||||
.map(|s| s == "IntegerBox")
|
.map(|s| s == "IntegerBox")
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
@ -541,7 +554,8 @@ impl super::MirBuilder {
|
|||||||
};
|
};
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
let in_guard = self
|
let in_guard = self
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.starts_with(guard_prefix))
|
.map(|f| f.signature.name.starts_with(guard_prefix))
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|||||||
@ -23,7 +23,10 @@ pub(crate) fn annotate_me_origin(builder: &mut MirBuilder, me_id: ValueId) {
|
|||||||
}
|
}
|
||||||
if let Some(c) = cls {
|
if let Some(c) = cls {
|
||||||
// Record both origin class and a Box type hint for downstream passes(観測用)。
|
// Record both origin class and a Box type hint for downstream passes(観測用)。
|
||||||
builder.type_ctx.value_origin_newbox.insert(me_id, c.clone());
|
builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.insert(me_id, c.clone());
|
||||||
builder.type_ctx.value_types.insert(me_id, MirType::Box(c));
|
builder.type_ctx.value_types.insert(me_id, MirType::Box(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,8 @@ impl MirBuilder {
|
|||||||
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
||||||
func.update_cfg();
|
func.update_cfg();
|
||||||
}
|
}
|
||||||
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block)
|
if let (Some(func), Some(cur_bb)) =
|
||||||
|
(&self.scope_ctx.current_function, self.current_block)
|
||||||
{
|
{
|
||||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||||
func, cur_bb, &inputs,
|
func, cur_bb, &inputs,
|
||||||
@ -94,7 +95,9 @@ impl MirBuilder {
|
|||||||
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
type_hint: None, // Phase 63-6: Legacy path, no type hint
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
self.variable_ctx.variable_map.insert(pin_name.clone(), merged);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert(pin_name.clone(), merged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,7 +175,8 @@ impl MirBuilder {
|
|||||||
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
||||||
func.update_cfg();
|
func.update_cfg();
|
||||||
}
|
}
|
||||||
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block)
|
if let (Some(func), Some(cur_bb)) =
|
||||||
|
(&self.scope_ctx.current_function, self.current_block)
|
||||||
{
|
{
|
||||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||||
func, cur_bb, &inputs,
|
func, cur_bb, &inputs,
|
||||||
@ -205,7 +209,8 @@ impl MirBuilder {
|
|||||||
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
if let Some(func) = self.scope_ctx.current_function.as_mut() {
|
||||||
func.update_cfg();
|
func.update_cfg();
|
||||||
}
|
}
|
||||||
if let (Some(func), Some(cur_bb)) = (&self.scope_ctx.current_function, self.current_block)
|
if let (Some(func), Some(cur_bb)) =
|
||||||
|
(&self.scope_ctx.current_function, self.current_block)
|
||||||
{
|
{
|
||||||
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
crate::mir::phi_core::common::debug_verify_phi_inputs(
|
||||||
func, cur_bb, &inputs,
|
func, cur_bb, &inputs,
|
||||||
|
|||||||
@ -89,9 +89,10 @@ impl<'a> PhiMergeHelper<'a> {
|
|||||||
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
|
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
|
||||||
func.update_cfg();
|
func.update_cfg();
|
||||||
}
|
}
|
||||||
if let (Some(func), Some(cur_bb)) =
|
if let (Some(func), Some(cur_bb)) = (
|
||||||
(&self.builder.scope_ctx.current_function, self.builder.current_block)
|
&self.builder.scope_ctx.current_function,
|
||||||
{
|
self.builder.current_block,
|
||||||
|
) {
|
||||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
||||||
}
|
}
|
||||||
let merged = self.builder.insert_phi(inputs)?;
|
let merged = self.builder.insert_phi(inputs)?;
|
||||||
@ -135,9 +136,10 @@ impl<'a> PhiMergeHelper<'a> {
|
|||||||
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
|
if let Some(func) = self.builder.scope_ctx.current_function.as_mut() {
|
||||||
func.update_cfg();
|
func.update_cfg();
|
||||||
}
|
}
|
||||||
if let (Some(func), Some(cur_bb)) =
|
if let (Some(func), Some(cur_bb)) = (
|
||||||
(&self.builder.scope_ctx.current_function, self.builder.current_block)
|
&self.builder.scope_ctx.current_function,
|
||||||
{
|
self.builder.current_block,
|
||||||
|
) {
|
||||||
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs);
|
||||||
}
|
}
|
||||||
self.builder.insert_phi_with_dst(dst, inputs)?;
|
self.builder.insert_phi_with_dst(dst, inputs)?;
|
||||||
|
|||||||
@ -23,13 +23,15 @@ pub fn finalize_method_receiver(builder: &mut MirBuilder, callee: &mut Callee) {
|
|||||||
// Optional dev trace for receiver aliases
|
// Optional dev trace for receiver aliases
|
||||||
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
|
||||||
let current_fn = builder
|
let current_fn = builder
|
||||||
.scope_ctx.current_function
|
.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.clone())
|
.map(|f| f.signature.name.clone())
|
||||||
.unwrap_or_else(|| "<none>".to_string());
|
.unwrap_or_else(|| "<none>".to_string());
|
||||||
let bb = builder.current_block;
|
let bb = builder.current_block;
|
||||||
let names: Vec<String> = builder
|
let names: Vec<String> = builder
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, &vid)| vid == r)
|
.filter(|(_, &vid)| vid == r)
|
||||||
.map(|(k, _)| k.clone())
|
.map(|(k, _)| k.clone())
|
||||||
|
|||||||
@ -40,7 +40,12 @@ pub(crate) fn try_known_rewrite(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Receiver must be Known (origin 由来)
|
// Receiver must be Known (origin 由来)
|
||||||
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() {
|
if builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Only user-defined boxes (plugin/core boxesは対象外)
|
// Only user-defined boxes (plugin/core boxesは対象外)
|
||||||
@ -54,7 +59,11 @@ pub(crate) fn try_known_rewrite(
|
|||||||
.ok()
|
.ok()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
== Some("1");
|
== Some("1");
|
||||||
let from_new_origin = builder.type_ctx.value_origin_newbox.get(&object_value).is_some();
|
let from_new_origin = builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_some();
|
||||||
let arity = arg_values.len();
|
let arity = arg_values.len();
|
||||||
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||||
cls, method, arity,
|
cls, method, arity,
|
||||||
@ -107,7 +116,12 @@ pub(crate) fn try_known_rewrite_to_dst(
|
|||||||
if !rewrite_enabled() {
|
if !rewrite_enabled() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() {
|
if builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
||||||
@ -119,7 +133,11 @@ pub(crate) fn try_known_rewrite_to_dst(
|
|||||||
.ok()
|
.ok()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
== Some("1");
|
== Some("1");
|
||||||
let from_new_origin = builder.type_ctx.value_origin_newbox.get(&object_value).is_some();
|
let from_new_origin = builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_some();
|
||||||
let arity = arg_values.len();
|
let arity = arg_values.len();
|
||||||
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
|
||||||
cls, method, arity,
|
cls, method, arity,
|
||||||
@ -171,7 +189,12 @@ pub(crate) fn try_unique_suffix_rewrite(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Only attempt if receiver is Known (keeps behavior stable and avoids surprises)
|
// Only attempt if receiver is Known (keeps behavior stable and avoids surprises)
|
||||||
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() {
|
if builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
|
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
|
||||||
@ -222,7 +245,12 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
|||||||
if !rewrite_enabled() {
|
if !rewrite_enabled() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if builder.type_ctx.value_origin_newbox.get(&object_value).is_none() {
|
if builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.get(&object_value)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
|
let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
|
||||||
|
|||||||
@ -64,7 +64,10 @@ impl BlockScheduleBox {
|
|||||||
if std::env::var("NYASH_BLOCK_SCHEDULE_VERIFY").ok().as_deref() != Some("1") {
|
if std::env::var("NYASH_BLOCK_SCHEDULE_VERIFY").ok().as_deref() != Some("1") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (f_opt, bb_opt) = (builder.scope_ctx.current_function.as_ref(), builder.current_block);
|
let (f_opt, bb_opt) = (
|
||||||
|
builder.scope_ctx.current_function.as_ref(),
|
||||||
|
builder.current_block,
|
||||||
|
);
|
||||||
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else {
|
let (Some(fun), Some(bb_id)) = (f_opt, bb_opt) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -72,8 +72,7 @@ impl ScopeContext {
|
|||||||
/// Push new lexical scope frame
|
/// Push new lexical scope frame
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn push_lexical_scope(&mut self) {
|
pub(super) fn push_lexical_scope(&mut self) {
|
||||||
self.lexical_scope_stack
|
self.lexical_scope_stack.push(LexicalScopeFrame::default());
|
||||||
.push(LexicalScopeFrame::default());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pop lexical scope frame (returns frame for restoration)
|
/// Pop lexical scope frame (returns frame for restoration)
|
||||||
|
|||||||
@ -63,7 +63,8 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
|||||||
let mut slot_name_opt: Option<String> = None;
|
let mut slot_name_opt: Option<String> = None;
|
||||||
|
|
||||||
let names_for_v: Vec<String> = builder
|
let names_for_v: Vec<String> = builder
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
|
.filter(|(k, &vid)| vid == v && k.starts_with("__pin$"))
|
||||||
.map(|(k, _)| k.clone())
|
.map(|(k, _)| k.clone())
|
||||||
@ -116,13 +117,17 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId
|
|||||||
builder.type_ctx.value_types.insert(loc, t);
|
builder.type_ctx.value_types.insert(loc, t);
|
||||||
}
|
}
|
||||||
if let Some(cls) = builder.type_ctx.value_origin_newbox.get(&v).cloned() {
|
if let Some(cls) = builder.type_ctx.value_origin_newbox.get(&v).cloned() {
|
||||||
builder.type_ctx.value_origin_newbox.insert(loc, cls.clone());
|
builder
|
||||||
|
.type_ctx
|
||||||
|
.value_origin_newbox
|
||||||
|
.insert(loc, cls.clone());
|
||||||
|
|
||||||
// CRITICAL FIX: For receiver kind, if type is missing but origin exists,
|
// CRITICAL FIX: For receiver kind, if type is missing but origin exists,
|
||||||
// infer MirType::Box from origin
|
// infer MirType::Box from origin
|
||||||
if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() {
|
if kind == LocalKind::Recv && builder.type_ctx.value_types.get(&loc).is_none() {
|
||||||
builder
|
builder
|
||||||
.type_ctx.value_types
|
.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(loc, crate::mir::MirType::Box(cls));
|
.insert(loc, crate::mir::MirType::Box(cls));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,7 +181,8 @@ impl super::MirBuilder {
|
|||||||
idx + 1,
|
idx + 1,
|
||||||
total,
|
total,
|
||||||
self.current_block,
|
self.current_block,
|
||||||
self.scope_ctx.current_function
|
self.scope_ctx
|
||||||
|
.current_function
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.signature.name.as_str())
|
.map(|f| f.signature.name.as_str())
|
||||||
.unwrap_or("none")
|
.unwrap_or("none")
|
||||||
@ -427,9 +428,12 @@ impl super::MirBuilder {
|
|||||||
})?;
|
})?;
|
||||||
// Future spawn returns a Future<T>; the inner type is not statically known here.
|
// Future spawn returns a Future<T>; the inner type is not statically known here.
|
||||||
// Register at least Future<Unknown> to avoid later fail-fast type inference panics.
|
// Register at least Future<Unknown> to avoid later fail-fast type inference panics.
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(future_id, MirType::Future(Box::new(MirType::Unknown)));
|
.insert(future_id, MirType::Future(Box::new(MirType::Unknown)));
|
||||||
self.variable_ctx.variable_map.insert(variable.clone(), future_id);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert(variable.clone(), future_id);
|
||||||
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
||||||
reg.ensure_slot(&variable, None);
|
reg.ensure_slot(&variable, None);
|
||||||
}
|
}
|
||||||
@ -442,13 +446,17 @@ impl super::MirBuilder {
|
|||||||
value: expression_value,
|
value: expression_value,
|
||||||
})?;
|
})?;
|
||||||
let inner = self
|
let inner = self
|
||||||
.type_ctx.value_types
|
.type_ctx
|
||||||
|
.value_types
|
||||||
.get(&expression_value)
|
.get(&expression_value)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(MirType::Unknown);
|
.unwrap_or(MirType::Unknown);
|
||||||
self.type_ctx.value_types
|
self.type_ctx
|
||||||
|
.value_types
|
||||||
.insert(future_id, MirType::Future(Box::new(inner)));
|
.insert(future_id, MirType::Future(Box::new(inner)));
|
||||||
self.variable_ctx.variable_map.insert(variable.clone(), future_id);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert(variable.clone(), future_id);
|
||||||
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
||||||
reg.ensure_slot(&variable, None);
|
reg.ensure_slot(&variable, None);
|
||||||
}
|
}
|
||||||
@ -487,7 +495,9 @@ impl super::MirBuilder {
|
|||||||
"__me__".to_string()
|
"__me__".to_string()
|
||||||
};
|
};
|
||||||
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
|
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
|
||||||
self.variable_ctx.variable_map.insert("me".to_string(), me_value);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert("me".to_string(), me_value);
|
||||||
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
||||||
reg.ensure_slot("me", None);
|
reg.ensure_slot("me", None);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -149,7 +149,8 @@ impl super::MirBuilder {
|
|||||||
// Second pass: update any user variables that pointed to old pin ids to the new ones
|
// Second pass: update any user variables that pointed to old pin ids to the new ones
|
||||||
if !pin_renames.is_empty() {
|
if !pin_renames.is_empty() {
|
||||||
let snapshot: Vec<(String, super::ValueId)> = self
|
let snapshot: Vec<(String, super::ValueId)> = self
|
||||||
.variable_ctx.variable_map
|
.variable_ctx
|
||||||
|
.variable_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(k, _)| !k.starts_with("__pin$"))
|
.filter(|(k, _)| !k.starts_with("__pin$"))
|
||||||
.map(|(k, &v)| (k.clone(), v))
|
.map(|(k, &v)| (k.clone(), v))
|
||||||
@ -316,7 +317,8 @@ impl super::MirBuilder {
|
|||||||
effects,
|
effects,
|
||||||
})?;
|
})?;
|
||||||
if let Some(d) = dst {
|
if let Some(d) = dst {
|
||||||
let mut recv_box: Option<String> = self.type_ctx.value_origin_newbox.get(&box_val).cloned();
|
let mut recv_box: Option<String> =
|
||||||
|
self.type_ctx.value_origin_newbox.get(&box_val).cloned();
|
||||||
if recv_box.is_none() {
|
if recv_box.is_none() {
|
||||||
if let Some(t) = self.type_ctx.value_types.get(&box_val) {
|
if let Some(t) = self.type_ctx.value_types.get(&box_val) {
|
||||||
match t {
|
match t {
|
||||||
@ -327,7 +329,11 @@ impl super::MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(bt) = recv_box {
|
if let Some(bt) = recv_box {
|
||||||
if let Some(mt) = self.comp_ctx.plugin_method_sigs.get(&(bt.clone(), method.clone())) {
|
if let Some(mt) = self
|
||||||
|
.comp_ctx
|
||||||
|
.plugin_method_sigs
|
||||||
|
.get(&(bt.clone(), method.clone()))
|
||||||
|
{
|
||||||
self.type_ctx.value_types.insert(d, mt.clone());
|
self.type_ctx.value_types.insert(d, mt.clone());
|
||||||
} else {
|
} else {
|
||||||
// Phase 84-4-B: ビルトイン Box のメソッド戻り値型推論
|
// Phase 84-4-B: ビルトイン Box のメソッド戻り値型推論
|
||||||
@ -489,7 +495,8 @@ impl super::MirBuilder {
|
|||||||
dst: super::ValueId,
|
dst: super::ValueId,
|
||||||
src: super::ValueId,
|
src: super::ValueId,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if let (Some(ref mut function), Some(bb)) = (&mut self.scope_ctx.current_function, self.current_block)
|
if let (Some(ref mut function), Some(bb)) =
|
||||||
|
(&mut self.scope_ctx.current_function, self.current_block)
|
||||||
{
|
{
|
||||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
@ -26,4 +26,3 @@ impl AssignmentResolverBox {
|
|||||||
Err(msg)
|
Err(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,9 @@ impl super::super::MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update both ValueId and BindingId mappings
|
// Update both ValueId and BindingId mappings
|
||||||
self.variable_ctx.variable_map.insert(name.to_string(), value);
|
self.variable_ctx
|
||||||
|
.variable_map
|
||||||
|
.insert(name.to_string(), value);
|
||||||
|
|
||||||
// Phase 74: Allocate and register new BindingId for this binding
|
// Phase 74: Allocate and register new BindingId for this binding
|
||||||
let binding_id = self.allocate_binding_id();
|
let binding_id = self.allocate_binding_id();
|
||||||
|
|||||||
@ -24,17 +24,15 @@ use super::common::{
|
|||||||
build_join_module, create_k_exit_function, create_loop_context, parse_program_json,
|
build_join_module, create_k_exit_function, create_loop_context, parse_program_json,
|
||||||
process_local_inits,
|
process_local_inits,
|
||||||
};
|
};
|
||||||
use super::param_guess::{build_param_order, compute_param_guess};
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use super::if_sum_break_pattern;
|
use super::if_sum_break_pattern;
|
||||||
|
use super::param_guess::{build_param_order, compute_param_guess};
|
||||||
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
||||||
use crate::mir::join_ir::{JoinFunction, JoinInst};
|
use crate::mir::join_ir::{JoinFunction, JoinInst};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::ownership::{
|
use crate::mir::join_ir::ownership::{plan_to_p2_inputs_with_relay, OwnershipAnalyzer};
|
||||||
plan_to_p2_inputs_with_relay, OwnershipAnalyzer,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Break パターンを JoinModule に変換
|
/// Break パターンを JoinModule に変換
|
||||||
///
|
///
|
||||||
@ -217,7 +215,11 @@ fn compute_param_order_from_ownership(
|
|||||||
// Fallback: any loop plan with relay_writes
|
// Fallback: any loop plan with relay_writes
|
||||||
.or_else(|| plans.iter().find(|p| !p.relay_writes.is_empty()))
|
.or_else(|| plans.iter().find(|p| !p.relay_writes.is_empty()))
|
||||||
// Last resort: any plan that owns loop_var_name (loop-local case)
|
// Last resort: any plan that owns loop_var_name (loop-local case)
|
||||||
.or_else(|| plans.iter().find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name)))?;
|
.or_else(|| {
|
||||||
|
plans
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name))
|
||||||
|
})?;
|
||||||
|
|
||||||
let inputs = plan_to_p2_inputs_with_relay(loop_plan, loop_var_name).ok()?;
|
let inputs = plan_to_p2_inputs_with_relay(loop_plan, loop_var_name).ok()?;
|
||||||
|
|
||||||
@ -316,18 +318,19 @@ fn create_loop_step_function_break(
|
|||||||
|
|
||||||
let mut body = Vec::new();
|
let mut body = Vec::new();
|
||||||
|
|
||||||
let (loop_cond_var, loop_cond_insts) =
|
let (loop_cond_var, loop_cond_insts) = lowerer.extract_value(loop_cond_expr, &mut step_ctx);
|
||||||
lowerer.extract_value(loop_cond_expr, &mut step_ctx);
|
|
||||||
body.extend(loop_cond_insts);
|
body.extend(loop_cond_insts);
|
||||||
let acc_current = step_ctx
|
let acc_current = step_ctx
|
||||||
.get_var(acc_name)
|
.get_var(acc_name)
|
||||||
.unwrap_or_else(|| panic!("{} must be initialized", acc_name));
|
.unwrap_or_else(|| panic!("{} must be initialized", acc_name));
|
||||||
let header_exit_flag = step_ctx.alloc_var();
|
let header_exit_flag = step_ctx.alloc_var();
|
||||||
body.push(JoinInst::Compute(crate::mir::join_ir::MirLikeInst::UnaryOp {
|
body.push(JoinInst::Compute(
|
||||||
dst: header_exit_flag,
|
crate::mir::join_ir::MirLikeInst::UnaryOp {
|
||||||
op: crate::mir::join_ir::UnaryOp::Not,
|
dst: header_exit_flag,
|
||||||
operand: loop_cond_var,
|
op: crate::mir::join_ir::UnaryOp::Not,
|
||||||
}));
|
operand: loop_cond_var,
|
||||||
|
},
|
||||||
|
));
|
||||||
body.push(JoinInst::Jump {
|
body.push(JoinInst::Jump {
|
||||||
cont: ctx.k_exit_id.as_cont(),
|
cont: ctx.k_exit_id.as_cont(),
|
||||||
args: vec![acc_current],
|
args: vec![acc_current],
|
||||||
@ -398,9 +401,7 @@ fn create_loop_step_function_break(
|
|||||||
Ok(JoinFunction {
|
Ok(JoinFunction {
|
||||||
id: ctx.loop_step_id,
|
id: ctx.loop_step_id,
|
||||||
name: format!("{}_loop_step", func_name),
|
name: format!("{}_loop_step", func_name),
|
||||||
params: (0..param_names.len())
|
params: (0..param_names.len()).map(|i| ValueId(i as u32)).collect(),
|
||||||
.map(|i| ValueId(i as u32))
|
|
||||||
.collect(),
|
|
||||||
body,
|
body,
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,8 +12,8 @@
|
|||||||
//! - `create_entry_function()`: entry 関数生成
|
//! - `create_entry_function()`: entry 関数生成
|
||||||
//! - `create_k_exit_function()`: k_exit 関数生成
|
//! - `create_k_exit_function()`: k_exit 関数生成
|
||||||
|
|
||||||
use super::{AstToJoinIrLowerer, JoinModule};
|
|
||||||
use super::super::stmt_handlers::StatementEffect;
|
use super::super::stmt_handlers::StatementEffect;
|
||||||
|
use super::{AstToJoinIrLowerer, JoinModule};
|
||||||
use crate::mir::join_ir::JoinIrPhase;
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
|
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
@ -127,7 +127,10 @@ pub fn process_local_inits(
|
|||||||
if let StatementEffect::VarUpdate { name, value_id } = effect {
|
if let StatementEffect::VarUpdate { name, value_id } = effect {
|
||||||
ctx.register_param(name, value_id);
|
ctx.register_param(name, value_id);
|
||||||
} else if matches!(effect, StatementEffect::SideEffect) {
|
} else if matches!(effect, StatementEffect::SideEffect) {
|
||||||
panic!("Unexpected side-effecting statement before Loop: {}", stmt_type);
|
panic!(
|
||||||
|
"Unexpected side-effecting statement before Loop: {}",
|
||||||
|
stmt_type
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("Unexpected statement type before Loop: {}", stmt_type),
|
_ => panic!("Unexpected statement type before Loop: {}", stmt_type),
|
||||||
|
|||||||
@ -205,11 +205,12 @@ fn create_loop_step_function_continue(
|
|||||||
// Phase 89-2: StepCalculator Box に抽出済み(再利用性向上)
|
// Phase 89-2: StepCalculator Box に抽出済み(再利用性向上)
|
||||||
|
|
||||||
let mut i_next_continue = i_next;
|
let mut i_next_continue = i_next;
|
||||||
let continue_then = continue_if_stmt["then"]
|
let continue_then =
|
||||||
.as_array()
|
continue_if_stmt["then"]
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.as_array()
|
||||||
message: "Continue pattern If must have 'then' array".to_string(),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: "Continue pattern If must have 'then' array".to_string(),
|
||||||
|
})?;
|
||||||
if let Some(then_i_local) = continue_then
|
if let Some(then_i_local) = continue_then
|
||||||
.iter()
|
.iter()
|
||||||
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i"))
|
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("i"))
|
||||||
@ -283,10 +284,12 @@ fn create_loop_step_function_continue(
|
|||||||
|
|
||||||
// Phase 88: Continue 分岐側でも acc を更新できるようにする(例: acc += 1)
|
// Phase 88: Continue 分岐側でも acc を更新できるようにする(例: acc += 1)
|
||||||
let mut acc_then_val = step_acc;
|
let mut acc_then_val = step_acc;
|
||||||
if let Some(then_acc_local) = continue_then.iter().find(|stmt| {
|
if let Some(then_acc_local) = continue_then
|
||||||
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc")
|
.iter()
|
||||||
}) {
|
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
|
||||||
let (acc_then, acc_then_insts) = lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
|
{
|
||||||
|
let (acc_then, acc_then_insts) =
|
||||||
|
lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
|
||||||
body.extend(acc_then_insts);
|
body.extend(acc_then_insts);
|
||||||
acc_then_val = acc_then;
|
acc_then_val = acc_then;
|
||||||
}
|
}
|
||||||
@ -298,7 +301,7 @@ fn create_loop_step_function_continue(
|
|||||||
cond: continue_cond_var,
|
cond: continue_cond_var,
|
||||||
then_val: acc_then_val,
|
then_val: acc_then_val,
|
||||||
else_val: acc_increment,
|
else_val: acc_increment,
|
||||||
type_hint: None, // Phase 63-3
|
type_hint: None, // Phase 63-3
|
||||||
});
|
});
|
||||||
|
|
||||||
// Phase 88: Continue/通常 で次 i を切り替える
|
// Phase 88: Continue/通常 で次 i を切り替える
|
||||||
|
|||||||
@ -128,11 +128,12 @@ pub fn lower(
|
|||||||
let return_if_stmt = return_if_stmts[0];
|
let return_if_stmt = return_if_stmts[0];
|
||||||
|
|
||||||
// 5-2. Return が then にあることを確認(Fail-Fast)
|
// 5-2. Return が then にあることを確認(Fail-Fast)
|
||||||
let return_then = return_if_stmt["then"]
|
let return_then =
|
||||||
.as_array()
|
return_if_stmt["then"]
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.as_array()
|
||||||
message: "Return If must have 'then' array".to_string(),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: "Return If must have 'then' array".to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
let return_stmt = return_then
|
let return_stmt = return_then
|
||||||
.iter()
|
.iter()
|
||||||
@ -318,11 +319,12 @@ fn create_loop_step_function_continue_return(
|
|||||||
|
|
||||||
// 3. Continue pattern: i のインクリメント処理
|
// 3. Continue pattern: i のインクリメント処理
|
||||||
// Continue If の then 内に i の更新がある場合、それを使う(例: i = i + 1)
|
// Continue If の then 内に i の更新がある場合、それを使う(例: i = i + 1)
|
||||||
let continue_then = continue_if_stmt["then"]
|
let continue_then =
|
||||||
.as_array()
|
continue_if_stmt["then"]
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.as_array()
|
||||||
message: "Continue If must have 'then' array".to_string(),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: "Continue If must have 'then' array".to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
let i_update_in_continue = continue_then
|
let i_update_in_continue = continue_then
|
||||||
.iter()
|
.iter()
|
||||||
@ -411,9 +413,7 @@ fn create_loop_step_function_continue_return(
|
|||||||
// 5. acc の更新値を計算(通常パス)
|
// 5. acc の更新値を計算(通常パス)
|
||||||
let acc_update_local = loop_body
|
let acc_update_local = loop_body
|
||||||
.iter()
|
.iter()
|
||||||
.find(|stmt| {
|
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
|
||||||
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc")
|
|
||||||
})
|
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
message: format!(
|
message: format!(
|
||||||
"ContinueReturn pattern validation failed: missing accumulator update.\n\
|
"ContinueReturn pattern validation failed: missing accumulator update.\n\
|
||||||
@ -429,9 +429,10 @@ fn create_loop_step_function_continue_return(
|
|||||||
|
|
||||||
// Continue 分岐側でも acc を更新できる場合(例: acc += 1)
|
// Continue 分岐側でも acc を更新できる場合(例: acc += 1)
|
||||||
let mut acc_then_val = step_acc;
|
let mut acc_then_val = step_acc;
|
||||||
if let Some(then_acc_local) = continue_then.iter().find(|stmt| {
|
if let Some(then_acc_local) = continue_then
|
||||||
stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc")
|
.iter()
|
||||||
}) {
|
.find(|stmt| stmt["type"].as_str() == Some("Local") && stmt["name"].as_str() == Some("acc"))
|
||||||
|
{
|
||||||
let (acc_then, acc_then_insts) =
|
let (acc_then, acc_then_insts) =
|
||||||
lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
|
lowerer.extract_value(&then_acc_local["expr"], &mut step_ctx);
|
||||||
body.extend(acc_then_insts);
|
body.extend(acc_then_insts);
|
||||||
|
|||||||
@ -17,9 +17,9 @@ use super::common::{
|
|||||||
process_local_inits,
|
process_local_inits,
|
||||||
};
|
};
|
||||||
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
use super::{AstToJoinIrLowerer, JoinModule, LoweringError};
|
||||||
|
use crate::mir::join_ir::ownership::{plan_to_p3_inputs_with_relay, OwnershipAnalyzer};
|
||||||
use crate::mir::join_ir::{BinOpKind, JoinFunction, JoinInst, MirLikeInst};
|
use crate::mir::join_ir::{BinOpKind, JoinFunction, JoinInst, MirLikeInst};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use crate::mir::join_ir::ownership::{plan_to_p3_inputs_with_relay, OwnershipAnalyzer};
|
|
||||||
|
|
||||||
pub fn try_lower_if_sum_break(
|
pub fn try_lower_if_sum_break(
|
||||||
lowerer: &mut AstToJoinIrLowerer,
|
lowerer: &mut AstToJoinIrLowerer,
|
||||||
@ -65,10 +65,7 @@ pub fn try_lower_if_sum_break(
|
|||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let counter_update_stmt = loop_body
|
let counter_update_stmt = loop_body.last().expect("loop_body len checked").clone();
|
||||||
.last()
|
|
||||||
.expect("loop_body len checked")
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let loop_var_name = detect_counter_update_loop_var(loop_body).ok_or_else(|| {
|
let loop_var_name = detect_counter_update_loop_var(loop_body).ok_or_else(|| {
|
||||||
LoweringError::InvalidLoopBody {
|
LoweringError::InvalidLoopBody {
|
||||||
@ -92,14 +89,17 @@ pub fn try_lower_if_sum_break(
|
|||||||
let loop_plan = plans
|
let loop_plan = plans
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p.relay_writes.iter().any(|r| r.name == loop_var_name))
|
.find(|p| p.relay_writes.iter().any(|r| r.name == loop_var_name))
|
||||||
.or_else(|| plans.iter().find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name)))
|
.or_else(|| {
|
||||||
|
plans
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.owned_vars.iter().any(|v| v.name == loop_var_name))
|
||||||
|
})
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
message: "if-sum-break: failed to find loop ownership plan".to_string(),
|
message: "if-sum-break: failed to find loop ownership plan".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let inputs = plan_to_p3_inputs_with_relay(loop_plan, &loop_var_name).map_err(|e| {
|
let inputs = plan_to_p3_inputs_with_relay(loop_plan, &loop_var_name)
|
||||||
LoweringError::JsonParseError { message: e }
|
.map_err(|e| LoweringError::JsonParseError { message: e })?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// Ensure carriers are exactly the return vars (fail-fast mixing protection).
|
// Ensure carriers are exactly the return vars (fail-fast mixing protection).
|
||||||
let carrier_names: std::collections::BTreeSet<String> =
|
let carrier_names: std::collections::BTreeSet<String> =
|
||||||
@ -119,20 +119,25 @@ pub fn try_lower_if_sum_break(
|
|||||||
let mut param_order: Vec<(String, ValueId)> = Vec::new();
|
let mut param_order: Vec<(String, ValueId)> = Vec::new();
|
||||||
let mut seen = std::collections::BTreeSet::<String>::new();
|
let mut seen = std::collections::BTreeSet::<String>::new();
|
||||||
|
|
||||||
let loop_var_id = entry_ctx
|
let loop_var_id =
|
||||||
.get_var(&loop_var_name)
|
entry_ctx
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.get_var(&loop_var_name)
|
||||||
message: format!("loop var '{}' must be initialized before loop", loop_var_name),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: format!(
|
||||||
|
"loop var '{}' must be initialized before loop",
|
||||||
|
loop_var_name
|
||||||
|
),
|
||||||
|
})?;
|
||||||
param_order.push((loop_var_name.clone(), loop_var_id));
|
param_order.push((loop_var_name.clone(), loop_var_id));
|
||||||
seen.insert(loop_var_name.clone());
|
seen.insert(loop_var_name.clone());
|
||||||
|
|
||||||
for carrier in &inputs.carriers {
|
for carrier in &inputs.carriers {
|
||||||
let id = entry_ctx
|
let id =
|
||||||
.get_var(&carrier.name)
|
entry_ctx
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.get_var(&carrier.name)
|
||||||
message: format!("carrier '{}' must be initialized before loop", carrier.name),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: format!("carrier '{}' must be initialized before loop", carrier.name),
|
||||||
|
})?;
|
||||||
param_order.push((carrier.name.clone(), id));
|
param_order.push((carrier.name.clone(), id));
|
||||||
seen.insert(carrier.name.clone());
|
seen.insert(carrier.name.clone());
|
||||||
}
|
}
|
||||||
@ -155,13 +160,8 @@ pub fn try_lower_if_sum_break(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry_func = create_entry_function_if_sum_break(
|
let entry_func =
|
||||||
&ctx,
|
create_entry_function_if_sum_break(&ctx, &parsed, init_insts, &mut entry_ctx, ¶m_order);
|
||||||
&parsed,
|
|
||||||
init_insts,
|
|
||||||
&mut entry_ctx,
|
|
||||||
¶m_order,
|
|
||||||
);
|
|
||||||
|
|
||||||
let loop_step_func = create_loop_step_function_if_sum_break(
|
let loop_step_func = create_loop_step_function_if_sum_break(
|
||||||
lowerer,
|
lowerer,
|
||||||
@ -179,12 +179,14 @@ pub fn try_lower_if_sum_break(
|
|||||||
|
|
||||||
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
|
let k_exit_func = create_k_exit_function(&ctx, &parsed.func_name);
|
||||||
|
|
||||||
Ok(Some(build_join_module(entry_func, loop_step_func, k_exit_func)))
|
Ok(Some(build_join_module(
|
||||||
|
entry_func,
|
||||||
|
loop_step_func,
|
||||||
|
k_exit_func,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_return_var_plus_var(
|
fn parse_return_var_plus_var(expr: Option<&serde_json::Value>) -> Option<(String, String)> {
|
||||||
expr: Option<&serde_json::Value>,
|
|
||||||
) -> Option<(String, String)> {
|
|
||||||
let expr = expr?;
|
let expr = expr?;
|
||||||
if expr["type"].as_str()? != "Binary" {
|
if expr["type"].as_str()? != "Binary" {
|
||||||
return None;
|
return None;
|
||||||
@ -300,11 +302,12 @@ fn create_loop_step_function_if_sum_break(
|
|||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
message: format!("{} must exist", sum_name),
|
message: format!("{} must exist", sum_name),
|
||||||
})?;
|
})?;
|
||||||
let count_before = step_ctx
|
let count_before =
|
||||||
.get_var(count_name)
|
step_ctx
|
||||||
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
.get_var(count_name)
|
||||||
message: format!("{} must exist", count_name),
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: format!("{} must exist", count_name),
|
||||||
|
})?;
|
||||||
let acc_before = step_ctx.alloc_var();
|
let acc_before = step_ctx.alloc_var();
|
||||||
body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
dst: acc_before,
|
dst: acc_before,
|
||||||
@ -333,19 +336,14 @@ fn create_loop_step_function_if_sum_break(
|
|||||||
lowerer.extract_value(update_cond_expr, &mut step_ctx);
|
lowerer.extract_value(update_cond_expr, &mut step_ctx);
|
||||||
body.extend(update_cond_insts);
|
body.extend(update_cond_insts);
|
||||||
|
|
||||||
let (sum_then_expr, sum_else_expr) =
|
let (sum_then_expr, sum_else_expr) = extract_if_branch_assignment(update_if_stmt, sum_name)?;
|
||||||
extract_if_branch_assignment(update_if_stmt, sum_name)?;
|
|
||||||
let (count_then_expr, count_else_expr) =
|
let (count_then_expr, count_else_expr) =
|
||||||
extract_if_branch_assignment(update_if_stmt, count_name)?;
|
extract_if_branch_assignment(update_if_stmt, count_name)?;
|
||||||
|
|
||||||
let (sum_then_val, sum_then_insts) =
|
let (sum_then_val, sum_then_insts) = lowerer.extract_value(&sum_then_expr, &mut step_ctx);
|
||||||
lowerer.extract_value(&sum_then_expr, &mut step_ctx);
|
let (sum_else_val, sum_else_insts) = lowerer.extract_value(&sum_else_expr, &mut step_ctx);
|
||||||
let (sum_else_val, sum_else_insts) =
|
let (count_then_val, count_then_insts) = lowerer.extract_value(&count_then_expr, &mut step_ctx);
|
||||||
lowerer.extract_value(&sum_else_expr, &mut step_ctx);
|
let (count_else_val, count_else_insts) = lowerer.extract_value(&count_else_expr, &mut step_ctx);
|
||||||
let (count_then_val, count_then_insts) =
|
|
||||||
lowerer.extract_value(&count_then_expr, &mut step_ctx);
|
|
||||||
let (count_else_val, count_else_insts) =
|
|
||||||
lowerer.extract_value(&count_else_expr, &mut step_ctx);
|
|
||||||
body.extend(sum_then_insts);
|
body.extend(sum_then_insts);
|
||||||
body.extend(sum_else_insts);
|
body.extend(sum_else_insts);
|
||||||
body.extend(count_then_insts);
|
body.extend(count_then_insts);
|
||||||
@ -370,11 +368,12 @@ fn create_loop_step_function_if_sum_break(
|
|||||||
step_ctx.register_param(count_name.to_string(), count_next);
|
step_ctx.register_param(count_name.to_string(), count_next);
|
||||||
|
|
||||||
// Counter update (must update loop var)
|
// Counter update (must update loop var)
|
||||||
let counter_expr = counter_update_stmt.get("expr").ok_or_else(|| {
|
let counter_expr =
|
||||||
LoweringError::InvalidLoopBody {
|
counter_update_stmt
|
||||||
message: "counter update must have 'expr'".to_string(),
|
.get("expr")
|
||||||
}
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
})?;
|
message: "counter update must have 'expr'".to_string(),
|
||||||
|
})?;
|
||||||
let (i_next, i_insts) = lowerer.extract_value(counter_expr, &mut step_ctx);
|
let (i_next, i_insts) = lowerer.extract_value(counter_expr, &mut step_ctx);
|
||||||
body.extend(i_insts);
|
body.extend(i_insts);
|
||||||
step_ctx.register_param(loop_var_name.to_string(), i_next);
|
step_ctx.register_param(loop_var_name.to_string(), i_next);
|
||||||
@ -401,9 +400,7 @@ fn create_loop_step_function_if_sum_break(
|
|||||||
Ok(JoinFunction {
|
Ok(JoinFunction {
|
||||||
id: ctx.loop_step_id,
|
id: ctx.loop_step_id,
|
||||||
name: format!("{}_loop_step", func_name),
|
name: format!("{}_loop_step", func_name),
|
||||||
params: (0..param_names.len())
|
params: (0..param_names.len()).map(|i| ValueId(i as u32)).collect(),
|
||||||
.map(|i| ValueId(i as u32))
|
|
||||||
.collect(),
|
|
||||||
body,
|
body,
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
})
|
})
|
||||||
@ -432,14 +429,20 @@ fn extract_if_branch_assignment(
|
|||||||
if_stmt: &serde_json::Value,
|
if_stmt: &serde_json::Value,
|
||||||
target: &str,
|
target: &str,
|
||||||
) -> Result<(serde_json::Value, serde_json::Value), LoweringError> {
|
) -> Result<(serde_json::Value, serde_json::Value), LoweringError> {
|
||||||
fn find_assignment_expr(branch: &[serde_json::Value], target: &str) -> Result<Option<serde_json::Value>, LoweringError> {
|
fn find_assignment_expr(
|
||||||
|
branch: &[serde_json::Value],
|
||||||
|
target: &str,
|
||||||
|
) -> Result<Option<serde_json::Value>, LoweringError> {
|
||||||
let mut found: Option<serde_json::Value> = None;
|
let mut found: Option<serde_json::Value> = None;
|
||||||
for stmt in branch {
|
for stmt in branch {
|
||||||
match stmt["type"].as_str() {
|
match stmt["type"].as_str() {
|
||||||
Some("Local") => {
|
Some("Local") => {
|
||||||
let name = stmt["name"].as_str().ok_or_else(|| LoweringError::InvalidLoopBody {
|
let name =
|
||||||
message: "Local must have 'name'".to_string(),
|
stmt["name"]
|
||||||
})?;
|
.as_str()
|
||||||
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
|
message: "Local must have 'name'".to_string(),
|
||||||
|
})?;
|
||||||
if name != target {
|
if name != target {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -448,15 +451,20 @@ fn extract_if_branch_assignment(
|
|||||||
message: format!("if-sum-break: multiple assignments to '{}'", target),
|
message: format!("if-sum-break: multiple assignments to '{}'", target),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let expr = stmt.get("expr").ok_or_else(|| LoweringError::InvalidLoopBody {
|
let expr = stmt
|
||||||
message: "Local must have 'expr'".to_string(),
|
.get("expr")
|
||||||
})?;
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
|
message: "Local must have 'expr'".to_string(),
|
||||||
|
})?;
|
||||||
found = Some(expr.clone());
|
found = Some(expr.clone());
|
||||||
}
|
}
|
||||||
Some("Assignment") | Some("Assign") => {
|
Some("Assignment") | Some("Assign") => {
|
||||||
let name = stmt["target"].as_str().ok_or_else(|| LoweringError::InvalidLoopBody {
|
let name =
|
||||||
message: "Assignment must have 'target'".to_string(),
|
stmt["target"]
|
||||||
})?;
|
.as_str()
|
||||||
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
|
message: "Assignment must have 'target'".to_string(),
|
||||||
|
})?;
|
||||||
if name != target {
|
if name != target {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -465,11 +473,12 @@ fn extract_if_branch_assignment(
|
|||||||
message: format!("if-sum-break: multiple assignments to '{}'", target),
|
message: format!("if-sum-break: multiple assignments to '{}'", target),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let expr = stmt.get("expr").or_else(|| stmt.get("value")).ok_or_else(|| {
|
let expr = stmt
|
||||||
LoweringError::InvalidLoopBody {
|
.get("expr")
|
||||||
|
.or_else(|| stmt.get("value"))
|
||||||
|
.ok_or_else(|| LoweringError::InvalidLoopBody {
|
||||||
message: "Assignment must have 'expr' or 'value'".to_string(),
|
message: "Assignment must have 'expr' or 'value'".to_string(),
|
||||||
}
|
})?;
|
||||||
})?;
|
|
||||||
found = Some(expr.clone());
|
found = Some(expr.clone());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -493,11 +502,9 @@ fn extract_if_branch_assignment(
|
|||||||
message: "If must have 'else' array".to_string(),
|
message: "If must have 'else' array".to_string(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let then_expr = find_assignment_expr(then_branch, target)?.unwrap_or_else(|| {
|
let then_expr = find_assignment_expr(then_branch, target)?
|
||||||
serde_json::json!({"type":"Var","name":target})
|
.unwrap_or_else(|| serde_json::json!({"type":"Var","name":target}));
|
||||||
});
|
let else_expr = find_assignment_expr(else_branch, target)?
|
||||||
let else_expr = find_assignment_expr(else_branch, target)?.unwrap_or_else(|| {
|
.unwrap_or_else(|| serde_json::json!({"type":"Var","name":target}));
|
||||||
serde_json::json!({"type":"Var","name":target})
|
|
||||||
});
|
|
||||||
Ok((then_expr, else_expr))
|
Ok((then_expr, else_expr))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,9 @@ pub mod continue_return_pattern;
|
|||||||
pub mod filter;
|
pub mod filter;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod if_sum_break_pattern;
|
pub mod if_sum_break_pattern;
|
||||||
|
pub mod param_guess;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod parse_string_composite_pattern;
|
pub mod parse_string_composite_pattern;
|
||||||
pub mod param_guess;
|
|
||||||
pub mod print_tokens;
|
pub mod print_tokens;
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
pub mod step_calculator;
|
pub mod step_calculator;
|
||||||
|
|||||||
@ -103,7 +103,10 @@ mod tests {
|
|||||||
"lhs": {"type": "Var", "name": "i"},
|
"lhs": {"type": "Var", "name": "i"},
|
||||||
"rhs": {"type": "Int", "value": 2}
|
"rhs": {"type": "Int", "value": 2}
|
||||||
});
|
});
|
||||||
assert_eq!(StepCalculator::extract_linear_increment(&expr, "i"), Some(2));
|
assert_eq!(
|
||||||
|
StepCalculator::extract_linear_increment(&expr, "i"),
|
||||||
|
Some(2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -114,7 +117,10 @@ mod tests {
|
|||||||
"lhs": {"type": "Int", "value": 3},
|
"lhs": {"type": "Int", "value": 3},
|
||||||
"rhs": {"type": "Var", "name": "i"}
|
"rhs": {"type": "Var", "name": "i"}
|
||||||
});
|
});
|
||||||
assert_eq!(StepCalculator::extract_linear_increment(&expr, "i"), Some(3));
|
assert_eq!(
|
||||||
|
StepCalculator::extract_linear_increment(&expr, "i"),
|
||||||
|
Some(3)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -87,15 +87,13 @@ impl AstToJoinIrLowerer {
|
|||||||
.as_str()
|
.as_str()
|
||||||
.expect("Function must have 'name'");
|
.expect("Function must have 'name'");
|
||||||
|
|
||||||
let route = resolve_function_route(func_name)
|
let route = resolve_function_route(func_name).unwrap_or_else(|msg| panic!("{msg}"));
|
||||||
.unwrap_or_else(|msg| panic!("{msg}"));
|
|
||||||
|
|
||||||
match route {
|
match route {
|
||||||
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
|
FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
|
||||||
FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name(
|
FunctionRoute::LoopFrontend => {
|
||||||
self,
|
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
|
||||||
program_json,
|
}
|
||||||
),
|
|
||||||
FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
|
FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
|
||||||
FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
|
FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,8 +57,8 @@
|
|||||||
//! // - carrier_info.carriers["is_digit_pos"].binding_id = Some(BindingId(10))
|
//! // - carrier_info.carriers["is_digit_pos"].binding_id = Some(BindingId(10))
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::mir::MirBuilder;
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
|
use crate::mir::MirBuilder;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CarrierBindingError {
|
pub enum CarrierBindingError {
|
||||||
@ -87,9 +87,7 @@ impl CarrierBindingAssigner {
|
|||||||
Some(bid) => bid,
|
Some(bid) => bid,
|
||||||
None => {
|
None => {
|
||||||
let bid = builder.allocate_binding_id();
|
let bid = builder.allocate_binding_id();
|
||||||
builder
|
builder.binding_map.insert(original_name.to_string(), bid);
|
||||||
.binding_map
|
|
||||||
.insert(original_name.to_string(), bid);
|
|
||||||
bid
|
bid
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -189,14 +187,16 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_assign_promoted_binding_success() {
|
fn test_assign_promoted_binding_success() {
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||||
|
|
||||||
// Create a MirBuilder with binding_map
|
// Create a MirBuilder with binding_map
|
||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
|
|
||||||
// Add original binding to builder.binding_map
|
// Add original binding to builder.binding_map
|
||||||
let original_bid = builder.allocate_binding_id();
|
let original_bid = builder.allocate_binding_id();
|
||||||
builder.binding_map.insert("digit_pos".to_string(), original_bid);
|
builder
|
||||||
|
.binding_map
|
||||||
|
.insert("digit_pos".to_string(), original_bid);
|
||||||
|
|
||||||
// Create CarrierInfo with promoted carrier
|
// Create CarrierInfo with promoted carrier
|
||||||
let mut carrier_info = CarrierInfo::with_carriers(
|
let mut carrier_info = CarrierInfo::with_carriers(
|
||||||
@ -227,7 +227,11 @@ mod tests {
|
|||||||
assert!(promoted_bid.is_some());
|
assert!(promoted_bid.is_some());
|
||||||
|
|
||||||
// Verify: carrier.binding_id was set
|
// Verify: carrier.binding_id was set
|
||||||
let carrier = carrier_info.carriers.iter().find(|c| c.name == "is_digit_pos").unwrap();
|
let carrier = carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.name == "is_digit_pos")
|
||||||
|
.unwrap();
|
||||||
assert_eq!(carrier.binding_id, promoted_bid);
|
assert_eq!(carrier.binding_id, promoted_bid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,16 +242,18 @@ mod tests {
|
|||||||
// Create MirBuilder WITHOUT adding "digit_pos" to binding_map
|
// Create MirBuilder WITHOUT adding "digit_pos" to binding_map
|
||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
|
|
||||||
let mut carrier_info = CarrierInfo::with_carriers("test_loop".to_string(), ValueId(0), vec![
|
let mut carrier_info = CarrierInfo::with_carriers(
|
||||||
CarrierVar {
|
"test_loop".to_string(),
|
||||||
|
ValueId(0),
|
||||||
|
vec![CarrierVar {
|
||||||
name: "is_digit_pos".to_string(),
|
name: "is_digit_pos".to_string(),
|
||||||
host_id: ValueId(0),
|
host_id: ValueId(0),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
role: CarrierRole::ConditionOnly,
|
role: CarrierRole::ConditionOnly,
|
||||||
init: CarrierInit::BoolConst(false),
|
init: CarrierInit::BoolConst(false),
|
||||||
binding_id: None,
|
binding_id: None,
|
||||||
},
|
}],
|
||||||
]);
|
);
|
||||||
|
|
||||||
// Should succeed (original/promoted BindingId are allocated on demand).
|
// Should succeed (original/promoted BindingId are allocated on demand).
|
||||||
let result = CarrierBindingAssigner::assign_promoted_binding(
|
let result = CarrierBindingAssigner::assign_promoted_binding(
|
||||||
@ -270,12 +276,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_assign_promoted_binding_multiple_carriers() {
|
fn test_assign_promoted_binding_multiple_carriers() {
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||||
let mut builder = MirBuilder::new();
|
let mut builder = MirBuilder::new();
|
||||||
|
|
||||||
// Add two original bindings
|
// Add two original bindings
|
||||||
let digit_pos_bid = builder.allocate_binding_id();
|
let digit_pos_bid = builder.allocate_binding_id();
|
||||||
builder.binding_map.insert("digit_pos".to_string(), digit_pos_bid);
|
builder
|
||||||
|
.binding_map
|
||||||
|
.insert("digit_pos".to_string(), digit_pos_bid);
|
||||||
|
|
||||||
let ch_bid = builder.allocate_binding_id();
|
let ch_bid = builder.allocate_binding_id();
|
||||||
builder.binding_map.insert("ch".to_string(), ch_bid);
|
builder.binding_map.insert("ch".to_string(), ch_bid);
|
||||||
@ -309,24 +317,36 @@ mod tests {
|
|||||||
&mut carrier_info,
|
&mut carrier_info,
|
||||||
"digit_pos",
|
"digit_pos",
|
||||||
"is_digit_pos",
|
"is_digit_pos",
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
CarrierBindingAssigner::assign_promoted_binding(
|
CarrierBindingAssigner::assign_promoted_binding(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
&mut carrier_info,
|
&mut carrier_info,
|
||||||
"ch",
|
"ch",
|
||||||
"is_ch_match",
|
"is_ch_match",
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Verify both mappings exist
|
// Verify both mappings exist
|
||||||
assert!(carrier_info.resolve_promoted_with_binding(digit_pos_bid).is_some());
|
assert!(carrier_info
|
||||||
|
.resolve_promoted_with_binding(digit_pos_bid)
|
||||||
|
.is_some());
|
||||||
assert!(carrier_info.resolve_promoted_with_binding(ch_bid).is_some());
|
assert!(carrier_info.resolve_promoted_with_binding(ch_bid).is_some());
|
||||||
|
|
||||||
// Verify both carriers have binding_id set
|
// Verify both carriers have binding_id set
|
||||||
let is_digit_pos = carrier_info.carriers.iter().find(|c| c.name == "is_digit_pos").unwrap();
|
let is_digit_pos = carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.name == "is_digit_pos")
|
||||||
|
.unwrap();
|
||||||
assert!(is_digit_pos.binding_id.is_some());
|
assert!(is_digit_pos.binding_id.is_some());
|
||||||
|
|
||||||
let is_ch_match = carrier_info.carriers.iter().find(|c| c.name == "is_ch_match").unwrap();
|
let is_ch_match = carrier_info
|
||||||
|
.carriers
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.name == "is_ch_match")
|
||||||
|
.unwrap();
|
||||||
assert!(is_ch_match.binding_id.is_some());
|
assert!(is_ch_match.binding_id.is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -649,7 +649,11 @@ impl CarrierInfo {
|
|||||||
/// don't have access to binding_map. Actual population happens in a future phase when
|
/// don't have access to binding_map. Actual population happens in a future phase when
|
||||||
/// we integrate BindingId tracking into the promotion pipeline.
|
/// we integrate BindingId tracking into the promotion pipeline.
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn record_promoted_binding(&mut self, original_binding: BindingId, promoted_binding: BindingId) {
|
pub fn record_promoted_binding(
|
||||||
|
&mut self,
|
||||||
|
original_binding: BindingId,
|
||||||
|
promoted_binding: BindingId,
|
||||||
|
) {
|
||||||
use super::debug_output_box::DebugOutputBox;
|
use super::debug_output_box::DebugOutputBox;
|
||||||
|
|
||||||
// Phase 86: Use DebugOutputBox for consistent debug output
|
// Phase 86: Use DebugOutputBox for consistent debug output
|
||||||
@ -663,7 +667,8 @@ impl CarrierInfo {
|
|||||||
original_binding, promoted_binding
|
original_binding, promoted_binding
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.promoted_bindings.insert(original_binding, promoted_binding);
|
self.promoted_bindings
|
||||||
|
.insert(original_binding, promoted_binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -191,11 +191,7 @@ impl ConditionEnv {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn register_carrier_binding(
|
pub fn register_carrier_binding(&mut self, binding_id: BindingId, join_value_id: ValueId) {
|
||||||
&mut self,
|
|
||||||
binding_id: BindingId,
|
|
||||||
join_value_id: ValueId,
|
|
||||||
) {
|
|
||||||
self.binding_id_map.insert(binding_id, join_value_id);
|
self.binding_id_map.insert(binding_id, join_value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,11 +215,7 @@ impl ConditionEnv {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn register_loop_var_binding(
|
pub fn register_loop_var_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
|
||||||
&mut self,
|
|
||||||
binding_id: BindingId,
|
|
||||||
value_id: ValueId,
|
|
||||||
) {
|
|
||||||
self.binding_id_map.insert(binding_id, value_id);
|
self.binding_id_map.insert(binding_id, value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,11 +242,7 @@ impl ConditionEnv {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn register_condition_binding(
|
pub fn register_condition_binding(&mut self, binding_id: BindingId, value_id: ValueId) {
|
||||||
&mut self,
|
|
||||||
binding_id: BindingId,
|
|
||||||
value_id: ValueId,
|
|
||||||
) {
|
|
||||||
self.binding_id_map.insert(binding_id, value_id);
|
self.binding_id_map.insert(binding_id, value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +303,10 @@ impl ConditionEnv {
|
|||||||
if let Some(&value_id) = self.binding_id_map.get(&bid) {
|
if let Some(&value_id) = self.binding_id_map.get(&bid) {
|
||||||
debug.log(
|
debug.log(
|
||||||
"hit",
|
"hit",
|
||||||
&format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name),
|
&format!(
|
||||||
|
"BindingId({}) -> ValueId({}) for '{}'",
|
||||||
|
bid.0, value_id.0, name
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return Some(value_id);
|
return Some(value_id);
|
||||||
} else {
|
} else {
|
||||||
@ -330,7 +321,10 @@ impl ConditionEnv {
|
|||||||
} else {
|
} else {
|
||||||
// Legacy: no BindingId, use name lookup
|
// Legacy: no BindingId, use name lookup
|
||||||
let result = self.get(name);
|
let result = self.get(name);
|
||||||
debug.log("legacy", &format!("No BindingId, name '{}' -> {:?}", name, result));
|
debug.log(
|
||||||
|
"legacy",
|
||||||
|
&format!("No BindingId, name '{}' -> {:?}", name, result),
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -129,8 +129,7 @@ fn lower_comparison(
|
|||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
env: &ConditionEnv,
|
env: &ConditionEnv,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
// Lower left and right sides
|
// Lower left and right sides
|
||||||
let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
|
let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
|
||||||
let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
|
let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
|
||||||
@ -164,8 +163,7 @@ fn lower_logical_and(
|
|||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
env: &ConditionEnv,
|
env: &ConditionEnv,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
// Logical AND: evaluate both sides and combine
|
// Logical AND: evaluate both sides and combine
|
||||||
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
|
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
|
||||||
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
|
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
|
||||||
@ -189,8 +187,7 @@ fn lower_logical_or(
|
|||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
env: &ConditionEnv,
|
env: &ConditionEnv,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
// Logical OR: evaluate both sides and combine
|
// Logical OR: evaluate both sides and combine
|
||||||
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
|
let lhs = lower_condition_recursive(left, alloc_value, env, instructions)?;
|
||||||
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
|
let rhs = lower_condition_recursive(right, alloc_value, env, instructions)?;
|
||||||
@ -213,8 +210,7 @@ fn lower_not_operator(
|
|||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
env: &ConditionEnv,
|
env: &ConditionEnv,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
let operand_val = lower_condition_recursive(operand, alloc_value, env, instructions)?;
|
let operand_val = lower_condition_recursive(operand, alloc_value, env, instructions)?;
|
||||||
let dst = alloc_value();
|
let dst = alloc_value();
|
||||||
|
|
||||||
@ -233,8 +229,7 @@ fn lower_literal(
|
|||||||
value: &LiteralValue,
|
value: &LiteralValue,
|
||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
let dst = alloc_value();
|
let dst = alloc_value();
|
||||||
let const_value = match value {
|
let const_value = match value {
|
||||||
LiteralValue::Integer(n) => ConstValue::Integer(*n),
|
LiteralValue::Integer(n) => ConstValue::Integer(*n),
|
||||||
@ -322,8 +317,7 @@ fn lower_arithmetic_binop(
|
|||||||
alloc_value: &mut dyn FnMut() -> ValueId,
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
env: &ConditionEnv,
|
env: &ConditionEnv,
|
||||||
instructions: &mut Vec<JoinInst>,
|
instructions: &mut Vec<JoinInst>,
|
||||||
) -> Result<ValueId, String>
|
) -> Result<ValueId, String> {
|
||||||
{
|
|
||||||
let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
|
let lhs = lower_value_expression(left, alloc_value, env, instructions)?;
|
||||||
let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
|
let rhs = lower_value_expression(right, alloc_value, env, instructions)?;
|
||||||
let dst = alloc_value();
|
let dst = alloc_value();
|
||||||
|
|||||||
@ -206,7 +206,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -49,16 +49,21 @@ fn is_if_sum_value_expr(expr: &ASTNode) -> bool {
|
|||||||
match expr {
|
match expr {
|
||||||
ASTNode::Variable { .. } | ASTNode::Literal { .. } => true,
|
ASTNode::Variable { .. } | ASTNode::Literal { .. } => true,
|
||||||
ASTNode::BinaryOp {
|
ASTNode::BinaryOp {
|
||||||
operator, left, right, ..
|
|
||||||
} => matches!(
|
|
||||||
operator,
|
operator,
|
||||||
BinaryOperator::Add
|
left,
|
||||||
| BinaryOperator::Subtract
|
right,
|
||||||
| BinaryOperator::Multiply
|
..
|
||||||
| BinaryOperator::Divide
|
} => {
|
||||||
| BinaryOperator::Modulo
|
matches!(
|
||||||
) && is_if_sum_value_expr(left.as_ref())
|
operator,
|
||||||
&& is_if_sum_value_expr(right.as_ref()),
|
BinaryOperator::Add
|
||||||
|
| BinaryOperator::Subtract
|
||||||
|
| BinaryOperator::Multiply
|
||||||
|
| BinaryOperator::Divide
|
||||||
|
| BinaryOperator::Modulo
|
||||||
|
) && is_if_sum_value_expr(left.as_ref())
|
||||||
|
&& is_if_sum_value_expr(right.as_ref())
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -192,7 +192,11 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
|
|||||||
// 2. Build ConditionEnv from ScopeManager
|
// 2. Build ConditionEnv from ScopeManager
|
||||||
// Phase 79-1: Use BindingId-aware version when available
|
// Phase 79-1: Use BindingId-aware version when available
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(self.scope, ast, self.builder)?;
|
let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(
|
||||||
|
self.scope,
|
||||||
|
ast,
|
||||||
|
self.builder,
|
||||||
|
)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
let condition_env = scope_resolution::build_condition_env_from_scope(self.scope, ast)?;
|
let condition_env = scope_resolution::build_condition_env_from_scope(self.scope, ast)?;
|
||||||
@ -279,13 +283,17 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
|
|||||||
|
|
||||||
// Build ConditionEnv from the provided scope (the caller controls the scope + allocator).
|
// Build ConditionEnv from the provided scope (the caller controls the scope + allocator).
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
let condition_env =
|
let condition_env = scope_resolution::build_condition_env_from_scope_with_binding(
|
||||||
scope_resolution::build_condition_env_from_scope_with_binding(context.scope, condition, self.builder)
|
context.scope,
|
||||||
.map_err(|e| e.to_string())?;
|
condition,
|
||||||
|
self.builder,
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
let condition_env = scope_resolution::build_condition_env_from_scope(context.scope, condition)
|
let condition_env =
|
||||||
.map_err(|e| e.to_string())?;
|
scope_resolution::build_condition_env_from_scope(context.scope, condition)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
// Delegate to the well-tested lowerer, but use the caller-provided allocator (SSOT).
|
// Delegate to the well-tested lowerer, but use the caller-provided allocator (SSOT).
|
||||||
let (result_value, instructions) =
|
let (result_value, instructions) =
|
||||||
@ -347,7 +355,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -393,7 +401,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -439,7 +447,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -521,7 +529,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
|
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
|
||||||
@ -550,7 +558,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
|
let boxed_carrier: Box<CarrierInfo> = Box::new(carrier_info);
|
||||||
@ -701,7 +709,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use super::{ExprLoweringError, ScopeManager};
|
use super::{ExprLoweringError, ScopeManager};
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||||
|
|
||||||
/// Phase 79-1: Build ConditionEnv with BindingId support (dev-only)
|
/// Phase 79-1: Build ConditionEnv with BindingId support (dev-only)
|
||||||
///
|
///
|
||||||
|
|||||||
@ -116,9 +116,9 @@ pub struct LoopExitBinding {
|
|||||||
pub struct JoinInlineBoundary {
|
pub struct JoinInlineBoundary {
|
||||||
/// JoinIR-local ValueIds that act as "input slots"
|
/// JoinIR-local ValueIds that act as "input slots"
|
||||||
///
|
///
|
||||||
/// These are the ValueIds used **inside** the JoinIR fragment to refer
|
/// These are the ValueIds used **inside** the JoinIR fragment to refer
|
||||||
/// to values that come from the host. They should be in the JoinValueSpace
|
/// to values that come from the host. They should be in the JoinValueSpace
|
||||||
/// Param region (100-999). (They are typically allocated sequentially.)
|
/// Param region (100-999). (They are typically allocated sequentially.)
|
||||||
///
|
///
|
||||||
/// Example: For a loop variable `i`, JoinIR uses ValueId(100) as the parameter.
|
/// Example: For a loop variable `i`, JoinIR uses ValueId(100) as the parameter.
|
||||||
pub join_inputs: Vec<ValueId>,
|
pub join_inputs: Vec<ValueId>,
|
||||||
|
|||||||
@ -70,4 +70,3 @@ impl LoopToJoinLowerer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,9 +108,10 @@ impl LoopToJoinLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !generic_case_a_enabled() {
|
if !generic_case_a_enabled() {
|
||||||
if !func_name
|
if !func_name.map_or(
|
||||||
.map_or(false, super::super::loop_scope_shape::is_case_a_minimal_target)
|
false,
|
||||||
{
|
super::super::loop_scope_shape::is_case_a_minimal_target,
|
||||||
|
) {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}",
|
"[LoopToJoinLowerer] rejected by name filter (generic disabled): {:?}",
|
||||||
@ -163,4 +164,3 @@ mod tests {
|
|||||||
assert!(!lowerer.debug || lowerer.debug);
|
assert!(!lowerer.debug || lowerer.debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,8 +12,7 @@
|
|||||||
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
|
//! - Phase 進捗ログはここに混ぜない(現役の導線のみ)。
|
||||||
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
|
//! - “とりあえず通す”フォールバックは増やさない。失敗は `None` で上位に返す。
|
||||||
|
|
||||||
mod core;
|
|
||||||
mod case_a_entrypoints;
|
mod case_a_entrypoints;
|
||||||
|
mod core;
|
||||||
|
|
||||||
pub use core::LoopToJoinLowerer;
|
pub use core::LoopToJoinLowerer;
|
||||||
|
|
||||||
|
|||||||
@ -261,9 +261,7 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
"[joinir/pattern2] Phase 201: loop_step params - i_param={:?}, carrier_params={:?}",
|
"[joinir/pattern2] Phase 201: loop_step params - i_param={:?}, carrier_params={:?}",
|
||||||
i_param, carrier_param_ids
|
i_param, carrier_param_ids
|
||||||
);
|
);
|
||||||
if crate::config::env::joinir_dev_enabled()
|
if crate::config::env::joinir_dev_enabled() || crate::config::env::joinir_test_debug_enabled() {
|
||||||
|| crate::config::env::joinir_test_debug_enabled()
|
|
||||||
{
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/pattern2/debug] loop_var='{}' env.get(loop_var)={:?}, carriers={:?}",
|
"[joinir/pattern2/debug] loop_var='{}' env.get(loop_var)={:?}, carriers={:?}",
|
||||||
loop_var_name,
|
loop_var_name,
|
||||||
@ -668,7 +666,9 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block),
|
Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block),
|
||||||
Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block),
|
Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block),
|
||||||
// Phase 47-A: P3 steps not used in P2 lowering (handled in Pattern3 lowerer)
|
// Phase 47-A: P3 steps not used in P2 lowering (handled in Pattern3 lowerer)
|
||||||
Pattern2StepKind::IfCond | Pattern2StepKind::ThenUpdates | Pattern2StepKind::ElseUpdates => {
|
Pattern2StepKind::IfCond
|
||||||
|
| Pattern2StepKind::ThenUpdates
|
||||||
|
| Pattern2StepKind::ElseUpdates => {
|
||||||
panic!("Pattern3 step kinds should not appear in Pattern2 lowering");
|
panic!("Pattern3 step kinds should not appear in Pattern2 lowering");
|
||||||
}
|
}
|
||||||
// Phase 48-A: P4 steps not used in P2 lowering (handled in Pattern4 lowerer)
|
// Phase 48-A: P4 steps not used in P2 lowering (handled in Pattern4 lowerer)
|
||||||
|
|||||||
@ -127,8 +127,8 @@ fn test_pattern2_header_condition_via_exprlowerer() {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let carrier_updates = BTreeMap::new();
|
let carrier_updates = BTreeMap::new();
|
||||||
|
|||||||
@ -126,7 +126,9 @@ pub(crate) fn lower_loop_with_continue_minimal(
|
|||||||
carrier_info: &CarrierInfo,
|
carrier_info: &CarrierInfo,
|
||||||
carrier_updates: &BTreeMap<String, UpdateExpr>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
carrier_updates: &BTreeMap<String, UpdateExpr>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
join_value_space: &mut JoinValueSpace,
|
join_value_space: &mut JoinValueSpace,
|
||||||
#[cfg(feature = "normalized_dev")] binding_map: Option<&std::collections::BTreeMap<String, crate::mir::BindingId>>,
|
#[cfg(feature = "normalized_dev")] binding_map: Option<
|
||||||
|
&std::collections::BTreeMap<String, crate::mir::BindingId>,
|
||||||
|
>,
|
||||||
) -> Result<(JoinModule, ExitMeta), String> {
|
) -> Result<(JoinModule, ExitMeta), String> {
|
||||||
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
|
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
|
||||||
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
||||||
|
|||||||
@ -25,8 +25,6 @@ pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
|
|||||||
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
|
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
|
||||||
pub(crate) mod common; // Internal lowering utilities
|
pub(crate) mod common; // Internal lowering utilities
|
||||||
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
|
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
|
||||||
pub mod debug_output_box; // Phase 85: Centralized debug output management
|
|
||||||
pub mod error_tags; // Phase 86: Centralized error message formatting
|
|
||||||
pub mod condition_env; // Phase 171-fix: Condition expression environment
|
pub mod condition_env; // Phase 171-fix: Condition expression environment
|
||||||
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
|
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
|
||||||
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
|
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
|
||||||
@ -34,7 +32,9 @@ pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (sim
|
|||||||
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
|
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
|
||||||
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
|
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
|
||||||
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
|
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
|
||||||
|
pub mod debug_output_box; // Phase 85: Centralized debug output management
|
||||||
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
|
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
|
||||||
|
pub mod error_tags; // Phase 86: Centralized error message formatting
|
||||||
pub(crate) mod exit_args_resolver; // Internal exit argument resolution
|
pub(crate) mod exit_args_resolver; // Internal exit argument resolution
|
||||||
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
|
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
|
||||||
pub mod funcscanner_append_defs;
|
pub mod funcscanner_append_defs;
|
||||||
@ -64,12 +64,12 @@ pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
|
|||||||
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
|
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
|
||||||
pub mod loop_with_continue_minimal;
|
pub mod loop_with_continue_minimal;
|
||||||
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
|
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
|
||||||
pub(crate) mod step_schedule; // Phase 47-A: Generic step scheduler for P2/P3 (renamed from pattern2_step_schedule)
|
|
||||||
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
|
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
|
||||||
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
|
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod scope_manager_bindingid_poc; // Phase 73: BindingId-based scope PoC (dev-only)
|
pub mod scope_manager_bindingid_poc;
|
||||||
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
|
pub(crate) mod step_schedule; // Phase 47-A: Generic step scheduler for P2/P3 (renamed from pattern2_step_schedule) // Phase 73: BindingId-based scope PoC (dev-only)
|
||||||
|
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
|
||||||
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
|
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
|
||||||
pub mod min_loop;
|
pub mod min_loop;
|
||||||
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
|
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
|
||||||
|
|||||||
@ -274,7 +274,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
|||||||
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) {
|
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) {
|
||||||
debug.log(
|
debug.log(
|
||||||
"direct",
|
"direct",
|
||||||
&format!("BindingId({}) -> ValueId({}) for '{}'", bid.0, value_id.0, name),
|
&format!(
|
||||||
|
"BindingId({}) -> ValueId({}) for '{}'",
|
||||||
|
bid.0, value_id.0, name
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return Some(value_id);
|
return Some(value_id);
|
||||||
}
|
}
|
||||||
@ -282,7 +285,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
|||||||
// Step 2: **NEW (Phase 76)**: Check promoted_bindings map
|
// Step 2: **NEW (Phase 76)**: Check promoted_bindings map
|
||||||
if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) {
|
if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) {
|
||||||
// Promoted BindingId found, lookup in ConditionEnv
|
// Promoted BindingId found, lookup in ConditionEnv
|
||||||
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(promoted_bid), name) {
|
if let Some(value_id) = self
|
||||||
|
.condition_env
|
||||||
|
.resolve_var_with_binding(Some(promoted_bid), name)
|
||||||
|
{
|
||||||
debug.log(
|
debug.log(
|
||||||
"promoted",
|
"promoted",
|
||||||
&format!(
|
&format!(
|
||||||
@ -304,7 +310,10 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
|||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
debug.log(
|
debug.log(
|
||||||
"fallback",
|
"fallback",
|
||||||
&format!("BindingId({}) miss, falling back to name '{}' lookup", bid.0, name),
|
&format!(
|
||||||
|
"BindingId({}) miss, falling back to name '{}' lookup",
|
||||||
|
bid.0, name
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +340,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -429,7 +438,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -464,7 +473,7 @@ mod tests {
|
|||||||
carriers: vec![],
|
carriers: vec![],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -29,8 +29,8 @@ pub struct ConditionEnvV2 {
|
|||||||
name_to_join: BTreeMap<String, ValueId>,
|
name_to_join: BTreeMap<String, ValueId>,
|
||||||
|
|
||||||
// Phase 73+: NEW - BindingId-based tracking
|
// Phase 73+: NEW - BindingId-based tracking
|
||||||
binding_to_join: BTreeMap<BindingId, ValueId>, // BindingId → JoinIR ValueId
|
binding_to_join: BTreeMap<BindingId, ValueId>, // BindingId → JoinIR ValueId
|
||||||
name_to_binding: BTreeMap<String, BindingId>, // Name → current BindingId (shadowing)
|
name_to_binding: BTreeMap<String, BindingId>, // Name → current BindingId (shadowing)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConditionEnvV2 {
|
impl ConditionEnvV2 {
|
||||||
@ -93,7 +93,7 @@ pub struct CarrierVarV2 {
|
|||||||
pub join_id: Option<ValueId>,
|
pub join_id: Option<ValueId>,
|
||||||
|
|
||||||
// Phase 73+: NEW - BindingId tracking
|
// Phase 73+: NEW - BindingId tracking
|
||||||
pub host_binding: Option<BindingId>, // HOST function's BindingId
|
pub host_binding: Option<BindingId>, // HOST function's BindingId
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 73 PoC: CarrierInfo with BindingId-based promotion
|
/// Phase 73 PoC: CarrierInfo with BindingId-based promotion
|
||||||
@ -104,7 +104,7 @@ pub struct CarrierInfoV2 {
|
|||||||
pub carriers: Vec<CarrierVarV2>,
|
pub carriers: Vec<CarrierVarV2>,
|
||||||
|
|
||||||
// Phase 73+: Replace string list with BindingId map
|
// Phase 73+: Replace string list with BindingId map
|
||||||
pub promoted_bindings: BTreeMap<BindingId, BindingId>, // Original → Promoted
|
pub promoted_bindings: BTreeMap<BindingId, BindingId>, // Original → Promoted
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarrierInfoV2 {
|
impl CarrierInfoV2 {
|
||||||
@ -225,7 +225,10 @@ mod tests {
|
|||||||
let promoted = BindingId(10);
|
let promoted = BindingId(10);
|
||||||
carrier_info.add_promoted_binding(original, promoted);
|
carrier_info.add_promoted_binding(original, promoted);
|
||||||
|
|
||||||
assert_eq!(carrier_info.resolve_promoted_binding(original), Some(promoted));
|
assert_eq!(
|
||||||
|
carrier_info.resolve_promoted_binding(original),
|
||||||
|
Some(promoted)
|
||||||
|
);
|
||||||
assert_eq!(carrier_info.resolve_promoted_binding(BindingId(99)), None);
|
assert_eq!(carrier_info.resolve_promoted_binding(BindingId(99)), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +311,7 @@ mod tests {
|
|||||||
env.insert_by_binding("new_var".to_string(), binding, ValueId(888));
|
env.insert_by_binding("new_var".to_string(), binding, ValueId(888));
|
||||||
|
|
||||||
// Unified lookup should find both
|
// Unified lookup should find both
|
||||||
assert_eq!(env.lookup("legacy_var"), Some(ValueId(999))); // Fallback to name
|
assert_eq!(env.lookup("legacy_var"), Some(ValueId(999))); // Fallback to name
|
||||||
assert_eq!(env.lookup("new_var"), Some(ValueId(888))); // BindingId first
|
assert_eq!(env.lookup("new_var"), Some(ValueId(888))); // BindingId first
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -193,7 +193,9 @@ pub(crate) fn lower_simple_while_minimal(
|
|||||||
// Phase 188-Impl-1-E: Use Print instruction
|
// Phase 188-Impl-1-E: Use Print instruction
|
||||||
loop_step_func
|
loop_step_func
|
||||||
.body
|
.body
|
||||||
.push(JoinInst::Compute(MirLikeInst::Print { value: i_step_param }));
|
.push(JoinInst::Compute(MirLikeInst::Print {
|
||||||
|
value: i_step_param,
|
||||||
|
}));
|
||||||
|
|
||||||
// i_next = i + 1
|
// i_next = i + 1
|
||||||
// Step 1: const 1
|
// Step 1: const 1
|
||||||
|
|||||||
@ -17,16 +17,16 @@ use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub(crate) enum Pattern2StepKind {
|
pub(crate) enum Pattern2StepKind {
|
||||||
// P2 (Pattern2 Break) steps
|
// P2 (Pattern2 Break) steps
|
||||||
HeaderCond, // loop(cond)
|
HeaderCond, // loop(cond)
|
||||||
BodyInit, // local ch = ...
|
BodyInit, // local ch = ...
|
||||||
BreakCheck, // if (cond) break
|
BreakCheck, // if (cond) break
|
||||||
Updates, // sum = sum + 1
|
Updates, // sum = sum + 1
|
||||||
Tail, // i = i + 1
|
Tail, // i = i + 1
|
||||||
|
|
||||||
// Phase 47-A: P3 (Pattern3 If-Sum) steps
|
// Phase 47-A: P3 (Pattern3 If-Sum) steps
|
||||||
IfCond, // if (cond) in body
|
IfCond, // if (cond) in body
|
||||||
ThenUpdates, // carrier updates in then branch
|
ThenUpdates, // carrier updates in then branch
|
||||||
ElseUpdates, // carrier updates in else branch (if any)
|
ElseUpdates, // carrier updates in else branch (if any)
|
||||||
|
|
||||||
// Phase 48-A: P4 (Pattern4 Continue) steps
|
// Phase 48-A: P4 (Pattern4 Continue) steps
|
||||||
ContinueCheck, // if (cond) continue
|
ContinueCheck, // if (cond) continue
|
||||||
@ -158,11 +158,11 @@ fn log_schedule(ctx: &Pattern2ScheduleContext, schedule: &Pattern2StepSchedule)
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn pattern3_if_sum_schedule() -> Vec<Pattern2StepKind> {
|
pub(crate) fn pattern3_if_sum_schedule() -> Vec<Pattern2StepKind> {
|
||||||
vec![
|
vec![
|
||||||
Pattern2StepKind::HeaderCond, // loop(i < n)
|
Pattern2StepKind::HeaderCond, // loop(i < n)
|
||||||
Pattern2StepKind::IfCond, // if (i > 0)
|
Pattern2StepKind::IfCond, // if (i > 0)
|
||||||
Pattern2StepKind::ThenUpdates, // sum = sum + i
|
Pattern2StepKind::ThenUpdates, // sum = sum + i
|
||||||
// ElseUpdates omitted for minimal (no else branch)
|
// ElseUpdates omitted for minimal (no else branch)
|
||||||
Pattern2StepKind::Tail, // i = i + 1
|
Pattern2StepKind::Tail, // i = i + 1
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,12 +189,7 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
CarrierInit::FromHost
|
CarrierInit::FromHost
|
||||||
};
|
};
|
||||||
CarrierVar::with_role_and_init(
|
CarrierVar::with_role_and_init("c".to_string(), ValueId(1), CarrierRole::LoopState, init)
|
||||||
"c".to_string(),
|
|
||||||
ValueId(1),
|
|
||||||
CarrierRole::LoopState,
|
|
||||||
init,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn carrier_info(carriers: Vec<CarrierVar>) -> CarrierInfo {
|
fn carrier_info(carriers: Vec<CarrierVar>) -> CarrierInfo {
|
||||||
@ -204,7 +199,7 @@ mod tests {
|
|||||||
carriers,
|
carriers,
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,8 +244,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn loop_local_carrier_triggers_body_first() {
|
fn loop_local_carrier_triggers_body_first() {
|
||||||
let ctx =
|
let ctx = Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
|
||||||
Pattern2ScheduleContext::from_env(None, &carrier_info(vec![carrier(true)]));
|
|
||||||
let schedule = build_pattern2_schedule(&ctx);
|
let schedule = build_pattern2_schedule(&ctx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
schedule.steps(),
|
schedule.steps(),
|
||||||
|
|||||||
@ -15,13 +15,13 @@ use std::collections::HashMap;
|
|||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
|
||||||
pub mod fixtures;
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod dev_env;
|
pub mod dev_env;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod dev_fixtures;
|
pub mod dev_fixtures;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub mod fixtures;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod loop_step_inspector;
|
pub mod loop_step_inspector;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub mod shape_guard;
|
pub mod shape_guard;
|
||||||
@ -97,7 +97,10 @@ pub enum JpOp {
|
|||||||
BinOp(BinOpKind),
|
BinOp(BinOpKind),
|
||||||
Unary(UnaryOp),
|
Unary(UnaryOp),
|
||||||
Compare(CompareOp),
|
Compare(CompareOp),
|
||||||
BoxCall { box_name: String, method: String },
|
BoxCall {
|
||||||
|
box_name: String,
|
||||||
|
method: String,
|
||||||
|
},
|
||||||
/// 三項演算子(cond ? then : else)
|
/// 三項演算子(cond ? then : else)
|
||||||
Select,
|
Select,
|
||||||
}
|
}
|
||||||
@ -462,13 +465,11 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
cond,
|
cond,
|
||||||
then_val,
|
then_val,
|
||||||
else_val,
|
else_val,
|
||||||
}) => {
|
}) => body.push(JpInst::Let {
|
||||||
body.push(JpInst::Let {
|
dst: *dst,
|
||||||
dst: *dst,
|
op: JpOp::Select,
|
||||||
op: JpOp::Select,
|
args: vec![*cond, *then_val, *else_val],
|
||||||
args: vec![*cond, *then_val, *else_val],
|
}),
|
||||||
})
|
|
||||||
}
|
|
||||||
JoinInst::Jump { cont, args, cond } => {
|
JoinInst::Jump { cont, args, cond } => {
|
||||||
if let Some(cond_val) = cond {
|
if let Some(cond_val) = cond {
|
||||||
body.push(JpInst::If {
|
body.push(JpInst::If {
|
||||||
@ -497,7 +498,9 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
|||||||
args: vec![*cond, *then_val, *else_val],
|
args: vec![*cond, *then_val, *else_val],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
JoinInst::Call { func, args, k_next, .. } => {
|
JoinInst::Call {
|
||||||
|
func, args, k_next, ..
|
||||||
|
} => {
|
||||||
if k_next.is_none() {
|
if k_next.is_none() {
|
||||||
body.push(JpInst::TailCallFn {
|
body.push(JpInst::TailCallFn {
|
||||||
target: JpFuncId(func.0),
|
target: JpFuncId(func.0),
|
||||||
@ -620,9 +623,7 @@ pub fn normalize_pattern3_if_sum_json_minimal(
|
|||||||
|
|
||||||
/// Phase 50: selfhost if-sum P3 を Normalized に載せる(dev-only)。
|
/// Phase 50: selfhost if-sum P3 を Normalized に載せる(dev-only)。
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn normalize_selfhost_if_sum_p3(
|
pub fn normalize_selfhost_if_sum_p3(structured: &JoinModule) -> Result<NormalizedModule, String> {
|
||||||
structured: &JoinModule,
|
|
||||||
) -> Result<NormalizedModule, String> {
|
|
||||||
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
|
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,14 +770,12 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
|
|||||||
op: *op,
|
op: *op,
|
||||||
operand: args.get(0).copied().unwrap_or(ValueId(0)),
|
operand: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||||
})),
|
})),
|
||||||
JpOp::Select => func
|
JpOp::Select => func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
||||||
.body
|
dst: *dst,
|
||||||
.push(JoinInst::Compute(MirLikeInst::Select {
|
cond: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||||
dst: *dst,
|
then_val: args.get(1).copied().unwrap_or(ValueId(0)),
|
||||||
cond: args.get(0).copied().unwrap_or(ValueId(0)),
|
else_val: args.get(2).copied().unwrap_or(ValueId(0)),
|
||||||
then_val: args.get(1).copied().unwrap_or(ValueId(0)),
|
})),
|
||||||
else_val: args.get(2).copied().unwrap_or(ValueId(0)),
|
|
||||||
})),
|
|
||||||
JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
dst: *dst,
|
dst: *dst,
|
||||||
op: *op,
|
op: *op,
|
||||||
@ -832,9 +831,7 @@ fn verify_normalized_pattern2(
|
|||||||
max_env_fields: usize,
|
max_env_fields: usize,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if module.phase != JoinIrPhase::Normalized {
|
if module.phase != JoinIrPhase::Normalized {
|
||||||
return Err(
|
return Err("[joinir/normalized-dev] pattern2: phase must be Normalized".to_string());
|
||||||
"[joinir/normalized-dev] pattern2: phase must be Normalized".to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
|
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
|
||||||
@ -871,10 +868,8 @@ fn verify_normalized_pattern2(
|
|||||||
| JpInst::If { env, .. } => {
|
| JpInst::If { env, .. } => {
|
||||||
if let Some(expected) = expected_env_len {
|
if let Some(expected) = expected_env_len {
|
||||||
if env.is_empty() {
|
if env.is_empty() {
|
||||||
return Err(
|
return Err("[joinir/normalized-dev] pattern2: env must not be empty"
|
||||||
"[joinir/normalized-dev] pattern2: env must not be empty"
|
.to_string());
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let _ = expected;
|
let _ = expected;
|
||||||
}
|
}
|
||||||
@ -885,9 +880,7 @@ fn verify_normalized_pattern2(
|
|||||||
|
|
||||||
if let Some(last) = func.body.last() {
|
if let Some(last) = func.body.last() {
|
||||||
match last {
|
match last {
|
||||||
JpInst::TailCallFn { .. }
|
JpInst::TailCallFn { .. } | JpInst::TailCallKont { .. } | JpInst::If { .. } => {}
|
||||||
| JpInst::TailCallKont { .. }
|
|
||||||
| JpInst::If { .. } => {}
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"[joinir/normalized-dev] pattern2: function '{}' does not end with tail call/if",
|
"[joinir/normalized-dev] pattern2: function '{}' does not end with tail call/if",
|
||||||
@ -1069,7 +1062,9 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
|
|
||||||
let shapes = shape_guard::supported_shapes(module);
|
let shapes = shape_guard::supported_shapes(module);
|
||||||
if shapes.is_empty() {
|
if shapes.is_empty() {
|
||||||
return Err("[joinir/normalized-dev] module shape is not supported by normalized_dev".into());
|
return Err(
|
||||||
|
"[joinir/normalized-dev] module shape is not supported by normalized_dev".into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let debug = dev_env::normalized_dev_logs_enabled() && crate::config::env::joinir_dev_enabled();
|
let debug = dev_env::normalized_dev_logs_enabled() && crate::config::env::joinir_dev_enabled();
|
||||||
@ -1092,15 +1087,15 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
| NormalizedDevShape::JsonparserSkipWsReal
|
| NormalizedDevShape::JsonparserSkipWsReal
|
||||||
| NormalizedDevShape::JsonparserAtoiMini
|
| NormalizedDevShape::JsonparserAtoiMini
|
||||||
| NormalizedDevShape::JsonparserAtoiReal
|
| NormalizedDevShape::JsonparserAtoiReal
|
||||||
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(
|
| NormalizedDevShape::JsonparserParseNumberReal => {
|
||||||
|| {
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm = normalize_pattern2_minimal(module);
|
let norm = normalize_pattern2_minimal(module);
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
},
|
}))
|
||||||
)),
|
}
|
||||||
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm =
|
let norm = normalize_selfhost_token_scan_p2(module)
|
||||||
normalize_selfhost_token_scan_p2(module).expect("selfhost P2 normalization failed");
|
.expect("selfhost P2 normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
|
||||||
@ -1110,8 +1105,8 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
})),
|
})),
|
||||||
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
|
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
|
||||||
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm = normalize_pattern3_if_sum_minimal(module)
|
let norm =
|
||||||
.expect("P3 normalization failed");
|
normalize_pattern3_if_sum_minimal(module).expect("P3 normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::Pattern3IfSumMulti => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern3IfSumMulti => catch_unwind(AssertUnwindSafe(|| {
|
||||||
@ -1125,8 +1120,8 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm = normalize_selfhost_if_sum_p3(module)
|
let norm =
|
||||||
.expect("selfhost P3 normalization failed");
|
normalize_selfhost_if_sum_p3(module).expect("selfhost P3 normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
|
||||||
@ -1156,36 +1151,38 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
|||||||
})),
|
})),
|
||||||
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
|
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
|
||||||
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm = normalize_pattern4_continue_minimal(module)
|
let norm =
|
||||||
.expect("P4 normalization failed");
|
normalize_pattern4_continue_minimal(module).expect("P4 normalization failed");
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
})),
|
})),
|
||||||
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|
||||||
catch_unwind(AssertUnwindSafe(|| {
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm =
|
let norm = normalize_jsonparser_parse_array_continue_skip_ws(module)
|
||||||
normalize_jsonparser_parse_array_continue_skip_ws(module)
|
.expect("P4 array normalization failed");
|
||||||
.expect("P4 array normalization failed");
|
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|
||||||
catch_unwind(AssertUnwindSafe(|| {
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
let norm =
|
let norm = normalize_jsonparser_parse_object_continue_skip_ws(module)
|
||||||
normalize_jsonparser_parse_object_continue_skip_ws(module)
|
.expect("P4 object normalization failed");
|
||||||
.expect("P4 object normalization failed");
|
|
||||||
normalized_pattern2_to_structured(&norm)
|
normalized_pattern2_to_structured(&norm)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
|
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
|
||||||
NormalizedDevShape::PatternContinueReturnMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::PatternContinueReturnMinimal => {
|
||||||
let norm = normalize_pattern2_minimal(module);
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
normalized_pattern2_to_structured(&norm)
|
let norm = normalize_pattern2_minimal(module);
|
||||||
})),
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
}))
|
||||||
|
}
|
||||||
// Phase 90: Parse String Composite pattern (dev-only, delegates to P2 for now)
|
// Phase 90: Parse String Composite pattern (dev-only, delegates to P2 for now)
|
||||||
NormalizedDevShape::ParseStringCompositeMinimal => catch_unwind(AssertUnwindSafe(|| {
|
NormalizedDevShape::ParseStringCompositeMinimal => {
|
||||||
let norm = normalize_pattern2_minimal(module);
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
normalized_pattern2_to_structured(&norm)
|
let norm = normalize_pattern2_minimal(module);
|
||||||
})),
|
normalized_pattern2_to_structured(&norm)
|
||||||
|
}))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match attempt {
|
match attempt {
|
||||||
|
|||||||
@ -132,8 +132,8 @@ impl NormalizedDevFixture {
|
|||||||
use super::super::frontend::ast_lowerer::AstToJoinIrLowerer;
|
use super::super::frontend::ast_lowerer::AstToJoinIrLowerer;
|
||||||
|
|
||||||
let fixture_json = self.fixture_content();
|
let fixture_json = self.fixture_content();
|
||||||
let program_json: serde_json::Value = serde_json::from_str(fixture_json)
|
let program_json: serde_json::Value =
|
||||||
.unwrap_or_else(|e| {
|
serde_json::from_str(fixture_json).unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
"{} fixture should be valid JSON: {}",
|
"{} fixture should be valid JSON: {}",
|
||||||
self.function_name(),
|
self.function_name(),
|
||||||
@ -165,11 +165,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_all_fixtures_have_unique_names() {
|
fn test_all_fixtures_have_unique_names() {
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
let names: HashSet<_> = ALL_DEV_FIXTURES
|
let names: HashSet<_> = ALL_DEV_FIXTURES.iter().map(|f| f.function_name()).collect();
|
||||||
.iter()
|
assert_eq!(
|
||||||
.map(|f| f.function_name())
|
names.len(),
|
||||||
.collect();
|
ALL_DEV_FIXTURES.len(),
|
||||||
assert_eq!(names.len(), ALL_DEV_FIXTURES.len(), "Fixture names must be unique");
|
"Fixture names must be unique"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -24,7 +24,12 @@ fn const_i64(func: &mut JoinFunction, dst: ValueId, value: i64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compare(func: &mut JoinFunction, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) {
|
fn compare(func: &mut JoinFunction, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) {
|
||||||
func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst, op, lhs, rhs }));
|
func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst,
|
||||||
|
op,
|
||||||
|
lhs,
|
||||||
|
rhs,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unary_not(func: &mut JoinFunction, dst: ValueId, operand: ValueId) {
|
fn unary_not(func: &mut JoinFunction, dst: ValueId, operand: ValueId) {
|
||||||
@ -44,7 +49,13 @@ fn bin_add(func: &mut JoinFunction, dst: ValueId, lhs: ValueId, rhs: ValueId) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(func: &mut JoinFunction, dst: ValueId, cond: ValueId, then_val: ValueId, else_val: ValueId) {
|
fn select(
|
||||||
|
func: &mut JoinFunction,
|
||||||
|
dst: ValueId,
|
||||||
|
cond: ValueId,
|
||||||
|
then_val: ValueId,
|
||||||
|
else_val: ValueId,
|
||||||
|
) {
|
||||||
func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
||||||
dst,
|
dst,
|
||||||
cond,
|
cond,
|
||||||
@ -165,8 +176,8 @@ pub fn build_jsonparser_skip_ws_real_structured_for_normalized_dev() -> JoinModu
|
|||||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json"
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_skip_ws_real.program.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program_json: serde_json::Value =
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
serde_json::from_str(FIXTURE).expect("jsonparser skip_ws real fixture should be valid JSON");
|
.expect("jsonparser skip_ws real fixture should be valid JSON");
|
||||||
|
|
||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
let module = lowerer.lower_program_json(&program_json);
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
@ -189,8 +200,8 @@ pub fn build_jsonparser_parse_number_real_structured_for_normalized_dev() -> Joi
|
|||||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json"
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_number_real.program.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program_json: serde_json::Value =
|
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
||||||
serde_json::from_str(FIXTURE).expect("jsonparser parse_number real fixture should be valid JSON");
|
.expect("jsonparser parse_number real fixture should be valid JSON");
|
||||||
|
|
||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
let module = lowerer.lower_program_json(&program_json);
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
@ -319,13 +330,7 @@ pub fn build_pattern3_if_sum_multi_min_structured_for_normalized_dev() -> JoinMo
|
|||||||
let cond_cmp = alloc();
|
let cond_cmp = alloc();
|
||||||
let zero_const = alloc();
|
let zero_const = alloc();
|
||||||
const_i64(&mut loop_step, zero_const, 0);
|
const_i64(&mut loop_step, zero_const, 0);
|
||||||
compare(
|
compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
|
||||||
&mut loop_step,
|
|
||||||
cond_cmp,
|
|
||||||
CompareOp::Gt,
|
|
||||||
i_param,
|
|
||||||
zero_const,
|
|
||||||
);
|
|
||||||
|
|
||||||
// then: sum = sum + 1, count = count + 1
|
// then: sum = sum + 1, count = count + 1
|
||||||
let one_const = alloc();
|
let one_const = alloc();
|
||||||
@ -415,18 +420,17 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
|
|||||||
// loop_step params: i, sum
|
// loop_step params: i, sum
|
||||||
let i_param = alloc();
|
let i_param = alloc();
|
||||||
let sum_param = alloc();
|
let sum_param = alloc();
|
||||||
let mut loop_step = JoinFunction::new(loop_id, "loop_step".to_string(), vec![i_param, sum_param]);
|
let mut loop_step =
|
||||||
|
JoinFunction::new(loop_id, "loop_step".to_string(), vec![i_param, sum_param]);
|
||||||
|
|
||||||
// loop condition: i < 5
|
// loop condition: i < 5
|
||||||
let limit_const = alloc();
|
let limit_const = alloc();
|
||||||
let cmp_loop = alloc();
|
let cmp_loop = alloc();
|
||||||
let exit_cond = alloc();
|
let exit_cond = alloc();
|
||||||
loop_step
|
loop_step.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
.body
|
dst: limit_const,
|
||||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
value: ConstValue::Integer(5),
|
||||||
dst: limit_const,
|
}));
|
||||||
value: ConstValue::Integer(5),
|
|
||||||
}));
|
|
||||||
compare(
|
compare(
|
||||||
&mut loop_step,
|
&mut loop_step,
|
||||||
cmp_loop,
|
cmp_loop,
|
||||||
@ -445,13 +449,7 @@ pub fn build_pattern3_json_if_sum_min_structured_for_normalized_dev() -> JoinMod
|
|||||||
let cond_cmp = alloc();
|
let cond_cmp = alloc();
|
||||||
let zero_const = alloc();
|
let zero_const = alloc();
|
||||||
const_i64(&mut loop_step, zero_const, 0);
|
const_i64(&mut loop_step, zero_const, 0);
|
||||||
compare(
|
compare(&mut loop_step, cond_cmp, CompareOp::Gt, i_param, zero_const);
|
||||||
&mut loop_step,
|
|
||||||
cond_cmp,
|
|
||||||
CompareOp::Gt,
|
|
||||||
i_param,
|
|
||||||
zero_const,
|
|
||||||
);
|
|
||||||
|
|
||||||
// then: sum = sum + i
|
// then: sum = sum + i
|
||||||
let sum_then = alloc();
|
let sum_then = alloc();
|
||||||
@ -524,8 +522,8 @@ pub fn build_selfhost_if_sum_p3_ext_structured_for_normalized_dev() -> JoinModul
|
|||||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json"
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_if_sum_p3_ext.program.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
let program_json: serde_json::Value =
|
||||||
.expect("selfhost if_sum P3 ext fixture should be valid JSON");
|
serde_json::from_str(FIXTURE).expect("selfhost if_sum P3 ext fixture should be valid JSON");
|
||||||
|
|
||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
let module = lowerer.lower_program_json(&program_json);
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
@ -548,8 +546,8 @@ pub fn build_selfhost_args_parse_p2_structured_for_normalized_dev() -> JoinModul
|
|||||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json"
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_args_parse_p2.program.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
let program_json: serde_json::Value =
|
||||||
.expect("selfhost args_parse P2 fixture should be valid JSON");
|
serde_json::from_str(FIXTURE).expect("selfhost args_parse P2 fixture should be valid JSON");
|
||||||
|
|
||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
let module = lowerer.lower_program_json(&program_json);
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
@ -572,8 +570,8 @@ pub fn build_selfhost_stmt_count_p3_structured_for_normalized_dev() -> JoinModul
|
|||||||
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json"
|
"../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/selfhost_stmt_count_p3.program.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
let program_json: serde_json::Value = serde_json::from_str(FIXTURE)
|
let program_json: serde_json::Value =
|
||||||
.expect("selfhost stmt_count P3 fixture should be valid JSON");
|
serde_json::from_str(FIXTURE).expect("selfhost stmt_count P3 fixture should be valid JSON");
|
||||||
|
|
||||||
let mut lowerer = AstToJoinIrLowerer::new();
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
let module = lowerer.lower_program_json(&program_json);
|
let module = lowerer.lower_program_json(&program_json);
|
||||||
@ -604,7 +602,7 @@ pub fn build_jsonparser_atoi_structured_for_normalized_dev() -> JoinModule {
|
|||||||
|
|
||||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
|
"[joinir/normalized-dev] jsonparser_atoi_mini structured module: {:#?}",
|
||||||
module
|
module
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -679,18 +677,9 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule {
|
|||||||
let loop_condition = bin(BinaryOperator::Less, var("i"), int_lit(3));
|
let loop_condition = bin(BinaryOperator::Less, var("i"), int_lit(3));
|
||||||
let if_condition = bin(BinaryOperator::Greater, var("i"), int_lit(0));
|
let if_condition = bin(BinaryOperator::Greater, var("i"), int_lit(0));
|
||||||
|
|
||||||
let then_update = assignment(
|
let then_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(1)));
|
||||||
var("sum"),
|
let else_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(0)));
|
||||||
bin(BinaryOperator::Add, var("sum"), int_lit(1)),
|
let counter_update = assignment(var("i"), bin(BinaryOperator::Add, var("i"), int_lit(1)));
|
||||||
);
|
|
||||||
let else_update = assignment(
|
|
||||||
var("sum"),
|
|
||||||
bin(BinaryOperator::Add, var("sum"), int_lit(0)),
|
|
||||||
);
|
|
||||||
let counter_update = assignment(
|
|
||||||
var("i"),
|
|
||||||
bin(BinaryOperator::Add, var("i"), int_lit(1)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let if_stmt = ASTNode::If {
|
let if_stmt = ASTNode::If {
|
||||||
condition: Box::new(if_condition),
|
condition: Box::new(if_condition),
|
||||||
@ -707,9 +696,14 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule {
|
|||||||
let i_id = join_value_space.alloc_param();
|
let i_id = join_value_space.alloc_param();
|
||||||
cond_env.insert("i".to_string(), i_id);
|
cond_env.insert("i".to_string(), i_id);
|
||||||
|
|
||||||
let (module, _meta) =
|
let (module, _meta) = lower_if_sum_pattern(
|
||||||
lower_if_sum_pattern(&loop_condition, &if_stmt, &body, &cond_env, &mut join_value_space)
|
&loop_condition,
|
||||||
.expect("if-sum lowering should succeed for minimal pattern3");
|
&if_stmt,
|
||||||
|
&body,
|
||||||
|
&cond_env,
|
||||||
|
&mut join_value_space,
|
||||||
|
)
|
||||||
|
.expect("if-sum lowering should succeed for minimal pattern3");
|
||||||
|
|
||||||
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
if joinir_dev_enabled() && joinir_test_debug_enabled() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -743,7 +737,8 @@ pub fn build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_d
|
|||||||
/// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。
|
/// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。
|
||||||
///
|
///
|
||||||
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json
|
/// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json
|
||||||
pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule {
|
pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule
|
||||||
|
{
|
||||||
use super::dev_fixtures::NormalizedDevFixture;
|
use super::dev_fixtures::NormalizedDevFixture;
|
||||||
NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs.load_and_lower()
|
NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs.load_and_lower()
|
||||||
}
|
}
|
||||||
@ -818,15 +813,15 @@ pub mod prelude {
|
|||||||
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_real_structured_for_normalized_dev,
|
||||||
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
build_jsonparser_skip_ws_structured_for_normalized_dev,
|
||||||
build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev,
|
build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev,
|
||||||
|
build_parse_array_min_structured_for_normalized_dev,
|
||||||
|
build_parse_object_min_structured_for_normalized_dev,
|
||||||
|
build_parse_string_composite_min_structured_for_normalized_dev,
|
||||||
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
build_pattern2_break_fixture_structured, build_pattern2_minimal_structured,
|
||||||
build_pattern3_if_sum_min_structured_for_normalized_dev,
|
build_pattern3_if_sum_min_structured_for_normalized_dev,
|
||||||
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
build_pattern3_if_sum_multi_min_structured_for_normalized_dev,
|
||||||
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
build_pattern3_json_if_sum_min_structured_for_normalized_dev,
|
||||||
build_pattern4_continue_min_structured_for_normalized_dev,
|
build_pattern4_continue_min_structured_for_normalized_dev,
|
||||||
build_pattern_continue_return_min_structured_for_normalized_dev,
|
build_pattern_continue_return_min_structured_for_normalized_dev,
|
||||||
build_parse_string_composite_min_structured_for_normalized_dev,
|
|
||||||
build_parse_array_min_structured_for_normalized_dev,
|
|
||||||
build_parse_object_min_structured_for_normalized_dev,
|
|
||||||
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
|
build_selfhost_if_sum_p3_ext_structured_for_normalized_dev,
|
||||||
build_selfhost_if_sum_p3_structured_for_normalized_dev,
|
build_selfhost_if_sum_p3_structured_for_normalized_dev,
|
||||||
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,
|
build_selfhost_token_scan_p2_accum_structured_for_normalized_dev,
|
||||||
|
|||||||
@ -29,12 +29,10 @@ impl LoopStepInspector {
|
|||||||
///
|
///
|
||||||
/// ループ条件や continue/break 判定で使用される。
|
/// ループ条件や continue/break 判定で使用される。
|
||||||
pub fn has_compare_instruction(loop_step_func: &JoinFunction) -> bool {
|
pub fn has_compare_instruction(loop_step_func: &JoinFunction) -> bool {
|
||||||
loop_step_func.body.iter().any(|inst| {
|
loop_step_func
|
||||||
matches!(
|
.body
|
||||||
inst,
|
.iter()
|
||||||
JoinInst::Compute(MirLikeInst::Compare { .. })
|
.any(|inst| matches!(inst, JoinInst::Compute(MirLikeInst::Compare { .. })))
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 条件付き Jump の個数をカウント
|
/// 条件付き Jump の個数をカウント
|
||||||
@ -77,10 +75,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::join_ir::{JoinFuncId, VarId};
|
use crate::mir::join_ir::{JoinFuncId, VarId};
|
||||||
|
|
||||||
fn make_dummy_loop_step(
|
fn make_dummy_loop_step(body: Vec<JoinInst>, param_count: usize) -> JoinFunction {
|
||||||
body: Vec<JoinInst>,
|
|
||||||
param_count: usize,
|
|
||||||
) -> JoinFunction {
|
|
||||||
let params: Vec<_> = (0..param_count).map(|i| VarId(i as u32)).collect();
|
let params: Vec<_> = (0..param_count).map(|i| VarId(i as u32)).collect();
|
||||||
JoinFunction {
|
JoinFunction {
|
||||||
id: JoinFuncId(1),
|
id: JoinFuncId(1),
|
||||||
@ -105,7 +100,9 @@ mod tests {
|
|||||||
assert!(LoopStepInspector::has_select_instruction(&func_with_select));
|
assert!(LoopStepInspector::has_select_instruction(&func_with_select));
|
||||||
|
|
||||||
let func_without_select = make_dummy_loop_step(vec![], 2);
|
let func_without_select = make_dummy_loop_step(vec![], 2);
|
||||||
assert!(!LoopStepInspector::has_select_instruction(&func_without_select));
|
assert!(!LoopStepInspector::has_select_instruction(
|
||||||
|
&func_without_select
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -124,7 +121,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
JoinInst::Jump {
|
JoinInst::Jump {
|
||||||
target: JoinFuncId(2),
|
target: JoinFuncId(2),
|
||||||
cond: None, // 無条件は含まれない
|
cond: None, // 無条件は含まれない
|
||||||
args: vec![],
|
args: vec![],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -139,7 +136,7 @@ mod tests {
|
|||||||
vec![JoinInst::Call {
|
vec![JoinInst::Call {
|
||||||
target: JoinFuncId(1),
|
target: JoinFuncId(1),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
k_next: None, // tail call
|
k_next: None, // tail call
|
||||||
results: vec![],
|
results: vec![],
|
||||||
}],
|
}],
|
||||||
2,
|
2,
|
||||||
@ -150,7 +147,7 @@ mod tests {
|
|||||||
vec![JoinInst::Call {
|
vec![JoinInst::Call {
|
||||||
target: JoinFuncId(1),
|
target: JoinFuncId(1),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
k_next: Some(JoinFuncId(3)), // not tail call
|
k_next: Some(JoinFuncId(3)), // not tail call
|
||||||
results: vec![],
|
results: vec![],
|
||||||
}],
|
}],
|
||||||
2,
|
2,
|
||||||
@ -160,10 +157,20 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_reasonable_param_count() {
|
fn test_has_reasonable_param_count() {
|
||||||
assert!(!LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 1)));
|
assert!(!LoopStepInspector::has_reasonable_param_count(
|
||||||
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 2)));
|
&make_dummy_loop_step(vec![], 1)
|
||||||
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 3)));
|
));
|
||||||
assert!(LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 4)));
|
assert!(LoopStepInspector::has_reasonable_param_count(
|
||||||
assert!(!LoopStepInspector::has_reasonable_param_count(&make_dummy_loop_step(vec![], 5)));
|
&make_dummy_loop_step(vec![], 2)
|
||||||
|
));
|
||||||
|
assert!(LoopStepInspector::has_reasonable_param_count(
|
||||||
|
&make_dummy_loop_step(vec![], 3)
|
||||||
|
));
|
||||||
|
assert!(LoopStepInspector::has_reasonable_param_count(
|
||||||
|
&make_dummy_loop_step(vec![], 4)
|
||||||
|
));
|
||||||
|
assert!(!LoopStepInspector::has_reasonable_param_count(
|
||||||
|
&make_dummy_loop_step(vec![], 5)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,6 @@ pub enum ShapeCapabilityKind {
|
|||||||
|
|
||||||
/// Selfhost P3 if-sum family
|
/// Selfhost P3 if-sum family
|
||||||
SelfhostP3IfSum,
|
SelfhostP3IfSum,
|
||||||
|
|
||||||
// Future: Other P2 patterns
|
// Future: Other P2 patterns
|
||||||
// P2MidAtOfLoop,
|
// P2MidAtOfLoop,
|
||||||
// P2HeavyString,
|
// P2HeavyString,
|
||||||
@ -100,8 +99,14 @@ pub enum NormalizedDevShape {
|
|||||||
type Detector = fn(&JoinModule) -> bool;
|
type Detector = fn(&JoinModule) -> bool;
|
||||||
|
|
||||||
const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
|
const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
|
||||||
(NormalizedDevShape::Pattern1Mini, detectors::is_pattern1_mini),
|
(
|
||||||
(NormalizedDevShape::Pattern2Mini, detectors::is_pattern2_mini),
|
NormalizedDevShape::Pattern1Mini,
|
||||||
|
detectors::is_pattern1_mini,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
NormalizedDevShape::Pattern2Mini,
|
||||||
|
detectors::is_pattern2_mini,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
NormalizedDevShape::JsonparserSkipWsMini,
|
NormalizedDevShape::JsonparserSkipWsMini,
|
||||||
detectors::is_jsonparser_skip_ws_mini,
|
detectors::is_jsonparser_skip_ws_mini,
|
||||||
@ -552,18 +557,16 @@ mod detectors {
|
|||||||
|
|
||||||
let has_select = loop_func.body.iter().any(|inst| match inst {
|
let has_select = loop_func.body.iter().any(|inst| match inst {
|
||||||
JoinInst::Select { .. } => true,
|
JoinInst::Select { .. } => true,
|
||||||
JoinInst::Compute(mir_inst) => matches!(
|
JoinInst::Compute(mir_inst) => {
|
||||||
mir_inst,
|
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
|
||||||
crate::mir::join_ir::MirLikeInst::Select { .. }
|
}
|
||||||
),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let has_boxcall = loop_func.body.iter().any(|inst| match inst {
|
let has_boxcall = loop_func.body.iter().any(|inst| match inst {
|
||||||
JoinInst::Compute(mir_inst) => matches!(
|
JoinInst::Compute(mir_inst) => {
|
||||||
mir_inst,
|
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
|
||||||
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
|
}
|
||||||
),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -601,8 +604,7 @@ mod detectors {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
|
.any(|inst| matches!(inst, JoinInst::Call { k_next: None, .. }));
|
||||||
|
|
||||||
let param_set: std::collections::BTreeSet<_> =
|
let param_set: std::collections::BTreeSet<_> = loop_step.params.iter().copied().collect();
|
||||||
loop_step.params.iter().copied().collect();
|
|
||||||
|
|
||||||
let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match inst {
|
let has_ge_compare_between_params = loop_step.body.iter().any(|inst| match inst {
|
||||||
JoinInst::Compute(mir_inst) => match mir_inst {
|
JoinInst::Compute(mir_inst) => match mir_inst {
|
||||||
@ -617,10 +619,9 @@ mod detectors {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let has_boxcall = loop_step.body.iter().any(|inst| match inst {
|
let has_boxcall = loop_step.body.iter().any(|inst| match inst {
|
||||||
JoinInst::Compute(mir_inst) => matches!(
|
JoinInst::Compute(mir_inst) => {
|
||||||
mir_inst,
|
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::BoxCall { .. })
|
||||||
crate::mir::join_ir::MirLikeInst::BoxCall { .. }
|
}
|
||||||
),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -667,10 +668,9 @@ mod detectors {
|
|||||||
// Phase 220: Select can be either JoinInst::Select or Compute(MirLikeInst::Select)
|
// Phase 220: Select can be either JoinInst::Select or Compute(MirLikeInst::Select)
|
||||||
let has_select = loop_step.body.iter().any(|inst| match inst {
|
let has_select = loop_step.body.iter().any(|inst| match inst {
|
||||||
JoinInst::Select { .. } => true,
|
JoinInst::Select { .. } => true,
|
||||||
JoinInst::Compute(mir_inst) => matches!(
|
JoinInst::Compute(mir_inst) => {
|
||||||
mir_inst,
|
matches!(mir_inst, crate::mir::join_ir::MirLikeInst::Select { .. })
|
||||||
crate::mir::join_ir::MirLikeInst::Select { .. }
|
}
|
||||||
),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -896,7 +896,7 @@ mod detectors {
|
|||||||
&& has_select
|
&& has_select
|
||||||
&& has_tail_call
|
&& has_tail_call
|
||||||
&& reasonable_param_count
|
&& reasonable_param_count
|
||||||
&& k_exit_jumps_count == 1 // Exactly one loop break (not early return)
|
&& k_exit_jumps_count == 1 // Exactly one loop break (not early return)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_jsonparser_parse_array_continue_skip_ws(module: &JoinModule) -> bool {
|
pub(crate) fn is_jsonparser_parse_array_continue_skip_ws(module: &JoinModule) -> bool {
|
||||||
@ -952,7 +952,7 @@ mod detectors {
|
|||||||
&& has_select
|
&& has_select
|
||||||
&& has_tail_call
|
&& has_tail_call
|
||||||
&& reasonable_param_count
|
&& reasonable_param_count
|
||||||
&& k_exit_jumps_count >= 2 // At least 2: loop break + early return
|
&& k_exit_jumps_count >= 2 // At least 2: loop break + early return
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 90: Check if module matches Parse String Composite pattern
|
/// Phase 90: Check if module matches Parse String Composite pattern
|
||||||
@ -993,8 +993,12 @@ mod detectors {
|
|||||||
// Check if rhs is a const value 2 (indicating i+=2 for escape)
|
// Check if rhs is a const value 2 (indicating i+=2 for escape)
|
||||||
// We need to check if rhs points to a Const instruction with value 2
|
// We need to check if rhs points to a Const instruction with value 2
|
||||||
loop_step.body.iter().any(|other_inst| match other_inst {
|
loop_step.body.iter().any(|other_inst| match other_inst {
|
||||||
JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const { dst, value }) => {
|
JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
|
||||||
dst == rhs && matches!(value, crate::mir::join_ir::ConstValue::Integer(2))
|
dst,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
dst == rhs
|
||||||
|
&& matches!(value, crate::mir::join_ir::ConstValue::Integer(2))
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
@ -1171,7 +1175,9 @@ mod tests {
|
|||||||
shapes
|
shapes
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!shapes.iter().any(|s| matches!(s, NormalizedDevShape::Pattern3IfSumMinimal)),
|
!shapes
|
||||||
|
.iter()
|
||||||
|
.any(|s| matches!(s, NormalizedDevShape::Pattern3IfSumMinimal)),
|
||||||
"selfhost_if_sum_p3 should not rely on canonical P3 minimal detection: {:?}",
|
"selfhost_if_sum_p3 should not rely on canonical P3 minimal detection: {:?}",
|
||||||
shapes
|
shapes
|
||||||
);
|
);
|
||||||
@ -1286,7 +1292,9 @@ mod tests {
|
|||||||
k_next: None,
|
k_next: None,
|
||||||
dst: Some(ValueId(2)),
|
dst: Some(ValueId(2)),
|
||||||
},
|
},
|
||||||
JoinInst::Ret { value: Some(ValueId(2)) },
|
JoinInst::Ret {
|
||||||
|
value: Some(ValueId(2)),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
};
|
};
|
||||||
@ -1348,7 +1356,9 @@ mod tests {
|
|||||||
k_next: None,
|
k_next: None,
|
||||||
dst: Some(ValueId(40)),
|
dst: Some(ValueId(40)),
|
||||||
},
|
},
|
||||||
JoinInst::Ret { value: Some(ValueId(40)) },
|
JoinInst::Ret {
|
||||||
|
value: Some(ValueId(40)),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
};
|
};
|
||||||
@ -1358,7 +1368,9 @@ mod tests {
|
|||||||
id: JoinFuncId::new(2),
|
id: JoinFuncId::new(2),
|
||||||
name: "k_exit".to_string(),
|
name: "k_exit".to_string(),
|
||||||
params: vec![ValueId(0)],
|
params: vec![ValueId(0)],
|
||||||
body: vec![JoinInst::Ret { value: Some(ValueId(0)) }],
|
body: vec![JoinInst::Ret {
|
||||||
|
value: Some(ValueId(0)),
|
||||||
|
}],
|
||||||
exit_cont: None,
|
exit_cont: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -77,7 +77,9 @@ impl OwnershipAnalyzer {
|
|||||||
return Err("OwnershipAnalyzer: no FunctionDef found in 'defs'".to_string());
|
return Err("OwnershipAnalyzer: no FunctionDef found in 'defs'".to_string());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err("OwnershipAnalyzer: expected top-level 'functions' or 'defs' array".to_string());
|
return Err(
|
||||||
|
"OwnershipAnalyzer: expected top-level 'functions' or 'defs' array".to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert ScopeInfo to OwnershipPlan
|
// Convert ScopeInfo to OwnershipPlan
|
||||||
@ -87,26 +89,37 @@ impl OwnershipAnalyzer {
|
|||||||
fn alloc_scope(&mut self, kind: ScopeKind, parent: Option<ScopeId>) -> ScopeId {
|
fn alloc_scope(&mut self, kind: ScopeKind, parent: Option<ScopeId>) -> ScopeId {
|
||||||
let id = ScopeId(self.next_scope_id);
|
let id = ScopeId(self.next_scope_id);
|
||||||
self.next_scope_id += 1;
|
self.next_scope_id += 1;
|
||||||
self.scopes.insert(id, ScopeInfo {
|
self.scopes.insert(
|
||||||
id,
|
id,
|
||||||
kind,
|
ScopeInfo {
|
||||||
parent,
|
id,
|
||||||
defined: BTreeSet::new(),
|
kind,
|
||||||
reads: BTreeSet::new(),
|
parent,
|
||||||
writes: BTreeSet::new(),
|
defined: BTreeSet::new(),
|
||||||
condition_reads: BTreeSet::new(),
|
reads: BTreeSet::new(),
|
||||||
});
|
writes: BTreeSet::new(),
|
||||||
|
condition_reads: BTreeSet::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_function(&mut self, func: &Value, parent: Option<ScopeId>) -> Result<ScopeId, String> {
|
fn analyze_function(
|
||||||
|
&mut self,
|
||||||
|
func: &Value,
|
||||||
|
parent: Option<ScopeId>,
|
||||||
|
) -> Result<ScopeId, String> {
|
||||||
let scope_id = self.alloc_scope(ScopeKind::Function, parent);
|
let scope_id = self.alloc_scope(ScopeKind::Function, parent);
|
||||||
|
|
||||||
// Collect function parameters as defined
|
// Collect function parameters as defined
|
||||||
if let Some(params) = func.get("params").and_then(|p| p.as_array()) {
|
if let Some(params) = func.get("params").and_then(|p| p.as_array()) {
|
||||||
for param in params {
|
for param in params {
|
||||||
if let Some(name) = param.as_str() {
|
if let Some(name) = param.as_str() {
|
||||||
self.scopes.get_mut(&scope_id).unwrap().defined.insert(name.to_string());
|
self.scopes
|
||||||
|
.get_mut(&scope_id)
|
||||||
|
.unwrap()
|
||||||
|
.defined
|
||||||
|
.insert(name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,7 +169,11 @@ impl OwnershipAnalyzer {
|
|||||||
}
|
}
|
||||||
"Assign" | "Assignment" => {
|
"Assign" | "Assignment" => {
|
||||||
if let Some(target) = stmt.get("target").and_then(|t| t.as_str()) {
|
if let Some(target) = stmt.get("target").and_then(|t| t.as_str()) {
|
||||||
self.scopes.get_mut(¤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")) {
|
if let Some(value) = stmt.get("value").or_else(|| stmt.get("expr")) {
|
||||||
self.analyze_expression(value, current_scope, false)?;
|
self.analyze_expression(value, current_scope, false)?;
|
||||||
@ -234,7 +251,12 @@ impl OwnershipAnalyzer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_expression(&mut self, expr: &Value, current_scope: ScopeId, is_condition: bool) -> Result<(), String> {
|
fn analyze_expression(
|
||||||
|
&mut self,
|
||||||
|
expr: &Value,
|
||||||
|
current_scope: ScopeId,
|
||||||
|
is_condition: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
let kind = expr
|
let kind = expr
|
||||||
.get("kind")
|
.get("kind")
|
||||||
.or_else(|| expr.get("type"))
|
.or_else(|| expr.get("type"))
|
||||||
@ -372,8 +394,11 @@ impl OwnershipAnalyzer {
|
|||||||
// owned_vars: defined in this scope
|
// owned_vars: defined in this scope
|
||||||
for name in &scope.defined {
|
for name in &scope.defined {
|
||||||
let is_written = scope.writes.contains(name);
|
let is_written = scope.writes.contains(name);
|
||||||
let is_condition_only = scope.condition_reads.contains(name) &&
|
let is_condition_only = scope.condition_reads.contains(name)
|
||||||
!scope.writes.iter().any(|w| w == name && !scope.condition_reads.contains(w));
|
&& !scope
|
||||||
|
.writes
|
||||||
|
.iter()
|
||||||
|
.any(|w| w == name && !scope.condition_reads.contains(w));
|
||||||
|
|
||||||
plan.owned_vars.push(ScopeOwnedVar {
|
plan.owned_vars.push(ScopeOwnedVar {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@ -530,10 +555,15 @@ mod tests {
|
|||||||
|
|
||||||
// Find the loop plan
|
// Find the loop plan
|
||||||
let loop_plan = plans.iter().find(|p| {
|
let loop_plan = plans.iter().find(|p| {
|
||||||
p.relay_writes.iter().any(|r| r.name == "sum" || r.name == "i")
|
p.relay_writes
|
||||||
|
.iter()
|
||||||
|
.any(|r| r.name == "sum" || r.name == "i")
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(loop_plan.is_some(), "Should have a loop plan with relay writes");
|
assert!(
|
||||||
|
loop_plan.is_some(),
|
||||||
|
"Should have a loop plan with relay writes"
|
||||||
|
);
|
||||||
let loop_plan = loop_plan.unwrap();
|
let loop_plan = loop_plan.unwrap();
|
||||||
|
|
||||||
// sum and i are written in loop but owned by function -> relay_writes
|
// sum and i are written in loop but owned by function -> relay_writes
|
||||||
@ -576,15 +606,19 @@ mod tests {
|
|||||||
let plans = analyzer.analyze_json(&json).unwrap();
|
let plans = analyzer.analyze_json(&json).unwrap();
|
||||||
|
|
||||||
// Find loop plan
|
// Find loop plan
|
||||||
let loop_plan = plans.iter().find(|p| {
|
let loop_plan = plans
|
||||||
p.owned_vars.iter().any(|v| v.name == "count")
|
.iter()
|
||||||
});
|
.find(|p| p.owned_vars.iter().any(|v| v.name == "count"));
|
||||||
|
|
||||||
assert!(loop_plan.is_some(), "Loop should own 'count'");
|
assert!(loop_plan.is_some(), "Loop should own 'count'");
|
||||||
let loop_plan = loop_plan.unwrap();
|
let loop_plan = loop_plan.unwrap();
|
||||||
|
|
||||||
// count is owned AND written -> carrier
|
// count is owned AND written -> carrier
|
||||||
let count_var = loop_plan.owned_vars.iter().find(|v| v.name == "count").unwrap();
|
let count_var = loop_plan
|
||||||
|
.owned_vars
|
||||||
|
.iter()
|
||||||
|
.find(|v| v.name == "count")
|
||||||
|
.unwrap();
|
||||||
assert!(count_var.is_written, "count should be marked as written");
|
assert!(count_var.is_written, "count should be marked as written");
|
||||||
|
|
||||||
// No relay for count (it's owned)
|
// No relay for count (it's owned)
|
||||||
@ -631,9 +665,9 @@ mod tests {
|
|||||||
let plans = analyzer.analyze_json(&json).unwrap();
|
let plans = analyzer.analyze_json(&json).unwrap();
|
||||||
|
|
||||||
// Find loop plan
|
// Find loop plan
|
||||||
let loop_plan = plans.iter().find(|p| {
|
let loop_plan = plans
|
||||||
p.captures.iter().any(|c| c.name == "limit")
|
.iter()
|
||||||
});
|
.find(|p| p.captures.iter().any(|c| c.name == "limit"));
|
||||||
|
|
||||||
assert!(loop_plan.is_some(), "Loop should capture 'limit'");
|
assert!(loop_plan.is_some(), "Loop should capture 'limit'");
|
||||||
let loop_plan = loop_plan.unwrap();
|
let loop_plan = loop_plan.unwrap();
|
||||||
@ -642,7 +676,10 @@ mod tests {
|
|||||||
assert!(loop_plan.captures.iter().any(|c| c.name == "limit"));
|
assert!(loop_plan.captures.iter().any(|c| c.name == "limit"));
|
||||||
|
|
||||||
// limit should also be in condition_captures
|
// limit should also be in condition_captures
|
||||||
assert!(loop_plan.condition_captures.iter().any(|c| c.name == "limit"));
|
assert!(loop_plan
|
||||||
|
.condition_captures
|
||||||
|
.iter()
|
||||||
|
.any(|c| c.name == "limit"));
|
||||||
|
|
||||||
// limit is NOT in relay_writes (not written)
|
// limit is NOT in relay_writes (not written)
|
||||||
assert!(!loop_plan.relay_writes.iter().any(|r| r.name == "limit"));
|
assert!(!loop_plan.relay_writes.iter().any(|r| r.name == "limit"));
|
||||||
@ -687,9 +724,9 @@ mod tests {
|
|||||||
let plans = analyzer.analyze_json(&json).unwrap();
|
let plans = analyzer.analyze_json(&json).unwrap();
|
||||||
|
|
||||||
// At least one loop should relay total
|
// At least one loop should relay total
|
||||||
let any_relay = plans.iter().any(|p| {
|
let any_relay = plans
|
||||||
p.relay_writes.iter().any(|r| r.name == "total")
|
.iter()
|
||||||
});
|
.any(|p| p.relay_writes.iter().any(|r| r.name == "total"));
|
||||||
|
|
||||||
assert!(any_relay, "Some loop should relay 'total' to function");
|
assert!(any_relay, "Some loop should relay 'total' to function");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,20 +26,20 @@
|
|||||||
//! - Phase 59: plan_to_lowering helper for P3 (if-sum patterns)
|
//! - Phase 59: plan_to_lowering helper for P3 (if-sum patterns)
|
||||||
//! - Phase 71-Pre: plan_validator box (reusable validation)
|
//! - Phase 71-Pre: plan_validator box (reusable validation)
|
||||||
|
|
||||||
mod types;
|
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
mod plan_to_lowering;
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
|
||||||
mod ast_analyzer;
|
mod ast_analyzer;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
mod plan_validator;
|
mod plan_to_lowering;
|
||||||
|
|
||||||
pub use types::*;
|
|
||||||
pub use analyzer::*;
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub use plan_to_lowering::*;
|
mod plan_validator;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use analyzer::*;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub use ast_analyzer::*;
|
pub use ast_analyzer::*;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
|
pub use plan_to_lowering::*;
|
||||||
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub use plan_validator::*;
|
pub use plan_validator::*;
|
||||||
|
pub use types::*;
|
||||||
|
|||||||
@ -34,15 +34,15 @@ pub struct P3LoweringInputs {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns Err if relay_writes is non-empty (Phase 58 scope limitation).
|
/// Returns Err if relay_writes is non-empty (Phase 58 scope limitation).
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn plan_to_p2_inputs(
|
pub fn plan_to_p2_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P2LoweringInputs, String> {
|
||||||
plan: &OwnershipPlan,
|
|
||||||
loop_var: &str,
|
|
||||||
) -> Result<P2LoweringInputs, String> {
|
|
||||||
// Fail-Fast: relay_writes not supported in Phase 58
|
// Fail-Fast: relay_writes not supported in Phase 58
|
||||||
if !plan.relay_writes.is_empty() {
|
if !plan.relay_writes.is_empty() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Phase 58 limitation: relay_writes not yet supported. Found: {:?}",
|
"Phase 58 limitation: relay_writes not yet supported. Found: {:?}",
|
||||||
plan.relay_writes.iter().map(|r| &r.name).collect::<Vec<_>>()
|
plan.relay_writes
|
||||||
|
.iter()
|
||||||
|
.map(|r| &r.name)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ pub fn plan_to_p2_inputs(
|
|||||||
carriers.push(CarrierVar {
|
carriers.push(CarrierVar {
|
||||||
name: var.name.clone(),
|
name: var.name.clone(),
|
||||||
role,
|
role,
|
||||||
init: CarrierInit::FromHost, // Default (Phase 228)
|
init: CarrierInit::FromHost, // Default (Phase 228)
|
||||||
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
|
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
|
||||||
join_id: None,
|
join_id: None,
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
@ -206,15 +206,15 @@ pub fn plan_to_p2_inputs_with_relay(
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns Err if relay_writes is non-empty (Phase 59 scope limitation).
|
/// Returns Err if relay_writes is non-empty (Phase 59 scope limitation).
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub fn plan_to_p3_inputs(
|
pub fn plan_to_p3_inputs(plan: &OwnershipPlan, loop_var: &str) -> Result<P3LoweringInputs, String> {
|
||||||
plan: &OwnershipPlan,
|
|
||||||
loop_var: &str,
|
|
||||||
) -> Result<P3LoweringInputs, String> {
|
|
||||||
// Fail-Fast: relay_writes not supported in Phase 59
|
// Fail-Fast: relay_writes not supported in Phase 59
|
||||||
if !plan.relay_writes.is_empty() {
|
if !plan.relay_writes.is_empty() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Phase 59 limitation: relay_writes not yet supported for P3. Found: {:?}",
|
"Phase 59 limitation: relay_writes not yet supported for P3. Found: {:?}",
|
||||||
plan.relay_writes.iter().map(|r| &r.name).collect::<Vec<_>>()
|
plan.relay_writes
|
||||||
|
.iter()
|
||||||
|
.map(|r| &r.name)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ pub fn plan_to_p3_inputs(
|
|||||||
carriers.push(CarrierVar {
|
carriers.push(CarrierVar {
|
||||||
name: var.name.clone(),
|
name: var.name.clone(),
|
||||||
role,
|
role,
|
||||||
init: CarrierInit::FromHost, // Default (Phase 228)
|
init: CarrierInit::FromHost, // Default (Phase 228)
|
||||||
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
|
host_id: crate::mir::ValueId(0), // Placeholder - not used in dev analysis
|
||||||
join_id: None,
|
join_id: None,
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
@ -378,11 +378,12 @@ mod tests {
|
|||||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||||
plan.relay_writes.push(RelayVar {
|
plan.relay_writes.push(RelayVar {
|
||||||
name: "counter".to_string(),
|
name: "counter".to_string(),
|
||||||
owner_scope: ScopeId(0), // L1 owns
|
owner_scope: ScopeId(0), // L1 owns
|
||||||
relay_path: vec![ScopeId(1), ScopeId(2)], // L3 → L2 → L1
|
relay_path: vec![ScopeId(1), ScopeId(2)], // L3 → L2 → L1
|
||||||
});
|
});
|
||||||
|
|
||||||
let inputs = plan_to_p2_inputs_with_relay(&plan, "i").expect("Phase 66: multihop should be accepted");
|
let inputs = plan_to_p2_inputs_with_relay(&plan, "i")
|
||||||
|
.expect("Phase 66: multihop should be accepted");
|
||||||
assert_eq!(inputs.carriers.len(), 1);
|
assert_eq!(inputs.carriers.len(), 1);
|
||||||
assert_eq!(inputs.carriers[0].name, "counter");
|
assert_eq!(inputs.carriers[0].name, "counter");
|
||||||
assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState);
|
assert_eq!(inputs.carriers[0].role, CarrierRole::LoopState);
|
||||||
@ -433,7 +434,9 @@ mod tests {
|
|||||||
|
|
||||||
let result = plan_to_p2_inputs_with_relay(&plan, "i");
|
let result = plan_to_p2_inputs_with_relay(&plan, "i");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("relay cannot be owned by same scope"));
|
assert!(result
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("relay cannot be owned by same scope"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -454,7 +457,9 @@ mod tests {
|
|||||||
|
|
||||||
let result = plan_to_p2_inputs_with_relay(&plan, "i");
|
let result = plan_to_p2_inputs_with_relay(&plan, "i");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result.unwrap_err().contains("appears in both owned_vars and relay_writes"));
|
assert!(result
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("appears in both owned_vars and relay_writes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -546,12 +551,11 @@ mod tests {
|
|||||||
fn test_p3_five_plus_carriers() {
|
fn test_p3_five_plus_carriers() {
|
||||||
// Selfhost P3 pattern: 5+ carriers
|
// Selfhost P3 pattern: 5+ carriers
|
||||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||||
plan.owned_vars
|
plan.owned_vars.push(ScopeOwnedVar {
|
||||||
.push(ScopeOwnedVar {
|
name: "i".to_string(),
|
||||||
name: "i".to_string(),
|
is_written: true,
|
||||||
is_written: true,
|
is_condition_only: false,
|
||||||
is_condition_only: false,
|
});
|
||||||
});
|
|
||||||
plan.owned_vars.push(ScopeOwnedVar {
|
plan.owned_vars.push(ScopeOwnedVar {
|
||||||
name: "total".to_string(),
|
name: "total".to_string(),
|
||||||
is_written: true,
|
is_written: true,
|
||||||
|
|||||||
@ -297,7 +297,7 @@ mod tests {
|
|||||||
)],
|
)],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
promoted_loopbodylocals: vec![],
|
promoted_loopbodylocals: vec![],
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
promoted_bindings: std::collections::BTreeMap::new(),
|
promoted_bindings: std::collections::BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,9 @@ impl OwnershipPlan {
|
|||||||
|
|
||||||
/// Get condition-only carriers (owned, written, condition-only).
|
/// Get condition-only carriers (owned, written, condition-only).
|
||||||
pub fn condition_only_carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> {
|
pub fn condition_only_carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> {
|
||||||
self.owned_vars.iter().filter(|v| v.is_written && v.is_condition_only)
|
self.owned_vars
|
||||||
|
.iter()
|
||||||
|
.filter(|v| v.is_written && v.is_condition_only)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check invariant: no variable appears in multiple categories.
|
/// Check invariant: no variable appears in multiple categories.
|
||||||
@ -134,7 +136,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
plan.owned_vars.push(ScopeOwnedVar {
|
plan.owned_vars.push(ScopeOwnedVar {
|
||||||
name: "limit".to_string(),
|
name: "limit".to_string(),
|
||||||
is_written: false, // read-only owned
|
is_written: false, // read-only owned
|
||||||
is_condition_only: false,
|
is_condition_only: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -59,9 +59,7 @@ pub fn disable_observation() {
|
|||||||
/// Analyze PHI dst distribution
|
/// Analyze PHI dst distribution
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn analyze_distribution(observations: &[u32]) -> PhiDistributionReport {
|
pub fn analyze_distribution(observations: &[u32]) -> PhiDistributionReport {
|
||||||
use crate::mir::join_ir::lowering::join_value_space::{
|
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MIN, PARAM_MIN, PHI_RESERVED_MAX};
|
||||||
PHI_RESERVED_MAX, PARAM_MIN, LOCAL_MIN,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut in_reserved = 0;
|
let mut in_reserved = 0;
|
||||||
let mut in_param = 0;
|
let mut in_param = 0;
|
||||||
|
|||||||
@ -32,11 +32,9 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
|
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
|
||||||
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
|
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::{
|
use crate::mir::join_ir::normalized::{dev_env, normalized_dev_roundtrip_structured, shape_guard};
|
||||||
dev_env, normalized_dev_roundtrip_structured, shape_guard,
|
use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinInst, JoinModule, MirLikeInst, VarId};
|
||||||
};
|
|
||||||
|
|
||||||
// Phase 27.8: ops box からの再エクスポート
|
// Phase 27.8: ops box からの再エクスポート
|
||||||
pub use crate::mir::join_ir_ops::{JoinIrOpError, JoinValue};
|
pub use crate::mir::join_ir_ops::{JoinIrOpError, JoinValue};
|
||||||
@ -104,7 +102,9 @@ fn run_joinir_function_normalized_dev(
|
|||||||
let shapes = shape_guard::supported_shapes(module);
|
let shapes = shape_guard::supported_shapes(module);
|
||||||
if shapes.is_empty() {
|
if shapes.is_empty() {
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!("[joinir/normalized-dev/runner] shape unsupported; staying on Structured path");
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev/runner] shape unsupported; staying on Structured path"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return execute_function(vm, module, entry, args_vec);
|
return execute_function(vm, module, entry, args_vec);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,13 @@ use std::collections::BTreeMap;
|
|||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
|
use crate::config::env::joinir_dev::{current_joinir_mode, JoinIrMode};
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::JoinIrPhase;
|
use crate::mir::join_ir::normalized::shape_guard::{self, NormalizedDevShape};
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::{
|
use crate::mir::join_ir::normalized::{
|
||||||
normalize_pattern1_minimal, normalize_pattern2_minimal, NormalizedModule,
|
normalize_pattern1_minimal, normalize_pattern2_minimal, NormalizedModule,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir::normalized::shape_guard::{self, NormalizedDevShape};
|
use crate::mir::join_ir::JoinIrPhase;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
use crate::mir::join_ir_vm_bridge::lower_normalized_to_mir_minimal;
|
use crate::mir::join_ir_vm_bridge::lower_normalized_to_mir_minimal;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
@ -124,22 +124,22 @@ fn normalize_for_shape(
|
|||||||
.expect("P4 normalization failed")
|
.expect("P4 normalization failed")
|
||||||
})),
|
})),
|
||||||
// Phase 48-B: JsonParser continue skip_ws (array/object)
|
// Phase 48-B: JsonParser continue skip_ws (array/object)
|
||||||
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => catch_unwind(AssertUnwindSafe(
|
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|
||||||
|| {
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
crate::mir::join_ir::normalized::normalize_jsonparser_parse_array_continue_skip_ws(
|
crate::mir::join_ir::normalized::normalize_jsonparser_parse_array_continue_skip_ws(
|
||||||
module,
|
module,
|
||||||
)
|
)
|
||||||
.expect("P4 array normalization failed")
|
.expect("P4 array normalization failed")
|
||||||
},
|
}))
|
||||||
)),
|
}
|
||||||
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => catch_unwind(AssertUnwindSafe(
|
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|
||||||
|| {
|
catch_unwind(AssertUnwindSafe(|| {
|
||||||
crate::mir::join_ir::normalized::normalize_jsonparser_parse_object_continue_skip_ws(
|
crate::mir::join_ir::normalized::normalize_jsonparser_parse_object_continue_skip_ws(
|
||||||
module,
|
module,
|
||||||
)
|
)
|
||||||
.expect("P4 object normalization failed")
|
.expect("P4 object normalization failed")
|
||||||
},
|
}))
|
||||||
)),
|
}
|
||||||
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
|
// Phase 89: Continue + Early Return pattern (dev-only, delegates to P2 for now)
|
||||||
NormalizedDevShape::PatternContinueReturnMinimal => {
|
NormalizedDevShape::PatternContinueReturnMinimal => {
|
||||||
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
|
||||||
|
|||||||
@ -72,7 +72,10 @@ impl JoinIrBlockConverter {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Debug: show what instruction we're processing
|
// Debug: show what instruction we're processing
|
||||||
log_dbg(format!("[joinir_block] Compute instruction: {:?}", mir_like));
|
log_dbg(format!(
|
||||||
|
"[joinir_block] Compute instruction: {:?}",
|
||||||
|
mir_like
|
||||||
|
));
|
||||||
let mir_inst = convert_mir_like_inst(mir_like)?;
|
let mir_inst = convert_mir_like_inst(mir_like)?;
|
||||||
self.current_instructions.push(mir_inst);
|
self.current_instructions.push(mir_inst);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,9 +55,9 @@ pub(crate) use bridge::{bridge_joinir_to_mir, bridge_joinir_to_mir_with_meta};
|
|||||||
pub(crate) use convert::convert_joinir_to_mir;
|
pub(crate) use convert::convert_joinir_to_mir;
|
||||||
pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules
|
pub(crate) use convert::convert_mir_like_inst; // helper for sub-modules
|
||||||
pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
|
pub(crate) use joinir_function_converter::JoinIrFunctionConverter;
|
||||||
|
pub use meta::convert_join_module_to_mir_with_meta;
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
pub(crate) use normalized_bridge::lower_normalized_to_mir_minimal;
|
pub(crate) use normalized_bridge::lower_normalized_to_mir_minimal;
|
||||||
pub use meta::convert_join_module_to_mir_with_meta;
|
|
||||||
pub use runner::run_joinir_via_vm;
|
pub use runner::run_joinir_via_vm;
|
||||||
|
|
||||||
/// Phase 27-shortterm S-4 エラー型
|
/// Phase 27-shortterm S-4 エラー型
|
||||||
|
|||||||
@ -19,7 +19,11 @@ fn dev_debug_enabled() -> bool {
|
|||||||
pub(super) fn log_dev(category: &str, message: impl AsRef<str>, important: bool) {
|
pub(super) fn log_dev(category: &str, message: impl AsRef<str>, important: bool) {
|
||||||
let debug = dev_debug_enabled();
|
let debug = dev_debug_enabled();
|
||||||
if debug || important {
|
if debug || important {
|
||||||
eprintln!("[joinir/normalized-dev/bridge/{}] {}", category, message.as_ref());
|
eprintln!(
|
||||||
|
"[joinir/normalized-dev/bridge/{}] {}",
|
||||||
|
category,
|
||||||
|
message.as_ref()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
#![cfg(feature = "normalized_dev")]
|
#![cfg(feature = "normalized_dev")]
|
||||||
|
|
||||||
|
use super::super::convert_mir_like_inst;
|
||||||
use super::super::join_func_name;
|
use super::super::join_func_name;
|
||||||
use super::super::JoinIrVmBridgeError;
|
use super::super::JoinIrVmBridgeError;
|
||||||
use super::super::convert_mir_like_inst;
|
|
||||||
use crate::ast::Span;
|
use crate::ast::Span;
|
||||||
use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule};
|
use crate::mir::join_ir::normalized::{JpFuncId, JpFunction, JpInst, JpOp, NormalizedModule};
|
||||||
use crate::mir::join_ir::{JoinFuncId, JoinIrPhase, MirLikeInst};
|
use crate::mir::join_ir::{JoinFuncId, JoinIrPhase, MirLikeInst};
|
||||||
@ -83,7 +83,9 @@ fn lower_normalized_function_direct(
|
|||||||
|
|
||||||
let remap = |id: ValueId, map: &mut BTreeMap<ValueId, ValueId>| remap_value(id, map);
|
let remap = |id: ValueId, map: &mut BTreeMap<ValueId, ValueId>| remap_value(id, map);
|
||||||
let remap_vec = |ids: &[ValueId], map: &mut BTreeMap<ValueId, ValueId>| {
|
let remap_vec = |ids: &[ValueId], map: &mut BTreeMap<ValueId, ValueId>| {
|
||||||
ids.iter().map(|id| remap_value(*id, map)).collect::<Vec<_>>()
|
ids.iter()
|
||||||
|
.map(|id| remap_value(*id, map))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
let remapped_params = remap_vec(¶ms, &mut value_map);
|
let remapped_params = remap_vec(¶ms, &mut value_map);
|
||||||
@ -188,7 +190,8 @@ fn lower_normalized_function_direct(
|
|||||||
}
|
}
|
||||||
JpInst::TailCallFn { target, env } => {
|
JpInst::TailCallFn { target, env } => {
|
||||||
let env_remapped = remap_vec(env, &mut value_map);
|
let env_remapped = remap_vec(env, &mut value_map);
|
||||||
let (instructions, terminator) = build_tail_call(&mut mir_func, target, &env_remapped);
|
let (instructions, terminator) =
|
||||||
|
build_tail_call(&mut mir_func, target, &env_remapped);
|
||||||
finalize_block(
|
finalize_block(
|
||||||
&mut mir_func,
|
&mut mir_func,
|
||||||
current_block_id,
|
current_block_id,
|
||||||
@ -371,7 +374,12 @@ fn build_tail_call(
|
|||||||
effects: EffectMask::PURE,
|
effects: EffectMask::PURE,
|
||||||
});
|
});
|
||||||
|
|
||||||
(instructions, MirInstruction::Return { value: Some(result_id) })
|
(
|
||||||
|
instructions,
|
||||||
|
MirInstruction::Return {
|
||||||
|
value: Some(result_id),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_exit_or_tail_branch(
|
fn build_exit_or_tail_branch(
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||||
|
|
||||||
use super::capability_guard::{capability_tags, RoutingDecision};
|
use super::capability_guard::{CapabilityTag, RoutingDecision};
|
||||||
use super::pattern_recognizer::try_extract_skip_whitespace_pattern;
|
use super::pattern_recognizer::try_extract_skip_whitespace_pattern;
|
||||||
use super::skeleton_types::{
|
use super::skeleton_types::{
|
||||||
CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
|
CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
|
||||||
@ -63,9 +63,9 @@ pub fn canonicalize_loop_expr(
|
|||||||
|
|
||||||
// Step 2: Body statements (if any)
|
// Step 2: Body statements (if any)
|
||||||
if !body_stmts.is_empty() {
|
if !body_stmts.is_empty() {
|
||||||
skeleton.steps.push(SkeletonStep::Body {
|
skeleton
|
||||||
stmts: body_stmts,
|
.steps
|
||||||
});
|
.push(SkeletonStep::Body { stmts: body_stmts });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Update step
|
// Step 3: Update step
|
||||||
@ -100,7 +100,7 @@ pub fn canonicalize_loop_expr(
|
|||||||
Ok((
|
Ok((
|
||||||
LoopSkeleton::new(span),
|
LoopSkeleton::new(span),
|
||||||
RoutingDecision::fail_fast(
|
RoutingDecision::fail_fast(
|
||||||
vec![capability_tags::CAP_MISSING_CONST_STEP],
|
vec![CapabilityTag::ConstStep],
|
||||||
"Phase 3: Loop does not match skip_whitespace pattern".to_string(),
|
"Phase 3: Loop does not match skip_whitespace pattern".to_string(),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
@ -192,14 +192,8 @@ mod tests {
|
|||||||
|
|
||||||
// Verify skeleton structure
|
// Verify skeleton structure
|
||||||
assert_eq!(skeleton.steps.len(), 2); // HeaderCond + Update
|
assert_eq!(skeleton.steps.len(), 2); // HeaderCond + Update
|
||||||
assert!(matches!(
|
assert!(matches!(skeleton.steps[0], SkeletonStep::HeaderCond { .. }));
|
||||||
skeleton.steps[0],
|
assert!(matches!(skeleton.steps[1], SkeletonStep::Update { .. }));
|
||||||
SkeletonStep::HeaderCond { .. }
|
|
||||||
));
|
|
||||||
assert!(matches!(
|
|
||||||
skeleton.steps[1],
|
|
||||||
SkeletonStep::Update { .. }
|
|
||||||
));
|
|
||||||
|
|
||||||
// Verify carrier
|
// Verify carrier
|
||||||
assert_eq!(skeleton.carriers.len(), 1);
|
assert_eq!(skeleton.carriers.len(), 1);
|
||||||
@ -300,15 +294,9 @@ mod tests {
|
|||||||
|
|
||||||
// Verify skeleton has Body step
|
// Verify skeleton has Body step
|
||||||
assert_eq!(skeleton.steps.len(), 3); // HeaderCond + Body + Update
|
assert_eq!(skeleton.steps.len(), 3); // HeaderCond + Body + Update
|
||||||
assert!(matches!(
|
assert!(matches!(skeleton.steps[0], SkeletonStep::HeaderCond { .. }));
|
||||||
skeleton.steps[0],
|
|
||||||
SkeletonStep::HeaderCond { .. }
|
|
||||||
));
|
|
||||||
assert!(matches!(skeleton.steps[1], SkeletonStep::Body { .. }));
|
assert!(matches!(skeleton.steps[1], SkeletonStep::Body { .. }));
|
||||||
assert!(matches!(
|
assert!(matches!(skeleton.steps[2], SkeletonStep::Update { .. }));
|
||||||
skeleton.steps[2],
|
|
||||||
SkeletonStep::Update { .. }
|
|
||||||
));
|
|
||||||
|
|
||||||
// Verify body contains 1 statement
|
// Verify body contains 1 statement
|
||||||
match &skeleton.steps[1] {
|
match &skeleton.steps[1] {
|
||||||
@ -360,9 +348,7 @@ mod tests {
|
|||||||
|
|
||||||
let (_, decision) = result.unwrap();
|
let (_, decision) = result.unwrap();
|
||||||
assert!(decision.is_fail_fast());
|
assert!(decision.is_fail_fast());
|
||||||
assert!(decision
|
assert!(decision.notes[0].contains("does not match skip_whitespace pattern"));
|
||||||
.notes[0]
|
|
||||||
.contains("does not match skip_whitespace pattern"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -18,8 +18,8 @@ pub struct RoutingDecision {
|
|||||||
/// Selected pattern (None = Fail-Fast)
|
/// Selected pattern (None = Fail-Fast)
|
||||||
pub chosen: Option<LoopPatternKind>,
|
pub chosen: Option<LoopPatternKind>,
|
||||||
|
|
||||||
/// Missing capabilities that prevented other patterns
|
/// Missing capabilities that prevented other patterns (type-safe!)
|
||||||
pub missing_caps: Vec<&'static str>,
|
pub missing_caps: Vec<CapabilityTag>,
|
||||||
|
|
||||||
/// Selection reasoning (for debugging)
|
/// Selection reasoning (for debugging)
|
||||||
pub notes: Vec<String>,
|
pub notes: Vec<String>,
|
||||||
@ -40,12 +40,17 @@ impl RoutingDecision {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a failed routing decision (Fail-Fast)
|
/// Create a failed routing decision (Fail-Fast)
|
||||||
pub fn fail_fast(missing_caps: Vec<&'static str>, reason: String) -> Self {
|
pub fn fail_fast(missing_caps: Vec<CapabilityTag>, reason: String) -> Self {
|
||||||
|
let error_tags = missing_caps
|
||||||
|
.iter()
|
||||||
|
.map(|cap| cap.to_tag().to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
chosen: None,
|
chosen: None,
|
||||||
missing_caps,
|
missing_caps,
|
||||||
notes: vec![reason.clone()],
|
notes: vec![reason.clone()],
|
||||||
error_tags: vec![format!("[loop_canonicalizer/fail_fast] {}", reason)],
|
error_tags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,38 +130,3 @@ impl CapabilityTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Legacy Capability Tags (Deprecated - will be removed in Phase 139-P3-B)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/// Legacy capability tags - Standardized vocabulary for Fail-Fast reasons
|
|
||||||
///
|
|
||||||
/// ⚠️ DEPRECATED: Use CapabilityTag enum instead for type safety.
|
|
||||||
/// This module will be removed in Phase 139-P3-B.
|
|
||||||
#[deprecated(since = "Phase 139-P3-A", note = "Use CapabilityTag enum instead")]
|
|
||||||
pub mod capability_tags {
|
|
||||||
/// Requires: Carrier update is constant step (`i = i + const`)
|
|
||||||
pub const CAP_MISSING_CONST_STEP: &str = "CAP_MISSING_CONST_STEP";
|
|
||||||
|
|
||||||
/// Requires: Single break point only
|
|
||||||
pub const CAP_MISSING_SINGLE_BREAK: &str = "CAP_MISSING_SINGLE_BREAK";
|
|
||||||
|
|
||||||
/// Requires: Single continue point only
|
|
||||||
pub const CAP_MISSING_SINGLE_CONTINUE: &str = "CAP_MISSING_SINGLE_CONTINUE";
|
|
||||||
|
|
||||||
/// Requires: Loop header condition has no side effects
|
|
||||||
pub const CAP_MISSING_PURE_HEADER: &str = "CAP_MISSING_PURE_HEADER";
|
|
||||||
|
|
||||||
/// Requires: Condition variable defined in outer local scope
|
|
||||||
pub const CAP_MISSING_OUTER_LOCAL_COND: &str = "CAP_MISSING_OUTER_LOCAL_COND";
|
|
||||||
|
|
||||||
/// Requires: All exit bindings are complete (no missing values)
|
|
||||||
pub const CAP_MISSING_EXIT_BINDINGS: &str = "CAP_MISSING_EXIT_BINDINGS";
|
|
||||||
|
|
||||||
/// Requires: LoopBodyLocal can be promoted to carrier
|
|
||||||
pub const CAP_MISSING_CARRIER_PROMOTION: &str = "CAP_MISSING_CARRIER_PROMOTION";
|
|
||||||
|
|
||||||
/// Requires: Break value types are consistent across all break points
|
|
||||||
pub const CAP_MISSING_BREAK_VALUE_TYPE: &str = "CAP_MISSING_BREAK_VALUE_TYPE";
|
|
||||||
}
|
|
||||||
|
|||||||
@ -34,10 +34,10 @@
|
|||||||
// Module Declarations
|
// Module Declarations
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
mod skeleton_types;
|
mod canonicalizer;
|
||||||
mod capability_guard;
|
mod capability_guard;
|
||||||
mod pattern_recognizer;
|
mod pattern_recognizer;
|
||||||
mod canonicalizer;
|
mod skeleton_types;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Public Re-exports
|
// Public Re-exports
|
||||||
@ -45,20 +45,11 @@ mod canonicalizer;
|
|||||||
|
|
||||||
// Skeleton Types
|
// Skeleton Types
|
||||||
pub use skeleton_types::{
|
pub use skeleton_types::{
|
||||||
CarrierRole,
|
CapturedSlot, CarrierRole, CarrierSlot, ExitContract, LoopSkeleton, SkeletonStep, UpdateKind,
|
||||||
CarrierSlot,
|
|
||||||
CapturedSlot,
|
|
||||||
ExitContract,
|
|
||||||
LoopSkeleton,
|
|
||||||
SkeletonStep,
|
|
||||||
UpdateKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capability Guard
|
// Capability Guard
|
||||||
pub use capability_guard::{
|
pub use capability_guard::{CapabilityTag, RoutingDecision};
|
||||||
capability_tags,
|
|
||||||
RoutingDecision,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Canonicalization Entry Point
|
// Canonicalization Entry Point
|
||||||
pub use canonicalizer::canonicalize_loop_expr;
|
pub use canonicalizer::canonicalize_loop_expr;
|
||||||
@ -97,13 +88,12 @@ mod tests {
|
|||||||
assert!(success.is_success());
|
assert!(success.is_success());
|
||||||
assert!(!success.is_fail_fast());
|
assert!(!success.is_fail_fast());
|
||||||
|
|
||||||
let fail = RoutingDecision::fail_fast(
|
let fail =
|
||||||
vec![capability_tags::CAP_MISSING_CONST_STEP],
|
RoutingDecision::fail_fast(vec![CapabilityTag::ConstStep], "Test failure".to_string());
|
||||||
"Test failure".to_string(),
|
|
||||||
);
|
|
||||||
assert!(!fail.is_success());
|
assert!(!fail.is_success());
|
||||||
assert!(fail.is_fail_fast());
|
assert!(fail.is_fail_fast());
|
||||||
assert_eq!(fail.missing_caps.len(), 1);
|
assert_eq!(fail.missing_caps.len(), 1);
|
||||||
|
assert_eq!(fail.missing_caps[0], CapabilityTag::ConstStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -39,20 +39,13 @@ pub struct LoopSkeleton {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SkeletonStep {
|
pub enum SkeletonStep {
|
||||||
/// Loop continuation condition (the `cond` in `loop(cond)`)
|
/// Loop continuation condition (the `cond` in `loop(cond)`)
|
||||||
HeaderCond {
|
HeaderCond { expr: Box<ASTNode> },
|
||||||
expr: Box<ASTNode>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Early exit check (`if cond { break }`)
|
/// Early exit check (`if cond { break }`)
|
||||||
BreakCheck {
|
BreakCheck { cond: Box<ASTNode>, has_value: bool },
|
||||||
cond: Box<ASTNode>,
|
|
||||||
has_value: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Skip check (`if cond { continue }`)
|
/// Skip check (`if cond { continue }`)
|
||||||
ContinueCheck {
|
ContinueCheck { cond: Box<ASTNode> },
|
||||||
cond: Box<ASTNode>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Carrier update (`i = i + 1`, etc.)
|
/// Carrier update (`i = i + 1`, etc.)
|
||||||
Update {
|
Update {
|
||||||
@ -61,9 +54,7 @@ pub enum SkeletonStep {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// Loop body (all other statements)
|
/// Loop body (all other statements)
|
||||||
Body {
|
Body { stmts: Vec<ASTNode> },
|
||||||
stmts: Vec<ASTNode>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update kind - How a carrier variable is updated
|
/// Update kind - How a carrier variable is updated
|
||||||
@ -72,9 +63,7 @@ pub enum SkeletonStep {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum UpdateKind {
|
pub enum UpdateKind {
|
||||||
/// Constant step (`i = i + const`)
|
/// Constant step (`i = i + const`)
|
||||||
ConstStep {
|
ConstStep { delta: i64 },
|
||||||
delta: i64,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Conditional update (`if cond { x = a } else { x = b }`)
|
/// Conditional update (`if cond { x = a } else { x = b }`)
|
||||||
Conditional {
|
Conditional {
|
||||||
|
|||||||
@ -441,11 +441,7 @@ mod tests {
|
|||||||
|
|
||||||
let result = DigitPosDetector::detect(&condition, &loop_body, "p");
|
let result = DigitPosDetector::detect(&condition, &loop_body, "p");
|
||||||
|
|
||||||
assert!(
|
assert!(result.is_some(), "Expected detection for operator {:?}", op);
|
||||||
result.is_some(),
|
|
||||||
"Expected detection for operator {:?}",
|
|
||||||
op
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,9 +56,7 @@ pub(super) fn body_matches(a: &[ASTNode], b: &[ASTNode]) -> bool {
|
|||||||
/// Collect local variable declarations from statements
|
/// Collect local variable declarations from statements
|
||||||
///
|
///
|
||||||
/// Returns Vec<(name, init_expr)> for each variable declared with `local`.
|
/// Returns Vec<(name, init_expr)> for each variable declared with `local`.
|
||||||
pub(super) fn collect_local_declarations(
|
pub(super) fn collect_local_declarations(stmts: &[ASTNode]) -> Vec<(String, Option<Box<ASTNode>>)> {
|
||||||
stmts: &[ASTNode],
|
|
||||||
) -> Vec<(String, Option<Box<ASTNode>>)> {
|
|
||||||
let mut locals = Vec::new();
|
let mut locals = Vec::new();
|
||||||
|
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
|
|||||||
@ -80,8 +80,9 @@ impl TrimPatternInfo {
|
|||||||
/// - JoinInlineBoundary will handle the boundary mapping
|
/// - JoinInlineBoundary will handle the boundary mapping
|
||||||
pub fn to_carrier_info(
|
pub fn to_carrier_info(
|
||||||
&self,
|
&self,
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")] binding_map: Option<
|
||||||
binding_map: Option<&std::collections::BTreeMap<String, crate::mir::BindingId>>,
|
&std::collections::BTreeMap<String, crate::mir::BindingId>,
|
||||||
|
>,
|
||||||
) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
|
) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
|
||||||
use super::trim_loop_helper::TrimLoopHelper;
|
use super::trim_loop_helper::TrimLoopHelper;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||||
@ -110,11 +111,9 @@ impl TrimPatternInfo {
|
|||||||
#[cfg(not(feature = "normalized_dev"))]
|
#[cfg(not(feature = "normalized_dev"))]
|
||||||
let recorder = PromotedBindingRecorder::new();
|
let recorder = PromotedBindingRecorder::new();
|
||||||
|
|
||||||
if let Err(e) = recorder.record_promotion(
|
if let Err(e) =
|
||||||
&mut carrier_info,
|
recorder.record_promotion(&mut carrier_info, &self.var_name, &self.carrier_name)
|
||||||
&self.var_name,
|
{
|
||||||
&self.carrier_name,
|
|
||||||
) {
|
|
||||||
log_trim_promotion_error(&e);
|
log_trim_promotion_error(&e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,8 +194,7 @@ impl LoopBodyCarrierPromoter {
|
|||||||
// 3. 各 LoopBodyLocal に対して TrimDetector で検出を試行
|
// 3. 各 LoopBodyLocal に対して TrimDetector で検出を試行
|
||||||
for var_name in &body_locals {
|
for var_name in &body_locals {
|
||||||
// Phase 79: Use TrimDetector for pure detection logic
|
// Phase 79: Use TrimDetector for pure detection logic
|
||||||
if let Some(detection) = TrimDetector::detect(break_cond, request.loop_body, var_name)
|
if let Some(detection) = TrimDetector::detect(break_cond, request.loop_body, var_name) {
|
||||||
{
|
|
||||||
if is_joinir_debug() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
|
if is_joinir_debug() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",
|
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",
|
||||||
|
|||||||
@ -148,11 +148,8 @@ impl DigitPosPromoter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Use DigitPosDetector for pure detection
|
// Step 3: Use DigitPosDetector for pure detection
|
||||||
let detection = DigitPosDetector::detect(
|
let detection =
|
||||||
condition.unwrap(),
|
DigitPosDetector::detect(condition.unwrap(), req.loop_body, req.loop_param_name);
|
||||||
req.loop_body,
|
|
||||||
req.loop_param_name,
|
|
||||||
);
|
|
||||||
|
|
||||||
if detection.is_none() {
|
if detection.is_none() {
|
||||||
return DigitPosPromotionResult::CannotPromote {
|
return DigitPosPromotionResult::CannotPromote {
|
||||||
@ -171,16 +168,14 @@ impl DigitPosPromoter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Build CarrierInfo
|
// Step 4: Build CarrierInfo
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
|
||||||
CarrierInit, CarrierRole, CarrierVar,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Boolean carrier (condition-only, for break)
|
// Boolean carrier (condition-only, for break)
|
||||||
let promoted_carrier_bool = CarrierVar {
|
let promoted_carrier_bool = CarrierVar {
|
||||||
name: detection.bool_carrier_name.clone(),
|
name: detection.bool_carrier_name.clone(),
|
||||||
host_id: ValueId(0), // Placeholder (will be remapped)
|
host_id: ValueId(0), // Placeholder (will be remapped)
|
||||||
join_id: None, // Will be allocated later
|
join_id: None, // Will be allocated later
|
||||||
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
|
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
|
||||||
init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false
|
init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false
|
||||||
#[cfg(feature = "normalized_dev")]
|
#[cfg(feature = "normalized_dev")]
|
||||||
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
|
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
|
||||||
|
|||||||
@ -116,7 +116,7 @@ fn classify_body(body: &[ASTNode]) -> LoopPatternKind {
|
|||||||
carrier_count: carrier_count(body),
|
carrier_count: carrier_count(body),
|
||||||
break_count: if has_break_flag { 1 } else { 0 },
|
break_count: if has_break_flag { 1 } else { 0 },
|
||||||
continue_count: if has_continue_flag { 1 } else { 0 },
|
continue_count: if has_continue_flag { 1 } else { 0 },
|
||||||
is_infinite_loop: false, // テストでは通常ループを想定
|
is_infinite_loop: false, // テストでは通常ループを想定
|
||||||
update_summary: None,
|
update_summary: None,
|
||||||
};
|
};
|
||||||
classify(&features)
|
classify(&features)
|
||||||
|
|||||||
@ -424,7 +424,11 @@ mod tests {
|
|||||||
|
|
||||||
let result = TrimDetector::detect(&condition, &loop_body, var_name);
|
let result = TrimDetector::detect(&condition, &loop_body, var_name);
|
||||||
|
|
||||||
assert!(result.is_some(), "Expected detection for var '{}'", var_name);
|
assert!(
|
||||||
|
result.is_some(),
|
||||||
|
"Expected detection for var '{}'",
|
||||||
|
var_name
|
||||||
|
);
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
assert_eq!(result.match_var, var_name);
|
assert_eq!(result.match_var, var_name);
|
||||||
assert_eq!(result.carrier_name, format!("is_{}_match", var_name));
|
assert_eq!(result.carrier_name, format!("is_{}_match", var_name));
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user