diff --git a/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml new file mode 100644 index 00000000000..25bdea70129 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "overflowing_assignment" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr new file mode 100644 index 00000000000..78535a853a6 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + let x:u8 = -1; + let y:u8 = 300; + assert(x!=y); +} diff --git a/crates/noirc_frontend/src/hir/type_check/errors.rs b/crates/noirc_frontend/src/hir/type_check/errors.rs index 84bf511d314..25503228410 100644 --- a/crates/noirc_frontend/src/hir/type_check/errors.rs +++ b/crates/noirc_frontend/src/hir/type_check/errors.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; use thiserror::Error; @@ -29,6 +30,8 @@ pub enum Source { pub enum TypeCheckError { #[error("Operator {op:?} cannot be used in a {place:?}")] OpCannotBeUsed { op: HirBinaryOp, place: &'static str, span: Span }, + #[error("The literal `{expr:?}` cannot fit into `{ty}` which has range `{range}`")] + OverflowingAssignment { expr: FieldElement, ty: Type, range: String, span: Span }, #[error("Type {typ:?} cannot be used in a {place:?}")] TypeCannotBeUsed { typ: Type, place: &'static str, span: Span }, #[error("Expected type {expected_typ:?} is not the same as {expr_typ:?}")] @@ -170,7 +173,8 @@ impl From for Diagnostic { | TypeCheckError::IntegerTypeMismatch { span, .. } | TypeCheckError::FieldComparison { span, .. } | TypeCheckError::AmbiguousBitWidth { span, .. } - | TypeCheckError::IntegerAndFieldBinaryOperation { span } => { + | TypeCheckError::IntegerAndFieldBinaryOperation { span } + | TypeCheckError::OverflowingAssignment { span, .. } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/crates/noirc_frontend/src/hir/type_check/stmt.rs b/crates/noirc_frontend/src/hir/type_check/stmt.rs index b3a672cb05b..b279e454620 100644 --- a/crates/noirc_frontend/src/hir/type_check/stmt.rs +++ b/crates/noirc_frontend/src/hir/type_check/stmt.rs @@ -1,6 +1,6 @@ use noirc_errors::{Location, Span}; -use crate::hir_def::expr::HirIdent; +use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral}; use crate::hir_def::stmt::{ HirAssignStatement, HirConstrainStatement, HirLValue, HirLetStatement, HirPattern, HirStatement, }; @@ -259,9 +259,39 @@ impl<'interner> TypeChecker<'interner> { expr_span, } }); + if annotated_type.is_unsigned() { + self.lint_overflowing_uint(&rhs_expr, &annotated_type); + } annotated_type } else { expr_type } } + + /// Check if an assignment is overflowing with respect to `annotated_type` + /// in a declaration statement where `annotated_type` is an unsigned integer + fn lint_overflowing_uint(&mut self, rhs_expr: &ExprId, annotated_type: &Type) { + let expr = self.interner.expression(rhs_expr); + let span = self.interner.expr_span(rhs_expr); + match expr { + HirExpression::Literal(HirLiteral::Integer(value)) => { + let v = value.to_u128(); + if let Type::Integer(_, bit_count) = annotated_type { + let max = 1 << bit_count; + if v >= max { + self.errors.push(TypeCheckError::OverflowingAssignment { + expr: value, + ty: annotated_type.clone(), + range: format!("0..={}", max - 1), + span, + }); + }; + }; + } + HirExpression::Prefix(_) => self + .errors + .push(TypeCheckError::InvalidUnaryOp { kind: annotated_type.to_string(), span }), + _ => {} + } + } } diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index fea3c31bf98..b29b01e1ed2 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -447,6 +447,10 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Signed, _)) } + pub fn is_unsigned(&self) -> bool { + matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) + } + fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { // True if the given type is a NamedGeneric with the target_id let named_generic_id_matches_target = |typ: &Type| {