core: Add compile-time error and runtime assertion for negative shifts

This commit is contained in:
David Mak 2023-11-02 16:04:55 +08:00
parent 2cd1beaeda
commit 5d28e4e780
2 changed files with 58 additions and 11 deletions

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, convert::TryInto, cmp::Ordering, iter::once, iter::zip};
use std::{collections::HashMap, convert::TryInto, iter::once, iter::zip};
use crate::{
codegen::{
@ -299,19 +299,49 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
(Operator::BitXor, _) => self.builder.build_xor(lhs, rhs, "xor").into(),
(Operator::BitAnd, _) => self.builder.build_and(lhs, rhs, "and").into(),
(Operator::LShift, _) => {
let rhs = match lhs.get_type().get_bit_width().cmp(&rhs.get_type().get_bit_width()) {
Ordering::Less => self.builder.build_int_truncate(rhs, lhs.get_type(), ""),
Ordering::Equal => rhs,
Ordering::Greater => self.builder.build_int_z_extend(rhs, lhs.get_type(), ""),
let rhs = if lhs.get_type().get_bit_width() > rhs.get_type().get_bit_width() {
if signed {
self.builder.build_int_s_extend(rhs, lhs.get_type(), "")
} else {
self.builder.build_int_z_extend(rhs, lhs.get_type(), "")
}
} else {
self.builder.build_int_truncate_or_bit_cast(rhs, lhs.get_type(), "")
};
let rhs_gez = self.builder.build_int_compare(IntPredicate::SGE, rhs, lhs.get_type().const_zero(), "");
self.make_assert(
generator,
rhs_gez,
"ValueError",
"negative shift count",
[None, None, None],
self.current_loc
);
self.builder.build_left_shift(lhs, rhs, "lshift").into()
},
(Operator::RShift, _) => {
let rhs = match lhs.get_type().get_bit_width().cmp(&rhs.get_type().get_bit_width()) {
Ordering::Less => self.builder.build_int_truncate(rhs, lhs.get_type(), ""),
Ordering::Equal => rhs,
Ordering::Greater => self.builder.build_int_z_extend(rhs, lhs.get_type(), ""),
let rhs = if lhs.get_type().get_bit_width() > rhs.get_type().get_bit_width() {
if signed {
self.builder.build_int_s_extend(rhs, lhs.get_type(), "")
} else {
self.builder.build_int_z_extend(rhs, lhs.get_type(), "")
}
} else {
self.builder.build_int_truncate_or_bit_cast(rhs, lhs.get_type(), "")
};
let rhs_gez = self.builder.build_int_compare(IntPredicate::SGE, rhs, lhs.get_type().const_zero(), "");
self.make_assert(
generator,
rhs_gez,
"ValueError",
"negative shift count",
[None, None, None],
self.current_loc
);
self.builder.build_right_shift(lhs, rhs, true, "rshift").into()
},
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),

View File

@ -2,8 +2,9 @@ use crate::typecheck::typedef::TypeEnum;
use super::type_inferencer::Inferencer;
use super::typedef::Type;
use nac3parser::ast::{self, Expr, ExprKind, Stmt, StmtKind, StrRef};
use nac3parser::ast::{self, Constant, Expr, ExprKind, Stmt, StmtKind, StrRef};
use std::{collections::HashSet, iter::once};
use nac3parser::ast::Operator::{LShift, RShift};
impl<'a> Inferencer<'a> {
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), String> {
@ -107,11 +108,27 @@ impl<'a> Inferencer<'a> {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
}
ExprKind::BinOp { left, right, .. } => {
ExprKind::BinOp { left, op, right } => {
self.check_expr(left, defined_identifiers)?;
self.check_expr(right, defined_identifiers)?;
self.should_have_value(left)?;
self.should_have_value(right)?;
if *op == LShift || *op == RShift {
if let ExprKind::Constant { value, .. } = &right.node {
let rhs_val = match value {
Constant::Int(v) => v,
_ => unreachable!(),
};
if *rhs_val < 0 {
return Err(format!(
"shift count is negative at {}",
right.location
));
}
}
}
}
ExprKind::UnaryOp { operand, .. } => {
self.check_expr(operand, defined_identifiers)?;