use super::{MirInstruction, MirType, ValueId}; use crate::ast::{ASTNode, BinaryOperator}; use crate::mir::loop_api::LoopBuilderApi; // for current_block() use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp}; // Internal classification for binary operations #[derive(Debug)] enum BinaryOpType { Arithmetic(BinaryOp), Comparison(CompareOp), } impl super::MirBuilder { // Build a binary operation pub(super) fn build_binary_op( &mut self, left: ASTNode, operator: BinaryOperator, right: ASTNode, ) -> Result { // Short-circuit logical ops: lower to control-flow so RHS is evaluated conditionally if matches!(operator, BinaryOperator::And | BinaryOperator::Or) { return self.build_logical_shortcircuit(left, operator, right); } let lhs_raw = self.build_expression(left)?; let rhs_raw = self.build_expression(right)?; // Correctness-first: ensure both operands have block-local definitions // so they participate in PHI/materialization and avoid use-before-def across // complex control-flow (e.g., loop headers and nested branches). let lhs = self .ensure_slotified_for_use(lhs_raw, "@binop_lhs") .unwrap_or(lhs_raw); let rhs = self .ensure_slotified_for_use(rhs_raw, "@binop_rhs") .unwrap_or(rhs_raw); let dst = self.next_value_id(); let mir_op = self.convert_binary_operator(operator)?; let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL") .ok() .as_deref() == Some("1"); match mir_op { // Arithmetic operations BinaryOpType::Arithmetic(op) => { // Dev: Lower '+' を演算子ボックス呼び出しに置換(既定OFF) let in_add_op = self .current_function .as_ref() .map(|f| f.signature.name.starts_with("AddOperator.apply/")) .unwrap_or(false); if matches!(op, crate::mir::BinaryOp::Add) && !in_add_op && (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL") .ok() .as_deref() == Some("1")) { // AddOperator.apply/2(lhs, rhs) let name = "AddOperator.apply/2".to_string(); self.emit_legacy_call( Some(dst), super::builder_calls::CallTarget::Global(name), vec![lhs, rhs], )?; // Phase 196: TypeFacts SSOT - AddOperator call type annotation // Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox) let type_facts = super::type_facts::TypeFactsBox::new( &self.value_types, &self.value_origin_newbox, ); let lhs_type = type_facts.classify_operand_type(lhs); let rhs_type = type_facts.classify_operand_type(rhs); use super::type_facts::OperandTypeClass::*; match (lhs_type, rhs_type) { (String, String) => { // BOTH are strings: result is string self.value_types .insert(dst, MirType::Box("StringBox".to_string())); self.value_origin_newbox .insert(dst, "StringBox".to_string()); } (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => { // TypeFact: Integer + anything non-String = Integer self.value_types.insert(dst, MirType::Integer); } (String, Integer) | (Integer, String) => { // Mixed types: leave as Unknown for use-site coercion } (Unknown, Unknown) => { // Both Unknown: cannot infer } (String, Unknown) | (Unknown, String) => { // One side is String, other is Unknown: cannot infer safely } } } else if all_call { // Lower other arithmetic ops to operator boxes under ALL flag let (name, guard_prefix) = match op { crate::mir::BinaryOp::Sub => ("SubOperator.apply/2", "SubOperator.apply/"), crate::mir::BinaryOp::Mul => ("MulOperator.apply/2", "MulOperator.apply/"), crate::mir::BinaryOp::Div => ("DivOperator.apply/2", "DivOperator.apply/"), crate::mir::BinaryOp::Mod => ("ModOperator.apply/2", "ModOperator.apply/"), crate::mir::BinaryOp::Shl => ("ShlOperator.apply/2", "ShlOperator.apply/"), crate::mir::BinaryOp::Shr => ("ShrOperator.apply/2", "ShrOperator.apply/"), crate::mir::BinaryOp::BitAnd => { ("BitAndOperator.apply/2", "BitAndOperator.apply/") } crate::mir::BinaryOp::BitOr => { ("BitOrOperator.apply/2", "BitOrOperator.apply/") } crate::mir::BinaryOp::BitXor => { ("BitXorOperator.apply/2", "BitXorOperator.apply/") } _ => ("", ""), }; if !name.is_empty() { let in_guard = self .current_function .as_ref() .map(|f| f.signature.name.starts_with(guard_prefix)) .unwrap_or(false); if !in_guard { self.emit_legacy_call( Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![lhs, rhs], )?; // 型注釈: 算術はおおむね整数(Addは上で注釈済み) self.value_types.insert(dst, MirType::Integer); } else { // guard中は従来のBinOp if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { crate::mir::ssot::binop_lower::emit_binop_to_dst( func, cur_bb, dst, op, lhs, rhs, ); } else { self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?; } self.value_types.insert(dst, MirType::Integer); } } else { // 既存の算術経路 if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { crate::mir::ssot::binop_lower::emit_binop_to_dst( func, cur_bb, dst, op, lhs, rhs, ); } else { self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?; } // Phase 196: TypeFacts SSOT - BinOp type is determined by operands only // String concatenation is handled at use-site in LLVM lowering if matches!(op, crate::mir::BinaryOp::Add) { // Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox) let type_facts = super::type_facts::TypeFactsBox::new( &self.value_types, &self.value_origin_newbox, ); let lhs_type = type_facts.classify_operand_type(lhs); let rhs_type = type_facts.classify_operand_type(rhs); use super::type_facts::OperandTypeClass::*; match (lhs_type, rhs_type) { (String, String) => { // BOTH are strings: result is definitely a string self.value_types .insert(dst, MirType::Box("StringBox".to_string())); self.value_origin_newbox .insert(dst, "StringBox".to_string()); } (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => { // TypeFact: Integer + anything non-String = Integer // This handles `counter + 1` where counter might be Unknown self.value_types.insert(dst, MirType::Integer); } (String, Integer) | (Integer, String) => { // Mixed types: leave as Unknown for use-site coercion // LLVM backend will handle string concatenation } (Unknown, Unknown) => { // Both Unknown: cannot infer, leave as Unknown } (String, Unknown) | (Unknown, String) => { // One side is String, other is Unknown: cannot infer safely // Leave as Unknown } } } else { self.value_types.insert(dst, MirType::Integer); } } } else { // 既存の算術経路 if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) { crate::mir::ssot::binop_lower::emit_binop_to_dst( func, cur_bb, dst, op, lhs, rhs, ); } else { self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?; } // Phase 196: TypeFacts SSOT - BinOp type is determined by operands only // String concatenation is handled at use-site in LLVM lowering if matches!(op, crate::mir::BinaryOp::Add) { // Check if BOTH operands are known to be strings (TypeFacts) let lhs_is_str = match self.value_types.get(&lhs) { Some(MirType::String) => true, Some(MirType::Box(bt)) if bt == "StringBox" => true, _ => self .value_origin_newbox .get(&lhs) .map(|s| s == "StringBox") .unwrap_or(false), }; let rhs_is_str = match self.value_types.get(&rhs) { Some(MirType::String) => true, Some(MirType::Box(bt)) if bt == "StringBox" => true, _ => self .value_origin_newbox .get(&rhs) .map(|s| s == "StringBox") .unwrap_or(false), }; if lhs_is_str && rhs_is_str { // BOTH are strings: result is definitely a string self.value_types .insert(dst, MirType::Box("StringBox".to_string())); self.value_origin_newbox .insert(dst, "StringBox".to_string()); } else if !lhs_is_str && !rhs_is_str { // NEITHER is a string: numeric addition self.value_types.insert(dst, MirType::Integer); } // else: Mixed types (string + int or int + string) // Leave dst type as Unknown - LLVM will handle coercion at use-site } else { self.value_types.insert(dst, MirType::Integer); } } } // Comparison operations BinaryOpType::Comparison(op) => { // Dev: Lower 比較 を演算子ボックス呼び出しに置換(既定OFF) let in_cmp_op = self .current_function .as_ref() .map(|f| f.signature.name.starts_with("CompareOperator.apply/")) .unwrap_or(false); if !in_cmp_op && (all_call || std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL") .ok() .as_deref() == Some("1")) { // op名の文字列化 let opname = match op { CompareOp::Eq => "Eq", CompareOp::Ne => "Ne", CompareOp::Lt => "Lt", CompareOp::Le => "Le", CompareOp::Gt => "Gt", CompareOp::Ge => "Ge", }; let op_const = crate::mir::builder::emission::constant::emit_string(self, opname); // そのまま値を渡す(型変換/slot化は演算子内orVMで行う) let name = "CompareOperator.apply/3".to_string(); self.emit_legacy_call( Some(dst), super::builder_calls::CallTarget::Global(name), vec![op_const, lhs, rhs], )?; self.value_types.insert(dst, MirType::Bool); } else { // 既存の比較経路(安全のための型注釈/slot化含む) let (lhs2_raw, rhs2_raw) = if self .value_origin_newbox .get(&lhs) .map(|s| s == "IntegerBox") .unwrap_or(false) && self .value_origin_newbox .get(&rhs) .map(|s| s == "IntegerBox") .unwrap_or(false) { let li = self.next_value_id(); let ri = self.next_value_id(); self.emit_instruction(MirInstruction::TypeOp { dst: li, op: TypeOpKind::Cast, value: lhs, ty: MirType::Integer, })?; self.emit_instruction(MirInstruction::TypeOp { dst: ri, op: TypeOpKind::Cast, value: rhs, ty: MirType::Integer, })?; (li, ri) } else { (lhs, rhs) }; // Finalize compare operands in current block via LocalSSA let mut lhs2 = lhs2_raw; let mut rhs2 = rhs2_raw; crate::mir::builder::ssa::local::finalize_compare(self, &mut lhs2, &mut rhs2); crate::mir::builder::emission::compare::emit_to(self, dst, op, lhs2, rhs2)?; } } } Ok(dst) } /// Lower logical && / || with proper short-circuit semantics. /// Result is a Bool, and RHS is only evaluated if needed. fn build_logical_shortcircuit( &mut self, left: ASTNode, operator: BinaryOperator, right: ASTNode, ) -> Result { let is_and = matches!(operator, BinaryOperator::And); // Evaluate LHS only once and pin to a slot so it can be reused safely across blocks let lhs_val0 = self.build_expression(left)?; let lhs_val = self.pin_to_slot(lhs_val0, "@sc_lhs")?; // Prepare blocks let then_block = self.next_block_id(); let else_block = self.next_block_id(); let merge_block = self.next_block_id(); // Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM) let mut lhs_cond = self.local_cond(lhs_val); crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond); crate::mir::builder::emission::branch::emit_conditional( self, lhs_cond, then_block, else_block, )?; // Record predecessor block for branch (for single-pred PHI materialization) let pre_branch_bb = self.current_block()?; // Snapshot variables before entering branches let pre_if_var_map = self.variable_map.clone(); // ---- THEN branch ---- self.start_new_block(then_block)?; self.hint_scope_enter(0); // Reset scope to pre-if snapshot for clean deltas self.variable_map = pre_if_var_map.clone(); // Materialize all variables at entry via single-pred PHI (correctness-first) for (name, &pre_v) in pre_if_var_map.iter() { let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?; self.variable_map.insert(name.clone(), phi_val); } // AND: then → evaluate RHS and reduce to bool // OR: then → constant true let then_value_raw = if is_and { // Reduce arbitrary RHS to bool by branching on its truthiness and returning consts let rhs_true = self.next_block_id(); let rhs_false = self.next_block_id(); let rhs_join = self.next_block_id(); let rhs_val = self.build_expression(right.clone())?; let mut rhs_cond = self.local_cond(rhs_val); crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond); crate::mir::builder::emission::branch::emit_conditional( self, rhs_cond, rhs_true, rhs_false, )?; // true path self.start_new_block(rhs_true)?; let t_id = crate::mir::builder::emission::constant::emit_bool(self, true); crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?; let rhs_true_exit = self.current_block()?; // false path self.start_new_block(rhs_false)?; let f_id = crate::mir::builder::emission::constant::emit_bool(self, false); crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?; let rhs_false_exit = self.current_block()?; // join rhs result into a single bool self.start_new_block(rhs_join)?; if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?; self.value_types.insert(rhs_bool, MirType::Bool); rhs_bool } else { let t_id = crate::mir::builder::emission::constant::emit_bool(self, true); t_id }; let then_exit_block = self.current_block()?; let then_reaches_merge = !self.is_current_block_terminated(); let then_var_map_end = self.variable_map.clone(); if then_reaches_merge { self.hint_scope_leave(0); crate::mir::builder::emission::branch::emit_jump(self, merge_block)?; } // ---- ELSE branch ---- self.start_new_block(else_block)?; self.hint_scope_enter(0); self.variable_map = pre_if_var_map.clone(); // Materialize all variables at entry via single-pred PHI (correctness-first) for (name, &pre_v) in pre_if_var_map.iter() { let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?; self.variable_map.insert(name.clone(), phi_val); } // AND: else → false // OR: else → evaluate RHS and reduce to bool let else_value_raw = if is_and { let f_id = crate::mir::builder::emission::constant::emit_bool(self, false); f_id } else { let rhs_true = self.next_block_id(); let rhs_false = self.next_block_id(); let rhs_join = self.next_block_id(); let rhs_val = self.build_expression(right)?; let mut rhs_cond = self.local_cond(rhs_val); crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond); crate::mir::builder::emission::branch::emit_conditional( self, rhs_cond, rhs_true, rhs_false, )?; // true path self.start_new_block(rhs_true)?; let t_id = crate::mir::builder::emission::constant::emit_bool(self, true); crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?; let rhs_true_exit = self.current_block()?; // false path self.start_new_block(rhs_false)?; let f_id = crate::mir::builder::emission::constant::emit_bool(self, false); crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?; let rhs_false_exit = self.current_block()?; // join rhs result into a single bool self.start_new_block(rhs_join)?; if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?; self.value_types.insert(rhs_bool, MirType::Bool); rhs_bool }; let else_exit_block = self.current_block()?; let else_reaches_merge = !self.is_current_block_terminated(); let else_var_map_end = self.variable_map.clone(); if else_reaches_merge { self.hint_scope_leave(0); crate::mir::builder::emission::branch::emit_jump(self, merge_block)?; } // ---- MERGE ---- // Merge block: suppress entry pin copy so PHIs remain first and materialize pins explicitly self.suppress_next_entry_pin_copy(); self.start_new_block(merge_block)?; self.push_if_merge(merge_block); // Result PHI (bool) — consider only reachable predecessors let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new(); if then_reaches_merge { inputs.push((then_exit_block, then_value_raw)); } if else_reaches_merge { inputs.push((else_exit_block, else_value_raw)); } let result_val = if inputs.len() >= 2 { if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } let dst = self.insert_phi(inputs)?; self.value_types.insert(dst, MirType::Bool); dst } else if inputs.len() == 1 { inputs[0].1 } else { // Unreachable fallthrough: synthesize false (defensive) crate::mir::builder::emission::constant::emit_bool(self, false) }; // Merge modified vars from both branches back into current scope self.merge_modified_vars( then_block, else_block, if then_reaches_merge { Some(then_exit_block) } else { None }, if else_reaches_merge { Some(else_exit_block) } else { None }, &pre_if_var_map, &then_var_map_end, &Some(else_var_map_end), None, )?; self.pop_if_merge(); Ok(result_val) } // Build a unary operation pub(super) fn build_unary_op( &mut self, operator: String, operand: ASTNode, ) -> Result { let operand_val = self.build_expression(operand)?; let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL") .ok() .as_deref() == Some("1"); if all_call { let (name, guard_prefix, rett) = match operator.as_str() { "-" => ( "NegOperator.apply/1", "NegOperator.apply/", MirType::Integer, ), "!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool), "~" => ( "BitNotOperator.apply/1", "BitNotOperator.apply/", MirType::Integer, ), _ => ("", "", MirType::Integer), }; if !name.is_empty() { let in_guard = self .current_function .as_ref() .map(|f| f.signature.name.starts_with(guard_prefix)) .unwrap_or(false); let dst = self.next_value_id(); if !in_guard { self.emit_legacy_call( Some(dst), super::builder_calls::CallTarget::Global(name.to_string()), vec![operand_val], )?; self.value_types.insert(dst, rett); return Ok(dst); } } } // Core-13 純化: UnaryOp を直接 展開(Neg/Not/BitNot) if crate::config::env::mir_core13_pure() { match operator.as_str() { "-" => { let zero = crate::mir::builder::emission::constant::emit_integer(self, 0); let dst = self.next_value_id(); self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::Sub, lhs: zero, rhs: operand_val, })?; return Ok(dst); } "!" | "not" => { let f = crate::mir::builder::emission::constant::emit_bool(self, false); let dst = self.next_value_id(); crate::mir::builder::emission::compare::emit_to( self, dst, crate::mir::CompareOp::Eq, operand_val, f, )?; return Ok(dst); } "~" => { let all1 = crate::mir::builder::emission::constant::emit_integer(self, -1); let dst = self.next_value_id(); self.emit_instruction(MirInstruction::BinOp { dst, op: crate::mir::BinaryOp::BitXor, lhs: operand_val, rhs: all1, })?; return Ok(dst); } _ => {} } } let dst = self.next_value_id(); let mir_op = self.convert_unary_operator(operator)?; self.emit_instruction(MirInstruction::UnaryOp { dst, op: mir_op, operand: operand_val, })?; Ok(dst) } // Convert AST binary operator to MIR enum or compare fn convert_binary_operator(&self, op: BinaryOperator) -> Result { match op { BinaryOperator::Add => Ok(BinaryOpType::Arithmetic(BinaryOp::Add)), BinaryOperator::Subtract => Ok(BinaryOpType::Arithmetic(BinaryOp::Sub)), BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)), BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)), BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)), BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)), BinaryOperator::Shr => Ok(BinaryOpType::Arithmetic(BinaryOp::Shr)), BinaryOperator::BitAnd => Ok(BinaryOpType::Arithmetic(BinaryOp::BitAnd)), BinaryOperator::BitOr => Ok(BinaryOpType::Arithmetic(BinaryOp::BitOr)), BinaryOperator::BitXor => Ok(BinaryOpType::Arithmetic(BinaryOp::BitXor)), BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)), BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)), BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)), BinaryOperator::LessEqual => Ok(BinaryOpType::Comparison(CompareOp::Le)), BinaryOperator::Greater => Ok(BinaryOpType::Comparison(CompareOp::Gt)), BinaryOperator::GreaterEqual => Ok(BinaryOpType::Comparison(CompareOp::Ge)), BinaryOperator::And => Ok(BinaryOpType::Arithmetic(BinaryOp::And)), BinaryOperator::Or => Ok(BinaryOpType::Arithmetic(BinaryOp::Or)), } } // Convert AST unary operator to MIR operator fn convert_unary_operator(&self, op: String) -> Result { match op.as_str() { "-" => Ok(UnaryOp::Neg), "!" | "not" => Ok(UnaryOp::Not), "~" => Ok(UnaryOp::BitNot), _ => Err(format!("Unsupported unary operator: {}", op)), } } }