From 8024bd44b69a8864074576a8a6a4b8d2128a1709 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 15 Jul 2024 16:02:15 -0500 Subject: [PATCH 01/12] Add Type::InfixExpr --- compiler/noirc_driver/src/abi_gen.rs | 1 + .../noirc_frontend/src/elaborator/types.rs | 12 ++++---- .../src/hir/comptime/hir_to_display_ast.rs | 7 +++++ .../src/hir/resolution/resolver.rs | 4 +++ compiler/noirc_frontend/src/hir_def/types.rs | 30 +++++++++++++++++++ .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/node_interner.rs | 1 + 7 files changed, 49 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index d0b33945f40..706321454dd 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -100,6 +100,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { Type::Error | Type::Unit | Type::Constant(_) + | Type::InfixExpr(..) | Type::TraitAsType(..) | Type::TypeVariable(_, _) | Type::NamedGeneric(..) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 35ff421ed32..16e864f24f6 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -384,7 +384,6 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, _) => { - let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); let lhs = self.convert_expression_type(*lhs); let rhs = self.convert_expression_type(*rhs); @@ -392,12 +391,7 @@ impl<'context> Elaborator<'context> { (Type::Constant(lhs), Type::Constant(rhs)) => { Type::Constant(op.function()(lhs, rhs)) } - (lhs, _) => { - let span = - if !matches!(lhs, Type::Constant(_)) { lhs_span } else { rhs_span }; - self.push_err(ResolverError::InvalidArrayLengthExpr { span }); - Type::Constant(0) - } + (lhs, rhs) => Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)), } } } @@ -1598,6 +1592,10 @@ impl<'context> Elaborator<'context> { } Self::find_numeric_generics_in_type(fields, found); } + Type::InfixExpr(lhs, _op, rhs) => { + Self::find_numeric_generics_in_type(lhs, found); + Self::find_numeric_generics_in_type(rhs, found); + }, } } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index 22763c9cb64..efe6d0b2ac4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -352,6 +352,13 @@ impl Type { Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), Type::Quoted(quoted_type) => UnresolvedTypeData::Quoted(*quoted_type), Type::Error => UnresolvedTypeData::Error, + Type::InfixExpr(lhs, op, rhs) => { + let lhs = Box::new(lhs.to_type_expression()); + let rhs = Box::new(rhs.to_type_expression()); + let span = Span::default(); + let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, span); + UnresolvedTypeData::Expression(expr) + }, }; UnresolvedType { typ, span: None } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 55cb2145ba8..0f1d5b73584 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1263,6 +1263,10 @@ impl<'a> Resolver<'a> { } Self::find_numeric_generics_in_type(fields, found); } + Type::InfixExpr(lhs, _, rhs) => { + Self::find_numeric_generics_in_type(lhs, found); + Self::find_numeric_generics_in_type(rhs, found); + }, } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 91a374a0787..83a017d0d21 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -107,6 +107,8 @@ pub enum Type { /// The type of quoted code in macros. This is always a comptime-only type Quoted(QuotedType), + InfixExpr(Box, BinaryTypeOperator, Box), + /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with /// an invalid type would otherwise issue a new error each time it is called @@ -641,6 +643,7 @@ impl std::fmt::Display for Type { write!(f, "&mut {element}") } Type::Quoted(quoted) => write!(f, "{}", quoted), + Type::InfixExpr(lhs, op, rhs) => write!(f, "({lhs} {op} {rhs})"), } } } @@ -834,6 +837,8 @@ impl Type { elements.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) } + Type::InfixExpr(lhs, _op, rhs) => + lhs.contains_numeric_typevar(target_id) || rhs.contains_numeric_typevar(target_id), } } @@ -913,6 +918,10 @@ impl Type { elements.find_numeric_type_vars(found_names); named_generic_is_numeric(length, found_names); } + Type::InfixExpr(lhs, _op, rhs) => { + lhs.find_numeric_type_vars(found_names); + rhs.find_numeric_type_vars(found_names); + } } } @@ -942,6 +951,7 @@ impl Type { | Type::Forall(_, _) | Type::Quoted(_) | Type::Slice(_) + | Type::InfixExpr(_, _, _) | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { @@ -979,6 +989,7 @@ impl Type { | Type::Constant(_) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) + | Type::InfixExpr(..) | Type::Error => true, Type::FmtString(_, _) @@ -1024,6 +1035,7 @@ impl Type { | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::FmtString(_, _) + | Type::InfixExpr(..) | Type::Error => true, // Quoted objects only exist at compile-time where the only execution @@ -1158,6 +1170,7 @@ impl Type { | Type::Constant(_) | Type::Quoted(_) | Type::Slice(_) + | Type::InfixExpr(..) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } } @@ -1894,6 +1907,11 @@ impl Type { }); Type::TraitAsType(*s, name.clone(), args) } + Type::InfixExpr(lhs, op, rhs) => { + let lhs = lhs.substitute_helper(type_bindings, substitute_bound_typevars); + let rhs = rhs.substitute_helper(type_bindings, substitute_bound_typevars); + Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs)) + }, Type::FieldElement | Type::Integer(_, _) @@ -1937,6 +1955,7 @@ impl Type { || env.occurs(target_id) } Type::MutableReference(element) => element.occurs(target_id), + Type::InfixExpr(lhs, _op, rhs) => lhs.occurs(target_id) || rhs.occurs(target_id), Type::FieldElement | Type::Integer(_, _) @@ -1997,6 +2016,11 @@ impl Type { let args = vecmap(args, |arg| arg.follow_bindings()); TraitAsType(*s, name.clone(), args) } + InfixExpr(lhs, op, rhs) => { + let lhs = lhs.follow_bindings(); + let rhs = rhs.follow_bindings(); + InfixExpr(Box::new(lhs), *op, Box::new(rhs)) + }, // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), @@ -2084,6 +2108,10 @@ impl Type { } Type::MutableReference(elem) => elem.replace_named_generics_with_type_variables(), Type::Forall(_, typ) => typ.replace_named_generics_with_type_variables(), + Type::InfixExpr(lhs, _op, rhs) => { + lhs.replace_named_generics_with_type_variables(); + rhs.replace_named_generics_with_type_variables(); + }, } } } @@ -2215,6 +2243,7 @@ impl From<&Type> for PrintableType { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } Type::Quoted(_) => unreachable!(), + Type::InfixExpr(..) => unreachable!(), } } } @@ -2307,6 +2336,7 @@ impl std::fmt::Debug for Type { write!(f, "&mut {element:?}") } Type::Quoted(quoted) => write!(f, "{}", quoted), + Type::InfixExpr(lhs, op, rhs) => write!(f, "({lhs:?} {op} {rhs:?})"), } } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index a46f32e3094..e9cd90beeb3 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1012,7 +1012,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::MutableReference(Box::new(element)) } - HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { + HirType::Forall(_, _) | HirType::Constant(_) | HirType::InfixExpr(..) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } HirType::Quoted(_) => unreachable!("Tried to translate Code type into runtime code"), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 6520dc6aa1f..d009c2c4bc4 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -2060,6 +2060,7 @@ fn get_type_method_key(typ: &Type) -> Option { | Type::Constant(_) | Type::Error | Type::Struct(_, _) + | Type::InfixExpr(..) | Type::TraitAsType(..) => None, } } From b8a487da193373dff198d5a9d77dcdeea5a24f6d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 16 Jul 2024 09:32:47 -0500 Subject: [PATCH 02/12] Add unification rules --- .../noirc_frontend/src/elaborator/types.rs | 2 +- .../src/hir/comptime/hir_to_display_ast.rs | 2 +- .../src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 35 ++++++++++++++++--- .../src/monomorphization/mod.rs | 5 ++- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 16e864f24f6..66f0cb3e843 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1595,7 +1595,7 @@ impl<'context> Elaborator<'context> { Type::InfixExpr(lhs, _op, rhs) => { Self::find_numeric_generics_in_type(lhs, found); Self::find_numeric_generics_in_type(rhs, found); - }, + } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index efe6d0b2ac4..048d2964908 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -358,7 +358,7 @@ impl Type { let span = Span::default(); let expr = UnresolvedTypeExpression::BinaryOperation(lhs, *op, rhs, span); UnresolvedTypeData::Expression(expr) - }, + } }; UnresolvedType { typ, span: None } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 0f1d5b73584..2c3de4d51e0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1266,7 +1266,7 @@ impl<'a> Resolver<'a> { Type::InfixExpr(lhs, _, rhs) => { Self::find_numeric_generics_in_type(lhs, found); Self::find_numeric_generics_in_type(rhs, found); - }, + } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 83a017d0d21..5ce71103ff9 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -837,8 +837,9 @@ impl Type { elements.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) } - Type::InfixExpr(lhs, _op, rhs) => - lhs.contains_numeric_typevar(target_id) || rhs.contains_numeric_typevar(target_id), + Type::InfixExpr(lhs, _op, rhs) => { + lhs.contains_numeric_typevar(target_id) || rhs.contains_numeric_typevar(target_id) + } } } @@ -1539,6 +1540,26 @@ impl Type { elem_a.try_unify(elem_b, bindings) } + (InfixExpr(lhs_a, op_a, rhs_a), InfixExpr(lhs_b, op_b, rhs_b)) => { + if op_a == op_b { + let old_bindings = bindings.clone(); + + let result_a = lhs_a.try_unify(lhs_b, bindings); + let result_b = rhs_a.try_unify(rhs_b, bindings); + + if op_a.is_commutative() && (result_a.is_err() || result_b.is_err()) { + *bindings = old_bindings; + lhs_a.try_unify(rhs_b, bindings)?; + rhs_a.try_unify(lhs_b, bindings) + } else { + result_a?; + result_b + } + } else { + Err(UnificationError) + } + } + (other_a, other_b) => { if other_a == other_b { Ok(()) @@ -1911,7 +1932,7 @@ impl Type { let lhs = lhs.substitute_helper(type_bindings, substitute_bound_typevars); let rhs = rhs.substitute_helper(type_bindings, substitute_bound_typevars); Type::InfixExpr(Box::new(lhs), *op, Box::new(rhs)) - }, + } Type::FieldElement | Type::Integer(_, _) @@ -2020,7 +2041,7 @@ impl Type { let lhs = lhs.follow_bindings(); let rhs = rhs.follow_bindings(); InfixExpr(Box::new(lhs), *op, Box::new(rhs)) - }, + } // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), @@ -2111,7 +2132,7 @@ impl Type { Type::InfixExpr(lhs, _op, rhs) => { lhs.replace_named_generics_with_type_variables(); rhs.replace_named_generics_with_type_variables(); - }, + } } } } @@ -2161,6 +2182,10 @@ impl BinaryTypeOperator { BinaryTypeOperator::Modulo => |a, b| a.wrapping_rem(b), // % b, } } + + fn is_commutative(&self) -> bool { + matches!(self, BinaryTypeOperator::Addition | BinaryTypeOperator::Multiplication) + } } impl TypeVariableKind { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index e9cd90beeb3..70bc37c3005 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1012,7 +1012,10 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::MutableReference(Box::new(element)) } - HirType::Forall(_, _) | HirType::Constant(_) | HirType::InfixExpr(..) | HirType::Error => { + HirType::Forall(_, _) + | HirType::Constant(_) + | HirType::InfixExpr(..) + | HirType::Error => { unreachable!("Unexpected type {} found", typ) } HirType::Quoted(_) => unreachable!("Tried to translate Code type into runtime code"), From 91986e7ea90e8a9a1b9f3e968dd7594d0ff5f3f8 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 29 Jul 2024 10:55:58 -0500 Subject: [PATCH 03/12] Add test program --- .../noirc_frontend/src/elaborator/types.rs | 2 +- .../src/hir/comptime/interpreter/builtin.rs | 1 + compiler/noirc_frontend/src/hir_def/types.rs | 39 +++++++++++++++---- .../arithmetic_generics/Nargo.toml | 7 ++++ .../arithmetic_generics/src/main.nr | 16 ++++++++ tooling/lsp/src/requests/inlay_hint.rs | 1 + 6 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 test_programs/compile_success_empty/arithmetic_generics/Nargo.toml create mode 100644 test_programs/compile_success_empty/arithmetic_generics/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 7e8d349b0bc..590094166be 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -390,7 +390,7 @@ impl<'context> Elaborator<'context> { match (lhs, rhs) { (Type::Constant(lhs), Type::Constant(rhs)) => { - Type::Constant(op.function()(lhs, rhs)) + Type::Constant(op.function(lhs, rhs)) } (lhs, rhs) => Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)), } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 92890cb66b8..dc0937cd0ca 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -538,6 +538,7 @@ fn zeroed(return_type: Type) -> IResult { Type::TypeVariable(_, _) | Type::Forall(_, _) | Type::Constant(_) + | Type::InfixExpr(..) | Type::Quoted(_) | Type::Error | Type::TraitAsType(_, _, _) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 1c2ec327892..70d6a3058e1 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -646,7 +646,13 @@ impl std::fmt::Display for Type { write!(f, "&mut {element}") } Type::Quoted(quoted) => write!(f, "{}", quoted), - Type::InfixExpr(lhs, op, rhs) => write!(f, "({lhs} {op} {rhs})"), + Type::InfixExpr(lhs, op, rhs) => { + if let Some(value) = self.evaluate_to_u32() { + write!(f, "{value}") + } else { + write!(f, "({lhs} {op} {rhs})") + } + } } } } @@ -1564,6 +1570,18 @@ impl Type { } } + (Constant(value), other) | (other, Constant(value)) => { + if let Some(other_value) = other.evaluate_to_u32() { + if *value == other_value { + Ok(()) + } else { + Err(UnificationError) + } + } else { + Err(UnificationError) + } + } + (other_a, other_b) => { if other_a == other_b { Ok(()) @@ -1671,6 +1689,11 @@ impl Type { Type::TypeVariable(_, TypeVariableKind::Constant(size)) => Some(*size), Type::Array(len, _elem) => len.evaluate_to_u32(), Type::Constant(x) => Some(*x), + Type::InfixExpr(lhs, op, rhs) => { + let lhs = lhs.evaluate_to_u32()?; + let rhs = rhs.evaluate_to_u32()?; + Some(op.function(lhs, rhs)) + } _ => None, } } @@ -2179,14 +2202,14 @@ fn convert_array_expression_to_slice( } impl BinaryTypeOperator { - /// Return the actual rust numeric function associated with this operator - pub fn function(self) -> fn(u32, u32) -> u32 { + /// Perform the actual rust numeric operation associated with this operator + pub fn function(self, a: u32, b: u32) -> u32 { match self { - BinaryTypeOperator::Addition => |a, b| a.wrapping_add(b), - BinaryTypeOperator::Subtraction => |a, b| a.wrapping_sub(b), - BinaryTypeOperator::Multiplication => |a, b| a.wrapping_mul(b), - BinaryTypeOperator::Division => |a, b| a.wrapping_div(b), - BinaryTypeOperator::Modulo => |a, b| a.wrapping_rem(b), // % b, + BinaryTypeOperator::Addition => a.wrapping_add(b), + BinaryTypeOperator::Subtraction => a.wrapping_sub(b), + BinaryTypeOperator::Multiplication => a.wrapping_mul(b), + BinaryTypeOperator::Division => a.wrapping_div(b), + BinaryTypeOperator::Modulo => a.wrapping_rem(b), } } diff --git a/test_programs/compile_success_empty/arithmetic_generics/Nargo.toml b/test_programs/compile_success_empty/arithmetic_generics/Nargo.toml new file mode 100644 index 00000000000..2352ae0c562 --- /dev/null +++ b/test_programs/compile_success_empty/arithmetic_generics/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "arithmetic_generics" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr new file mode 100644 index 00000000000..e64b569971e --- /dev/null +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -0,0 +1,16 @@ +fn main() { + let (first, rest) = split_first([1, 2, 3, 4]); + println(first); + println(rest); +} + +fn split_first(array: [T; N]) -> (T, [T; N - 1]) { + std::static_assert(N != 0, "split_first called on empty array"); + let mut new_array: [T; N - 1] = std::unsafe::zeroed(); + + for i in 0 .. N - 1 { + new_array[i] = array[i + 1]; + } + + (array[0], new_array) +} diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index 3fc6a6752df..80769c0cdee 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -584,6 +584,7 @@ fn push_type_parts(typ: &Type, parts: &mut Vec, files: &File | Type::NamedGeneric(..) | Type::Forall(..) | Type::Constant(..) + | Type::InfixExpr(..) | Type::Quoted(..) | Type::Error => { parts.push(string_part(typ.to_string())); From a7e2a62d8bb4835f09eca6b6fcd814b296689312 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 29 Jul 2024 11:10:56 -0500 Subject: [PATCH 04/12] Remove println --- .../compile_success_empty/arithmetic_generics/src/main.nr | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index e64b569971e..a4a6e5c1da3 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -1,7 +1,10 @@ fn main() { let (first, rest) = split_first([1, 2, 3, 4]); - println(first); - println(rest); + assert_eq(first, 1); + assert_eq(rest, [2, 3, 4]); + + // Type inference works without the type constraints from assert_eq as well + let _ = split_first([1, 2, 3]); } fn split_first(array: [T; N]) -> (T, [T; N - 1]) { From 5b665ca98ca37806aee9d46d0e9b8ac305b211b0 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 12:49:57 -0500 Subject: [PATCH 05/12] Make analysis more sophisticated w/ canonicalization --- compiler/noirc_frontend/src/ast/mod.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 177 +++++++++++++++--- .../arithmetic_generics/src/main.nr | 27 +++ 3 files changed, 180 insertions(+), 26 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index ef4932fa9f0..48582beaa45 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -291,7 +291,7 @@ impl UnresolvedTypeData { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] pub enum Signedness { Unsigned, Signed, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 70d6a3058e1..d01cfb917b0 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -21,7 +21,7 @@ use crate::{ use super::expr::{HirCallExpression, HirExpression, HirIdent}; -#[derive(PartialEq, Eq, Clone, Hash)] +#[derive(PartialEq, Eq, Clone, Hash, Ord, PartialOrd)] pub enum Type { /// A primitive Field type FieldElement, @@ -122,7 +122,7 @@ pub enum Type { /// For example, the type of a struct field or a function parameter is expected to be /// a type of kind * (represented here as `Normal`). Types used in positions where a number /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. -#[derive(PartialEq, Eq, Clone, Hash, Debug)] +#[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord)] pub enum Kind { Normal, Numeric(Box), @@ -137,7 +137,7 @@ impl std::fmt::Display for Kind { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] pub enum QuotedType { Expr, Quoted, @@ -205,6 +205,18 @@ impl PartialEq for StructType { } } +impl PartialOrd for StructType { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for StructType { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + impl StructType { pub fn new( id: StructId, @@ -335,6 +347,18 @@ impl PartialEq for TypeAlias { } } +impl Ord for TypeAlias { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + +impl PartialOrd for TypeAlias { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl std::fmt::Display for TypeAlias { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.name) @@ -427,7 +451,7 @@ impl Shared { /// A restricted subset of binary operators useable on /// type level integers for use in the array length positions of types. -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum BinaryTypeOperator { Addition, Subtraction, @@ -436,7 +460,7 @@ pub enum BinaryTypeOperator { Modulo, } -#[derive(Debug, PartialEq, Eq, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub enum TypeVariableKind { /// Can bind to any type Normal, @@ -460,7 +484,7 @@ pub enum TypeVariableKind { /// A TypeVariable is a mutable reference that is either /// bound to some type, or unbound with a given TypeVariableId. -#[derive(PartialEq, Eq, Clone, Hash)] +#[derive(PartialEq, Eq, Clone, Hash, PartialOrd, Ord)] pub struct TypeVariable(TypeVariableId, Shared); impl TypeVariable { @@ -529,7 +553,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)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub enum TypeBinding { Bound(Type), Unbound(TypeVariableId), @@ -542,7 +566,7 @@ impl TypeBinding { } /// A unique ID used to differentiate different type variables -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TypeVariableId(pub usize); impl std::fmt::Display for Type { @@ -647,8 +671,11 @@ impl std::fmt::Display for Type { } Type::Quoted(quoted) => write!(f, "{}", quoted), Type::InfixExpr(lhs, op, rhs) => { - if let Some(value) = self.evaluate_to_u32() { - write!(f, "{value}") + let this = self.canonicalize(); + + // Prevent infinite recursion + if this != *self { + write!(f, "{this}") } else { write!(f, "({lhs} {op} {rhs})") } @@ -1436,7 +1463,17 @@ impl Type { use Type::*; use TypeVariableKind as Kind; - match (self, other) { + let lhs = match self { + Type::InfixExpr(..) => Cow::Owned(self.canonicalize()), + other => Cow::Borrowed(other), + }; + + let rhs = match other { + Type::InfixExpr(..) => Cow::Owned(other.canonicalize()), + other => Cow::Borrowed(other), + }; + + match (lhs.as_ref(), rhs.as_ref()) { (Error, _) | (_, Error) => Ok(()), (Alias(alias, args), other) | (other, Alias(alias, args)) => { @@ -1552,19 +1589,8 @@ impl Type { (InfixExpr(lhs_a, op_a, rhs_a), InfixExpr(lhs_b, op_b, rhs_b)) => { if op_a == op_b { - let old_bindings = bindings.clone(); - - let result_a = lhs_a.try_unify(lhs_b, bindings); - let result_b = rhs_a.try_unify(rhs_b, bindings); - - if op_a.is_commutative() && (result_a.is_err() || result_b.is_err()) { - *bindings = old_bindings; - lhs_a.try_unify(rhs_b, bindings)?; - rhs_a.try_unify(lhs_b, bindings) - } else { - result_a?; - result_b - } + lhs_a.try_unify(lhs_b, bindings)?; + rhs_a.try_unify(rhs_b, bindings) } else { Err(UnificationError) } @@ -1592,6 +1618,107 @@ impl Type { } } + /// Try to canonicalize the representation of this type. + /// Currently the only type with a canonical representation is + /// `Type::Infix` where for each consecutive commutative operator + /// we sort the non-constant operands by `Type: Ord` and place all constant + /// operands at the end, constant folded. + /// + /// For example: + /// - `canonicalize[((1 + N) + M) + 2] = (M + N) + 3` + /// - `canonicalize[A + 2 * B + 3 - 2] = A + (B * 2) + 3 - 2` + fn canonicalize(&self) -> Type { + match self.follow_bindings() { + Type::InfixExpr(lhs, op, rhs) => { + if let Some(value) = self.evaluate_to_u32() { + return Type::Constant(value); + } + + let lhs = lhs.canonicalize(); + let rhs = rhs.canonicalize(); + + if let Some(result) = Self::try_simplify_subtraction(&lhs, op, &rhs) { + return result; + } + + if op.is_commutative() { + return Self::sort_commutative(&lhs, op, &rhs); + } + + Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)) + } + other => other, + } + } + + fn sort_commutative(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Type { + let mut queue = vec![lhs.clone(), rhs.clone()]; + + let mut sorted = BTreeSet::new(); + + let zero_value = if op == BinaryTypeOperator::Addition { 0 } else { 1 }; + let mut constant = zero_value; + + // 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() { + Type::InfixExpr(lhs, new_op, rhs) if new_op == op => { + queue.push(*lhs); + queue.push(*rhs); + } + Type::Constant(new_constant) => { + constant = op.function(constant, new_constant); + } + other => { + sorted.insert(other); + } + } + } + + if let Some(first) = sorted.pop_first() { + let mut typ = first.clone(); + + for rhs in sorted { + typ = Type::InfixExpr(Box::new(typ), op, Box::new(rhs.clone())); + } + + if constant != zero_value { + typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant))); + } + + typ + } else { + // Every type must have been a constant + Type::Constant(constant) + } + } + + /// Try to simplify a subtraction expression of `lhs - rhs`. + /// + /// - Simplifies `(a + C1) - C2` to `a + (C1 - C2)` if C1 and C2 are constants. + fn try_simplify_subtraction(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Option { + use BinaryTypeOperator::*; + match lhs { + Type::InfixExpr(l_lhs, l_op, l_rhs) => { + // Simplify `(N + 2) - 1` + if op == Subtraction && *l_op == Addition { + if let (Some(lhs_const), Some(rhs_const)) = + (l_rhs.evaluate_to_u32(), rhs.evaluate_to_u32()) + { + if lhs_const > rhs_const { + let constant = Box::new(Type::Constant(lhs_const - rhs_const)); + return Some( + Type::InfixExpr(l_lhs.clone(), *l_op, constant).canonicalize(), + ); + } + } + } + None + } + _ => None, + } + } + /// Try to unify a type variable to `self`. /// This is a helper function factored out from try_unify. fn try_unify_to_type_variable( @@ -2213,7 +2340,7 @@ impl BinaryTypeOperator { } } - fn is_commutative(&self) -> bool { + fn is_commutative(self) -> bool { matches!(self, BinaryTypeOperator::Addition | BinaryTypeOperator::Multiplication) } } diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index a4a6e5c1da3..d2f7a2ef75b 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -5,6 +5,8 @@ fn main() { // Type inference works without the type constraints from assert_eq as well let _ = split_first([1, 2, 3]); + + let _ = push_multiple([1, 2, 3]); } fn split_first(array: [T; N]) -> (T, [T; N - 1]) { @@ -17,3 +19,28 @@ fn split_first(array: [T; N]) -> (T, [T; N - 1]) { (array[0], new_array) } + +fn push(array: [Field; N], element: Field) -> [Field; N + 1] { + let mut result: [_; N + 1] = std::unsafe::zeroed(); + result[array.len()] = element; + + for i in 0 .. array.len() { + result[i] = array[i]; + } + + result +} + +fn push_multiple(array: [Field; N]) -> [Field; N + 2] { + // : [Field; N + 1] + let array2 = push(array, 4); + + // : [Field; (N + 1) + 1] + let array3 = push(array2, 5); + + // [Field; (N + 1) + 1] = [Field; N + 2] + array3 +} + +// This signature fails because we can't match `_ + 1` to `3` at the call site +// fn push_multiple(array: [Field; 1 + N]) -> [Field; N + 3] { From 94464026b3953a6f3a171a98346a2cf2f3ed3c37 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 13:16:47 -0500 Subject: [PATCH 06/12] Add --arithmetic-generics flag --- aztec_macros/src/utils/hir_utils.rs | 4 ++-- compiler/noirc_driver/src/lib.rs | 22 +++++++++---------- .../noirc_frontend/src/elaborator/comptime.rs | 1 + compiler/noirc_frontend/src/elaborator/mod.rs | 13 +++++++++-- .../noirc_frontend/src/elaborator/types.rs | 11 +++++++++- .../noirc_frontend/src/hir/comptime/tests.rs | 2 +- .../src/hir/def_collector/dc_crate.rs | 5 ++++- .../noirc_frontend/src/hir/def_map/mod.rs | 2 ++ .../src/hir/resolution/errors.rs | 9 ++++++++ compiler/noirc_frontend/src/hir_def/types.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 5 +++-- tooling/lsp/src/notifications/mod.rs | 2 +- tooling/lsp/src/requests/code_lens_request.rs | 2 +- tooling/lsp/src/requests/mod.rs | 2 +- tooling/lsp/src/requests/test_run.rs | 2 +- tooling/lsp/src/requests/tests.rs | 2 +- tooling/nargo_cli/build.rs | 2 +- tooling/nargo_cli/src/cli/check_cmd.rs | 14 ++++-------- tooling/nargo_cli/src/cli/export_cmd.rs | 5 +---- tooling/nargo_cli/src/cli/test_cmd.rs | 9 ++------ 20 files changed, 67 insertions(+), 49 deletions(-) diff --git a/aztec_macros/src/utils/hir_utils.rs b/aztec_macros/src/utils/hir_utils.rs index 200ce3099cb..0a8ce371708 100644 --- a/aztec_macros/src/utils/hir_utils.rs +++ b/aztec_macros/src/utils/hir_utils.rs @@ -195,7 +195,7 @@ pub fn inject_fn( let trait_id = None; items.functions.push(UnresolvedFunctions { file_id, functions, trait_id, self_type: None }); - let mut errors = Elaborator::elaborate(context, *crate_id, items, None); + let mut errors = Elaborator::elaborate(context, *crate_id, items, None, false); errors.retain(|(error, _)| !CustomDiagnostic::from(error).is_warning()); if !errors.is_empty() { @@ -241,7 +241,7 @@ pub fn inject_global( let mut items = CollectedItems::default(); items.globals.push(UnresolvedGlobal { file_id, module_id, global_id, stmt_def: global }); - let _errors = Elaborator::elaborate(context, *crate_id, items, None); + let _errors = Elaborator::elaborate(context, *crate_id, items, None, false); } pub fn fully_qualified_note_path(context: &HirContext, note_id: StructId) -> Option { diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index f430eb8ad19..20ab1fb97f8 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -113,6 +113,10 @@ pub struct CompileOptions { /// Outputs the paths to any modified artifacts #[arg(long, hide = true)] pub show_artifact_paths: bool, + + /// Outputs the paths to any modified artifacts + #[arg(long, hide = true)] + pub arithmetic_generics: bool, } pub fn parse_expression_width(input: &str) -> Result { @@ -262,21 +266,19 @@ pub fn add_dep( pub fn check_crate( context: &mut Context, crate_id: CrateId, - deny_warnings: bool, - disable_macros: bool, - debug_comptime_in_file: Option<&str>, + options: &CompileOptions, ) -> CompilationResult<()> { let macros: &[&dyn MacroProcessor] = - if disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; + if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; let mut errors = vec![]; - let diagnostics = CrateDefMap::collect_defs(crate_id, context, debug_comptime_in_file, macros); + let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.debug_comptime_in_file.as_deref(), options.arithmetic_generics, macros); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { let diagnostic = CustomDiagnostic::from(&error); diagnostic.in_file(file_id) })); - if has_errors(&errors, deny_warnings) { + if has_errors(&errors, options.deny_warnings) { Err(errors) } else { Ok(((), errors)) @@ -305,9 +307,7 @@ pub fn compile_main( let (_, mut warnings) = check_crate( context, crate_id, - options.deny_warnings, - options.disable_macros, - options.debug_comptime_in_file.as_deref(), + options, )?; let main = context.get_main_function(&crate_id).ok_or_else(|| { @@ -346,9 +346,7 @@ pub fn compile_contract( let (_, warnings) = check_crate( context, crate_id, - options.deny_warnings, - options.disable_macros, - options.debug_comptime_in_file.as_deref(), + options, )?; // TODO: We probably want to error if contracts is empty diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 402ff31dafe..e274c12b3b2 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -21,6 +21,7 @@ impl<'context> Elaborator<'context> { self.def_maps, self.crate_id, self.debug_comptime_in_file, + self.enable_arithmetic_generics, ); elaborator.function_context.push(FunctionContext::default()); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 84572a7b413..4691eacdc42 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -176,6 +176,9 @@ pub struct Elaborator<'context> { /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). unresolved_globals: BTreeMap, + + /// Temporary flag to enable the experimental arithmetic generics feature + enable_arithmetic_generics: bool, } #[derive(Default)] @@ -198,6 +201,7 @@ impl<'context> Elaborator<'context> { def_maps: &'context mut DefMaps, crate_id: CrateId, debug_comptime_in_file: Option, + enable_arithmetic_generics: bool, ) -> Self { Self { scopes: ScopeForest::default(), @@ -219,6 +223,7 @@ impl<'context> Elaborator<'context> { comptime_scopes: vec![HashMap::default()], debug_comptime_in_file, unresolved_globals: BTreeMap::new(), + enable_arithmetic_generics, } } @@ -226,12 +231,14 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, debug_comptime_in_file: Option, + enable_arithmetic_generics: bool, ) -> Self { Self::new( &mut context.def_interner, &mut context.def_maps, crate_id, debug_comptime_in_file, + enable_arithmetic_generics, ) } @@ -240,8 +247,9 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, items: CollectedItems, debug_comptime_in_file: Option, + enable_arithmetic_generics: bool, ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self(context, crate_id, items, debug_comptime_in_file).errors + Self::elaborate_and_return_self(context, crate_id, items, debug_comptime_in_file, enable_arithmetic_generics).errors } pub fn elaborate_and_return_self( @@ -249,8 +257,9 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, items: CollectedItems, debug_comptime_in_file: Option, + enable_arithmetic_generics: bool, ) -> Self { - let mut this = Self::from_context(context, crate_id, debug_comptime_in_file); + let mut this = Self::from_context(context, crate_id, debug_comptime_in_file, enable_arithmetic_generics); this.elaborate_items(items); this.check_and_pop_function_context(); this diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 590094166be..e148c65ac9c 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -385,6 +385,7 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, _) => { + let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); let lhs = self.convert_expression_type(*lhs); let rhs = self.convert_expression_type(*rhs); @@ -392,7 +393,15 @@ impl<'context> Elaborator<'context> { (Type::Constant(lhs), Type::Constant(rhs)) => { Type::Constant(op.function(lhs, rhs)) } - (lhs, rhs) => Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)), + (lhs, rhs) => { + if !self.enable_arithmetic_generics { + let span = + if !matches!(lhs, Type::Constant(_)) { lhs_span } else { rhs_span }; + self.push_err(ResolverError::InvalidArrayLengthExpr { span }); + } + + Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)).canonicalize() + } } } } diff --git a/compiler/noirc_frontend/src/hir/comptime/tests.rs b/compiler/noirc_frontend/src/hir/comptime/tests.rs index b4ffa1bd01d..4c1adf9fca0 100644 --- a/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -45,7 +45,7 @@ fn interpret_helper(src: &str) -> Result { let main = context.get_main_function(&krate).expect("Expected 'main' function"); let mut elaborator = - Elaborator::elaborate_and_return_self(&mut context, krate, collector.items, None); + Elaborator::elaborate_and_return_self(&mut context, krate, collector.items, None, false); assert_eq!(elaborator.errors.len(), 0); let mut interpreter = elaborator.setup_interpreter(); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 60489660762..ed3b98f0a1a 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -247,6 +247,7 @@ impl DefCollector { ast: SortedModule, root_file_id: FileId, debug_comptime_in_file: Option<&str>, + enable_arithmetic_generics: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; @@ -264,6 +265,7 @@ impl DefCollector { dep.crate_id, context, debug_comptime_in_file, + enable_arithmetic_generics, macro_processors, )); @@ -387,7 +389,8 @@ impl DefCollector { }); let mut more_errors = - Elaborator::elaborate(context, crate_id, def_collector.items, debug_comptime_in_file); + Elaborator::elaborate(context, crate_id, def_collector.items, debug_comptime_in_file, enable_arithmetic_generics); + errors.append(&mut more_errors); for macro_processor in macro_processors { diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 45f1f17940d..e607de52ff1 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -76,6 +76,7 @@ impl CrateDefMap { crate_id: CrateId, context: &mut Context, debug_comptime_in_file: Option<&str>, + enable_arithmetic_generics: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled @@ -125,6 +126,7 @@ impl CrateDefMap { ast, root_file_id, debug_comptime_in_file, + enable_arithmetic_generics, macro_processors, )); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index bf6de746791..c573698184f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -116,6 +116,8 @@ pub enum ResolverError { NonFunctionInAnnotation { span: Span }, #[error("Unknown annotation")] UnknownAnnotation { span: Span }, + #[error("Arithmetic Generics are currently experimental")] + ArithmeticGenerics { span: Span }, } impl ResolverError { @@ -467,6 +469,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::ArithmeticGenerics { span } => { + Diagnostic::simple_warning( + "Arithmetic Generics are currently an experimental feature".into(), + "Use --arithmetic-generics to enable this feature".into(), + *span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index d01cfb917b0..f5e43a2a5db 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1627,7 +1627,7 @@ impl Type { /// For example: /// - `canonicalize[((1 + N) + M) + 2] = (M + N) + 3` /// - `canonicalize[A + 2 * B + 3 - 2] = A + (B * 2) + 3 - 2` - fn canonicalize(&self) -> Type { + pub fn canonicalize(&self) -> Type { match self.follow_bindings() { Type::InfixExpr(lhs, op, rhs) => { if let Some(value) = self.evaluate_to_u32() { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index f2b83a48022..65fdaae81c4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -82,8 +82,9 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation &mut context, program.clone().into_sorted(), root_file_id, - None, // No debug_comptime_in_file - &[], // No macro processors + None, // No debug_comptime_in_file + false, // Disallow arithmetic generics + &[], // No macro processors )); } (program, context, errors) diff --git a/tooling/lsp/src/notifications/mod.rs b/tooling/lsp/src/notifications/mod.rs index 24409e85db8..ad658eefcf0 100644 --- a/tooling/lsp/src/notifications/mod.rs +++ b/tooling/lsp/src/notifications/mod.rs @@ -162,7 +162,7 @@ pub(crate) fn process_workspace_for_noir_document( let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); - let file_diagnostics = match check_crate(&mut context, crate_id, false, false, None) { + let file_diagnostics = match check_crate(&mut context, crate_id, &Default::default()) { Ok(((), warnings)) => warnings, Err(errors_and_warnings) => errors_and_warnings, }; diff --git a/tooling/lsp/src/requests/code_lens_request.rs b/tooling/lsp/src/requests/code_lens_request.rs index 51336a324da..17edf5e11c5 100644 --- a/tooling/lsp/src/requests/code_lens_request.rs +++ b/tooling/lsp/src/requests/code_lens_request.rs @@ -73,7 +73,7 @@ fn on_code_lens_request_inner( let (mut context, crate_id) = prepare_source(source_string, state); // We ignore the warnings and errors produced by compilation for producing code lenses // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut context, crate_id, false, false, None); + let _ = check_crate(&mut context, crate_id, &Default::default()); let collected_lenses = collect_lenses_for_package(&context, crate_id, &workspace, package, None); diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index 4d261c1b50a..067cc22be63 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -382,7 +382,7 @@ where interner = def_interner; } else { // We ignore the warnings and errors produced by compilation while resolving the definition - let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, None); + let _ = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); interner = &context.def_interner; } diff --git a/tooling/lsp/src/requests/test_run.rs b/tooling/lsp/src/requests/test_run.rs index bf4d9763faf..fc4054633e2 100644 --- a/tooling/lsp/src/requests/test_run.rs +++ b/tooling/lsp/src/requests/test_run.rs @@ -61,7 +61,7 @@ fn on_test_run_request_inner( Some(package) => { let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); - if check_crate(&mut context, crate_id, false, false, None).is_err() { + if check_crate(&mut context, crate_id, &Default::default()).is_err() { let result = NargoTestRunResult { id: params.id.clone(), result: "error".to_string(), diff --git a/tooling/lsp/src/requests/tests.rs b/tooling/lsp/src/requests/tests.rs index 20b96029696..7203aca7f09 100644 --- a/tooling/lsp/src/requests/tests.rs +++ b/tooling/lsp/src/requests/tests.rs @@ -65,7 +65,7 @@ fn on_tests_request_inner( crate::prepare_package(&workspace_file_manager, &parsed_files, package); // We ignore the warnings and errors produced by compilation for producing tests // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut context, crate_id, false, false, None); + let _ = check_crate(&mut context, crate_id, &Default::default()); // We don't add test headings for a package if it contains no `#[test]` functions get_package_tests_in_crate(&context, &crate_id, &package.name) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 74e07efb5c1..3f8cd055569 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -218,7 +218,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa &test_dir, &format!( r#" - nargo.arg("info").arg("--json").arg("--force"); + nargo.arg("info").arg("--arithmetic-generics").arg("--json").arg("--force"); {assert_zero_opcodes}"#, ), diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index d40bae1ecfd..eb5c7b897bb 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -84,10 +84,7 @@ fn check_package( check_crate_and_report_errors( &mut context, crate_id, - compile_options.deny_warnings, - compile_options.disable_macros, - compile_options.silence_warnings, - compile_options.debug_comptime_in_file.as_deref(), + compile_options, )?; if package.is_library() || package.is_contract() { @@ -157,14 +154,11 @@ fn create_input_toml_template( pub(crate) fn check_crate_and_report_errors( context: &mut Context, crate_id: CrateId, - deny_warnings: bool, - disable_macros: bool, - silence_warnings: bool, - debug_comptime_in_file: Option<&str>, + options: &CompileOptions, ) -> Result<(), CompileError> { let result = - check_crate(context, crate_id, deny_warnings, disable_macros, debug_comptime_in_file); - report_errors(result, &context.file_manager, deny_warnings, silence_warnings) + check_crate(context, crate_id, options); + report_errors(result, &context.file_manager, options.deny_warnings, options.silence_warnings) } #[cfg(test)] diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 1b7ba97d68d..3c4c5be2e05 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -86,10 +86,7 @@ fn compile_exported_functions( check_crate_and_report_errors( &mut context, crate_id, - compile_options.deny_warnings, - compile_options.disable_macros, - compile_options.silence_warnings, - compile_options.debug_comptime_in_file.as_deref(), + compile_options, )?; let exported_functions = context.get_all_exported_functions_in_crate(&crate_id); diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index c8848e2e304..363b3205481 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -174,9 +174,7 @@ fn run_test + Default>( check_crate( &mut context, crate_id, - compile_options.deny_warnings, - compile_options.disable_macros, - compile_options.debug_comptime_in_file.as_deref(), + compile_options, ) .expect("Any errors should have occurred when collecting test functions"); @@ -240,10 +238,7 @@ fn get_tests_in_package( check_crate_and_report_errors( &mut context, crate_id, - compile_options.deny_warnings, - compile_options.disable_macros, - compile_options.silence_warnings, - compile_options.debug_comptime_in_file.as_deref(), + compile_options, )?; Ok(context From 7a87785f4b9e6b057ac6827e17ef6cd3e533e2ed Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 13:18:26 -0500 Subject: [PATCH 07/12] Remove error type --- compiler/noirc_driver/src/lib.rs | 27 ++++++++++--------- compiler/noirc_frontend/src/elaborator/mod.rs | 16 +++++++++-- .../src/hir/def_collector/dc_crate.rs | 9 +++++-- .../src/hir/resolution/errors.rs | 9 ------- tooling/nargo_cli/src/cli/check_cmd.rs | 9 ++----- tooling/nargo_cli/src/cli/export_cmd.rs | 6 +---- tooling/nargo_cli/src/cli/test_cmd.rs | 14 +++------- 7 files changed, 41 insertions(+), 49 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 20ab1fb97f8..6ba7716f9ef 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -268,11 +268,20 @@ pub fn check_crate( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult<()> { - let macros: &[&dyn MacroProcessor] = - if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; + let macros: &[&dyn MacroProcessor] = if options.disable_macros { + &[] + } else { + &[&aztec_macros::AztecMacro as &dyn MacroProcessor] + }; let mut errors = vec![]; - let diagnostics = CrateDefMap::collect_defs(crate_id, context, options.debug_comptime_in_file.as_deref(), options.arithmetic_generics, macros); + let diagnostics = CrateDefMap::collect_defs( + crate_id, + context, + options.debug_comptime_in_file.as_deref(), + options.arithmetic_generics, + macros, + ); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { let diagnostic = CustomDiagnostic::from(&error); diagnostic.in_file(file_id) @@ -304,11 +313,7 @@ pub fn compile_main( options: &CompileOptions, cached_program: Option, ) -> CompilationResult { - let (_, mut warnings) = check_crate( - context, - crate_id, - options, - )?; + let (_, mut warnings) = check_crate(context, crate_id, options)?; let main = context.get_main_function(&crate_id).ok_or_else(|| { // TODO(#2155): This error might be a better to exist in Nargo @@ -343,11 +348,7 @@ pub fn compile_contract( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult { - let (_, warnings) = check_crate( - context, - crate_id, - options, - )?; + let (_, warnings) = check_crate(context, crate_id, options)?; // TODO: We probably want to error if contracts is empty let contracts = context.get_all_contracts(&crate_id); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 4691eacdc42..e98f8ff2644 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -249,7 +249,14 @@ impl<'context> Elaborator<'context> { debug_comptime_in_file: Option, enable_arithmetic_generics: bool, ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self(context, crate_id, items, debug_comptime_in_file, enable_arithmetic_generics).errors + Self::elaborate_and_return_self( + context, + crate_id, + items, + debug_comptime_in_file, + enable_arithmetic_generics, + ) + .errors } pub fn elaborate_and_return_self( @@ -259,7 +266,12 @@ impl<'context> Elaborator<'context> { debug_comptime_in_file: Option, enable_arithmetic_generics: bool, ) -> Self { - let mut this = Self::from_context(context, crate_id, debug_comptime_in_file, enable_arithmetic_generics); + let mut this = Self::from_context( + context, + crate_id, + debug_comptime_in_file, + enable_arithmetic_generics, + ); this.elaborate_items(items); this.check_and_pop_function_context(); this diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index ed3b98f0a1a..5a0569c9c38 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -388,8 +388,13 @@ impl DefCollector { }) }); - let mut more_errors = - Elaborator::elaborate(context, crate_id, def_collector.items, debug_comptime_in_file, enable_arithmetic_generics); + let mut more_errors = Elaborator::elaborate( + context, + crate_id, + def_collector.items, + debug_comptime_in_file, + enable_arithmetic_generics, + ); errors.append(&mut more_errors); diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index c573698184f..bf6de746791 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -116,8 +116,6 @@ pub enum ResolverError { NonFunctionInAnnotation { span: Span }, #[error("Unknown annotation")] UnknownAnnotation { span: Span }, - #[error("Arithmetic Generics are currently experimental")] - ArithmeticGenerics { span: Span }, } impl ResolverError { @@ -469,13 +467,6 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, - ResolverError::ArithmeticGenerics { span } => { - Diagnostic::simple_warning( - "Arithmetic Generics are currently an experimental feature".into(), - "Use --arithmetic-generics to enable this feature".into(), - *span, - ) - }, } } } diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index eb5c7b897bb..5239070b4d2 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -81,11 +81,7 @@ fn check_package( allow_overwrite: bool, ) -> Result { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors( - &mut context, - crate_id, - compile_options, - )?; + check_crate_and_report_errors(&mut context, crate_id, compile_options)?; if package.is_library() || package.is_contract() { // Libraries do not have ABIs while contracts have many, so we cannot generate a `Prover.toml` file. @@ -156,8 +152,7 @@ pub(crate) fn check_crate_and_report_errors( crate_id: CrateId, options: &CompileOptions, ) -> Result<(), CompileError> { - let result = - check_crate(context, crate_id, options); + let result = check_crate(context, crate_id, options); report_errors(result, &context.file_manager, options.deny_warnings, options.silence_warnings) } diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 3c4c5be2e05..19add7f30dc 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -83,11 +83,7 @@ fn compile_exported_functions( compile_options: &CompileOptions, ) -> Result<(), CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors( - &mut context, - crate_id, - compile_options, - )?; + check_crate_and_report_errors(&mut context, crate_id, compile_options)?; let exported_functions = context.get_all_exported_functions_in_crate(&crate_id); diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 363b3205481..1cf5b32c381 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -171,12 +171,8 @@ fn run_test + Default>( // We then need to construct a separate copy for each test. let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate( - &mut context, - crate_id, - compile_options, - ) - .expect("Any errors should have occurred when collecting test functions"); + check_crate(&mut context, crate_id, compile_options) + .expect("Any errors should have occurred when collecting test functions"); let test_functions = context .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Exact(fn_name)); @@ -235,11 +231,7 @@ fn get_tests_in_package( compile_options: &CompileOptions, ) -> Result, CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors( - &mut context, - crate_id, - compile_options, - )?; + check_crate_and_report_errors(&mut context, crate_id, compile_options)?; Ok(context .get_all_test_functions_in_crate_matching(&crate_id, fn_name) From ccaad52b4994af414d37954a5c0f2a5b4ece8265 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 13:23:57 -0500 Subject: [PATCH 08/12] Format --- .../compile_success_empty/arithmetic_generics/src/main.nr | 4 ++-- tooling/lsp/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index d2f7a2ef75b..cffc16224ad 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -13,7 +13,7 @@ fn split_first(array: [T; N]) -> (T, [T; N - 1]) { std::static_assert(N != 0, "split_first called on empty array"); let mut new_array: [T; N - 1] = std::unsafe::zeroed(); - for i in 0 .. N - 1 { + for i in 0..N - 1 { new_array[i] = array[i + 1]; } @@ -24,7 +24,7 @@ fn push(array: [Field; N], element: Field) -> [Field; N + 1] { let mut result: [_; N + 1] = std::unsafe::zeroed(); result[array.len()] = element; - for i in 0 .. array.len() { + for i in 0..array.len() { result[i] = array[i]; } diff --git a/tooling/lsp/src/lib.rs b/tooling/lsp/src/lib.rs index c7b70339e1d..be9ad13fc2d 100644 --- a/tooling/lsp/src/lib.rs +++ b/tooling/lsp/src/lib.rs @@ -428,7 +428,7 @@ fn prepare_package_from_source_string() { let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); - let _check_result = noirc_driver::check_crate(&mut context, crate_id, false, false, None); + let _check_result = noirc_driver::check_crate(&mut context, crate_id, &Default::default()); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); } From ba26199e314809e399c70bc4a0ab1b8e7db322a0 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 13:27:52 -0500 Subject: [PATCH 09/12] I push. CI finds errors. I fix. Repeat. --- tooling/nargo_cli/tests/stdlib-tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index c4cc792438e..0444f79d371 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -33,7 +33,7 @@ fn run_stdlib_tests() { let (mut context, dummy_crate_id) = prepare_package(&file_manager, &parsed_files, &dummy_package); - let result = check_crate(&mut context, dummy_crate_id, false, false, None); + let result = check_crate(&mut context, dummy_crate_id, &Default::default()); report_errors(result, &context.file_manager, true, false) .expect("Error encountered while compiling standard library"); From bace2536cfab432d4e10600f2228feb31d5d8b0e Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 30 Jul 2024 13:33:16 -0500 Subject: [PATCH 10/12] More check_crate --- compiler/noirc_driver/tests/stdlib_warnings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_driver/tests/stdlib_warnings.rs b/compiler/noirc_driver/tests/stdlib_warnings.rs index d2474444d13..e290842480d 100644 --- a/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -25,7 +25,7 @@ fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> let root_crate_id = prepare_crate(&mut context, file_name); let ((), warnings) = - noirc_driver::check_crate(&mut context, root_crate_id, false, false, None)?; + noirc_driver::check_crate(&mut context, root_crate_id, &Default::default())?; assert_eq!(warnings, Vec::new(), "stdlib is producing {} warnings", warnings.len()); From 094f6cc5466d731bd364ef3d49e5785eb9589de9 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 2 Aug 2024 14:29:18 -0500 Subject: [PATCH 11/12] Update compiler/noirc_driver/src/lib.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_driver/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 6ba7716f9ef..2e185c69461 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -114,7 +114,7 @@ pub struct CompileOptions { #[arg(long, hide = true)] pub show_artifact_paths: bool, - /// Outputs the paths to any modified artifacts + /// Temporary flag to enable the experimental arithmetic generics feature #[arg(long, hide = true)] pub arithmetic_generics: bool, } From 5553b699dab5226767443d12f044ace88e0cab8e Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 2 Aug 2024 15:28:45 -0500 Subject: [PATCH 12/12] Fix merge & expand test --- .../src/monomorphization/mod.rs | 4 ++ .../arithmetic_generics/src/main.nr | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9c5ba732835..5ac730db400 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1110,6 +1110,10 @@ impl<'interner> Monomorphizer<'interner> { } HirType::MutableReference(element) => Self::check_type(element, location), + HirType::InfixExpr(lhs, _, rhs) => { + Self::check_type(lhs, location)?; + Self::check_type(rhs, location) + } } } diff --git a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index cffc16224ad..d4f71d38413 100644 --- a/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -44,3 +44,60 @@ fn push_multiple(array: [Field; N]) -> [Field; N + 2] { // This signature fails because we can't match `_ + 1` to `3` at the call site // fn push_multiple(array: [Field; 1 + N]) -> [Field; N + 3] { + +// ********************************************* +// The rest of this file is setup for demo_proof +// ********************************************* + +struct W { } + +struct Equiv { + // TODO(https://github.com/noir-lang/noir/issues/5644): + // Bug with struct_obj.field_thats_a_fn(x) + + to_: fn[TU](T) -> U, + fro_: fn[UT](U) -> T, + // .. other coherence conditions +} + +impl Equiv { + fn to(self, x: T) -> U { + (self.to_)(x) + } + + fn fro(self, x: U) -> T { + (self.fro_)(x) + } +} + +fn equiv_trans( + x: Equiv, + y: Equiv +) -> Equiv, Equiv), V, (Equiv, Equiv)> { + Equiv { to_: |z| { y.to(x.to(z)) }, fro_: |z| { x.fro(y.fro(z)) } } +} + +fn mul_one_r() -> Equiv, (), W, ()> { + Equiv { to_: |_x| { W {} }, fro_: |_x| { W {} } } +} + +fn add_equiv_r(_: Equiv, EN, W, EM>) -> Equiv, (), W, ()> { + Equiv { to_: |_x| { W {} }, fro_: |_x| { W {} } } +} + +fn mul_comm() -> Equiv, (), W, ()> { + Equiv { to_: |_x| { W {} }, fro_: |_x| { W {} } } +} + +fn mul_add() -> Equiv, (), W, ()> { + Equiv { to_: |_x| { W {} }, fro_: |_x| { W {} } } +} + +// (N + 1) * N == N * N + N +fn demo_proof() -> Equiv, (Equiv, (), W, ()>, Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>), W, (Equiv, (Equiv, (), W, ()>, Equiv, (), W<(N * (N + 1))>, ()>), W, (Equiv, (), W<(N * (N + 1))>, ()>, Equiv, (), W, ()>)>, Equiv, (), W, ()>)> { + let p1: Equiv, (), W, ()> = mul_comm(); + let p2: Equiv, (), W, ()> = mul_add::(); + let p3_sub: Equiv, (), W, ()> = mul_one_r(); + let p3: Equiv, (), W, ()> = add_equiv_r::(p3_sub); + equiv_trans(equiv_trans(p1, p2), p3) +}