From 70d6c5e49bf15714260edd000a1e63e6c84b75ce Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Thu, 4 Jul 2024 17:46:31 -0300 Subject: [PATCH] Show integer literal range limits on IntegerOutOfBounds --- compiler/noirc_evaluator/src/errors.rs | 9 ++- compiler/noirc_evaluator/src/ssa/ir/types.rs | 56 ++++++++++++------- .../src/ssa/ssa_gen/context.rs | 3 +- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 0879056ad3..29e90f3a92 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -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")] diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 0b0ccb7e1a..56729a5cba 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -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 { 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, } } } @@ -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()); } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index fb79266768..652e96c537 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -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, }); }