Skip to content

Commit

Permalink
Show integer literal range limits on IntegerOutOfBounds
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Jul 4, 2024
1 parent c0feadf commit 70d6c5e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 24 deletions.
9 changes: 7 additions & 2 deletions compiler/noirc_evaluator/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ pub enum RuntimeError {
IndexOutOfBounds { index: usize, array_size: usize, call_stack: CallStack },
#[error("Range constraint of {num_bits} bits is too large for the Field size")]
InvalidRangeConstraint { num_bits: u32, call_stack: CallStack },
#[error("{value} does not fit within the type bounds for {typ}")]
IntegerOutOfBounds { value: FieldElement, typ: NumericType, call_stack: CallStack },
#[error("The value `{value:?}` cannot fit into `{typ}` which has range `{range}`")]
IntegerOutOfBounds {
value: FieldElement,
typ: NumericType,
range: String,
call_stack: CallStack,
},
#[error("Expected array index to fit into a u64")]
TypeConversion { from: String, into: String, call_stack: CallStack },
#[error("{name:?} is not initialized")]
Expand Down
56 changes: 35 additions & 21 deletions compiler/noirc_evaluator/src/ssa/ir/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,37 @@ impl NumericType {
}
}

/// Returns true if the given Field value is within the numeric limits
/// for the current NumericType.
pub(crate) fn value_is_within_limits(self, field: FieldElement, negative: bool) -> bool {
/// Returns None if the given Field value is within the numeric limits
/// for the current NumericType. Otherwise returns a string describing
/// the limits, as a range.
pub(crate) fn value_is_outside_limits(
self,
field: FieldElement,
negative: bool,
) -> Option<String> {
match self {
NumericType::Unsigned { bit_size } => {
let max = 2u128.pow(bit_size) - 1;
if negative {
return false;
return Some(format!("0..={}", max));
}
if field <= max.into() {
None
} else {
Some(format!("0..={}", max))
}
let max = 2u128.pow(bit_size) - 1;
field <= max.into()
}
NumericType::Signed { bit_size } => {
let max =
if negative { 2u128.pow(bit_size - 1) } else { 2u128.pow(bit_size - 1) - 1 };
field <= max.into()
let min = 2u128.pow(bit_size - 1);
let max = 2u128.pow(bit_size - 1) - 1;
let target_max = if negative { min } else { max };
if field <= target_max.into() {
None
} else {
Some(format!("-{}..={}", min, max))
}
}
NumericType::NativeField => true,
NumericType::NativeField => None,
}
}
}
Expand Down Expand Up @@ -224,21 +238,21 @@ mod tests {
use super::*;

#[test]
fn test_u8_is_within_limits() {
fn test_u8_value_is_outside_limits() {
let u8 = NumericType::Unsigned { bit_size: 8 };
assert!(!u8.value_is_within_limits(FieldElement::from(1_i128), true));
assert!(u8.value_is_within_limits(FieldElement::from(0_i128), false));
assert!(u8.value_is_within_limits(FieldElement::from(255_i128), false));
assert!(!u8.value_is_within_limits(FieldElement::from(256_i128), false));
assert!(u8.value_is_outside_limits(FieldElement::from(1_i128), true).is_some());
assert!(u8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none());
assert!(u8.value_is_outside_limits(FieldElement::from(255_i128), false).is_none());
assert!(u8.value_is_outside_limits(FieldElement::from(256_i128), false).is_some());
}

#[test]
fn test_i8_is_within_limits() {
fn test_i8_value_is_outside_limits() {
let i8 = NumericType::Signed { bit_size: 8 };
assert!(!i8.value_is_within_limits(FieldElement::from(129_i128), true));
assert!(i8.value_is_within_limits(FieldElement::from(128_i128), true));
assert!(i8.value_is_within_limits(FieldElement::from(0_i128), false));
assert!(i8.value_is_within_limits(FieldElement::from(127_i128), false));
assert!(!i8.value_is_within_limits(FieldElement::from(128_i128), false));
assert!(i8.value_is_outside_limits(FieldElement::from(129_i128), true).is_some());
assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), true).is_none());
assert!(i8.value_is_outside_limits(FieldElement::from(0_i128), false).is_none());
assert!(i8.value_is_outside_limits(FieldElement::from(127_i128), false).is_none());
assert!(i8.value_is_outside_limits(FieldElement::from(128_i128), false).is_some());
}
}
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,12 @@ impl<'a> FunctionContext<'a> {
let value = value.into();

if let Type::Numeric(numeric_type) = typ {
if !numeric_type.value_is_within_limits(value, negative) {
if let Some(range) = numeric_type.value_is_outside_limits(value, negative) {
let call_stack = self.builder.get_call_stack();
return Err(RuntimeError::IntegerOutOfBounds {
value,
typ: numeric_type,
range: range,
call_stack,
});
}
Expand Down

0 comments on commit 70d6c5e

Please sign in to comment.