diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 213bdd3fefb..d1d71112de0 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -273,6 +273,10 @@ impl BinaryOpKind { pub fn is_bit_shift(&self) -> bool { matches!(self, BinaryOpKind::ShiftRight | BinaryOpKind::ShiftLeft) } + + pub fn is_modulo(&self) -> bool { + matches!(self, BinaryOpKind::Modulo) + } } #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, Clone)] diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 41e0e8e0079..b2b360dc81e 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -78,6 +78,8 @@ pub enum TypeCheckError { IntegerTypeMismatch { typ: Type, span: Span }, #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] IntegerAndFieldBinaryOperation { span: Span }, + #[error("Cannot do modulo on Fields, try casting to an integer first")] + FieldModulo { span: Span }, #[error("Fields cannot be compared, try casting to an integer first")] FieldComparison { span: Span }, #[error("The number of bits to use for this bitwise operation is ambiguous. Either the operand's type or return type should be specified")] @@ -195,7 +197,8 @@ impl From for Diagnostic { | TypeCheckError::FieldComparison { span, .. } | TypeCheckError::AmbiguousBitWidth { span, .. } | TypeCheckError::IntegerAndFieldBinaryOperation { span } - | TypeCheckError::OverflowingAssignment { span, .. } => { + | TypeCheckError::OverflowingAssignment { span, .. } + | TypeCheckError::FieldModulo { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 28264970620..f5f26b79f15 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -11,7 +11,7 @@ use crate::{ types::Type, }, node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId}, - Signedness, TypeBinding, TypeVariableKind, UnaryOp, + BinaryOpKind, Signedness, TypeBinding, TypeVariableKind, UnaryOp, }; use super::{errors::TypeCheckError, TypeChecker}; @@ -974,8 +974,8 @@ impl<'interner> TypeChecker<'interner> { if let TypeBinding::Bound(binding) = &*int.borrow() { return self.infix_operand_type_rules(binding, op, other, span); } - - if op.is_bitwise() && (other.is_bindable() || other.is_field()) { + if (op.is_modulo() || op.is_bitwise()) && (other.is_bindable() || other.is_field()) + { let other = other.follow_bindings(); let kind = op.kind; // This will be an error if these types later resolve to a Field, or stay @@ -983,7 +983,11 @@ impl<'interner> TypeChecker<'interner> { // finishes resolving so we can still allow cases like `let x: u8 = 1 << 2;`. self.push_delayed_type_check(Box::new(move || { if other.is_field() { - Err(TypeCheckError::InvalidBitwiseOperationOnField { span }) + if kind == BinaryOpKind::Modulo { + Err(TypeCheckError::FieldModulo { span }) + } else { + Err(TypeCheckError::InvalidBitwiseOperationOnField { span }) + } } else if other.is_bindable() { Err(TypeCheckError::AmbiguousBitWidth { span }) } else if kind.is_bit_shift() && other.is_signed() { @@ -1054,6 +1058,9 @@ impl<'interner> TypeChecker<'interner> { if op.is_bitwise() { return Err(TypeCheckError::InvalidBitwiseOperationOnField { span }); } + if op.is_modulo() { + return Err(TypeCheckError::FieldModulo { span }); + } Ok(FieldElement) } diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 15d4b12d30b..157e424bc95 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -68,6 +68,10 @@ impl HirBinaryOp { pub fn is_bit_shift(&self) -> bool { self.kind.is_bit_shift() } + + pub fn is_modulo(&self) -> bool { + self.kind.is_modulo() + } } #[derive(Debug, Clone)] diff --git a/tooling/nargo_cli/tests/compile_failure/field_modulo/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/field_modulo/Nargo.toml new file mode 100644 index 00000000000..f6bc2dd70e2 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/field_modulo/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "field_modulo" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr b/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr new file mode 100644 index 00000000000..b27ba0892b3 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/field_modulo/src/main.nr @@ -0,0 +1,4 @@ + +fn main(x: Field) -> pub Field { + x % 2 +} diff --git a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr b/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr index 201353393a6..78c91818345 100644 --- a/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr @@ -4,8 +4,7 @@ // Binary addition, multiplication, division, constant modulo // x = 3, y = 4, z = 5 fn main(x : Field, y : Field, z : Field) -> pub Field { - //constant modulo - assert(x % 2 == 1); + //cast assert(y as u1 == 0); let a = x + x; // 3 + 3 = 6