diff --git a/apps/tests/phase217_methodize_test.hako b/apps/tests/phase217_methodize_test.hako new file mode 100644 index 00000000..57d5bdda --- /dev/null +++ b/apps/tests/phase217_methodize_test.hako @@ -0,0 +1,15 @@ +// Phase 21.7: Methodization test +// Tests Global("BoxName.method/arity") → Method{receiver=singleton} conversion + +static box Calculator { + add(a, b) { + return a + b + } + + main() { + // This will be called as Global("Calculator.add/2") by default + // With HAKO_MIR_BUILDER_METHODIZE=1, it becomes Method{Calculator.add, recv=singleton} + print("Result: " + ("" + Calculator.add(10, 20))) + return 0 + } +} diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 7d9e2d98..babc113a 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -221,6 +221,12 @@ pub struct MirBuilder { /// - Some(true): App mode (static box Main.main is entry) /// - Some(false): Script/Test mode (top-level Program runs sequentially) pub(super) root_is_app_mode: Option, + + /// đŸŽ¯ Phase 21.7: Static box singleton instances for methodization + /// Maps BoxName → ValueId of singleton instance (created on demand) + /// Used when HAKO_MIR_BUILDER_METHODIZE=1 to convert Global("BoxName.method/arity") + /// to Method{receiver=singleton} calls + pub(super) static_box_singletons: HashMap, } impl MirBuilder { @@ -282,6 +288,7 @@ impl MirBuilder { in_unified_boxcall_fallback: false, recursion_depth: 0, root_is_app_mode: None, + static_box_singletons: HashMap::new(), // Phase 21.7: methodization support } } diff --git a/src/mir/builder/calls/unified_emitter.rs b/src/mir/builder/calls/unified_emitter.rs index 579c52d3..4dfb74d2 100644 --- a/src/mir/builder/calls/unified_emitter.rs +++ b/src/mir/builder/calls/unified_emitter.rs @@ -173,6 +173,57 @@ impl UnifiedCallEmitterBox { } }; + // đŸŽ¯ Phase 21.7: Methodization (HAKO_MIR_BUILDER_METHODIZE=1) + // Convert Global("BoxName.method/arity") → Method{receiver=static singleton} + if std::env::var("HAKO_MIR_BUILDER_METHODIZE").ok().as_deref() == Some("1") { + if let Callee::Global(ref name) = callee { + let name_clone = name.clone(); // Clone to avoid borrow checker issues + // Try to decode as static box method + if let Some((box_name, method, arity)) = crate::mir::naming::decode_static_method(&name_clone) { + // Check if arity matches provided args + if arity == args.len() { + // Get or create static box singleton instance + let singleton = if let Some(&existing) = builder.static_box_singletons.get(box_name) { + existing + } else { + // Create new singleton instance + let singleton_id = builder.next_value_id(); + builder.emit_instruction(MirInstruction::NewBox { + dst: singleton_id, + box_type: box_name.to_string(), + args: Vec::new(), // Static box singleton, no constructor args + })?; + // Register type information + builder.value_types.insert( + singleton_id, + crate::mir::MirType::Box(box_name.to_string()), + ); + builder.value_origin_newbox.insert(singleton_id, box_name.to_string()); + // Cache for future use + builder.static_box_singletons.insert(box_name.to_string(), singleton_id); + singleton_id + }; + + // Convert to Method call + callee = Callee::Method { + box_name: box_name.to_string(), + method: method.to_string(), + receiver: Some(singleton), + certainty: crate::mir::definitions::call_unified::TypeCertainty::Known, + box_kind: crate::mir::definitions::call_unified::CalleeBoxKind::StaticCompiler, + }; + + if std::env::var("NYASH_METHODIZE_TRACE").ok().as_deref() == Some("1") { + eprintln!( + "[methodize] Global({}) → Method{{{}.{}, recv=%{}}}", + name_clone, box_name, method, singleton.0 + ); + } + } + } + } + } + // Structural guard FIRST: prevent static compiler boxes from being called with runtime receivers // įŽąį†čĢ–: CalleeGuardBox ãĢã‚ˆã‚‹æ§‹é€ įš„åˆ†é›ĸ // (Guard may convert Method → Global, so we check BEFORE materializing receiver)