fix(joinir): Phase 269 P1.2 - static call normalization for this.method()
Problem: - `this.method()` calls in static box loops were using string constant "StringUtils" as receiver - This violated Box Theory (static box this should not be runtime object) - Pattern8 was trying to pass receiver to JoinIR, causing SSA/PHI issues Solution (3-part fix): 1. **MeResolverBox Fail-Fast** (stmts.rs): Remove string fallback, error message cleanup 2. **ReceiverNormalizeBox** (calls/build.rs): Normalize `this/me.method()` → static call at compile-time 3. **Pattern8 Skip Static Box** (pattern8_scan_bool_predicate.rs): Reject Pattern8 for static box contexts Key changes: - **stmts.rs**: Update error message - remove MeBindingInitializerBox mentions - **calls/build.rs**: Add This/Me receiver check, normalize to static call if current_static_box exists - **calls/lowering.rs**: Remove NewBox-based "me" initialization (2 locations - fixes Stage1Cli regression) - **pattern8_scan_bool_predicate.rs**: Skip Pattern8 for static boxes (use Pattern1 fallback instead) Result: - ✅ phase269_p1_2_this_method_in_loop_vm.sh PASS (exit=7) - ✅ No regression: phase259_p0_is_integer_vm PASS - ✅ No regression: phase269_p0_pattern8_frag_vm PASS - ✅ Stage1Cli unit tests PASS - ✅ MIR uses CallTarget::Global, no const "StringUtils" as receiver 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -136,8 +136,22 @@ impl MirBuilder {
|
||||
return res;
|
||||
}
|
||||
|
||||
// 3. Handle me.method() calls
|
||||
if let ASTNode::Me { .. } = object {
|
||||
// 3. Handle this/me.method() calls
|
||||
// Phase 269 P1.2: ReceiverNormalizeBox - MethodCall 共通入口 SSOT
|
||||
// Static box context check for This/Me receiver
|
||||
if matches!(object, ASTNode::This { .. } | ASTNode::Me { .. }) {
|
||||
// Priority 1: Static box → compile-time static call normalization
|
||||
if let Some(box_name) = self.comp_ctx.current_static_box.clone() {
|
||||
// Debug trace
|
||||
if std::env::var("NYASH_TRACE_NORMALIZE").is_ok() {
|
||||
eprintln!("[trace:normalize] this.{}() → {}.{}() (static call)", method, box_name, method);
|
||||
}
|
||||
// Static call normalization (no runtime receiver object needed)
|
||||
// this.method(args) → current_static_box.method/arity(args)
|
||||
return self.handle_static_method_call(&box_name, &method, &arguments);
|
||||
}
|
||||
|
||||
// Instance method fallback (requires variable_map["me"])
|
||||
if let Some(result) = self.handle_me_method_call(&method, &arguments)? {
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
@ -114,6 +114,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// 🎯 箱理論: Step 3 - パラメータ設定
|
||||
/// Phase 269 P1.2: MeBindingInitializerBox - Initializes "me" for static methods
|
||||
#[allow(deprecated)]
|
||||
fn setup_function_params(&mut self, params: &[String]) {
|
||||
// Phase 136 Step 3/7: Clear scope_ctx (SSOT)
|
||||
@ -216,6 +217,7 @@ impl MirBuilder {
|
||||
&format!("body.len() = {}", body.len()),
|
||||
trace.is_enabled(),
|
||||
);
|
||||
|
||||
let program_ast = function_lowering::wrap_in_program(body);
|
||||
trace.emit_if(
|
||||
"debug",
|
||||
|
||||
@ -56,7 +56,21 @@ struct BoolPredicateScanParts {
|
||||
|
||||
/// Phase 269 P1: Detection for Pattern 8 (BoolPredicateScan)
|
||||
/// Now uses EdgeCFG Frag lowering via emission entrypoint
|
||||
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
/// Phase 269 P1.2: Skip Pattern8 for static box this.method() - let ReceiverNormalizeBox handle
|
||||
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
// Phase 269 P1.2: Skip Pattern8 for static box contexts
|
||||
// this.method() in static boxes should be normalized to static calls by ReceiverNormalizeBox
|
||||
// Pattern8 requires a receiver object, which doesn't exist for static box methods
|
||||
if builder.comp_ctx.current_static_box.is_some() {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"pattern8/can_lower",
|
||||
"reject: static box context (ReceiverNormalizeBox handles this.method())",
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
match extract_bool_predicate_scan_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(_)) => {
|
||||
if ctx.debug {
|
||||
@ -159,10 +173,16 @@ fn extract_bool_predicate_scan_parts(
|
||||
{
|
||||
// Extract receiver (e.g., "me")
|
||||
// Phase 259 P0: Support both Variable and Me node
|
||||
// Phase 269 P1.2: Skip This in static box context (handled by ReceiverNormalizeBox)
|
||||
// IMPORTANT: Me is registered as "me" in variable_map (not "this")
|
||||
let receiver = match object.as_ref() {
|
||||
ASTNode::Variable { name, .. } => name.clone(),
|
||||
ASTNode::Me { .. } => "me".to_string(), // Me is registered as "me" in MirBuilder
|
||||
ASTNode::This { .. } => {
|
||||
// Phase 269 P1.2: Skip pattern for This in static box context
|
||||
// Let ReceiverNormalizeBox handle static call normalization instead
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
|
||||
@ -557,25 +557,53 @@ impl super::MirBuilder {
|
||||
Ok(result_id)
|
||||
}
|
||||
|
||||
// me: resolve to param if present; else symbolic const (stable mapping)
|
||||
/// MeResolverBox - SSOT for "me" resolution
|
||||
/// 箱理論: variable_map["me"] のみを参照、文字列フォールバック禁止
|
||||
/// Phase 269 P1.2: Removed string constant fallback (Fail-Fast principle)
|
||||
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
||||
if let Some(id) = self.variable_ctx.variable_map.get("me").cloned() {
|
||||
// Phase 269 P1.2: SSOT - variable_map["me"] only (no string fallback)
|
||||
const ME_VAR: &str = "me"; // Small constant SSOT
|
||||
|
||||
// Fast path: return if "me" is in variable_map
|
||||
if let Some(id) = self.variable_ctx.variable_map.get(ME_VAR).cloned() {
|
||||
return Ok(id);
|
||||
}
|
||||
let me_tag = if let Some(ref cls) = self.comp_ctx.current_static_box {
|
||||
cls.clone()
|
||||
} else {
|
||||
"__me__".to_string()
|
||||
};
|
||||
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
|
||||
self.variable_ctx
|
||||
.variable_map
|
||||
.insert("me".to_string(), me_value);
|
||||
if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
|
||||
reg.ensure_slot("me", None);
|
||||
}
|
||||
// P0: Known 化 — 分かる範囲で me の起源クラスを付与(挙動不変)。
|
||||
super::origin::infer::annotate_me_origin(self, me_value);
|
||||
Ok(me_value)
|
||||
|
||||
// ✅ Fail-Fast: "me" must be in variable_map (no string fallback)
|
||||
// This is a contract violation - caller must initialize "me" before use
|
||||
|
||||
let function_context = self.scope_ctx.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.clone())
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
let static_box_context = self.comp_ctx.current_static_box
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("none");
|
||||
|
||||
Err(format!(
|
||||
"[Phase269/P1.2/MeResolverBox] 'me'/'this' not found in variable_map\n\
|
||||
\n\
|
||||
Function: {}\n\
|
||||
Static box context: {}\n\
|
||||
\n\
|
||||
This is an **instance method** context error.\n\
|
||||
The legacy string constant fallback has been removed (Fail-Fast principle).\n\
|
||||
\n\
|
||||
Expected: variable_map contains 'me' → Box receiver ValueId (instance method)\n\
|
||||
Got: variable_map missing 'me' entry\n\
|
||||
\n\
|
||||
Possible causes:\n\
|
||||
1. Instance method called without proper 'me' initialization\n\
|
||||
2. Method called from incorrect context (instance method in static context)\n\
|
||||
\n\
|
||||
Note: For **static box this.method()** calls, use ReceiverNormalizeBox\n\
|
||||
(MethodCall common entry point handles static call normalization).\n\
|
||||
\n\
|
||||
Hint: Enable NYASH_TRACE_VARMAP=1 to trace variable_map changes.",
|
||||
function_context,
|
||||
static_box_context
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user