diff --git a/compiler/noirc_frontend/proptest-regressions/hir_def/types/arithmetic.txt b/compiler/noirc_frontend/proptest-regressions/hir_def/types/arithmetic.txt index 80f5c7f1ead..12f8ad82072 100644 --- a/compiler/noirc_frontend/proptest-regressions/hir_def/types/arithmetic.txt +++ b/compiler/noirc_frontend/proptest-regressions/hir_def/types/arithmetic.txt @@ -5,3 +5,4 @@ # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc fc27f4091dfa5f938973048209b5fcf22aefa1cfaffaaa3e349f30e9b1f93f49 # shrinks to infix_and_bindings = (((0: numeric bool) % (Numeric(Shared(RefCell { value: Unbound('2, Numeric(bool)) }): bool) + Numeric(Shared(RefCell { value: Unbound('0, Numeric(bool)) }): bool))), [('0, (0: numeric bool)), ('1, (0: numeric bool)), ('2, (0: numeric bool))]) +cc ebe53a1b6d6dda87e80761d4c56069bfc39f9f5e7e301e6a8f9e4fbbc33af85c # shrinks to infix_type_bindings = (((Numeric(Shared(RefCell { value: '2 }): bool) + (0: numeric bool)) - ((0: numeric bool) - (0: numeric bool))), bool, [('0, (0: numeric bool)), ('1, (0: numeric bool)), ('2, (0: numeric bool)), ('3, (0: numeric bool)), ('4, (0: numeric bool)), ('5, (0: numeric bool)), ('6, (0: numeric bool)), ('7, (0: numeric bool))]) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6a61009d259..2e6664d1e30 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -646,7 +646,7 @@ impl Elaborator<'_> { (lhs, rhs) => { let infix = Type::infix_expr(Box::new(lhs), op, Box::new(rhs)); Type::CheckedCast { from: Box::new(infix.clone()), to: Box::new(infix) } - .canonicalize() + .canonicalize(&TypeBindings::default()) } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 5f97421774e..f17b9688b0d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -645,10 +645,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { unreachable!("Expected associated type to be numeric"); }; let location = self.elaborator.interner.expr_location(&id); - match associated_type - .typ - .evaluate_to_field_element(&associated_type.typ.kind(), location) - { + match associated_type.typ.evaluate_to_field_element( + &associated_type.typ.kind(), + &TypeBindings::default(), + location, + ) { Ok(value) => self.evaluate_integer(value.into(), id), Err(err) => Err(InterpreterError::NonIntegerArrayLength { typ: associated_type.typ.clone(), @@ -665,7 +666,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { fn evaluate_numeric_generic(&self, value: Type, expected: &Type, id: ExprId) -> IResult { let location = self.elaborator.interner.id_location(id); let value = value - .evaluate_to_field_element(&Kind::Numeric(Box::new(expected.clone())), location) + .evaluate_to_field_element( + &Kind::Numeric(Box::new(expected.clone())), + &TypeBindings::default(), + location, + ) .map_err(|err| { let typ = value; let err = Some(Box::new(err)); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 429b41db6ab..c1dc5d50e25 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -986,7 +986,7 @@ impl TypeVariable { /// TypeBindings are the mutable insides of a TypeVariable. /// They are either bound to some type, or are unbound. -#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum TypeBinding { Bound(Type), Unbound(TypeVariableId, Kind), @@ -1099,7 +1099,7 @@ impl std::fmt::Display for Type { } Type::Quoted(quoted) => write!(f, "{quoted}"), Type::InfixExpr(lhs, op, rhs, _) => { - let this = self.canonicalize_checked(); + let this = self.canonicalize_checked(&TypeBindings::default()); // Prevent infinite recursion if this != *self { write!(f, "{this}") } else { write!(f, "({lhs} {op} {rhs})") } @@ -1135,6 +1135,15 @@ impl std::fmt::Display for TypeBinding { } } +impl std::fmt::Debug for TypeBinding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeBinding::Bound(typ) => typ.fmt(f), + TypeBinding::Unbound(id, _) => id.fmt(f), + } + } +} + impl std::fmt::Display for QuotedType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -1841,11 +1850,13 @@ impl Type { /// If this type is a Type::Constant (used in array lengths), or is bound /// to a Type::Constant, return the constant as a u32. pub fn evaluate_to_u32(&self, location: Location) -> Result { - self.evaluate_to_field_element(&Kind::u32(), location).map(|field_element| { - field_element - .try_to_u32() - .expect("ICE: size should have already been checked by evaluate_to_field_element") - }) + self.evaluate_to_field_element(&Kind::u32(), &TypeBindings::default(), location).map( + |field_element| { + field_element.try_to_u32().expect( + "ICE: size should have already been checked by evaluate_to_field_element", + ) + }, + ) } // TODO(https://github.com/noir-lang/noir/issues/6260): remove @@ -1853,10 +1864,11 @@ impl Type { pub(crate) fn evaluate_to_field_element( &self, kind: &Kind, + bindings: &TypeBindings, location: Location, ) -> Result { let run_simplifications = true; - self.evaluate_to_field_element_helper(kind, location, run_simplifications) + self.evaluate_to_field_element_helper(kind, location, bindings, run_simplifications) } /// evaluate_to_field_element with optional generic arithmetic simplifications @@ -1864,21 +1876,37 @@ impl Type { &self, kind: &Kind, location: Location, + bindings: &TypeBindings, run_simplifications: bool, ) -> Result { if let Some((binding, binding_kind)) = self.get_inner_type_variable() { - if let TypeBinding::Bound(binding) = &*binding.borrow() { - if kind.unifies(&binding_kind) { - return binding.evaluate_to_field_element_helper( - &binding_kind, - location, - run_simplifications, - ); + match &*binding.borrow() { + TypeBinding::Bound(binding) => { + if kind.unifies(&binding_kind) { + return binding.evaluate_to_field_element_helper( + &binding_kind, + location, + bindings, + run_simplifications, + ); + } + } + TypeBinding::Unbound(type_variable_id, kind) => { + if kind.unifies(&binding_kind) { + if let Some((_, _, typ)) = bindings.get(type_variable_id) { + return typ.evaluate_to_field_element_helper( + &binding_kind, + location, + bindings, + run_simplifications, + ); + } + } } } } - match self.canonicalize_with_simplifications(run_simplifications) { + match self.canonicalize_with_simplifications(bindings, run_simplifications) { Type::Constant(x, constant_kind) => { if kind.unifies(&constant_kind) { kind.ensure_value_fits(x, location) @@ -1896,11 +1924,13 @@ impl Type { let lhs_value = lhs.evaluate_to_field_element_helper( &infix_kind, location, + bindings, run_simplifications, )?; let rhs_value = rhs.evaluate_to_field_element_helper( &infix_kind, location, + bindings, run_simplifications, )?; op.function(lhs_value, rhs_value, &infix_kind, location) @@ -1913,14 +1943,17 @@ impl Type { } } Type::CheckedCast { from, to } => { - let to_value = to.evaluate_to_field_element(kind, location)?; + let to_value = to.evaluate_to_field_element(kind, bindings, location)?; // if both 'to' and 'from' evaluate to a constant, // return None unless they match let skip_simplifications = false; - if let Ok(from_value) = - from.evaluate_to_field_element_helper(kind, location, skip_simplifications) - { + if let Ok(from_value) = from.evaluate_to_field_element_helper( + kind, + location, + bindings, + skip_simplifications, + ) { if to_value == from_value { Ok(to_value) } else { @@ -2779,7 +2812,7 @@ impl std::fmt::Debug for Type { Kind::Numeric(typ) => write!(f, "Numeric({binding:?}: {typ:?})"), } } else { - write!(f, "{}", binding.borrow()) + write!(f, "{:?}", binding.borrow()) } } Type::DataType(s, args) => { diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index b36ec560fd1..e8415a84b1a 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use acvm::{AcirField, FieldElement}; use noirc_errors::Location; -use crate::{BinaryTypeOperator, Type}; +use crate::{BinaryTypeOperator, Type, TypeBindings}; impl Type { /// Try to canonicalize the representation of this type. @@ -15,44 +15,48 @@ impl Type { /// For example: /// - `canonicalize[((1 + N) + M) + 2] = (M + N) + 3` /// - `canonicalize[A + 2 * B + 3 - 2] = A + (B * 2) + 3 - 2` - pub fn canonicalize(&self) -> Type { + pub fn canonicalize(&self, bindings: &TypeBindings) -> Type { match self.follow_bindings() { Type::CheckedCast { from, to } => Type::CheckedCast { - from: Box::new(from.canonicalize_checked()), - to: Box::new(to.canonicalize_unchecked()), + from: Box::new(from.canonicalize_checked(bindings)), + to: Box::new(to.canonicalize_unchecked(bindings)), }, other => { let non_checked_cast = false; let run_simplifications = true; - other.canonicalize_helper(non_checked_cast, run_simplifications) + other.canonicalize_helper(bindings, non_checked_cast, run_simplifications) } } } - pub(crate) fn canonicalize_with_simplifications(&self, run_simplifications: bool) -> Type { - self.follow_bindings().canonicalize_helper(false, run_simplifications) + pub(crate) fn canonicalize_with_simplifications( + &self, + bindings: &TypeBindings, + run_simplifications: bool, + ) -> Type { + self.follow_bindings().canonicalize_helper(bindings, false, run_simplifications) } /// Only simplify constants and drop/skip any CheckedCast's - pub(crate) fn canonicalize_checked(&self) -> Type { - self.follow_bindings().canonicalize_checked_helper() + pub(crate) fn canonicalize_checked(&self, bindings: &TypeBindings) -> Type { + self.follow_bindings().canonicalize_checked_helper(bindings) } /// Only simplify constants and drop/skip any CheckedCast's - fn canonicalize_checked_helper(&self) -> Type { + fn canonicalize_checked_helper(&self, bindings: &TypeBindings) -> Type { let found_checked_cast = true; let skip_simplifications = false; // We expect `self` to have already called `follow_bindings` - self.canonicalize_helper(found_checked_cast, skip_simplifications) + self.canonicalize_helper(bindings, found_checked_cast, skip_simplifications) } /// Run all simplifications and drop/skip any CheckedCast's - fn canonicalize_unchecked(&self) -> Type { + fn canonicalize_unchecked(&self, bindings: &TypeBindings) -> Type { let found_checked_cast = true; let run_simplifications = true; // We expect `self` to have already called `follow_bindings` - self.canonicalize_helper(found_checked_cast, run_simplifications) + self.canonicalize_helper(bindings, found_checked_cast, run_simplifications) } /// If `found_checked_cast`, then drop additional CheckedCast's @@ -63,21 +67,30 @@ impl Type { /// /// Otherwise also attempt try_simplify_partial_constants, sort_commutative, /// and other simplifications - fn canonicalize_helper(&self, found_checked_cast: bool, run_simplifications: bool) -> Type { + fn canonicalize_helper( + &self, + bindings: &TypeBindings, + found_checked_cast: bool, + run_simplifications: bool, + ) -> Type { match self { Type::InfixExpr(lhs, op, rhs, inversion) => { let kind = lhs.infix_kind(rhs); let dummy_location = Location::dummy(); - // evaluate_to_field_element also calls canonicalize so if we just called - // `self.evaluate_to_field_element(..)` we'd get infinite recursion. - if let Ok(lhs_value) = - lhs.evaluate_to_field_element_helper(&kind, dummy_location, run_simplifications) - { - if let Ok(rhs_value) = rhs.evaluate_to_field_element_helper( + + let evaluate = |typ: &Type| { + typ.evaluate_to_field_element_helper( &kind, dummy_location, + bindings, run_simplifications, - ) { + ) + }; + + // evaluate_to_field_element also calls canonicalize so if we just called + // `self.evaluate_to_field_element(..)` we'd get infinite recursion. + if let Ok(lhs_value) = evaluate(lhs) { + if let Ok(rhs_value) = evaluate(rhs) { if let Ok(result) = op.function(lhs_value, rhs_value, &kind, dummy_location) { return Type::Constant(result, kind); @@ -85,42 +98,69 @@ impl Type { } } - let lhs = lhs.canonicalize_helper(found_checked_cast, run_simplifications); - let rhs = rhs.canonicalize_helper(found_checked_cast, run_simplifications); + let lhs = + lhs.canonicalize_helper(bindings, found_checked_cast, run_simplifications); + let rhs = + rhs.canonicalize_helper(bindings, found_checked_cast, run_simplifications); + + // See if this is `X * 1` or `X / 1` in which case we can simplify it to `X` + if matches!(op, BinaryTypeOperator::Multiplication | BinaryTypeOperator::Division) { + if let Ok(rhs_value) = evaluate(&rhs) { + if rhs_value.is_one() { + return lhs; + } + } + } + + // See if this is `X + 0` or `X - 0`, in which case we can simplify it to `X` + if matches!(op, BinaryTypeOperator::Addition | BinaryTypeOperator::Subtraction) { + if let Ok(rhs_value) = evaluate(&rhs) { + if rhs_value.is_zero() { + return lhs; + } + } + } if !run_simplifications { return Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs), *inversion); } - if let Some(result) = Self::try_simplify_non_constants_in_lhs(&lhs, *op, &rhs) { - return result.canonicalize_unchecked(); + if let Some(result) = + Self::try_simplify_non_constants_in_lhs(&lhs, *op, &rhs, bindings) + { + return result.canonicalize_unchecked(bindings); } - if let Some(result) = Self::try_simplify_non_constants_in_rhs(&lhs, *op, &rhs) { - return result.canonicalize_unchecked(); + if let Some(result) = + Self::try_simplify_non_constants_in_rhs(&lhs, *op, &rhs, bindings) + { + return result.canonicalize_unchecked(bindings); } // Try to simplify partially constant expressions in the form `(N op1 C1) op2 C2` // where C1 and C2 are constants that can be combined (e.g. N + 5 - 3 = N + 2) - if let Some(result) = Self::try_simplify_partial_constants(&lhs, *op, &rhs) { - return result.canonicalize_unchecked(); + if let Some(result) = + Self::try_simplify_partial_constants(&lhs, *op, &rhs, bindings) + { + return result.canonicalize_unchecked(bindings); } if op.is_commutative() { - return Self::sort_commutative(&lhs, *op, &rhs); + return Self::sort_commutative(&lhs, *op, &rhs, bindings); } Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs), *inversion) } Type::CheckedCast { from, to } => { let inner_found_checked_cast = true; - let to = to.canonicalize_helper(inner_found_checked_cast, run_simplifications); + let to = + to.canonicalize_helper(bindings, inner_found_checked_cast, run_simplifications); if found_checked_cast { return to; } - let from = from.canonicalize_checked(); + let from = from.canonicalize_checked(bindings); Type::CheckedCast { from: Box::new(from), to: Box::new(to) } } @@ -128,7 +168,12 @@ impl Type { } } - fn sort_commutative(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Type { + fn sort_commutative( + lhs: &Type, + op: BinaryTypeOperator, + rhs: &Type, + bindings: &TypeBindings, + ) -> Type { let mut queue = vec![lhs.clone(), rhs.clone()]; // Maps each term to the number of times that term was used. @@ -143,7 +188,7 @@ impl Type { // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. while let Some(item) = queue.pop() { - match item.canonicalize_unchecked() { + match item.canonicalize_unchecked(bindings) { Type::InfixExpr(lhs_inner, new_op, rhs_inner, _) if new_op == op => { queue.push(*lhs_inner); queue.push(*rhs_inner); @@ -201,12 +246,13 @@ impl Type { lhs: &Type, op: BinaryTypeOperator, rhs: &Type, + bindings: &TypeBindings, ) -> Option { match lhs { Type::CheckedCast { from, to } => { // Apply operation directly to `from` while attempting simplification to `to`. let from = Type::infix_expr(from.clone(), op, Box::new(rhs.clone())); - let to = Self::try_simplify_non_constants_in_lhs(to, op, rhs)?; + let to = Self::try_simplify_non_constants_in_lhs(to, op, rhs, bindings)?; Some(Type::CheckedCast { from: Box::new(from), to: Box::new(to) }) } Type::InfixExpr(l_lhs, l_op, l_rhs, _) => { @@ -214,7 +260,7 @@ impl Type { // `rhs` is expected to already be in canonical form. if l_op.approx_inverse() != Some(op) || *l_op == BinaryTypeOperator::Division - || l_rhs.canonicalize_unchecked() != *rhs + || l_rhs.canonicalize_unchecked(bindings) != *rhs { return None; } @@ -238,12 +284,13 @@ impl Type { lhs: &Type, op: BinaryTypeOperator, rhs: &Type, + bindings: &TypeBindings, ) -> Option { match rhs { Type::CheckedCast { from, to } => { // Apply operation directly to `from` while attempting simplification to `to`. let from = Type::infix_expr(Box::new(lhs.clone()), op, from.clone()); - let to = Self::try_simplify_non_constants_in_rhs(lhs, op, to)?; + let to = Self::try_simplify_non_constants_in_rhs(lhs, op, to, bindings)?; Some(Type::CheckedCast { from: Box::new(from), to: Box::new(to) }) } Type::InfixExpr(r_lhs, r_op, r_rhs, _) => { @@ -256,7 +303,7 @@ impl Type { // Note that this is exact, syntactic equality, not unification. // `lhs` is expected to already be in canonical form. - if r_op.inverse() != Some(op) || *lhs != r_rhs.canonicalize_unchecked() { + if r_op.inverse() != Some(op) || *lhs != r_rhs.canonicalize_unchecked(bindings) { return None; } @@ -276,16 +323,17 @@ impl Type { fn parse_partial_constant_expr( lhs: &Type, rhs: &Type, + bindings: &TypeBindings, ) -> Option<(Box, BinaryTypeOperator, FieldElement, FieldElement)> { let kind = lhs.infix_kind(rhs); let dummy_location = Location::dummy(); - let rhs = rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; + let rhs = rhs.evaluate_to_field_element(&kind, bindings, dummy_location).ok()?; let Type::InfixExpr(l_type, l_op, l_rhs, _) = lhs.follow_bindings() else { return None; }; - let l_rhs = l_rhs.evaluate_to_field_element(&kind, dummy_location).ok()?; + let l_rhs = l_rhs.evaluate_to_field_element(&kind, bindings, dummy_location).ok()?; Some((l_type, l_op, l_rhs, rhs)) } @@ -300,9 +348,11 @@ impl Type { lhs: &Type, mut op: BinaryTypeOperator, rhs: &Type, + bindings: &TypeBindings, ) -> Option { use BinaryTypeOperator::*; - let (l_type, l_op, l_const, r_const) = Type::parse_partial_constant_expr(lhs, rhs)?; + let (l_type, l_op, l_const, r_const) = + Type::parse_partial_constant_expr(lhs, rhs, bindings)?; match (l_op, op) { (Addition | Subtraction, Addition | Subtraction) => { @@ -342,7 +392,7 @@ mod tests { use acvm::{AcirField, FieldElement}; use crate::{ - NamedGeneric, + NamedGeneric, TypeBindings, hir_def::types::{BinaryTypeOperator, Kind, Type, TypeVariable, TypeVariableId}, }; @@ -370,7 +420,7 @@ mod tests { Box::new(Type::Constant(FieldElement::one(), Kind::u32())), ); - let canonicalized_typ = n_minus_one_plus_one.canonicalize(); + let canonicalized_typ = n_minus_one_plus_one.canonicalize(&TypeBindings::default()); assert_eq!(n, canonicalized_typ); @@ -383,7 +433,7 @@ mod tests { Box::new(checked_cast_n_minus_one), ); - let canonicalized_typ = one_plus_n_minus_one.canonicalize(); + let canonicalized_typ = one_plus_n_minus_one.canonicalize(&TypeBindings::default()); assert_eq!(n, canonicalized_typ); } @@ -404,16 +454,16 @@ mod tests { Type::infix_expr(Box::new(one), BinaryTypeOperator::Addition, Box::new(x_type.clone())); // canonicalize - let lhs = lhs.canonicalize(); - let rhs = rhs.canonicalize(); + let lhs = lhs.canonicalize(&TypeBindings::default()); + let rhs = rhs.canonicalize(&TypeBindings::default()); // bind vars let two = Type::Constant(FieldElement::from(2u128), field_element_kind.clone()); x_var.bind(two); // canonicalize (expect constant) - let lhs = lhs.canonicalize(); - let rhs = rhs.canonicalize(); + let lhs = lhs.canonicalize(&TypeBindings::default()); + let rhs = rhs.canonicalize(&TypeBindings::default()); // ensure we've canonicalized to constants assert!(matches!(lhs, Type::Constant(..))); @@ -438,6 +488,7 @@ mod proptests { use proptest::result::maybe_ok; use proptest::strategy; + use crate::TypeBindings; use crate::ast::IntegerBitSize; use crate::hir_def::types::{BinaryTypeOperator, Kind, Type, TypeVariable, TypeVariableId}; use crate::shared::Signedness; @@ -571,7 +622,7 @@ mod proptests { let (infix, typ, bindings) = infix_type_bindings; // canonicalize - let infix_canonicalized = infix.canonicalize(); + let infix_canonicalized = infix.canonicalize(&TypeBindings::default()); // bind vars for (var, binding) in bindings { @@ -579,8 +630,8 @@ mod proptests { } // attempt to canonicalize to a constant - let infix = infix.canonicalize(); - let infix_canonicalized = infix_canonicalized.canonicalize(); + let infix = infix.canonicalize(&TypeBindings::default()); + let infix_canonicalized = infix_canonicalized.canonicalize(&TypeBindings::default()); // ensure we've canonicalized to constants prop_assert!(matches!(infix, Type::Constant(..))); @@ -606,7 +657,7 @@ mod proptests { }; // canonicalize - let infix_canonicalized = infix.canonicalize(); + let infix_canonicalized = infix.canonicalize(&TypeBindings::default()); // bind vars for (var, binding) in bindings { @@ -614,8 +665,8 @@ mod proptests { } // attempt to canonicalize to a constant - let infix = infix.canonicalize(); - let infix_canonicalized = infix_canonicalized.canonicalize(); + let infix = infix.canonicalize(&TypeBindings::default()); + let infix_canonicalized = infix_canonicalized.canonicalize(&TypeBindings::default()); // ensure result kinds are the same as the original kind let kind = Kind::numeric(typ); @@ -625,8 +676,9 @@ mod proptests { // ensure the results are still wrapped in CheckedCast's match (&infix, &infix_canonicalized) { (Type::CheckedCast { from, to }, Type::CheckedCast { from: from_canonicalized, to: to_canonicalized }) => { + let bindings = TypeBindings::default(); // ensure from's are the same - prop_assert_eq!(from, from_canonicalized); + prop_assert_eq!(from.canonicalize(&bindings), from_canonicalized.canonicalize(&bindings)); // ensure to's have the same kinds prop_assert_eq!(to.kind(), kind.clone()); diff --git a/compiler/noirc_frontend/src/hir_def/types/unification.rs b/compiler/noirc_frontend/src/hir_def/types/unification.rs index a025ea4b706..f2b8d3ecc04 100644 --- a/compiler/noirc_frontend/src/hir_def/types/unification.rs +++ b/compiler/noirc_frontend/src/hir_def/types/unification.rs @@ -110,12 +110,12 @@ impl Type { let rhs = other.follow_bindings_shallow(); let lhs = match lhs.as_ref() { - Type::InfixExpr(..) => Cow::Owned(self.canonicalize()), + Type::InfixExpr(..) => Cow::Owned(self.canonicalize(bindings)), other => Cow::Borrowed(other), }; let rhs = match rhs.as_ref() { - Type::InfixExpr(..) => Cow::Owned(other.canonicalize()), + Type::InfixExpr(..) => Cow::Owned(other.canonicalize(bindings)), other => Cow::Borrowed(other), }; @@ -272,7 +272,9 @@ impl Type { (Constant(value, kind), other) | (other, Constant(value, kind)) => { let dummy_location = Location::dummy(); - if let Ok(other_value) = other.evaluate_to_field_element(kind, dummy_location) { + if let Ok(other_value) = + other.evaluate_to_field_element(kind, bindings, dummy_location) + { if *value == other_value && kind.unifies(&other.kind()) { Ok(()) } else { @@ -472,7 +474,9 @@ impl Type { if let Some(lhs_op_inverse) = lhs_op.approx_inverse() { let kind = lhs_lhs.infix_kind(lhs_rhs); let dummy_location = Location::dummy(); - if let Ok(value) = lhs_rhs.evaluate_to_field_element(&kind, dummy_location) { + if let Ok(value) = + lhs_rhs.evaluate_to_field_element(&kind, bindings, dummy_location) + { let lhs_rhs = Box::new(Type::Constant(value, kind)); let new_rhs = Type::inverted_infix_expr(Box::new(other.clone()), lhs_op_inverse, lhs_rhs); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 387cf2e904a..f69a5630429 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1122,10 +1122,11 @@ impl<'interner> Monomorphizer<'interner> { let Kind::Numeric(numeric_type) = associated_type.typ.kind() else { unreachable!("Expected associated type to be numeric"); }; - match associated_type - .typ - .evaluate_to_field_element(&associated_type.typ.kind(), location) - { + match associated_type.typ.evaluate_to_field_element( + &associated_type.typ.kind(), + &TypeBindings::default(), + location, + ) { Ok(value) => { let typ = Self::convert_type(&numeric_type, location)?; let value = SignedField::positive(value); @@ -1151,13 +1152,13 @@ impl<'interner> Monomorphizer<'interner> { location: Location, ) -> Result { let expected_kind = Kind::Numeric(Box::new(expected_type.clone())); - let value = value.evaluate_to_field_element(&expected_kind, location).map_err(|err| { - MonomorphizationError::UnknownArrayLength { + let value = value + .evaluate_to_field_element(&expected_kind, &TypeBindings::default(), location) + .map_err(|err| MonomorphizationError::UnknownArrayLength { length: value.follow_bindings(), err, location, - } - })?; + })?; let expr_kind = Kind::Numeric(Box::new(expr_type.clone())); if !expected_kind.unifies(&expr_kind) { @@ -1564,11 +1565,15 @@ impl<'interner> Monomorphizer<'interner> { location, }); } - let to_value = to.evaluate_to_field_element(&to.kind(), location); + let to_value = to.evaluate_to_field_element(&to.kind(), &TypeBindings::default(), location); if to_value.is_ok() { let skip_simplifications = false; - let from_value = - from.evaluate_to_field_element_helper(&to.kind(), location, skip_simplifications); + let from_value = from.evaluate_to_field_element_helper( + &to.kind(), + location, + &TypeBindings::default(), + skip_simplifications, + ); if from_value.is_err() || from_value.unwrap() != to_value.clone().unwrap() { return Err(MonomorphizationError::CheckedCastFailed { actual: HirType::Constant(to_value.unwrap(), to.kind()), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index c13fb52574f..75f14731ea8 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1648,29 +1648,11 @@ impl NodeInterner { let mut fresh_bindings = type_bindings.clone(); - let mut check_trait_generics = - |impl_generics: &[Type], impl_associated_types: &[NamedType]| { - let generics_unify = trait_generics.iter().zip(impl_generics).all( - |(trait_generic, impl_generic)| { - let impl_generic = - impl_generic.force_substitute(&instantiation_bindings); - trait_generic.try_unify(&impl_generic, &mut fresh_bindings).is_ok() - }, - ); - - let associated_types_unify = trait_associated_types - .iter() - .zip(impl_associated_types) - .all(|(trait_generic, impl_generic)| { - let impl_generic2 = - impl_generic.typ.force_substitute(&instantiation_bindings); - trait_generic.typ.try_unify(&impl_generic2, &mut fresh_bindings).is_ok() - }); - - generics_unify && associated_types_unify - }; + if object_type.try_unify(&existing_object_type, &mut fresh_bindings).is_err() { + continue; + } - let trait_generics = match impl_kind { + let impl_trait_generics = match impl_kind { TraitImplKind::Normal(id) => { let shared_impl = self.get_trait_implementation(*id); let shared_impl = shared_impl.borrow(); @@ -1681,44 +1663,60 @@ impl NodeInterner { TraitImplKind::Assumed { trait_generics, .. } => trait_generics.clone(), }; - if !check_trait_generics(&trait_generics.ordered, &trait_generics.named) { + let generics_unify = trait_generics.iter().zip(&impl_trait_generics.ordered).all( + |(trait_generic, impl_generic)| { + let impl_generic = impl_generic.force_substitute(&instantiation_bindings); + trait_generic.try_unify(&impl_generic, &mut fresh_bindings).is_ok() + }, + ); + + if !generics_unify { continue; } - if object_type.try_unify(&existing_object_type, &mut fresh_bindings).is_ok() { - if let TraitImplKind::Normal(impl_id) = impl_kind { - let trait_impl = self.get_trait_implementation(*impl_id); - let trait_impl = trait_impl.borrow(); - - if let Err(error) = self.validate_where_clause( - &trait_impl.where_clause, - &mut fresh_bindings, - &instantiation_bindings, - recursion_limit, - ) { - // Only keep the first errors we get from a failing where clause - if where_clause_error.is_none() { - where_clause_error = Some(error); - } - continue; + if let TraitImplKind::Normal(impl_id) = impl_kind { + let trait_impl = self.get_trait_implementation(*impl_id); + let trait_impl = trait_impl.borrow(); + + if let Err(error) = self.validate_where_clause( + &trait_impl.where_clause, + &mut fresh_bindings, + &instantiation_bindings, + recursion_limit, + ) { + // Only keep the first errors we get from a failing where clause + if where_clause_error.is_none() { + where_clause_error = Some(error); } + continue; } + } - let constraint = TraitConstraint { - typ: existing_object_type, - trait_bound: ResolvedTraitBound { - trait_id, - trait_generics, - location: Location::dummy(), - }, - }; - matching_impls.push(( - impl_kind.clone(), - fresh_bindings, - instantiation_bindings, - constraint, - )); + let associated_types_unify = trait_associated_types + .iter() + .zip(&impl_trait_generics.named) + .all(|(trait_generic, impl_generic)| { + let impl_generic2 = impl_generic.typ.force_substitute(&instantiation_bindings); + trait_generic.typ.try_unify(&impl_generic2, &mut fresh_bindings).is_ok() + }); + if !associated_types_unify { + continue; } + + let constraint = TraitConstraint { + typ: existing_object_type, + trait_bound: ResolvedTraitBound { + trait_id, + trait_generics: impl_trait_generics, + location: Location::dummy(), + }, + }; + matching_impls.push(( + impl_kind.clone(), + fresh_bindings, + instantiation_bindings, + constraint, + )); } if matching_impls.len() == 1 { diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index 87ec219c659..9acdc156ca2 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1858,3 +1858,166 @@ fn suggests_importing_trait_via_module_reexport() { "#; check_errors!(src); } + +#[named] +#[test] +fn associated_constant_sum_of_other_constants() { + let src = r#" + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; Self::N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + struct Gen {} + + impl Deserialize for Gen + where + T: Deserialize, + { + let N: u32 = ::N + ::N; + + fn deserialize(_: [Field; Self::N]) {} + } + + fn main() { + let f = as Deserialize>::deserialize; + f([0; 2]); + } + "#; + assert_no_errors!(src); +} + +#[named] +#[test] +fn regression_9245_small_code() { + let src = r#" + pub trait From2 {} + + impl From2 for T {} + + pub trait Into2 {} + + impl From2 for Field {} + + impl, U> Into2 for U {} + + fn foo>() {} + + fn main() { + foo::(); + } + "#; + assert_no_errors!(src); +} + +#[named] +#[test] +fn associated_constant_sum_of_other_constants_2() { + let src = r#" + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N + M; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X + 1]); + } + + fn main() {} + "#; + assert_no_errors!(src); +} + +#[named] +#[test] +fn associated_constant_sum_of_other_constants_3() { + let src = r#" + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N + M - 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X]); + } + + fn main() {} + "#; + assert_no_errors!(src); +} + +#[named] +#[test] +fn associated_constant_mul_of_other_constants() { + let src = r#" + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N * M; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X]); + } + + fn main() {} + "#; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_empty/regression_9245/Nargo.toml b/test_programs/compile_success_empty/regression_9245/Nargo.toml new file mode 100644 index 00000000000..84f1eb47f78 --- /dev/null +++ b/test_programs/compile_success_empty/regression_9245/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_9245" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_9245/src/main.nr b/test_programs/compile_success_empty/regression_9245/src/main.nr new file mode 100644 index 00000000000..70eeb727ce1 --- /dev/null +++ b/test_programs/compile_success_empty/regression_9245/src/main.nr @@ -0,0 +1,39 @@ +pub trait Deserialize { + let N: u32; + + fn deserialize(fields: [Field; N]) -> Self; +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(fields: [Field; Self::N]) -> Self { + fields[0] + } +} + +impl Deserialize for [Field; M] { + let N: u32 = ::N * M; + + fn deserialize(fields: [Field; Self::N]) -> Self { + fields + } +} + +pub struct Log { + pub fields: [Field; O], +} + +impl Deserialize for Log { + let N: u32 = <[Field; O] as Deserialize>::N; + + fn deserialize(fields: [Field; Self::N]) -> Self { + Self { fields: <[Field; O] as Deserialize>::deserialize(fields) } + } +} + +fn main() { + let serialized = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let log = Log::<10>::deserialize(serialized); + assert_eq(serialized, log.fields); +} diff --git a/test_programs/compile_success_empty/regression_9248/Nargo.toml b/test_programs/compile_success_empty/regression_9248/Nargo.toml new file mode 100644 index 00000000000..9ce400a0302 --- /dev/null +++ b/test_programs/compile_success_empty/regression_9248/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_9248" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_9248/src/main.nr b/test_programs/compile_success_empty/regression_9248/src/main.nr new file mode 100644 index 00000000000..ca2c1b01dff --- /dev/null +++ b/test_programs/compile_success_empty/regression_9248/src/main.nr @@ -0,0 +1,49 @@ +pub trait Deserialize { + let N: u32; + + fn deserialize(fields: [Field; N]) -> Self; +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(fields: [Field; Self::N]) -> Self { + fields[0] + } +} + +impl Deserialize for [T; M] +where + T: Deserialize, +{ + let N: u32 = ::N * M; + + fn deserialize(fields: [Field; Self::N]) -> Self { + let mut result: [T; M] = std::mem::zeroed(); + + for i in 0..M { + let mut element_fields = [0; ::N]; + for j in 0..::N { + element_fields[j] = fields[i * ::N + j]; + } + result[i] = T::deserialize(element_fields); + } + + result + } +} + +pub struct Log { + pub fields: [Field; M], +} + +impl Deserialize for Log { + let N: u32 = M; + + fn deserialize(serialized: [Field; Self::N]) -> Self { + let fields = <[Field; M] as Deserialize>::deserialize(serialized); + Self { fields } + } +} + +fn main() {} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/Nargo.toml new file mode 100644 index 00000000000..bd8f81d78e1 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_associated_constant_mul_of_other_constants" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src/main.nr new file mode 100644 index 00000000000..17541aba9f8 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src/main.nr @@ -0,0 +1,29 @@ + + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N * M; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X]); + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src_hash.txt new file mode 100644 index 00000000000..02432ad7421 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/src_hash.txt @@ -0,0 +1 @@ +7115975134939714584 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/Nargo.toml new file mode 100644 index 00000000000..27837cb5f6d --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_associated_constant_sum_of_other_constants" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src/main.nr new file mode 100644 index 00000000000..11aa3020485 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src/main.nr @@ -0,0 +1,29 @@ + + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; Self::N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + struct Gen {} + + impl Deserialize for Gen + where + T: Deserialize, + { + let N: u32 = ::N + ::N; + + fn deserialize(_: [Field; Self::N]) {} + } + + fn main() { + let f = as Deserialize>::deserialize; + f([0; 2]); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src_hash.txt new file mode 100644 index 00000000000..c608ec9acb9 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/src_hash.txt @@ -0,0 +1 @@ +13066449735956409492 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/Nargo.toml new file mode 100644 index 00000000000..98dcfb5a41a --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src/main.nr new file mode 100644 index 00000000000..03bc6b2657b --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src/main.nr @@ -0,0 +1,29 @@ + + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N + M; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X + 1]); + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src_hash.txt new file mode 100644 index 00000000000..3a92ffd25d4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/src_hash.txt @@ -0,0 +1 @@ +15087560913255529299 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/Nargo.toml new file mode 100644 index 00000000000..771161bbfe4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src/main.nr new file mode 100644 index 00000000000..3a8610ba0eb --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src/main.nr @@ -0,0 +1,29 @@ + + pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); + } + + impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + impl Deserialize for [T; M] + where + T: Deserialize, + { + let N: u32 = ::N + M - 1; + + fn deserialize(_: [Field; Self::N]) {} + } + + pub fn foo() { + let f = <[Field; X] as Deserialize>::deserialize; + let _ = f([0; X]); + } + + fn main() {} + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src_hash.txt new file mode 100644 index 00000000000..3bb3e03cdbf --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/src_hash.txt @@ -0,0 +1 @@ +2895288838149253593 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/Nargo.toml new file mode 100644 index 00000000000..530092561d9 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_traits_regression_9245_small_code" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src/main.nr new file mode 100644 index 00000000000..9e1cbc375cc --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src/main.nr @@ -0,0 +1,17 @@ + + pub trait From2 {} + + impl From2 for T {} + + pub trait Into2 {} + + impl From2 for Field {} + + impl, U> Into2 for U {} + + fn foo>() {} + + fn main() { + foo::(); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src_hash.txt new file mode 100644 index 00000000000..29f455584ba --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/src_hash.txt @@ -0,0 +1 @@ +14724163626332314258 \ No newline at end of file diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__expanded.snap new file mode 100644 index 00000000000..600ed964bac --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__expanded.snap @@ -0,0 +1,44 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(fields: [Field; N]) -> Self; +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(fields: [Self; 1]) -> Self { + fields[0_u32] + } +} + +impl Deserialize for [Field; M] { + let N: u32 = M; + + fn deserialize(fields: Self) -> Self { + fields + } +} + +pub struct Log { + pub fields: [Field; O], +} + +impl Deserialize for Log { + let N: u32 = O; + + fn deserialize(fields: [Field; O]) -> Self { + Self { fields: <[Field; O] as Deserialize>::deserialize(fields) } + } +} + +fn main() { + let serialized: [Field; 10] = + [0_Field, 1_Field, 2_Field, 3_Field, 4_Field, 5_Field, 6_Field, 7_Field, 8_Field, 9_Field]; + let log: Log<10> = Log::<10>::deserialize(serialized); + assert(serialized == log.fields); +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9245/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__expanded.snap new file mode 100644 index 00000000000..beb2d3b55b6 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__expanded.snap @@ -0,0 +1,52 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(fields: [Field; N]) -> Self; +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(fields: [Self; 1]) -> Self { + fields[0_u32] + } +} + +impl Deserialize for [T; M] +where + T: Deserialize, +{ + let N: u32 = M * ::N; + + fn deserialize(fields: [Field; M * ::N]) -> Self { + let mut result: Self = std::mem::zeroed(); + for i in 0_u32..M { + let mut element_fields: [Field; ::N] = + [0_Field; ::N]; + for j in 0_u32..T::N { + element_fields[j] = fields[(i * T::N) + j]; + } + result[i] = T::deserialize(element_fields); + } + result + } +} + +pub struct Log { + pub fields: [Field; M], +} + +impl Deserialize for Log { + let N: u32 = M; + + fn deserialize(serialized: [Field; M]) -> Self { + let fields: [Field; M] = <[Field; M] as Deserialize>::deserialize(serialized); + Self { fields: fields } + } +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__force_brillig_false_inliner_0.snap b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__force_brillig_false_inliner_0.snap new file mode 100644 index 00000000000..7de940827c8 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_empty/regression_9248/execute__tests__force_brillig_false_inliner_0.snap @@ -0,0 +1,26 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: artifact +--- +{ + "noir_version": "[noir_version]", + "hash": "[hash]", + "abi": { + "parameters": [], + "return_type": null, + "error_types": {} + }, + "bytecode": [ + "func 0", + "current witness index : _0", + "private parameters indices : []", + "public parameters indices : []", + "return value indices : []" + ], + "debug_symbols": "XY5BCsQwCEXv4rqLWfcqw1BsaosgJtikMITefWyYQOlK/3/6tcJCc9km1jXuML4rzMYivE0SA2aO6m49B+hyykbkFty4byU00gyjFpEBDpTShvaE2mpGc/oagHTx6oErC13d+XGBge158UBjnIX+ci0abjR/Uyf942Qx0FKMrqTGPPsH", + "file_map": {}, + "names": [ + "main" + ], + "brillig_names": [] +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/execute__tests__expanded.snap new file mode 100644 index 00000000000..1f7e10ae69c --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_mul_of_other_constants/execute__tests__expanded.snap @@ -0,0 +1,31 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Self; 1]) {} +} + +impl Deserialize for [T; M] +where + T: Deserialize, +{ + let N: u32 = M * ::N; + + fn deserialize(_: [Field; M * ::N]) {} +} + +pub fn foo() { + let f: fn([Field; X]) = <[Field; X] as Deserialize>::deserialize; + let _: () = f([0_Field; X]); +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/execute__tests__expanded.snap new file mode 100644 index 00000000000..313f29fefc0 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants/execute__tests__expanded.snap @@ -0,0 +1,31 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Self; 1]) {} +} + +struct Gen {} + +impl Deserialize for Gen +where + T: Deserialize, +{ + let N: u32 = ::N + ::N; + + fn deserialize(_: [Field; ::N + ::N]) {} +} + +fn main() { + let f: fn([Field; 2]) = as Deserialize>::deserialize; + f([0_Field; 2]); +} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/execute__tests__expanded.snap new file mode 100644 index 00000000000..80631c43102 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_2/execute__tests__expanded.snap @@ -0,0 +1,31 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Self; 1]) {} +} + +impl Deserialize for [T; M] +where + T: Deserialize, +{ + let N: u32 = M + ::N; + + fn deserialize(_: [Field; M + ::N]) {} +} + +pub fn foo() { + let f: fn([Field; X + 1]) = <[Field; X] as Deserialize>::deserialize; + let _: () = f([0_Field; X + 1]); +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/execute__tests__expanded.snap new file mode 100644 index 00000000000..41fb9dd2b27 --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_associated_constant_sum_of_other_constants_3/execute__tests__expanded.snap @@ -0,0 +1,31 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait Deserialize { + let N: u32; + + fn deserialize(_: [Field; N]); +} + +impl Deserialize for Field { + let N: u32 = 1; + + fn deserialize(_: [Self; 1]) {} +} + +impl Deserialize for [T; M] +where + T: Deserialize, +{ + let N: u32 = (M + ::N) - 1; + + fn deserialize(_: [Field; (M + ::N) - 1]) {} +} + +pub fn foo() { + let f: fn([Field; X]) = <[Field; X] as Deserialize>::deserialize; + let _: () = f([0_Field; X]); +} + +fn main() {} diff --git a/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/execute__tests__expanded.snap b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/execute__tests__expanded.snap new file mode 100644 index 00000000000..f9998b56c2a --- /dev/null +++ b/tooling/nargo_cli/tests/snapshots/compile_success_no_bug/noirc_frontend_tests_traits_regression_9245_small_code/execute__tests__expanded.snap @@ -0,0 +1,25 @@ +--- +source: tooling/nargo_cli/tests/execute.rs +expression: expanded_code +--- +pub trait From2 {} + +impl From2 for T {} + +impl From2 for Field {} + +pub trait Into2 {} + +impl Into2 for U +where + T: From2, +{} + +fn foo() +where + T: Into2, +{} + +fn main() { + foo::(); +}