/*! * MIR Builder - Converts AST to MIR/SSA form * * Implements AST → MIR conversion with SSA construction */ use super::{ MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule, FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp, MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator }; use super::slot_registry::{get_or_assign_type_id, reserve_method_slot}; use super::slot_registry::resolve_slot_by_type_name; use crate::ast::{ASTNode, LiteralValue, BinaryOperator}; use std::collections::HashMap; use std::collections::HashSet; use std::fs; mod builder_calls; mod stmts; mod ops; mod utils; // moved helpers to builder/utils.rs /// MIR builder for converting AST to SSA form pub struct MirBuilder { /// Current module being built pub(super) current_module: Option, /// Current function being built pub(super) current_function: Option, /// Current basic block being built pub(super) current_block: Option, /// Value ID generator pub(super) value_gen: ValueIdGenerator, /// Basic block ID generator pub(super) block_gen: BasicBlockIdGenerator, /// Variable name to ValueId mapping (for SSA conversion) pub(super) variable_map: HashMap, /// Pending phi functions to be inserted #[allow(dead_code)] pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>, /// Origin tracking for simple optimizations (e.g., object.method after new) /// Maps a ValueId to the class name if it was produced by NewBox of that class pub(super) value_origin_newbox: HashMap, /// Names of user-defined boxes declared in the current module pub(super) user_defined_boxes: HashSet, /// Weak field registry: BoxName -> {weak field names} pub(super) weak_fields_by_box: HashMap>, /// Remember class of object fields after assignments: (base_id, field) -> class_name pub(super) field_origin_class: HashMap<(ValueId, String), String>, /// Optional per-value type annotations (MIR-level): ValueId -> MirType pub(super) value_types: HashMap, /// Current static box name when lowering a static box body (e.g., "Main") current_static_box: Option, /// Include guards: currently loading file canonical paths include_loading: HashSet, /// Include visited cache: canonical path -> box name include_box_map: HashMap, } impl MirBuilder { /// Emit a Box method call (PluginInvoke unified) fn emit_box_or_plugin_call( &mut self, dst: Option, box_val: ValueId, method: String, method_id: Option, args: Vec, effects: EffectMask, ) -> Result<(), String> { let _ = method_id; // slot is no longer used in unified plugin invoke path self.emit_instruction(MirInstruction::PluginInvoke { dst, box_val, method, args, effects }) } /// Create a new MIR builder pub fn new() -> Self { Self { current_module: None, current_function: None, current_block: None, value_gen: ValueIdGenerator::new(), block_gen: BasicBlockIdGenerator::new(), variable_map: HashMap::new(), pending_phis: Vec::new(), value_origin_newbox: HashMap::new(), user_defined_boxes: HashSet::new(), weak_fields_by_box: HashMap::new(), field_origin_class: HashMap::new(), value_types: HashMap::new(), current_static_box: None, include_loading: HashSet::new(), include_box_map: HashMap::new(), } } /// Emit a type check instruction (Unified: TypeOp(Check)) #[allow(dead_code)] pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result { let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Check, value, ty: super::MirType::Box(expected_type) })?; Ok(dst) } /// Emit a cast instruction (Unified: TypeOp(Cast)) #[allow(dead_code)] pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result { let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Cast, value, ty: target_type.clone() })?; Ok(dst) } /// Emit a weak reference creation (Unified: WeakRef(New)) #[allow(dead_code)] pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result { let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::New, value: box_val })?; Ok(dst) } /// Emit a weak reference load (Unified: WeakRef(Load)) #[allow(dead_code)] pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result { let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::Load, value: weak_ref })?; Ok(dst) } /// Emit a barrier read (Unified: Barrier(Read)) #[allow(dead_code)] pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> { self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Read, ptr }) } /// Emit a barrier write (Unified: Barrier(Write)) #[allow(dead_code)] pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> { self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr }) } // moved to builder_calls.rs: lower_method_as_function /// Build a complete MIR module from AST pub fn build_module(&mut self, ast: ASTNode) -> Result { // Create a new module let module = MirModule::new("main".to_string()); // Create a main function to contain the AST let main_signature = FunctionSignature { name: "main".to_string(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE, }; let entry_block = self.block_gen.next(); let mut main_function = MirFunction::new(main_signature, entry_block); main_function.metadata.is_entry_point = true; // Set up building context self.current_module = Some(module); self.current_function = Some(main_function); self.current_block = Some(entry_block); // Optional: Add safepoint at function entry (disabled by default) if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY").ok().as_deref() == Some("1") { self.emit_instruction(MirInstruction::Safepoint)?; } // Convert AST to MIR let result_value = self.build_expression(ast)?; // Add return instruction if needed if let Some(block_id) = self.current_block { if let Some(ref mut function) = self.current_function { if let Some(block) = function.get_block_mut(block_id) { if !block.is_terminated() { block.add_instruction(MirInstruction::Return { value: Some(result_value), }); } // Infer return type from TyEnv (value_types) if let Some(mt) = self.value_types.get(&result_value).cloned() { function.signature.return_type = mt; } } } } // Finalize and return module let mut module = self.current_module.take().unwrap(); let mut function = self.current_function.take().unwrap(); // Flush value_types (TyEnv) into function metadata function.metadata.value_types = self.value_types.clone(); // 補助: 本文中に明示的な return が存在する場合、 // 末尾returnを挿入しなかった関数でも戻り型を推定する。 if matches!(function.signature.return_type, super::MirType::Void | super::MirType::Unknown) { let mut inferred: Option = None; 'outer: for (_bid, bb) in function.blocks.iter() { for inst in bb.instructions.iter() { if let super::MirInstruction::Return { value: Some(v) } = inst { if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break 'outer; } // 追加: v が PHI の場合は入力側の型から推定 if let Some(mt) = utils::infer_type_from_phi(&function, *v, &self.value_types) { inferred = Some(mt); break 'outer; } } } if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator { if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break; } if let Some(mt) = utils::infer_type_from_phi(&function, *v, &self.value_types) { inferred = Some(mt); break; } } } if let Some(mt) = inferred { function.signature.return_type = mt; } } module.add_function(function); Ok(module) } /// Build an expression and return its value ID pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result { match ast { ASTNode::Literal { value, .. } => { self.build_literal(value) }, ASTNode::BinaryOp { left, operator, right, .. } => { self.build_binary_op(*left, operator, *right) }, ASTNode::UnaryOp { operator, operand, .. } => { let op_string = match operator { crate::ast::UnaryOperator::Minus => "-".to_string(), crate::ast::UnaryOperator::Not => "not".to_string(), }; self.build_unary_op(op_string, *operand) }, ASTNode::Variable { name, .. } => { self.build_variable_access(name.clone()) }, ASTNode::Me { .. } => { self.build_me_expression() }, ASTNode::MethodCall { object, method, arguments, .. } => { // Early TypeOp lowering for method-style is()/as() if (method == "is" || method == "as") && arguments.len() == 1 { if let Some(type_name) = Self::extract_string_literal(&arguments[0]) { let obj_val = self.build_expression(*object.clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?; return Ok(dst); } } self.build_method_call(*object.clone(), method.clone(), arguments.clone()) }, ASTNode::FromCall { parent, method, arguments, .. } => { self.build_from_expression(parent.clone(), method.clone(), arguments.clone()) }, ASTNode::Assignment { target, value, .. } => { // Check if target is a field access for RefSet if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() { self.build_field_assignment(*object.clone(), field.clone(), *value.clone()) } else if let ASTNode::Variable { name, .. } = target.as_ref() { // Plain variable assignment - existing behavior self.build_assignment(name.clone(), *value.clone()) } else { Err("Complex assignment targets not yet supported in MIR".to_string()) } }, ASTNode::FunctionCall { name, arguments, .. } => { // Early TypeOp lowering for function-style isType()/asType() if (name == "isType" || name == "asType") && arguments.len() == 2 { if let Some(type_name) = Self::extract_string_literal(&arguments[1]) { let val = self.build_expression(arguments[0].clone())?; let ty = Self::parse_type_name_to_mir(&type_name); let dst = self.value_gen.next(); let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast }; self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?; return Ok(dst); } } self.build_function_call(name.clone(), arguments.clone()) }, ASTNode::Print { expression, .. } => { self.build_print_statement(*expression.clone()) }, ASTNode::Program { statements, .. } => { self.build_block(statements.clone()) }, ASTNode::If { condition, then_body, else_body, .. } => { let else_ast = if let Some(else_statements) = else_body { Some(ASTNode::Program { statements: else_statements.clone(), span: crate::ast::Span::unknown(), }) } else { None }; self.build_if_statement( *condition.clone(), ASTNode::Program { statements: then_body.clone(), span: crate::ast::Span::unknown(), }, else_ast ) }, ASTNode::Loop { condition, body, .. } => { self.build_loop_statement(*condition.clone(), body.clone()) }, ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => { self.build_try_catch_statement(try_body.clone(), catch_clauses.clone(), finally_body.clone()) }, ASTNode::Throw { expression, .. } => { self.build_throw_statement(*expression.clone()) }, ASTNode::Return { value, .. } => { self.build_return_statement(value.clone()) }, ASTNode::Local { variables, initial_values, .. } => { self.build_local_statement(variables.clone(), initial_values.clone()) }, ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => { if is_static && name == "Main" { self.build_static_main_box(name.clone(), methods.clone()) } else { // Support user-defined boxes - handle as statement, return void // Track as user-defined (eligible for method lowering) self.user_defined_boxes.insert(name.clone()); self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?; // Phase 2: Lower constructors (birth/N) into MIR functions // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1") for (ctor_key, ctor_ast) in constructors.clone() { if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast { let func_name = format!("{}.{}", name, ctor_key); self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; } } // Phase 3: Lower instance methods into MIR functions // Function name pattern: "{BoxName}.{method}/{N}" for (method_name, method_ast) in methods.clone() { if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast { if !is_static { let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len())); self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?; } } } // Return a void value since this is a statement let void_val = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: void_val, value: ConstValue::Void, })?; Ok(void_val) } }, ASTNode::FieldAccess { object, field, .. } => { self.build_field_access(*object.clone(), field.clone()) }, ASTNode::New { class, arguments, .. } => { self.build_new_expression(class.clone(), arguments.clone()) }, // Phase 7: Async operations ASTNode::Nowait { variable, expression, .. } => { self.build_nowait_statement(variable.clone(), *expression.clone()) }, ASTNode::AwaitExpression { expression, .. } => { self.build_await_expression(*expression.clone()) }, ASTNode::Include { filename, .. } => { // Resolve and read included file let mut path = utils::resolve_include_path_builder(&filename); if std::path::Path::new(&path).is_dir() { path = format!("{}/index.nyash", path.trim_end_matches('/')); } else if std::path::Path::new(&path).extension().is_none() { path.push_str(".nyash"); } // Cycle detection if self.include_loading.contains(&path) { return Err(format!("Circular include detected: {}", path)); } // Cache hit: build only the instance if let Some(name) = self.include_box_map.get(&path).cloned() { return self.build_new_expression(name, vec![]); } self.include_loading.insert(path.clone()); let content = fs::read_to_string(&path) .map_err(|e| format!("Include read error '{}': {}", filename, e))?; // Parse to AST let included_ast = crate::parser::NyashParser::parse_from_string(&content) .map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?; // Find first static box name let mut box_name: Option = None; if let crate::ast::ASTNode::Program { statements, .. } = &included_ast { for st in statements { if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st { if *is_static { box_name = Some(name.clone()); break; } } } } let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?; // Lower included AST into current MIR (register types/methods) let _ = self.build_expression(included_ast)?; // Mark caches self.include_loading.remove(&path); self.include_box_map.insert(path.clone(), bname.clone()); // Return a new instance of included box (no args) self.build_new_expression(bname, vec![]) }, _ => { Err(format!("Unsupported AST node type: {:?}", ast)) } } } /// Build a literal value fn build_literal(&mut self, literal: LiteralValue) -> Result { // Determine type without moving literal let ty_for_dst = match &literal { LiteralValue::Integer(_) => Some(super::MirType::Integer), LiteralValue::Float(_) => Some(super::MirType::Float), LiteralValue::Bool(_) => Some(super::MirType::Bool), LiteralValue::String(_) => Some(super::MirType::String), _ => None, }; let const_value = match literal { LiteralValue::Integer(n) => ConstValue::Integer(n), LiteralValue::Float(f) => ConstValue::Float(f), LiteralValue::String(s) => ConstValue::String(s), LiteralValue::Bool(b) => ConstValue::Bool(b), LiteralValue::Null => ConstValue::Null, LiteralValue::Void => ConstValue::Void, }; let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst, value: const_value, })?; // Annotate type if let Some(ty) = ty_for_dst { self.value_types.insert(dst, ty); } Ok(dst) } // build_binary_op moved to builder/ops.rs // build_unary_op moved to builder/ops.rs /// Build variable access fn build_variable_access(&mut self, name: String) -> Result { if let Some(&value_id) = self.variable_map.get(&name) { Ok(value_id) } else { Err(format!("Undefined variable: {}", name)) } } /// Build assignment fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result { let value_id = self.build_expression(value)?; // In SSA form, each assignment creates a new value self.variable_map.insert(var_name.clone(), value_id); Ok(value_id) } // build_function_call_legacy removed (use builder_calls::build_function_call) // build_print_statement_legacy moved to builder/stmts.rs // build_block_legacy moved to builder/stmts.rs // build_if_statement_legacy moved to builder/stmts.rs // extract_assigned_var moved to builder/stmts.rs (as module helper) /// Emit an instruction to the current basic block pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> { let block_id = self.current_block.ok_or("No current basic block")?; if let Some(ref mut function) = self.current_function { if let Some(block) = function.get_block_mut(block_id) { if utils::builder_debug_enabled() { eprintln!("[BUILDER] emit @bb{} -> {}", block_id, match &instruction { MirInstruction::TypeOp { dst, op, value, ty } => format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst), MirInstruction::Print { value, .. } => format!("print {}", value), MirInstruction::BoxCall { box_val, method, method_id, args, dst, .. } => { if let Some(mid) = method_id { format!("boxcall {}.{}[#{}]({:?}) -> {:?}", box_val, method, mid, args, dst) } else { format!("boxcall {}.{}({:?}) -> {:?}", box_val, method, args, dst) } }, MirInstruction::Call { func, args, dst, .. } => format!("call {}({:?}) -> {:?}", func, args, dst), MirInstruction::NewBox { dst, box_type, args } => format!("new {}({:?}) -> {}", box_type, args, dst), MirInstruction::Const { dst, value } => format!("const {:?} -> {}", value, dst), MirInstruction::Branch { condition, then_bb, else_bb } => format!("br {}, {}, {}", condition, then_bb, else_bb), MirInstruction::Jump { target } => format!("br {}", target), _ => format!("{:?}", instruction), }); } block.add_instruction(instruction); Ok(()) } else { Err(format!("Basic block {} does not exist", block_id)) } } else { Err("No current function".to_string()) } } /// Ensure a basic block exists in the current function pub(super) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> { if let Some(ref mut function) = self.current_function { if !function.blocks.contains_key(&block_id) { let block = BasicBlock::new(block_id); function.add_block(block); } Ok(()) } else { Err("No current function".to_string()) } } // build_loop_statement_legacy moved to builder/stmts.rs // build_try_catch_statement_legacy moved to builder/stmts.rs // build_throw_statement_legacy moved to builder/stmts.rs // build_local_statement_legacy moved to builder/stmts.rs // build_return_statement_legacy moved to builder/stmts.rs /// Build static box (e.g., Main) - extracts main() method body and converts to Program /// Also lowers other static methods into standalone MIR functions: BoxName.method/N fn build_static_main_box(&mut self, box_name: String, methods: std::collections::HashMap) -> Result { // Lower other static methods (except main) to standalone MIR functions so JIT can see them for (mname, mast) in methods.iter() { if mname == "main" { continue; } if let ASTNode::FunctionDeclaration { params, body, .. } = mast { let func_name = format!("{}.{}{}", box_name, mname, format!("/{}", params.len())); self.lower_static_method_as_function(func_name, params.clone(), body.clone())?; } } // Within this lowering, treat `me` receiver as this static box let saved_static = self.current_static_box.clone(); self.current_static_box = Some(box_name.clone()); // Look for the main() method let out = if let Some(main_method) = methods.get("main") { if let ASTNode::FunctionDeclaration { params, body, .. } = main_method { // Convert the method body to a Program AST node and lower it let program_ast = ASTNode::Program { statements: body.clone(), span: crate::ast::Span::unknown(), }; // Bind default parameters if present (e.g., args=[]) // Save current var map; inject defaults; restore after lowering let saved_var_map = std::mem::take(&mut self.variable_map); // Prepare defaults for known patterns for p in params.iter() { let pid = self.value_gen.next(); // Heuristic: for parameter named "args", create new ArrayBox(); else use Void if p == "args" { // new ArrayBox() -> pid // Emit NewBox for ArrayBox with no args self.emit_instruction(MirInstruction::NewBox { dst: pid, box_type: "ArrayBox".to_string(), args: vec![] })?; } else { self.emit_instruction(MirInstruction::Const { dst: pid, value: ConstValue::Void })?; } self.variable_map.insert(p.clone(), pid); } // Use existing Program lowering logic let lowered = self.build_expression(program_ast); // Restore variable map self.variable_map = saved_var_map; lowered } else { Err("main method in static box is not a FunctionDeclaration".to_string()) } } else { Err("static box must contain a main() method".to_string()) }; // Restore static box context self.current_static_box = saved_static; out } /// Build field access: object.field fn build_field_access(&mut self, object: ASTNode, field: String) -> Result { // Clone the object before building expression if we need to check it later let object_clone = object.clone(); // First, build the object expression to get its ValueId let object_value = self.build_expression(object)?; // Get the field from the object using RefGet let field_val = self.value_gen.next(); self.emit_instruction(MirInstruction::RefGet { dst: field_val, reference: object_value, field: field.clone(), })?; // If we recorded origin class for this field on this base object, propagate it to this value id if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() { self.value_origin_newbox.insert(field_val, class_name); } // If we can infer the box type and the field is weak, emit WeakLoad (+ optional barrier) let mut inferred_class: Option = self.value_origin_newbox.get(&object_value).cloned(); // Fallback: if the object is a nested field access like (X.Y).Z, consult recorded field origins for X.Y if inferred_class.is_none() { if let ASTNode::FieldAccess { object: inner_obj, field: ref parent_field, .. } = object_clone { // Build inner base to get a stable id and consult mapping if let Ok(base_id) = self.build_expression(*inner_obj.clone()) { if let Some(cls) = self.field_origin_class.get(&(base_id, parent_field.clone())) { inferred_class = Some(cls.clone()); } } } } if let Some(class_name) = inferred_class { if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { if weak_set.contains(&field) { // Barrier (read) PoC let _ = self.emit_barrier_read(field_val); // WeakLoad let loaded = self.emit_weak_load(field_val)?; return Ok(loaded); } } } Ok(field_val) } /// Build new expression: new ClassName(arguments) fn build_new_expression(&mut self, class: String, arguments: Vec) -> Result { // Phase 9.78a: Unified Box creation using NewBox instruction // Optimization: Primitive wrappers → emit Const directly when possible if class == "IntegerBox" && arguments.len() == 1 { if let ASTNode::Literal { value: LiteralValue::Integer(n), .. } = arguments[0].clone() { let dst = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(n) })?; self.value_types.insert(dst, super::MirType::Integer); return Ok(dst); } } // First, evaluate all arguments to get their ValueIds let mut arg_values = Vec::new(); for arg in arguments { let arg_value = self.build_expression(arg)?; arg_values.push(arg_value); } // Generate the destination ValueId let dst = self.value_gen.next(); // Emit NewBox instruction for all Box types // VM will handle optimization for basic types internally self.emit_instruction(MirInstruction::NewBox { dst, box_type: class.clone(), args: arg_values.clone(), })?; // Annotate primitive boxes match class.as_str() { "IntegerBox" => { self.value_types.insert(dst, super::MirType::Integer); }, "FloatBox" => { self.value_types.insert(dst, super::MirType::Float); }, "BoolBox" => { self.value_types.insert(dst, super::MirType::Bool); }, "StringBox" => { self.value_types.insert(dst, super::MirType::String); }, other => { self.value_types.insert(dst, super::MirType::Box(other.to_string())); } } // Record origin for optimization: dst was created by NewBox of class self.value_origin_newbox.insert(dst, class.clone()); // For plugin/builtin boxes, call birth(...). For user-defined boxes, skip (InstanceBox already constructed) if !self.user_defined_boxes.contains(&class) { let birt_mid = resolve_slot_by_type_name(&class, "birth"); self.emit_box_or_plugin_call( None, dst, "birth".to_string(), birt_mid, arg_values, EffectMask::READ.add(Effect::ReadHeap), )?; } Ok(dst) } /// Build field assignment: object.field = value fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result { // Build the object and value expressions let object_value = self.build_expression(object)?; let mut value_result = self.build_expression(value)?; // If we can infer the box type and the field is weak, create WeakRef before store if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { if weak_set.contains(&field) { value_result = self.emit_weak_new(value_result)?; } } } // Set the field using RefSet self.emit_instruction(MirInstruction::RefSet { reference: object_value, field: field.clone(), value: value_result, })?; // Emit a write barrier for weak fields (PoC) if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() { if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) { if weak_set.contains(&field) { let _ = self.emit_barrier_write(value_result); } } } // Record origin class for this field (if the value originates from NewBox of a known class) if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() { self.field_origin_class.insert((object_value, field.clone()), class_name); } // Return the assigned value Ok(value_result) } /// Start a new basic block pub(super) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> { if let Some(ref mut function) = self.current_function { function.add_block(BasicBlock::new(block_id)); self.current_block = Some(block_id); Ok(()) } else { Err("No current function".to_string()) } } /// Check if the current basic block is terminated fn is_current_block_terminated(&self) -> bool { if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) { if let Some(block) = function.get_block(block_id) { return block.is_terminated(); } } false } // convert_binary_operator moved to builder/ops.rs // convert_unary_operator moved to builder/ops.rs // build_nowait_statement_legacy moved to builder/stmts.rs // build_await_expression_legacy moved to builder/stmts.rs // build_me_expression_legacy moved to builder/stmts.rs // build_method_call_legacy removed (use builder_calls::build_method_call) // parse_type_name_to_mir_legacy removed (use builder_calls::parse_type_name_to_mir) // extract_string_literal_legacy removed (use builder_calls::extract_string_literal) // build_from_expression_legacy removed (use builder_calls::build_from_expression) // lower_static_method_as_function_legacy removed (use builder_calls::lower_static_method_as_function) /// Build box declaration: box Name { fields... methods... } fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap, fields: Vec, weak_fields: Vec) -> Result<(), String> { // For Phase 8.4, we'll emit metadata instructions to register the box type // In a full implementation, this would register type information for later use // Create a type registration constant let type_id = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: type_id, value: ConstValue::String(format!("__box_type_{}", name)), })?; // For each field, emit metadata about the field for field in fields { let field_id = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: field_id, value: ConstValue::String(format!("__field_{}_{}", name, field)), })?; } // Record weak fields for this box if !weak_fields.is_empty() { let set: HashSet = weak_fields.into_iter().collect(); self.weak_fields_by_box.insert(name.clone(), set); } // Reserve method slots for user-defined instance methods (deterministic, starts at 4) let mut instance_methods: Vec = Vec::new(); for (mname, mast) in &methods { if let ASTNode::FunctionDeclaration { is_static, .. } = mast { if !*is_static { instance_methods.push(mname.clone()); } } } instance_methods.sort(); if !instance_methods.is_empty() { let tyid = get_or_assign_type_id(&name); for (i, m) in instance_methods.iter().enumerate() { let slot = 4u16.saturating_add(i as u16); reserve_method_slot(tyid, m, slot); } } // Process methods - now methods is a HashMap for (method_name, method_ast) in methods { if let ASTNode::FunctionDeclaration { .. } = method_ast { let method_id = self.value_gen.next(); self.emit_instruction(MirInstruction::Const { dst: method_id, value: ConstValue::String(format!("__method_{}_{}", name, method_name)), })?; } } Ok(()) } } // BinaryOpType moved to builder/ops.rs impl Default for MirBuilder { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use crate::ast::{ASTNode, LiteralValue, Span}; #[test] fn test_literal_building() { let mut builder = MirBuilder::new(); let ast = ASTNode::Literal { value: LiteralValue::Integer(42), span: Span::unknown(), }; let result = builder.build_module(ast); assert!(result.is_ok()); let module = result.unwrap(); assert_eq!(module.function_names().len(), 1); assert!(module.get_function("main").is_some()); } #[test] fn test_binary_op_building() { let mut builder = MirBuilder::new(); let ast = ASTNode::BinaryOp { left: Box::new(ASTNode::Literal { value: LiteralValue::Integer(10), span: Span::unknown(), }), operator: BinaryOperator::Add, right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(32), span: Span::unknown(), }), span: Span::unknown(), }; let result = builder.build_module(ast); assert!(result.is_ok()); let module = result.unwrap(); let function = module.get_function("main").unwrap(); // Should have constants and binary operation let stats = function.stats(); assert!(stats.instruction_count >= 3); // 2 constants + 1 binop + 1 return } #[test] fn test_if_statement_building() { let mut builder = MirBuilder::new(); // Adapt test to current AST: If with statement bodies let ast = ASTNode::If { condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown(), }), then_body: vec![ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown(), }], else_body: Some(vec![ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown(), }]), span: Span::unknown(), }; let result = builder.build_module(ast); assert!(result.is_ok()); let module = result.unwrap(); let function = module.get_function("main").unwrap(); // Should have multiple blocks for if/then/else/merge assert!(function.blocks.len() >= 3); // Should have phi function in merge block let stats = function.stats(); assert!(stats.phi_count >= 1); } }