core: Add compile-time error and runtime assertion for negative shifts
This commit is contained in:
parent
2cd1beaeda
commit
5d28e4e780
|
@ -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(),
|
||||
|
|
|
@ -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)?;
|
||||
|
|
Loading…
Reference in New Issue