From 7bcf77f1c1aaaddf4dad671d2e4c48709555a2f3 Mon Sep 17 00:00:00 2001 From: Hydrostic Date: Sat, 13 Jun 2026 09:10:49 +0800 Subject: [PATCH] fix(sema, ir): Potential elvate bugs --- src/ast/parser/decl.rs | 30 ++++++++++ src/ast/parser/stmt.rs | 4 ++ src/ir/generator.rs | 121 +++++++++++++++++++++++++++++++---------- src/ir/types.rs | 5 +- src/sema/err.rs | 4 +- src/sema/types.rs | 5 +- 6 files changed, 135 insertions(+), 34 deletions(-) diff --git a/src/ast/parser/decl.rs b/src/ast/parser/decl.rs index a291ce2..735569d 100644 --- a/src/ast/parser/decl.rs +++ b/src/ast/parser/decl.rs @@ -205,6 +205,12 @@ impl Parser { self.advance(1); return Ok(vec![]); } + if self.peek().value == TokenValue::TypeIdent(TypeIdent::Void) + && self.peek_n(1).value == TokenValue::RParen + { + self.advance(2); + return Ok(vec![]); + } let mut params = vec![]; loop { @@ -328,6 +334,30 @@ impl Parser { Ok(Some(self.parse_expr()?)) } + fn skip_initializer_list(&mut self) -> Result<(), ParseProcessError> { + let mut depth = 0usize; + loop { + let token = self.next().clone(); + match token.value { + TokenValue::LBrace => depth += 1, + TokenValue::RBrace => { + depth -= 1; + if depth == 0 { + return Ok(()); + } + } + TokenValue::Eof => { + self.diagnostics.add_from_frontend_error( + ParseError::ExpectedBefore(TokenValue::Eof, "`}`"), + token.span, + ); + return Err(ParseProcessError::ErrorInMatch); + } + _ => {} + } + } + } + fn init_declarator_to_var_value( &mut self, init_declarator: InitDeclarator, diff --git a/src/ast/parser/stmt.rs b/src/ast/parser/stmt.rs index ac1474d..b30eb97 100644 --- a/src/ast/parser/stmt.rs +++ b/src/ast/parser/stmt.rs @@ -59,6 +59,10 @@ impl Parser { } fn parse_stmt(&mut self) -> Result { + if self.peek().value == TokenValue::Semicolon { + self.advance(1); + return Ok(Statement::Block(BlockStmt { statements: vec![] })); + } match self.parse_var_decl_stmt(ParseType::TryParse) { Ok(var_decl) => return Ok(Statement::VarDecl(var_decl)), Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch), diff --git a/src/ir/generator.rs b/src/ir/generator.rs index 200d52f..7cb5dfa 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -84,7 +84,7 @@ impl<'a> Generator<'a> { } } else if let Some(init) = value.value { self.current_exit_label.push(None); - let (init_instrs, init_var) = match self.generate_expr(init) { + let (mut init_instrs, init_var) = match self.generate_expr(init) { Some(res) => res, None => { self.current_exit_label.pop(); @@ -93,8 +93,9 @@ impl<'a> Generator<'a> { }; self.current_exit_label.pop(); let target = self.var_manager.get_symbol(value.symbol).unwrap(); + let init_var = self.coerce_value(&mut init_instrs, init_var.unwrap(), &target.data_type); instrs.extend(init_instrs); - instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var.unwrap()))); + instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var))); } } @@ -108,19 +109,26 @@ impl<'a> Generator<'a> { let value = Self::const_init_value(operand)?; match op { AstUnaryOp::Add => Some(value), - AstUnaryOp::Sub => value.checked_neg(), + AstUnaryOp::Sub => Some(value.wrapping_neg()), AstUnaryOp::Not => Some((value == 0) as i32), } } HirExprValue::BinaryOp { lhs, op, rhs } => { let lhs = Self::const_init_value(lhs)?; + match op { + AstBinaryOp::And if lhs == 0 => return Some(0), + AstBinaryOp::Or if lhs != 0 => return Some(1), + _ => {} + } let rhs = Self::const_init_value(rhs)?; match op { - AstBinaryOp::Add => lhs.checked_add(rhs), - AstBinaryOp::Sub => lhs.checked_sub(rhs), - AstBinaryOp::Mul => lhs.checked_mul(rhs), - AstBinaryOp::Div => lhs.checked_div(rhs), - AstBinaryOp::Mod => lhs.checked_rem(rhs), + AstBinaryOp::Add => Some(lhs.wrapping_add(rhs)), + AstBinaryOp::Sub => Some(lhs.wrapping_sub(rhs)), + AstBinaryOp::Mul => Some(lhs.wrapping_mul(rhs)), + AstBinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)), + AstBinaryOp::Div => None, + AstBinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)), + AstBinaryOp::Mod => None, AstBinaryOp::Equal => Some((lhs == rhs) as i32), AstBinaryOp::NotEqual => Some((lhs != rhs) as i32), AstBinaryOp::Less => Some((lhs < rhs) as i32), @@ -214,7 +222,7 @@ impl<'a> Generator<'a> { match return_stmt.value { Some(expr) => { self.current_exit_label.push(None); - let (value_instrs, value_var) = match self.generate_expr(expr) { + let (mut value_instrs, value_var) = match self.generate_expr(expr) { Some(res) => res, None => { self.current_exit_label.pop(); @@ -222,8 +230,10 @@ impl<'a> Generator<'a> { } }; self.current_exit_label.pop(); + let target = func_exit.1.unwrap(); + let value_var = self.coerce_value(&mut value_instrs, value_var.unwrap(), &target.data_type); instrs.extend(value_instrs); - instrs.push(IRInstr::Move(func_exit.1.unwrap(), MoveRValue::Var(value_var.unwrap()))); + instrs.push(IRInstr::Move(target, MoveRValue::Var(value_var))); } None => {}, } @@ -387,6 +397,35 @@ impl<'a> Generator<'a> { vec![] } } + fn coerce_value(&mut self, instrs: &mut Vec, var: Variable, target_type: &IRType) -> Variable { + if &var.data_type == target_type { + return var; + } + + match (&var.data_type, target_type) { + (IRType::I1, IRType::I32) => { + let dest = self.var_manager.declare_unamed_local(IRType::I32); + let true_label = self.request_label(); + let false_label = self.request_label(); + let final_label = self.request_label(); + instrs.push(IRInstr::CondGoto(var, true_label, false_label)); + instrs.push(IRInstr::Label(true_label)); + instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(1))); + instrs.push(IRInstr::Goto(final_label)); + instrs.push(IRInstr::Label(false_label)); + instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(0))); + instrs.push(IRInstr::Goto(final_label)); + instrs.push(IRInstr::Label(final_label)); + dest + } + (IRType::I32, IRType::I1) => { + let dest = self.var_manager.declare_temp(IRType::I1); + instrs.push(IRInstr::Cmp(dest.clone(), VariableOrIntLit::Var(var), CmpOp::Ne, VariableOrIntLit::IntLit(0))); + dest + } + _ => var, + } + } fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec, Option)> { // there may be some expr that doesn't produce value, like void func call let (mut instrs, var) = match expr.value { @@ -414,6 +453,7 @@ impl<'a> Generator<'a> { self.current_exit_label.push(None); let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?; self.current_exit_label.pop(); + let rvalue_var = self.coerce_value(&mut instrs, rvalue_var.unwrap(), &lvalue_ty); let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?; instrs.extend(lvalue_instrs); if is_addr { @@ -509,17 +549,22 @@ impl<'a> Generator<'a> { let operand_var = operand_var.unwrap(); let dest_var = match op { AstUnaryOp::Add => { - let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone()); + let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() }; + let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty); + let dest_var = self.var_manager.declare_temp(target_ty); instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var))); dest_var }, AstUnaryOp::Sub => { - let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone()); + let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() }; + let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty); + let dest_var = self.var_manager.declare_temp(target_ty); instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var)); dest_var }, AstUnaryOp::Not => { - let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type.clone()); + let dest_ty = if parent_is_logical { IRType::I1 } else { IRType::I32 }; + let dest_var = self.var_manager.declare_unamed_local(dest_ty); // child will do the cmp if !parent_is_logical { let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`) @@ -626,9 +671,7 @@ impl<'a> Generator<'a> { // do implicit convert if needed // TODO: further check if !op.is_logical() && convert_to != left_var.data_type { - let temp_var = self.var_manager.declare_temp(convert_to.clone()); - instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var))); - left_var = temp_var; + left_var = self.coerce_value(&mut instrs, left_var, &convert_to); } let result_type; match op { @@ -640,7 +683,13 @@ impl<'a> Generator<'a> { result_type = IRType::I1; } } - let dest_var = self.var_manager.declare_temp(result_type); + let dest_var = if op.is_logical() && parent_exit.is_none() { + self.var_manager.declare_unamed_local(IRType::I32) + } else if op.is_logical() { + self.var_manager.declare_unamed_local(result_type) + } else { + self.var_manager.declare_temp(result_type) + }; match op { AstBinaryOp::And | AstBinaryOp::Or => { instrs.push(IRInstr::Label(exit.unwrap().1)); @@ -666,9 +715,7 @@ impl<'a> Generator<'a> { // true_exit: instrs.extend(right_instrs); if !op.is_logical() && convert_to != right_var.data_type { - let temp_var = self.var_manager.declare_temp(convert_to); - instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_var))); - right_var = temp_var; + right_var = self.coerce_value(&mut instrs, right_var, &convert_to); } if !op.is_logical() { if let Some((true_exit, false_exit)) = parent_exit { @@ -684,6 +731,17 @@ impl<'a> Generator<'a> { } } if op.is_logical() { + if parent_exit.is_none() { + let (true_exit, _, false_exit) = exit.unwrap(); + let final_exit = self.request_label(); + instrs.push(IRInstr::Label(true_exit)); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1))); + instrs.push(IRInstr::Goto(final_exit)); + instrs.push(IRInstr::Label(false_exit)); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0))); + instrs.push(IRInstr::Goto(final_exit)); + instrs.push(IRInstr::Label(final_exit)); + } return Some((instrs, Some(dest_var))); } else { if op.is_cmp() { @@ -713,12 +771,13 @@ impl<'a> Generator<'a> { let mut arg_vars = vec![]; let func_def = self.ir_function(func_id); - for arg in args.into_iter() { + for (arg, param_type) in args.into_iter().zip(func_def.parameter_types.iter()) { self.current_exit_label.push(None); - let (arg_instrs, arg_var) = self.generate_expr(arg)?; + let (mut arg_instrs, arg_var) = self.generate_expr(arg)?; self.current_exit_label.pop(); + let arg_var = self.coerce_value(&mut arg_instrs, arg_var.unwrap(), param_type); instrs.extend(arg_instrs); - arg_vars.push(arg_var.unwrap()); + arg_vars.push(arg_var); } let ret_variable = if matches!(func_def.return_type, IRType::Void) { None @@ -730,10 +789,15 @@ impl<'a> Generator<'a> { } }; if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() { - let cmp_var = self.var_manager.declare_temp(IRType::I1); - instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0))); - instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit)); - Some((instrs, var)) + let var = var.clone().unwrap(); + if matches!(var.data_type, IRType::Array(_, _) | IRType::Ptr(_)) { + instrs.push(IRInstr::Goto(true_exit)); + } else { + let cmp_var = self.var_manager.declare_temp(IRType::I1); + instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone()), CmpOp::Ne, VariableOrIntLit::IntLit(0))); + instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit)); + } + Some((instrs, Some(var))) } else { Some((instrs, var)) } @@ -777,12 +841,13 @@ impl<'a> Generator<'a> { let (index_instrs, index_var) = self.generate_expr(index)?; self.current_exit_label.pop(); instrs.extend(index_instrs); + let index_var = self.coerce_value(&mut instrs, index_var.unwrap(), &IRType::I32); let addr_ty = IRType::Ptr(Box::new(elem_ty.into())); let dest = self.var_manager.declare_temp(addr_ty); let elem_size_var = self.var_manager.declare_temp(IRType::I32); let offset = self.var_manager.declare_temp(IRType::I32); instrs.push(IRInstr::Move(elem_size_var.clone(), MoveRValue::ConstInt(elem_size as i32))); - instrs.push(IRInstr::Binary(offset.clone(), index_var.unwrap(), AstBinaryOp::Mul.into(), elem_size_var)); + instrs.push(IRInstr::Binary(offset.clone(), index_var, AstBinaryOp::Mul.into(), elem_size_var)); instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset)); Some((instrs, dest)) } diff --git a/src/ir/types.rs b/src/ir/types.rs index e72a364..be7102f 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -124,10 +124,11 @@ impl IRType { } pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option { + if matches!((lhs, rhs), (IRType::I32 | IRType::I1, IRType::I32 | IRType::I1)) { + return Some(IRType::I32); + } if lhs == rhs { Some(lhs.clone()) - } else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) { - Some(IRType::I32) } else { None } diff --git a/src/sema/err.rs b/src/sema/err.rs index d41705a..8af7924 100644 --- a/src/sema/err.rs +++ b/src/sema/err.rs @@ -34,8 +34,8 @@ pub enum SemaError { ReturnExpressionOnVoidFunction, #[error("array dimension must be a positive integer literal")] InvalidArrayDimension, - #[error("subscripted value is not an array or pointer")] - NotSubscriptable, + #[error("subscripted value of type `{0}` is not an array or pointer")] + NotSubscriptable(SemaType), #[error("initializer element is not constant")] InitializerNotConstant, } diff --git a/src/sema/types.rs b/src/sema/types.rs index 8b575b7..2a66dac 100644 --- a/src/sema/types.rs +++ b/src/sema/types.rs @@ -13,10 +13,11 @@ pub enum SemaType { impl SemaType { pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option { + if matches!((lhs, rhs), (SemaType::I32 | SemaType::I1, SemaType::I32 | SemaType::I1)) { + return Some(SemaType::I32); + } if lhs == rhs { Some(lhs.clone()) - } else if (*lhs == SemaType::I32 && *rhs == SemaType::I1) || (*lhs == SemaType::I1 && *rhs == SemaType::I32) { - Some(SemaType::I32) } else { None }