feat(lexer,sema,ir): Add compoundassign
This commit is contained in:
@@ -235,6 +235,12 @@ impl AstGraphBuilder {
|
|||||||
self.add_expr(node, rvalue);
|
self.add_expr(node, rvalue);
|
||||||
node
|
node
|
||||||
},
|
},
|
||||||
|
ExprValue::CompoundAssign { lvalue, rvalue, .. } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, lvalue);
|
||||||
|
self.add_expr(node, rvalue);
|
||||||
|
node
|
||||||
|
},
|
||||||
ExprValue::UnaryOp { op: _, operand } => {
|
ExprValue::UnaryOp { op: _, operand } => {
|
||||||
let node = self.child(parent, expr.value.to_string());
|
let node = self.child(parent, expr.value.to_string());
|
||||||
self.add_expr(node, operand);
|
self.add_expr(node, operand);
|
||||||
|
|||||||
+19
-3
@@ -344,12 +344,28 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
let lvalue = self.parse_logical_or()?;
|
let lvalue = self.parse_logical_or()?;
|
||||||
if self.peek().value != TokenValue::Equal {
|
let compound_op = match self.peek().value {
|
||||||
return Ok(lvalue);
|
TokenValue::Equal => None,
|
||||||
}
|
TokenValue::PlusEqual => Some(BinaryOp::Add),
|
||||||
|
TokenValue::MinusEqual => Some(BinaryOp::Sub),
|
||||||
|
TokenValue::StarEqual => Some(BinaryOp::Mul),
|
||||||
|
TokenValue::SlashEqual => Some(BinaryOp::Div),
|
||||||
|
TokenValue::PercentEqual => Some(BinaryOp::Mod),
|
||||||
|
_ => return Ok(lvalue),
|
||||||
|
};
|
||||||
self.advance(1);
|
self.advance(1);
|
||||||
let rvalue = self.parse_assign()?;
|
let rvalue = self.parse_assign()?;
|
||||||
let span = Span::from_two(lvalue.span, rvalue.span);
|
let span = Span::from_two(lvalue.span, rvalue.span);
|
||||||
|
if let Some(op) = compound_op {
|
||||||
|
return Ok(Expr {
|
||||||
|
value: ExprValue::CompoundAssign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
op,
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
Ok(Expr {
|
Ok(Expr {
|
||||||
value: ExprValue::Assign {
|
value: ExprValue::Assign {
|
||||||
lvalue: Box::new(lvalue),
|
lvalue: Box::new(lvalue),
|
||||||
|
|||||||
@@ -104,10 +104,12 @@ pub struct ReturnStmt {
|
|||||||
pub value: Option<Expr>,
|
pub value: Option<Expr>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Expr {
|
pub struct Expr {
|
||||||
pub value: ExprValue,
|
pub value: ExprValue,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum ExprValue {
|
pub enum ExprValue {
|
||||||
IntLit(i64),
|
IntLit(i64),
|
||||||
Var(String),
|
Var(String),
|
||||||
@@ -134,6 +136,11 @@ pub enum ExprValue {
|
|||||||
lvalue: Box<Expr>,
|
lvalue: Box<Expr>,
|
||||||
rvalue: Box<Expr>
|
rvalue: Box<Expr>
|
||||||
},
|
},
|
||||||
|
CompoundAssign {
|
||||||
|
lvalue: Box<Expr>,
|
||||||
|
op: BinaryOp,
|
||||||
|
rvalue: Box<Expr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
@@ -310,6 +317,7 @@ impl fmt::Display for ExprValue {
|
|||||||
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
||||||
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
||||||
ExprValue::Assign { .. } => write!(f, "Assign"),
|
ExprValue::Assign { .. } => write!(f, "Assign"),
|
||||||
|
ExprValue::CompoundAssign { op, .. } => write!(f, "CompoundAssign({})", op),
|
||||||
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
||||||
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
|
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-2
@@ -417,9 +417,48 @@ impl<'a> Generator<'a> {
|
|||||||
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
||||||
instrs.extend(lvalue_instrs);
|
instrs.extend(lvalue_instrs);
|
||||||
if is_addr {
|
if is_addr {
|
||||||
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap()));
|
instrs.push(IRInstr::Store(target.clone(), rvalue_var));
|
||||||
} else {
|
} else {
|
||||||
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap())));
|
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var)));
|
||||||
|
}
|
||||||
|
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(temp_var.clone(), target));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(target)));
|
||||||
|
}
|
||||||
|
(instrs, Some(temp_var))
|
||||||
|
},
|
||||||
|
HirExprValue::CompoundAssign { lvalue, op, rvalue } => {
|
||||||
|
let lvalue_ty: IRType = lvalue.ty.clone().into();
|
||||||
|
let (mut instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
||||||
|
let mut old_var = self.var_manager.declare_temp(lvalue_ty.clone());
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(old_var.clone(), target.clone()));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(old_var.clone(), MoveRValue::Var(target.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (rvalue_instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(rvalue_instrs);
|
||||||
|
let mut rvalue_var = rvalue_var.unwrap();
|
||||||
|
|
||||||
|
let convert_to = IRType::get_elevate_result(&old_var.data_type, &rvalue_var.data_type).unwrap();
|
||||||
|
if convert_to != old_var.data_type {
|
||||||
|
old_var = self.coerce_value(&mut instrs, old_var, &convert_to);
|
||||||
|
}
|
||||||
|
if convert_to != rvalue_var.data_type {
|
||||||
|
rvalue_var = self.coerce_value(&mut instrs, rvalue_var, &convert_to);
|
||||||
|
}
|
||||||
|
let computed = self.var_manager.declare_temp(convert_to);
|
||||||
|
instrs.push(IRInstr::Binary(computed.clone(), old_var, op.into(), rvalue_var));
|
||||||
|
let stored = self.coerce_value(&mut instrs, computed, &lvalue_ty);
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Store(target.clone(), stored));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(stored)));
|
||||||
}
|
}
|
||||||
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
||||||
if is_addr {
|
if is_addr {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub enum TokenValue {
|
|||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
PlusPlus, MinusMinus,
|
PlusPlus, MinusMinus,
|
||||||
|
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
|
||||||
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
|
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
|
||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
@@ -56,6 +57,11 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::Minus => write!(f, "`-`"),
|
TokenValue::Minus => write!(f, "`-`"),
|
||||||
TokenValue::PlusPlus => write!(f, "`++`"),
|
TokenValue::PlusPlus => write!(f, "`++`"),
|
||||||
TokenValue::MinusMinus => write!(f, "`--`"),
|
TokenValue::MinusMinus => write!(f, "`--`"),
|
||||||
|
TokenValue::PlusEqual => write!(f, "`+=`"),
|
||||||
|
TokenValue::MinusEqual => write!(f, "`-=`"),
|
||||||
|
TokenValue::StarEqual => write!(f, "`*=`"),
|
||||||
|
TokenValue::SlashEqual => write!(f, "`/=`"),
|
||||||
|
TokenValue::PercentEqual => write!(f, "`%=`"),
|
||||||
TokenValue::Star => write!(f, "`*`"),
|
TokenValue::Star => write!(f, "`*`"),
|
||||||
TokenValue::Slash => write!(f, "`/`"),
|
TokenValue::Slash => write!(f, "`/`"),
|
||||||
TokenValue::Percent => write!(f, "`%`"),
|
TokenValue::Percent => write!(f, "`%`"),
|
||||||
@@ -98,6 +104,7 @@ pub enum TokenKind {
|
|||||||
TypeIdent,
|
TypeIdent,
|
||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
|
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
|
||||||
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
|
|||||||
+82
-14
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
@@ -21,6 +21,7 @@ use crate::{
|
|||||||
pub struct Analyzer {
|
pub struct Analyzer {
|
||||||
symbols: SymbolTable,
|
symbols: SymbolTable,
|
||||||
function_map: BTreeMap<String, FunctionId>,
|
function_map: BTreeMap<String, FunctionId>,
|
||||||
|
builtin_functions: BTreeSet<FunctionId>,
|
||||||
functions: Vec<FunctionSig>,
|
functions: Vec<FunctionSig>,
|
||||||
current_func_return_type: Option<SemaType>,
|
current_func_return_type: Option<SemaType>,
|
||||||
diagnostic: Diagnositics,
|
diagnostic: Diagnositics,
|
||||||
@@ -32,6 +33,7 @@ impl Analyzer {
|
|||||||
let mut analyzer = Self {
|
let mut analyzer = Self {
|
||||||
symbols: SymbolTable::new(),
|
symbols: SymbolTable::new(),
|
||||||
function_map: BTreeMap::new(),
|
function_map: BTreeMap::new(),
|
||||||
|
builtin_functions: BTreeSet::new(),
|
||||||
functions: vec![],
|
functions: vec![],
|
||||||
current_func_return_type: None,
|
current_func_return_type: None,
|
||||||
diagnostic: Diagnositics::new(),
|
diagnostic: Diagnositics::new(),
|
||||||
@@ -75,6 +77,7 @@ impl Analyzer {
|
|||||||
parameter_types,
|
parameter_types,
|
||||||
};
|
};
|
||||||
self.function_map.insert(name.to_string(), id);
|
self.function_map.insert(name.to_string(), id);
|
||||||
|
self.builtin_functions.insert(id);
|
||||||
self.functions.push(sig);
|
self.functions.push(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +159,11 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
|
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
|
||||||
if self.function_map.contains_key(&func_decl.name) {
|
let is_static = func_decl.storage_class == Some(StorageClass::Static);
|
||||||
|
let existing_function = self.function_map.get(&func_decl.name).cloned();
|
||||||
|
let can_shadow_builtin = is_static
|
||||||
|
&& existing_function.is_some_and(|function| self.builtin_functions.contains(&function));
|
||||||
|
if existing_function.is_some() && !can_shadow_builtin {
|
||||||
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -167,9 +174,14 @@ impl Analyzer {
|
|||||||
parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true));
|
parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true));
|
||||||
}
|
}
|
||||||
let return_type: SemaType = func_decl.return_type.into();
|
let return_type: SemaType = func_decl.return_type.into();
|
||||||
|
let ir_name = if is_static {
|
||||||
|
format!("__static_func_{}_{}", function_id.0, func_decl.name)
|
||||||
|
} else {
|
||||||
|
func_decl.name.clone()
|
||||||
|
};
|
||||||
let sig = FunctionSig {
|
let sig = FunctionSig {
|
||||||
id: function_id,
|
id: function_id,
|
||||||
name: func_decl.name.clone(),
|
name: ir_name,
|
||||||
return_type: return_type.clone(),
|
return_type: return_type.clone(),
|
||||||
parameter_types,
|
parameter_types,
|
||||||
};
|
};
|
||||||
@@ -208,24 +220,31 @@ impl Analyzer {
|
|||||||
|
|
||||||
fn eval_const_expr(expr: &Expr) -> Option<i32> {
|
fn eval_const_expr(expr: &Expr) -> Option<i32> {
|
||||||
match &expr.value {
|
match &expr.value {
|
||||||
ExprValue::IntLit(value) => i32::try_from(*value).ok(),
|
ExprValue::IntLit(value) => Some(*value as i32),
|
||||||
ExprValue::UnaryOp { op, operand } => {
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
let value = Self::eval_const_expr(operand)?;
|
let value = Self::eval_const_expr(operand)?;
|
||||||
match op {
|
match op {
|
||||||
crate::ast::types::UnaryOp::Add => Some(value),
|
crate::ast::types::UnaryOp::Add => Some(value),
|
||||||
crate::ast::types::UnaryOp::Sub => value.checked_neg(),
|
crate::ast::types::UnaryOp::Sub => Some(value.wrapping_neg()),
|
||||||
crate::ast::types::UnaryOp::Not => Some((value == 0) as i32),
|
crate::ast::types::UnaryOp::Not => Some((value == 0) as i32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprValue::BinaryOp { lhs, op, rhs } => {
|
ExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
let lhs = Self::eval_const_expr(lhs)?;
|
let lhs = Self::eval_const_expr(lhs)?;
|
||||||
|
match op {
|
||||||
|
BinaryOp::And if lhs == 0 => return Some(0),
|
||||||
|
BinaryOp::Or if lhs != 0 => return Some(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let rhs = Self::eval_const_expr(rhs)?;
|
let rhs = Self::eval_const_expr(rhs)?;
|
||||||
match op {
|
match op {
|
||||||
BinaryOp::Add => lhs.checked_add(rhs),
|
BinaryOp::Add => Some(lhs.wrapping_add(rhs)),
|
||||||
BinaryOp::Sub => lhs.checked_sub(rhs),
|
BinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
|
||||||
BinaryOp::Mul => lhs.checked_mul(rhs),
|
BinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
|
||||||
BinaryOp::Div => lhs.checked_div(rhs),
|
BinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
|
||||||
BinaryOp::Mod => lhs.checked_rem(rhs),
|
BinaryOp::Div => None,
|
||||||
|
BinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)),
|
||||||
|
BinaryOp::Mod => None,
|
||||||
BinaryOp::Equal => Some((lhs == rhs) as i32),
|
BinaryOp::Equal => Some((lhs == rhs) as i32),
|
||||||
BinaryOp::NotEqual => Some((lhs != rhs) as i32),
|
BinaryOp::NotEqual => Some((lhs != rhs) as i32),
|
||||||
BinaryOp::Less => Some((lhs < rhs) as i32),
|
BinaryOp::Less => Some((lhs < rhs) as i32),
|
||||||
@@ -248,12 +267,12 @@ impl Analyzer {
|
|||||||
for (i, dimension) in dimensions.iter().enumerate() {
|
for (i, dimension) in dimensions.iter().enumerate() {
|
||||||
match &dimension.value {
|
match &dimension.value {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
if let ExprValue::IntLit(value) = expr.value {
|
if let Some(value) = Self::eval_const_expr(expr) {
|
||||||
if value > 0 {
|
if value > 0 {
|
||||||
dims.push(value as usize);
|
dims.push(value as usize);
|
||||||
} else {
|
continue;
|
||||||
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
|
||||||
}
|
}
|
||||||
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
} else {
|
} else {
|
||||||
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
}
|
}
|
||||||
@@ -440,6 +459,7 @@ impl Analyzer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
|
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
|
||||||
|
ExprValue::CompoundAssign { lvalue, op, rvalue } => self.analyze_compound_assign_expr(*lvalue, op, *rvalue, span),
|
||||||
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
|
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
|
||||||
ExprValue::UnaryOp { op, operand } => {
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
let operand_span = operand.span;
|
let operand_span = operand.span;
|
||||||
@@ -450,6 +470,7 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
let ty = match op {
|
let ty = match op {
|
||||||
crate::ast::types::UnaryOp::Not => SemaType::I1,
|
crate::ast::types::UnaryOp::Not => SemaType::I1,
|
||||||
|
crate::ast::types::UnaryOp::Add | crate::ast::types::UnaryOp::Sub if operand.ty == SemaType::I1 => SemaType::I32,
|
||||||
_ => operand.ty.clone(),
|
_ => operand.ty.clone(),
|
||||||
};
|
};
|
||||||
Some(HirExpr {
|
Some(HirExpr {
|
||||||
@@ -517,6 +538,50 @@ impl Analyzer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyze_compound_assign_expr(&mut self, lvalue: Expr, op: BinaryOp, rvalue: Expr, span: Span) -> Option<HirExpr> {
|
||||||
|
if !matches!(lvalue.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let lvalue_span = lvalue.span;
|
||||||
|
let lvalue = self.analyze_expr(lvalue)?;
|
||||||
|
if lvalue.ty.is_array_like() {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rvalue_span = rvalue.span;
|
||||||
|
let rvalue = self.analyze_expr(rvalue)?;
|
||||||
|
if !lvalue.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(lvalue.ty.clone()), lvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !rvalue.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(rvalue.ty.clone()), rvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let result_ty = match SemaType::get_elevate_result(&lvalue.ty, &rvalue.ty) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), lvalue_span);
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), rvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !self.type_matches(&lvalue.ty, &result_ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), result_ty), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(HirExpr {
|
||||||
|
ty: lvalue.ty.clone(),
|
||||||
|
value: HirExprValue::CompoundAssign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
op,
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
|
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
|
||||||
let lhs_span = lhs.span;
|
let lhs_span = lhs.span;
|
||||||
let rhs_span = rhs.span;
|
let rhs_span = rhs.span;
|
||||||
@@ -619,7 +684,7 @@ impl Analyzer {
|
|||||||
let ty = match array.ty.indexed_type() {
|
let ty = match array.ty.indexed_type() {
|
||||||
Some(ty) => ty,
|
Some(ty) => ty,
|
||||||
None => {
|
None => {
|
||||||
self.add_error(SemaError::NotSubscriptable, span);
|
self.add_error(SemaError::NotSubscriptable(array.ty.clone()), span);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -637,6 +702,9 @@ impl Analyzer {
|
|||||||
if expected == actual {
|
if expected == actual {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if matches!((expected, actual), (SemaType::I32, SemaType::I1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
|
if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
|
||||||
return expected_elem == actual_elem
|
return expected_elem == actual_elem
|
||||||
&& expected_dims.len() == actual_dims.len()
|
&& expected_dims.len() == actual_dims.len()
|
||||||
|
|||||||
@@ -127,4 +127,9 @@ pub enum HirExprValue {
|
|||||||
lvalue: Box<HirExpr>,
|
lvalue: Box<HirExpr>,
|
||||||
rvalue: Box<HirExpr>,
|
rvalue: Box<HirExpr>,
|
||||||
},
|
},
|
||||||
|
CompoundAssign {
|
||||||
|
lvalue: Box<HirExpr>,
|
||||||
|
op: BinaryOp,
|
||||||
|
rvalue: Box<HirExpr>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user