From 1427ec0b3b2b12243937b7ed6c04240f7f77a267 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 13 Dec 2023 13:59:33 -0600 Subject: [PATCH 01/21] Check there is an impl for the operator --- .../src/hir/resolution/resolver.rs | 2 ++ .../noirc_frontend/src/hir/type_check/expr.rs | 2 ++ compiler/noirc_frontend/src/hir_def/expr.rs | 6 +++- compiler/noirc_frontend/src/node_interner.rs | 34 ++++++++++++++++++- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 0556698fb58..ce7ed2339e7 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1314,10 +1314,12 @@ impl<'a> Resolver<'a> { ExpressionKind::Infix(infix) => { let lhs = self.resolve_expression(infix.lhs); let rhs = self.resolve_expression(infix.rhs); + let trait_id = self.interner.get_operator_trait(infix.operator.contents); HirExpression::Infix(HirInfixExpression { lhs, operator: HirBinaryOp::new(infix.operator, self.file), + trait_id, rhs, }) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index f7154895150..e5284995998 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -136,6 +136,8 @@ impl<'interner> TypeChecker<'interner> { let rhs_span = self.interner.expr_span(&infix_expr.rhs); let span = lhs_span.merge(rhs_span); + self.verify_trait_constraint(&lhs_type, infix_expr.trait_id, *expr_id, span); + self.infix_operand_type_rules(&lhs_type, &infix_expr.operator, &rhs_type, span) .unwrap_or_else(|error| { self.errors.push(error); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index ef1c3af7ac0..a2112706c41 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; -use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId}; +use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, TraitId}; use crate::{BinaryOp, BinaryOpKind, Ident, Shared, UnaryOp}; use super::stmt::HirPattern; @@ -101,6 +101,10 @@ pub struct HirInfixExpression { pub lhs: ExprId, pub operator: HirBinaryOp, pub rhs: ExprId, + + /// The trait id for the operator trait that corresponds to this operator. + /// For derived operators like `!=`, this will lead to the derived trait (Eq in this case). + pub trait_id: TraitId, } /// This is always a struct field access `my_struct.field` diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 236f1e0b513..4246a043c28 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -22,7 +22,7 @@ use crate::hir_def::{ use crate::token::{Attributes, SecondaryAttribute}; use crate::{ ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, TypeAliasType, - TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, + TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, BinaryOpKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -110,6 +110,9 @@ pub struct NodeInterner { /// the context to get the concrete type of the object and select the correct impl itself. selected_trait_implementations: HashMap, + /// Holds the trait ids of the traits used for operator overloading + operator_traits: HashMap, + /// Map from ExprId (referring to a Function/Method call) to its corresponding TypeBindings, /// filled out during type checking from instantiated variables. Used during monomorphization /// to map call site types back onto function parameter types, and undo this binding as needed. @@ -423,6 +426,7 @@ impl Default for NodeInterner { trait_implementations: Vec::new(), trait_implementation_map: HashMap::new(), selected_trait_implementations: HashMap::new(), + operator_traits: HashMap::new(), instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: std::cell::Cell::new(0), @@ -1351,6 +1355,34 @@ impl NodeInterner { None => None, } } + + /// Retrieves the trait id for a given binary operator. + /// All binary operators correspond to a trait - although multiple may correspond + /// to the same trait (such as `==` and `!=`). + /// `self.operator_traits` is expected to be filled before name resolution, + /// during definition collection. + pub fn get_operator_trait(&self, operator: BinaryOpKind) -> TraitId { + self.operator_traits[&operator] + } + + /// Registers a trait as an operator trait used for operator overloading. + /// This should only be used for the correct traits defined in `std::ops`. + pub fn add_operator_trait(&mut self, operator: BinaryOpKind, id: TraitId) { + self.operator_traits.insert(operator, id); + + // Some operators also require we insert a matching entry for related operators + match operator { + BinaryOpKind::Equal => { + self.operator_traits.insert(BinaryOpKind::NotEqual, id); + }, + BinaryOpKind::Less => { + self.operator_traits.insert(BinaryOpKind::LessEqual, id); + self.operator_traits.insert(BinaryOpKind::Greater, id); + self.operator_traits.insert(BinaryOpKind::GreaterEqual, id); + }, + _ => (), + } + } } impl Methods { From aa2171ba8ea23b28f1f74bb29fb8b62ee4735e89 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 13 Dec 2023 14:10:20 -0600 Subject: [PATCH 02/21] Automatically register when operator traits are found in the stdlib --- .../src/hir/resolution/traits.rs | 4 +++ compiler/noirc_frontend/src/node_interner.rs | 34 ++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 54d2630c722..f2fad112bf8 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -88,6 +88,10 @@ fn resolve_trait_methods( }); let file = def_maps[&crate_id].file_id(unresolved_trait.module_id); + if crate_id.is_stdlib() { + interner.try_add_operator_trait(trait_id); + } + let mut functions = vec![]; let mut resolver_errors = vec![]; diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 4246a043c28..90df8cce38b 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1365,20 +1365,38 @@ impl NodeInterner { self.operator_traits[&operator] } - /// Registers a trait as an operator trait used for operator overloading. - /// This should only be used for the correct traits defined in `std::ops`. - pub fn add_operator_trait(&mut self, operator: BinaryOpKind, id: TraitId) { - self.operator_traits.insert(operator, id); + /// Add the given trait as an operator trait if its name matches one of the + /// operator trait names (Add, Sub, ...). + pub fn try_add_operator_trait(&mut self, trait_id: TraitId) { + let the_trait = self.get_trait(trait_id); + + let operator = match the_trait.name.0.contents.as_str() { + "Add" => BinaryOpKind::Add, + "Sub" => BinaryOpKind::Subtract, + "Mul" => BinaryOpKind::Multiply, + "Div" => BinaryOpKind::Divide, + "Rem" => BinaryOpKind::Modulo, + "Eq" => BinaryOpKind::Equal, + "Ord" => BinaryOpKind::Less, + "BitAnd" => BinaryOpKind::And, + "BitOr" => BinaryOpKind::Or, + "BitXor" => BinaryOpKind::Xor, + "Shl" => BinaryOpKind::ShiftLeft, + "Shr" => BinaryOpKind::ShiftRight, + _ => return, + }; + + self.operator_traits.insert(operator, trait_id); // Some operators also require we insert a matching entry for related operators match operator { BinaryOpKind::Equal => { - self.operator_traits.insert(BinaryOpKind::NotEqual, id); + self.operator_traits.insert(BinaryOpKind::NotEqual, trait_id); }, BinaryOpKind::Less => { - self.operator_traits.insert(BinaryOpKind::LessEqual, id); - self.operator_traits.insert(BinaryOpKind::Greater, id); - self.operator_traits.insert(BinaryOpKind::GreaterEqual, id); + self.operator_traits.insert(BinaryOpKind::LessEqual, trait_id); + self.operator_traits.insert(BinaryOpKind::Greater, trait_id); + self.operator_traits.insert(BinaryOpKind::GreaterEqual, trait_id); }, _ => (), } From 24fc12715ca9360c2dc87976073b3b704a9efa3d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 14 Dec 2023 11:19:22 -0600 Subject: [PATCH 03/21] Add cmp module --- noir_stdlib/src/cmp.nr | 8 ++++++++ noir_stdlib/src/lib.nr | 1 + 2 files changed, 9 insertions(+) create mode 100644 noir_stdlib/src/cmp.nr diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr new file mode 100644 index 00000000000..bbba6a0fe19 --- /dev/null +++ b/noir_stdlib/src/cmp.nr @@ -0,0 +1,8 @@ + +trait Eq { + fn eq(self, other: Self) -> bool; +} + +trait Ord { + fn cmp(self, other: Self) -> bool; +} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 3c47de61bab..b006a4bbd12 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -19,6 +19,7 @@ mod compat; mod option; mod string; mod test; +mod cmp; mod prelude; // Oracle calls are required to be wrapped in an unconstrained function From 34dc1d984bf24a74ed5fbee15e89c95c910a59fa Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Thu, 14 Dec 2023 16:10:47 -0600 Subject: [PATCH 04/21] It works --- .../src/hir/resolution/resolver.rs | 2 +- .../noirc_frontend/src/hir/type_check/expr.rs | 145 +++++++----------- compiler/noirc_frontend/src/hir_def/expr.rs | 4 +- .../src/monomorphization/mod.rs | 43 ++++-- compiler/noirc_frontend/src/node_interner.rs | 18 +-- 5 files changed, 99 insertions(+), 113 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 951c0822295..aa50f203c10 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1254,7 +1254,7 @@ impl<'a> Resolver<'a> { let expr_id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(expr_id, expr.span, self.file); self.interner - .select_impl_for_ident(expr_id, TraitImplKind::Assumed { object_type }); + .select_impl_for_expression(expr_id, TraitImplKind::Assumed { object_type }); return expr_id; } else { // If the Path is being used as an Expression, then it is referring to a global from a separate module diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index e5284995998..385c0ba6caa 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -136,13 +136,26 @@ impl<'interner> TypeChecker<'interner> { let rhs_span = self.interner.expr_span(&infix_expr.rhs); let span = lhs_span.merge(rhs_span); - self.verify_trait_constraint(&lhs_type, infix_expr.trait_id, *expr_id, span); - - self.infix_operand_type_rules(&lhs_type, &infix_expr.operator, &rhs_type, span) - .unwrap_or_else(|error| { + let operator = &infix_expr.operator; + match self.infix_operand_type_rules(&lhs_type, operator, &rhs_type, span) { + Ok((typ, use_impl)) => { + if use_impl { + let trait_id = infix_expr.trait_id; + self.verify_trait_constraint(&lhs_type, trait_id, *expr_id, span); + + // Monomorphization will later look for type bindings for this impl + // even though it has none (no operator traits have generic methods), + // so we have to insert empty bindings here. + let no_bindings = TypeBindings::new(); + self.interner.store_instantiation_bindings(*expr_id, no_bindings); + } + typ + } + Err(error) => { self.errors.push(error); Type::Error - }) + } + } } HirExpression::Index(index_expr) => self.check_index_expression(expr_id, index_expr), HirExpression::Call(call_expr) => { @@ -296,7 +309,7 @@ impl<'interner> TypeChecker<'interner> { // We must also remember to apply these substitutions to the object_type // referenced by the selected trait impl, if one has yet to be selected. - let impl_kind = self.interner.get_selected_impl_for_ident(*expr_id); + let impl_kind = self.interner.get_selected_impl_for_expression(*expr_id); if let Some(TraitImplKind::Assumed { object_type }) = impl_kind { let the_trait = self.interner.get_trait(method.trait_id); let object_type = object_type.substitute(&bindings); @@ -305,7 +318,7 @@ impl<'interner> TypeChecker<'interner> { (the_trait.self_type_typevar.clone(), object_type.clone()), ); self.interner - .select_impl_for_ident(*expr_id, TraitImplKind::Assumed { object_type }); + .select_impl_for_expression(*expr_id, TraitImplKind::Assumed { object_type }); } self.interner.store_instantiation_bindings(*expr_id, bindings); @@ -325,7 +338,7 @@ impl<'interner> TypeChecker<'interner> { span: Span, ) { match self.interner.lookup_trait_implementation(object_type, trait_id) { - Ok(impl_kind) => self.interner.select_impl_for_ident(function_ident_id, impl_kind), + Ok(impl_kind) => self.interner.select_impl_for_expression(function_ident_id, impl_kind), Err(erroring_constraints) => { // Don't show any errors where try_get_trait returns None. // This can happen if a trait is used that was never declared. @@ -755,19 +768,22 @@ impl<'interner> TypeChecker<'interner> { None } + // Given a binary comparison operator and another type. This method will produce the output type + // and a boolean indicating whether to use the trait impl corresponding to the operator + // or not. A value of false indicates the caller to use a primitive operation for this + // operator, while a true value indicates a user-provided trait impl is required. fn comparator_operand_type_rules( &mut self, lhs_type: &Type, rhs_type: &Type, op: &HirBinaryOp, span: Span, - ) -> Result { - use crate::BinaryOpKind::{Equal, NotEqual}; + ) -> Result<(Type, bool), TypeCheckError> { use Type::*; match (lhs_type, rhs_type) { // Avoid reporting errors multiple times - (Error, _) | (_, Error) => Ok(Bool), + (Error, _) | (_, Error) => Ok((Bool, false)), // Matches on TypeVariable must be first to follow any type // bindings. @@ -793,7 +809,7 @@ impl<'interner> TypeChecker<'interner> { || other == &Type::Error { Type::apply_type_bindings(bindings); - Ok(Bool) + Ok((Bool, false)) } else { Err(TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), @@ -818,72 +834,38 @@ impl<'interner> TypeChecker<'interner> { span, }); } - Ok(Bool) - } - (Integer(..), FieldElement) | (FieldElement, Integer(..)) => { - Err(TypeCheckError::IntegerAndFieldBinaryOperation { span }) - } - (Integer(..), typ) | (typ, Integer(..)) => { - Err(TypeCheckError::IntegerTypeMismatch { typ: typ.clone(), span }) + Ok((Bool, false)) } (FieldElement, FieldElement) => { if op.kind.is_valid_for_field_type() { - Ok(Bool) + Ok((Bool, false)) } else { Err(TypeCheckError::FieldComparison { span }) } } // <= and friends are technically valid for booleans, just not very useful - (Bool, Bool) => Ok(Bool), - - // Special-case == and != for arrays - (Array(x_size, x_type), Array(y_size, y_type)) - if matches!(op.kind, Equal | NotEqual) => - { - self.unify(x_type, y_type, || TypeCheckError::TypeMismatchWithSource { - expected: lhs_type.clone(), - actual: rhs_type.clone(), - source: Source::ArrayElements, - span: op.location.span, - }); + (Bool, Bool) => Ok((Bool, false)), + (String(x_size), String(y_size)) => { self.unify(x_size, y_size, || TypeCheckError::TypeMismatchWithSource { - expected: lhs_type.clone(), - actual: rhs_type.clone(), - source: Source::ArrayLen, + expected: *x_size.clone(), + actual: *y_size.clone(), span: op.location.span, + source: Source::StringLen, }); - Ok(Bool) + Ok((Bool, true)) } - (lhs @ NamedGeneric(binding_a, _), rhs @ NamedGeneric(binding_b, _)) => { - if binding_a == binding_b { - return Ok(Bool); - } - Err(TypeCheckError::TypeMismatchWithSource { + (lhs, rhs) => { + self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { expected: lhs.clone(), actual: rhs.clone(), - source: Source::Comparison, - span, - }) - } - (String(x_size), String(y_size)) => { - self.unify(x_size, y_size, || TypeCheckError::TypeMismatchWithSource { - expected: *x_size.clone(), - actual: *y_size.clone(), span: op.location.span, - source: Source::StringLen, + source: Source::Binary, }); - - Ok(Bool) + Ok((Bool, true)) } - (lhs, rhs) => Err(TypeCheckError::TypeMismatchWithSource { - expected: lhs.clone(), - actual: rhs.clone(), - source: Source::Comparison, - span, - }), } } @@ -1043,13 +1025,16 @@ impl<'interner> TypeChecker<'interner> { } // Given a binary operator and another type. This method will produce the output type + // and a boolean indicating whether to use the trait impl corresponding to the operator + // or not. A value of false indicates the caller to use a primitive operation for this + // operator, while a true value indicates a user-provided trait impl is required. fn infix_operand_type_rules( &mut self, lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, span: Span, - ) -> Result { + ) -> Result<(Type, bool), TypeCheckError> { if op.kind.is_comparator() { return self.comparator_operand_type_rules(lhs_type, rhs_type, op, span); } @@ -1057,7 +1042,7 @@ impl<'interner> TypeChecker<'interner> { use Type::*; match (lhs_type, rhs_type) { // An error type on either side will always return an error - (Error, _) | (_, Error) => Ok(Error), + (Error, _) | (_, Error) => Ok((Error, false)), // Matches on TypeVariable must be first so that we follow any type // bindings. @@ -1098,7 +1083,7 @@ impl<'interner> TypeChecker<'interner> { || other == &Type::Error { Type::apply_type_bindings(bindings); - Ok(other.clone()) + Ok((other.clone(), false)) } else { Err(TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), @@ -1128,26 +1113,9 @@ impl<'interner> TypeChecker<'interner> { { Err(TypeCheckError::InvalidInfixOp { kind: "Signed integer", span }) } else { - Ok(Integer(*sign_x, *bit_width_x)) + Ok((Integer(*sign_x, *bit_width_x), false)) } } - (Integer(..), FieldElement) | (FieldElement, Integer(..)) => { - Err(TypeCheckError::IntegerAndFieldBinaryOperation { span }) - } - (Integer(..), typ) | (typ, Integer(..)) => { - Err(TypeCheckError::IntegerTypeMismatch { typ: typ.clone(), span }) - } - // These types are not supported in binary operations - (Array(..), _) | (_, Array(..)) => { - Err(TypeCheckError::InvalidInfixOp { kind: "Arrays", span }) - } - (Struct(..), _) | (_, Struct(..)) => { - Err(TypeCheckError::InvalidInfixOp { kind: "Structs", span }) - } - (Tuple(_), _) | (_, Tuple(_)) => { - Err(TypeCheckError::InvalidInfixOp { kind: "Tuples", span }) - } - // The result of two Fields is always a witness (FieldElement, FieldElement) => { if op.is_bitwise() { @@ -1156,17 +1124,20 @@ impl<'interner> TypeChecker<'interner> { if op.is_modulo() { return Err(TypeCheckError::FieldModulo { span }); } - Ok(FieldElement) + Ok((FieldElement, false)) } - (Bool, Bool) => Ok(Bool), + (Bool, Bool) => Ok((Bool, false)), - (lhs, rhs) => Err(TypeCheckError::TypeMismatchWithSource { - expected: lhs.clone(), - actual: rhs.clone(), - source: Source::BinOp(op.kind), - span, - }), + (lhs, rhs) => { + self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { + expected: lhs.clone(), + actual: rhs.clone(), + span: op.location.span, + source: Source::Binary, + }); + Ok((lhs.clone(), true)) + } } } diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index a2112706c41..ea6ecf30d12 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -2,7 +2,9 @@ use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; -use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, TraitId}; +use crate::node_interner::{ + DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitMethodId, +}; use crate::{BinaryOp, BinaryOpKind, Ident, Shared, UnaryOp}; use super::stmt::HirPattern; diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 78cde11593b..7ee4496c1dc 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -354,11 +354,34 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Infix(infix) => { - let lhs = Box::new(self.expr(infix.lhs)); - let rhs = Box::new(self.expr(infix.rhs)); + let lhs = self.expr(infix.lhs); + let rhs = self.expr(infix.rhs); let operator = infix.operator.kind; let location = self.interner.expr_location(&expr); - ast::Expression::Binary(ast::Binary { lhs, rhs, operator, location }) + + if self.interner.get_selected_impl_for_expression(expr).is_some() { + // If an impl was selected for this infix operator, replace it + // with a method call to the appropriate trait impl method. + let lhs_type = self.interner.id_type(infix.lhs); + let args = vec![lhs_type.clone(), lhs_type]; + let ret = self.interner.id_type(expr); + let env = Box::new(Type::Unit); + let function_type = Type::Function(args, Box::new(ret.clone()), env); + + let method = TraitMethodId { trait_id: infix.trait_id, method_index: 0 }; + let func = self.resolve_trait_method_reference(expr, function_type, method); + + ast::Expression::Call(ast::Call { + func: Box::new(func), + arguments: vec![lhs, rhs], + return_type: self.convert_type(&ret), + location, + }) + } else { + let lhs = Box::new(lhs); + let rhs = Box::new(rhs); + ast::Expression::Binary(ast::Binary { lhs, rhs, operator, location }) + } } HirExpression::Index(index) => self.index(expr, index), @@ -398,13 +421,8 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Lambda(lambda) => self.lambda(lambda, expr), HirExpression::TraitMethodReference(method) => { - if let Type::Function(_, _, _) = self.interner.id_type(expr) { - self.resolve_trait_method_reference(expr, method) - } else { - unreachable!( - "Calling a non-function, this should've been caught in typechecking" - ); - } + let function_type = self.interner.id_type(expr); + self.resolve_trait_method_reference(expr, function_type, method) } HirExpression::MethodCall(hir_method_call) => { @@ -824,13 +842,12 @@ impl<'interner> Monomorphizer<'interner> { fn resolve_trait_method_reference( &mut self, expr_id: node_interner::ExprId, + function_type: HirType, method: TraitMethodId, ) -> ast::Expression { - let function_type = self.interner.id_type(expr_id); - let trait_impl = self .interner - .get_selected_impl_for_ident(expr_id) + .get_selected_impl_for_expression(expr_id) .expect("ICE: missing trait impl - should be caught during type checking"); let hir_func_id = match trait_impl { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 90df8cce38b..6953efba2c6 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -21,8 +21,8 @@ use crate::hir_def::{ }; use crate::token::{Attributes, SecondaryAttribute}; use crate::{ - ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, TypeAliasType, - TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, BinaryOpKind, + BinaryOpKind, ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, + TypeAliasType, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -1258,13 +1258,12 @@ impl NodeInterner { /// Tags the given identifier with the selected trait_impl so that monomorphization /// can later recover which impl was selected, or alternatively see if it needs to /// decide which impl to select (because the impl was Assumed). - pub fn select_impl_for_ident(&mut self, ident_id: ExprId, trait_impl: TraitImplKind) { + pub fn select_impl_for_expression(&mut self, ident_id: ExprId, trait_impl: TraitImplKind) { self.selected_trait_implementations.insert(ident_id, trait_impl); } - /// Retrieves the impl selected for a given IdentId during name resolution. - /// From type checking and on, the "ident" referred to is changed to a TraitMethodReference node. - pub fn get_selected_impl_for_ident(&self, ident_id: ExprId) -> Option { + /// Retrieves the impl selected for a given ExprId during name resolution. + pub fn get_selected_impl_for_expression(&self, ident_id: ExprId) -> Option { self.selected_trait_implementations.get(&ident_id).cloned() } @@ -1298,9 +1297,6 @@ impl NodeInterner { } HirExpression::Constructor(expr) => { let struct_type = &expr.r#type.borrow(); - - eprintln!("\n -> Resolve Constructor {struct_type:?}\n"); - Some(struct_type.location) } HirExpression::MemberAccess(expr_member_access) => { @@ -1392,12 +1388,12 @@ impl NodeInterner { match operator { BinaryOpKind::Equal => { self.operator_traits.insert(BinaryOpKind::NotEqual, trait_id); - }, + } BinaryOpKind::Less => { self.operator_traits.insert(BinaryOpKind::LessEqual, trait_id); self.operator_traits.insert(BinaryOpKind::Greater, trait_id); self.operator_traits.insert(BinaryOpKind::GreaterEqual, trait_id); - }, + } _ => (), } } From c5be5e58eff93e38d901971046ae7704c4625d13 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 15 Dec 2023 14:34:44 -0600 Subject: [PATCH 05/21] Everything is broken now --- .../src/hir/resolution/resolver.rs | 6 +- .../noirc_frontend/src/hir/type_check/expr.rs | 67 +- .../noirc_frontend/src/hir/type_check/mod.rs | 7 +- .../src/monomorphization/mod.rs | 74 +- compiler/noirc_frontend/src/node_interner.rs | 23 + compiler/noirc_frontend/src/tests.rs | 2 + noir_stdlib/src/cmp.nr | 8 + .../operator_overloading/Nargo.toml | 7 + .../operator_overloading/Prover.toml | 2 + .../operator_overloading/fail | 1064 +++++++++++++++++ .../operator_overloading/src/main.nr | 152 +++ .../operator_overloading/success | 664 ++++++++++ 12 files changed, 2055 insertions(+), 21 deletions(-) create mode 100644 test_programs/execution_success/operator_overloading/Nargo.toml create mode 100644 test_programs/execution_success/operator_overloading/Prover.toml create mode 100644 test_programs/execution_success/operator_overloading/fail create mode 100644 test_programs/execution_success/operator_overloading/src/main.nr create mode 100644 test_programs/execution_success/operator_overloading/success diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index aa50f203c10..d3175f3d5c3 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1253,8 +1253,10 @@ impl<'a> Resolver<'a> { if let Some((hir_expr, object_type)) = self.resolve_trait_generic_path(&path) { let expr_id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(expr_id, expr.span, self.file); - self.interner - .select_impl_for_expression(expr_id, TraitImplKind::Assumed { object_type }); + self.interner.select_impl_for_expression( + expr_id, + TraitImplKind::Assumed { object_type }, + ); return expr_id; } else { // If the Path is being used as an Expression, then it is referring to a global from a separate module diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 385c0ba6caa..6c4d9186689 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -142,12 +142,7 @@ impl<'interner> TypeChecker<'interner> { if use_impl { let trait_id = infix_expr.trait_id; self.verify_trait_constraint(&lhs_type, trait_id, *expr_id, span); - - // Monomorphization will later look for type bindings for this impl - // even though it has none (no operator traits have generic methods), - // so we have to insert empty bindings here. - let no_bindings = TypeBindings::new(); - self.interner.store_instantiation_bindings(*expr_id, no_bindings); + self.typecheck_operator_method(*expr_id, trait_id, &lhs_type, span); } typ } @@ -317,8 +312,10 @@ impl<'interner> TypeChecker<'interner> { the_trait.self_type_typevar_id, (the_trait.self_type_typevar.clone(), object_type.clone()), ); - self.interner - .select_impl_for_expression(*expr_id, TraitImplKind::Assumed { object_type }); + self.interner.select_impl_for_expression( + *expr_id, + TraitImplKind::Assumed { object_type }, + ); } self.interner.store_instantiation_bindings(*expr_id, bindings); @@ -1189,6 +1186,60 @@ impl<'interner> TypeChecker<'interner> { } } } + + /// Prerequisite: verify_trait_constraint of the operator's trait constraint. + /// + /// Although by this point the operator is expected to already have a trait impl, + /// we still need to match the operator's type against the method's instantiated type + /// to ensure the instantiation bindings are correct and the monomorphizer can + /// re-apply the needed bindings. + fn typecheck_operator_method( + &mut self, + expr_id: ExprId, + trait_id: TraitId, + object_type: &Type, + span: Span, + ) { + let the_trait = self.interner.get_trait(trait_id); + + // The first (and only) method of each operator trait should be the operator method + let method = &the_trait.methods[0]; + let (method_type, mut bindings) = method.typ.instantiate(&self.interner); + + match method_type { + Type::Function(args, _, _) => { + // We can cheat a bit and match against only the object type here since no operator + // overload uses other generic parameters or return types aside from the object type. + let expected_object_type = &args[0]; + self.unify(object_type, expected_object_type, || TypeCheckError::TypeMismatch { + expected_typ: expected_object_type.to_string(), + expr_typ: object_type.to_string(), + expr_span: span, + }); + } + other => { + unreachable!("Expected operator method to have a function type, but found {other}") + } + } + + // We must also remember to apply these substitutions to the object_type + // referenced by the selected trait impl, if one has yet to be selected. + let impl_kind = self.interner.get_selected_impl_for_expression(expr_id); + if let Some(TraitImplKind::Assumed { object_type }) = impl_kind { + let the_trait = self.interner.get_trait(trait_id); + let object_type = object_type.substitute(&bindings); + bindings.insert( + the_trait.self_type_typevar_id, + (the_trait.self_type_typevar.clone(), object_type.clone()), + ); + self.interner.select_impl_for_expression( + expr_id, + TraitImplKind::Assumed { object_type }, + ); + } + + self.interner.store_instantiation_bindings(expr_id, bindings); + } } /// Taken from: https://stackoverflow.com/a/47127500 diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 95991047091..638b2c9d95a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -241,7 +241,7 @@ mod test { function::{FuncMeta, HirFunction}, stmt::HirStatement, }; - use crate::node_interner::{DefinitionKind, FuncId, NodeInterner}; + use crate::node_interner::{DefinitionKind, FuncId, NodeInterner, TraitId}; use crate::{ hir::{ def_map::{CrateDefMap, LocalModuleId, ModuleDefId}, @@ -254,6 +254,7 @@ mod test { #[test] fn basic_let() { let mut interner = NodeInterner::default(); + interner.populate_dummy_operator_traits(); // Safety: The FileId in a location isn't used for tests let file = FileId::default(); @@ -284,7 +285,8 @@ mod test { // Create Infix let operator = HirBinaryOp { location, kind: BinaryOpKind::Add }; - let expr = HirInfixExpression { lhs: x_expr_id, operator, rhs: y_expr_id }; + let trait_id = TraitId(ModuleId::dummy_id()); + let expr = HirInfixExpression { lhs: x_expr_id, operator, rhs: y_expr_id, trait_id }; let expr_id = interner.push_expr(HirExpression::Infix(expr)); interner.push_expr_location(expr_id, Span::single_char(0), file); @@ -469,6 +471,7 @@ mod test { ) { let (program, errors) = parse_program(src); let mut interner = NodeInterner::default(); + interner.populate_dummy_operator_traits(); assert_eq!( errors.len(), diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 7ee4496c1dc..55beb9126ac 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -26,7 +26,7 @@ use crate::{ }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, token::FunctionAttribute, - ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariableKind, + ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariableKind, UnaryOp, Visibility, }; @@ -236,6 +236,7 @@ impl<'interner> Monomorphizer<'interner> { }); let parameters = self.parameters(meta.parameters); + let body = self.expr(body_expr_id); let unconstrained = modifiers.is_unconstrained || matches!(modifiers.contract_function_type, Some(ContractFunctionType::Open)); @@ -370,13 +371,7 @@ impl<'interner> Monomorphizer<'interner> { let method = TraitMethodId { trait_id: infix.trait_id, method_index: 0 }; let func = self.resolve_trait_method_reference(expr, function_type, method); - - ast::Expression::Call(ast::Call { - func: Box::new(func), - arguments: vec![lhs, rhs], - return_type: self.convert_type(&ret), - location, - }) + self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location) } else { let lhs = Box::new(lhs); let rhs = Box::new(rhs); @@ -1102,7 +1097,7 @@ impl<'interner> Monomorphizer<'interner> { function_type: HirType, ) -> FuncId { let new_id = self.next_function_id(); - self.define_global(id, function_type, new_id); + self.define_global(id, function_type.clone(), new_id); let bindings = self.interner.get_instantiation_bindings(expr_id); let bindings = self.follow_bindings(bindings); @@ -1432,6 +1427,67 @@ impl<'interner> Monomorphizer<'interner> { ), }) } + + /// Call an operator overloading method for the given operator. + /// This function handles the special cases some operators have which don't map + /// 1 to 1 onto their operator function. For example: != requires a negation on + /// the result of its `eq` method, and the comparison operators each require a + /// conversion from the `Ordering` result to a boolean. + fn create_operator_impl_call( + &self, + func: ast::Expression, + lhs: ast::Expression, + operator: HirBinaryOp, + rhs: ast::Expression, + ret: Type, + location: Location, + ) -> ast::Expression { + let arguments = vec![lhs, rhs]; + let func = Box::new(func); + let return_type = self.convert_type(&ret); + + let mut result = + ast::Expression::Call(ast::Call { func, arguments, return_type, location }); + + use crate::BinaryOpKind::*; + match operator.kind { + // Negate the result of the == operation + NotEqual => { + result = ast::Expression::Unary(ast::Unary { + operator: UnaryOp::Not, + rhs: Box::new(result), + result_type: ast::Type::Bool, + location, + }); + } + // All the comparison operators require special handling since their `cmp` method + // returns an `Ordering` rather than a boolean value. + // + // (a < b) => a.cmp(b) == Ordering::Less + // (a <= b) => a.cmp(b) != Ordering::Greater + // (a > b) => a.cmp(b) == Ordering::Greater + // (a >= b) => a.cmp(b) != Ordering::Less + Less | LessEqual | Greater | GreaterEqual => { + let ordering_value = if matches!(operator.kind, Less | GreaterEqual) { + FieldElement::zero() // Ordering::Less + } else { + 2u128.into() // Ordering::Greater + }; + + let operator = + if matches!(operator.kind, Less | Greater) { Equal } else { NotEqual }; + + let int_value = ast::Literal::Integer(ordering_value, ast::Type::Field, location); + let rhs = Box::new(ast::Expression::Literal(int_value)); + let lhs = Box::new(result); + + result = ast::Expression::Binary(ast::Binary { lhs, operator, rhs, location }); + } + _ => (), + } + + result + } } fn unwrap_tuple_type(typ: &HirType) -> Vec { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 6953efba2c6..8c3181f9420 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1397,6 +1397,29 @@ impl NodeInterner { _ => (), } } + + /// This function is needed when creating a NodeInterner for testing so that calls + /// to `get_operator_trait` do not panic when the stdlib isn't present. + #[cfg(test)] + pub fn populate_dummy_operator_traits(&mut self) { + let dummy_trait = TraitId(ModuleId::dummy_id()); + self.operator_traits.insert(BinaryOpKind::Add, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Subtract, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Multiply, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Divide, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Modulo, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Equal, dummy_trait); + self.operator_traits.insert(BinaryOpKind::NotEqual, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Less, dummy_trait); + self.operator_traits.insert(BinaryOpKind::LessEqual, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Greater, dummy_trait); + self.operator_traits.insert(BinaryOpKind::GreaterEqual, dummy_trait); + self.operator_traits.insert(BinaryOpKind::And, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Or, dummy_trait); + self.operator_traits.insert(BinaryOpKind::Xor, dummy_trait); + self.operator_traits.insert(BinaryOpKind::ShiftLeft, dummy_trait); + self.operator_traits.insert(BinaryOpKind::ShiftRight, dummy_trait); + } } impl Methods { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 3f4755aa0ef..e846a984ee1 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -55,6 +55,8 @@ mod test { let fm = FileManager::new(root); let graph = CrateGraph::default(); let mut context = Context::new(fm, graph); + context.def_interner.populate_dummy_operator_traits(); + let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); let (program, parser_errors) = parse_program(src); diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index d2db68803d0..21fd017b6c4 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -28,6 +28,14 @@ impl Eq for [T; N] where T: Eq { } } +impl Eq for str { + fn eq(self, other: str) -> bool { + let self_bytes = self.as_bytes(); + let other_bytes = other.as_bytes(); + self_bytes == other_bytes + } +} + impl Eq for (A, B) where A: Eq, B: Eq { fn eq(self, other: (A, B)) -> bool { self.0.eq(other.0) & self.1.eq(other.1) diff --git a/test_programs/execution_success/operator_overloading/Nargo.toml b/test_programs/execution_success/operator_overloading/Nargo.toml new file mode 100644 index 00000000000..7f9f18ff567 --- /dev/null +++ b/test_programs/execution_success/operator_overloading/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "operator_overloading" +type = "bin" +authors = [""] +compiler_version = ">=0.20.0" + +[dependencies] diff --git a/test_programs/execution_success/operator_overloading/Prover.toml b/test_programs/execution_success/operator_overloading/Prover.toml new file mode 100644 index 00000000000..516b7b4074c --- /dev/null +++ b/test_programs/execution_success/operator_overloading/Prover.toml @@ -0,0 +1,2 @@ +x = 3 +y = 9 diff --git a/test_programs/execution_success/operator_overloading/fail b/test_programs/execution_success/operator_overloading/fail new file mode 100644 index 00000000000..f3ead5e705e --- /dev/null +++ b/test_programs/execution_success/operator_overloading/fail @@ -0,0 +1,1064 @@ +Initial SSA: +acir fn main f0 { + b0(v0: u32, v1: u32): + v3 = call f1(v0) + v5 = call f1(v1) + v7 = call f2(v3, v5, v5, v3) + constrain v7 == Field 0 + v10 = call f2(v5, v3, v3, v5) + constrain v10 == Field 2 + return +} +acir fn new f1 { + b0(v0: u32): + return v0 +} +acir fn cmp f2 { + b0(v0: u32, v1: u32, v2: u32, v3: u32): + v5 = call f3(v0, v2) + v6 = allocate + store v5 at v6 + v8 = load v6 + v10 = call f5() + v11 = call f4(v8, v10) + jmpif v11 then: b1, else: b2 + b1(): + v13 = call f3(v1, v3) + store v13 at v6 + jmp b2() + b2(): + v14 = load v6 + return v14 +} +acir fn cmp f3 { + b0(v0: u32, v1: u32): + v3 = call f6(v0, v1) + return v3 +} +acir fn eq f4 { + b0(v0: Field, v1: Field): + v2 = eq v0, v1 + return v2 +} +acir fn equal f5 { + b0(): + return Field 1 +} +acir fn cmp f6 { + b0(v0: u32, v1: u32): + v2 = lt v0, v1 + jmpif v2 then: b1, else: b2 + b1(): + v4 = call f7() + jmp b3(v4) + b3(v11: Field): + return v11 + b2(): + v5 = lt v1, v0 + jmpif v5 then: b4, else: b5 + b4(): + v7 = call f8() + jmp b6(v7) + b6(v10: Field): + jmp b3(v10) + b5(): + v9 = call f5() + jmp b6(v9) +} +acir fn less f7 { + b0(): + return Field 0 +} +acir fn greater f8 { + b0(): + return Field 2 +} + +After Defunctionalization: +acir fn main f0 { + b0(v0: u32, v1: u32): + v3 = call f1(v0) + v5 = call f1(v1) + v7 = call f2(v3, v5, v5, v3) + constrain v7 == Field 0 + v10 = call f2(v5, v3, v3, v5) + constrain v10 == Field 2 + return +} +acir fn new f1 { + b0(v0: u32): + return v0 +} +acir fn cmp f2 { + b0(v0: u32, v1: u32, v2: u32, v3: u32): + v5 = call f3(v0, v2) + v6 = allocate + store v5 at v6 + v8 = load v6 + v10 = call f5() + v11 = call f4(v8, v10) + jmpif v11 then: b1, else: b2 + b1(): + v13 = call f3(v1, v3) + store v13 at v6 + jmp b2() + b2(): + v14 = load v6 + return v14 +} +acir fn cmp f3 { + b0(v0: u32, v1: u32): + v3 = call f6(v0, v1) + return v3 +} +acir fn eq f4 { + b0(v0: Field, v1: Field): + v2 = eq v0, v1 + return v2 +} +acir fn equal f5 { + b0(): + return Field 1 +} +acir fn cmp f6 { + b0(v0: u32, v1: u32): + v2 = lt v0, v1 + jmpif v2 then: b1, else: b2 + b1(): + v4 = call f7() + jmp b3(v4) + b3(v11: Field): + return v11 + b2(): + v5 = lt v1, v0 + jmpif v5 then: b4, else: b5 + b4(): + v7 = call f8() + jmp b6(v7) + b6(v10: Field): + jmp b3(v10) + b5(): + v9 = call f5() + jmp b6(v9) +} +acir fn less f7 { + b0(): + return Field 0 +} +acir fn greater f8 { + b0(): + return Field 2 +} + +After Inlining: +acir fn main f0 { + b0(v0: u32, v1: u32): + v7 = lt v0, v1 + jmpif v7 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v17 = allocate + store v12 at v17 + v18 = load v17 + v21 = eq v18, Field 1 + jmpif v21 then: b7, else: b8 + b7(): + v25 = lt v1, v0 + jmpif v25 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v17 + jmp b8() + b8(): + v22 = load v17 + constrain v22 == Field 0 + v35 = lt v1, v0 + jmpif v35 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v42 = allocate + store v39 at v42 + v43 = load v42 + v46 = eq v43, Field 1 + jmpif v46 then: b21, else: b22 + b21(): + v50 = lt v0, v1 + jmpif v50 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v42 + jmp b22() + b22(): + v47 = load v42 + constrain v47 == Field 2 + return + b24(): + v51 = lt v1, v0 + jmpif v51 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v36 = lt v0, v1 + jmpif v36 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v26 = lt v0, v1 + jmpif v26 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v8 = lt v1, v0 + jmpif v8 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v57 = lt v0, v1 + jmpif v57 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v59 = allocate + store v12 at v59 + v61 = eq v12, Field 1 + jmpif v61 then: b7, else: b8 + b7(): + v62 = lt v1, v0 + jmpif v62 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v59 + jmp b8() + b8(): + v64 = load v59 + constrain v64 == Field 0 + v65 = lt v1, v0 + jmpif v65 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v67 = allocate + store v39 at v67 + v69 = eq v39, Field 1 + jmpif v69 then: b21, else: b22 + b21(): + v70 = lt v0, v1 + jmpif v70 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v67 + jmp b22() + b22(): + v72 = load v67 + constrain v72 == Field 2 + return + b24(): + v71 = lt v1, v0 + jmpif v71 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v66 = lt v0, v1 + jmpif v66 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v63 = lt v0, v1 + jmpif v63 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v58 = lt v1, v0 + jmpif v58 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Assert Constant: +acir fn main f0 { + b0(v0: u32, v1: u32): + v57 = lt v0, v1 + jmpif v57 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v59 = allocate + store v12 at v59 + v61 = eq v12, Field 1 + jmpif v61 then: b7, else: b8 + b7(): + v62 = lt v1, v0 + jmpif v62 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v59 + jmp b8() + b8(): + v64 = load v59 + constrain v64 == Field 0 + v65 = lt v1, v0 + jmpif v65 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v67 = allocate + store v39 at v67 + v69 = eq v39, Field 1 + jmpif v69 then: b21, else: b22 + b21(): + v70 = lt v0, v1 + jmpif v70 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v67 + jmp b22() + b22(): + v72 = load v67 + constrain v72 == Field 2 + return + b24(): + v71 = lt v1, v0 + jmpif v71 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v66 = lt v0, v1 + jmpif v66 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v63 = lt v0, v1 + jmpif v63 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v58 = lt v1, v0 + jmpif v58 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Unrolling: +acir fn main f0 { + b0(v0: u32, v1: u32): + v57 = lt v0, v1 + jmpif v57 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v59 = allocate + store v12 at v59 + v61 = eq v12, Field 1 + jmpif v61 then: b7, else: b8 + b7(): + v62 = lt v1, v0 + jmpif v62 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v59 + jmp b8() + b8(): + v64 = load v59 + constrain v64 == Field 0 + v65 = lt v1, v0 + jmpif v65 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v67 = allocate + store v39 at v67 + v69 = eq v39, Field 1 + jmpif v69 then: b21, else: b22 + b21(): + v70 = lt v0, v1 + jmpif v70 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v67 + jmp b22() + b22(): + v72 = load v67 + constrain v72 == Field 2 + return + b24(): + v71 = lt v1, v0 + jmpif v71 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v66 = lt v0, v1 + jmpif v66 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v63 = lt v0, v1 + jmpif v63 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v58 = lt v1, v0 + jmpif v58 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Simplifying: +acir fn main f0 { + b0(v0: u32, v1: u32): + v57 = lt v0, v1 + jmpif v57 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v59 = allocate + store v12 at v59 + v61 = eq v12, Field 1 + jmpif v61 then: b7, else: b8 + b7(): + v62 = lt v1, v0 + jmpif v62 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v59 + jmp b8() + b8(): + v64 = load v59 + constrain v64 == Field 0 + v65 = lt v1, v0 + jmpif v65 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v67 = allocate + store v39 at v67 + v69 = eq v39, Field 1 + jmpif v69 then: b21, else: b22 + b21(): + v70 = lt v0, v1 + jmpif v70 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v67 + jmp b22() + b22(): + v72 = load v67 + constrain v72 == Field 2 + return + b24(): + v71 = lt v1, v0 + jmpif v71 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v66 = lt v0, v1 + jmpif v66 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v63 = lt v0, v1 + jmpif v63 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v58 = lt v1, v0 + jmpif v58 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v74 = lt v0, v1 + jmpif v74 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v76 = allocate + store v12 at v76 + v77 = eq v12, Field 1 + jmpif v77 then: b7, else: b8 + b7(): + v78 = lt v1, v0 + jmpif v78 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v76 + jmp b8() + b8(): + v80 = load v76 + constrain v80 == Field 0 + v81 = lt v1, v0 + jmpif v81 then: b15, else: b16 + b15(): + jmp b20(Field 0) + b20(v39: Field): + v83 = allocate + store v39 at v83 + v84 = eq v39, Field 1 + jmpif v84 then: b21, else: b22 + b21(): + v85 = lt v0, v1 + jmpif v85 then: b23, else: b24 + b23(): + jmp b28(Field 0) + b28(v54: Field): + store v54 at v83 + jmp b22() + b22(): + v87 = load v83 + constrain v87 == Field 2 + return + b24(): + v86 = lt v1, v0 + jmpif v86 then: b25, else: b26 + b25(): + jmp b27(Field 2) + b27(v53: Field): + jmp b28(v53) + b26(): + jmp b27(Field 1) + b16(): + v82 = lt v0, v1 + jmpif v82 then: b17, else: b18 + b17(): + jmp b19(Field 2) + b19(v38: Field): + jmp b20(v38) + b18(): + jmp b19(Field 1) + b10(): + v79 = lt v0, v1 + jmpif v79 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v75 = lt v1, v0 + jmpif v75 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Flattening: +acir fn main f0 { + b0(v0: u32, v1: u32): + v74 = lt v0, v1 + enable_side_effects v74 + v89 = not v74 + enable_side_effects v89 + v91 = lt v1, v0 + v92 = mul v89, v91 + enable_side_effects v92 + v93 = not v91 + v94 = mul v89, v93 + enable_side_effects v89 + v95 = cast v91 as Field + v96 = cast v93 as Field + v97 = mul v95, Field 2 + v98 = add v97, v96 + enable_side_effects u1 1 + v99 = cast v74 as Field + v100 = cast v89 as Field + v101 = mul v100, v98 + v102 = allocate + store v101 at v102 + v103 = eq v101, Field 1 + enable_side_effects v103 + v104 = lt v1, v0 + v105 = mul v103, v104 + enable_side_effects v105 + v106 = not v104 + v107 = mul v103, v106 + enable_side_effects v107 + v108 = lt v0, v1 + v109 = mul v107, v108 + enable_side_effects v109 + v110 = not v108 + v111 = mul v107, v110 + enable_side_effects v107 + v112 = cast v108 as Field + v113 = cast v110 as Field + v114 = mul v112, Field 2 + v115 = add v114, v113 + enable_side_effects v103 + v116 = cast v104 as Field + v117 = cast v106 as Field + v118 = mul v117, v115 + v119 = load v102 + store v118 at v102 + v120 = not v103 + store v119 at v102 + enable_side_effects u1 1 + v121 = cast v103 as Field + v122 = cast v120 as Field + v123 = mul v121, v118 + v124 = mul v122, v119 + v125 = add v123, v124 + store v125 at v102 + v126 = load v102 + constrain v126 == Field 0 + v127 = lt v1, v0 + enable_side_effects v127 + v128 = not v127 + enable_side_effects v128 + v129 = lt v0, v1 + v130 = mul v128, v129 + enable_side_effects v130 + v131 = not v129 + v132 = mul v128, v131 + enable_side_effects v128 + v133 = cast v129 as Field + v134 = cast v131 as Field + v135 = mul v133, Field 2 + v136 = add v135, v134 + enable_side_effects u1 1 + v137 = cast v127 as Field + v138 = cast v128 as Field + v139 = mul v138, v136 + v140 = allocate + store v139 at v140 + v141 = eq v139, Field 1 + enable_side_effects v141 + v142 = lt v0, v1 + v143 = mul v141, v142 + enable_side_effects v143 + v144 = not v142 + v145 = mul v141, v144 + enable_side_effects v145 + v146 = lt v1, v0 + v147 = mul v145, v146 + enable_side_effects v147 + v148 = not v146 + v149 = mul v145, v148 + enable_side_effects v145 + v150 = cast v146 as Field + v151 = cast v148 as Field + v152 = mul v150, Field 2 + v153 = add v152, v151 + enable_side_effects v141 + v154 = cast v142 as Field + v155 = cast v144 as Field + v156 = mul v155, v153 + v157 = load v140 + store v156 at v140 + v158 = not v141 + store v157 at v140 + enable_side_effects u1 1 + v159 = cast v141 as Field + v160 = cast v158 as Field + v161 = mul v159, v156 + v162 = mul v160, v157 + v163 = add v161, v162 + store v163 at v140 + v164 = load v140 + constrain v164 == Field 2 + return +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v165 = lt v0, v1 + enable_side_effects v165 + v166 = not v165 + enable_side_effects v166 + v167 = lt v1, v0 + v168 = mul v166, v167 + enable_side_effects v168 + v169 = not v167 + v170 = mul v166, v169 + enable_side_effects v166 + v171 = cast v167 as Field + v172 = cast v169 as Field + v173 = mul v171, Field 2 + v174 = add v173, v172 + enable_side_effects u1 1 + v175 = cast v165 as Field + v176 = cast v166 as Field + v177 = mul v176, v174 + v178 = allocate + v179 = eq v177, Field 1 + enable_side_effects v179 + v180 = lt v1, v0 + v181 = mul v179, v180 + enable_side_effects v181 + v182 = not v180 + v183 = mul v179, v182 + enable_side_effects v183 + v184 = lt v0, v1 + v185 = mul v183, v184 + enable_side_effects v185 + v186 = not v184 + v187 = mul v183, v186 + enable_side_effects v183 + v188 = cast v184 as Field + v189 = cast v186 as Field + v190 = mul v188, Field 2 + v191 = add v190, v189 + enable_side_effects v179 + v192 = cast v180 as Field + v193 = cast v182 as Field + v194 = mul v193, v191 + v196 = not v179 + enable_side_effects u1 1 + v197 = cast v179 as Field + v198 = cast v196 as Field + v199 = mul v197, v194 + v200 = mul v198, v177 + v201 = add v199, v200 + constrain v201 == Field 0 + v203 = lt v1, v0 + enable_side_effects v203 + v204 = not v203 + enable_side_effects v204 + v205 = lt v0, v1 + v206 = mul v204, v205 + enable_side_effects v206 + v207 = not v205 + v208 = mul v204, v207 + enable_side_effects v204 + v209 = cast v205 as Field + v210 = cast v207 as Field + v211 = mul v209, Field 2 + v212 = add v211, v210 + enable_side_effects u1 1 + v213 = cast v203 as Field + v214 = cast v204 as Field + v215 = mul v214, v212 + v216 = allocate + v217 = eq v215, Field 1 + enable_side_effects v217 + v218 = lt v0, v1 + v219 = mul v217, v218 + enable_side_effects v219 + v220 = not v218 + v221 = mul v217, v220 + enable_side_effects v221 + v222 = lt v1, v0 + v223 = mul v221, v222 + enable_side_effects v223 + v224 = not v222 + v225 = mul v221, v224 + enable_side_effects v221 + v226 = cast v222 as Field + v227 = cast v224 as Field + v228 = mul v226, Field 2 + v229 = add v228, v227 + enable_side_effects v217 + v230 = cast v218 as Field + v231 = cast v220 as Field + v232 = mul v231, v229 + v234 = not v217 + enable_side_effects u1 1 + v235 = cast v217 as Field + v236 = cast v234 as Field + v237 = mul v235, v232 + v238 = mul v236, v215 + v239 = add v237, v238 + constrain v239 == Field 2 + return +} + +After Constant Folding: +acir fn main f0 { + b0(v0: u32, v1: u32): + v241 = lt v0, v1 + enable_side_effects v241 + v242 = not v241 + enable_side_effects v242 + v243 = lt v1, v0 + v244 = mul v242, v243 + enable_side_effects v244 + v245 = not v243 + v246 = mul v242, v245 + enable_side_effects v242 + v247 = cast v243 as Field + v248 = cast v245 as Field + v249 = mul v247, Field 2 + v250 = add v249, v248 + enable_side_effects u1 1 + v251 = cast v241 as Field + v252 = cast v242 as Field + v253 = mul v252, v250 + v254 = allocate + v255 = eq v253, Field 1 + enable_side_effects v255 + v256 = mul v255, v243 + enable_side_effects v256 + v257 = mul v255, v245 + enable_side_effects v257 + v258 = mul v257, v241 + enable_side_effects v258 + v259 = mul v257, v242 + enable_side_effects v257 + v260 = mul v251, Field 2 + v261 = add v260, v252 + enable_side_effects v255 + v262 = mul v248, v261 + v263 = not v255 + enable_side_effects u1 1 + v264 = cast v255 as Field + v265 = cast v263 as Field + v266 = mul v264, v262 + v267 = mul v265, v253 + v268 = add v266, v267 + constrain v268 == Field 0 + enable_side_effects v245 + v269 = mul v245, v241 + enable_side_effects v269 + v270 = mul v245, v242 + enable_side_effects u1 1 + v271 = allocate + v272 = eq v262, Field 1 + enable_side_effects v272 + v273 = mul v272, v241 + enable_side_effects v273 + v274 = mul v272, v242 + enable_side_effects v274 + v275 = mul v274, v243 + enable_side_effects v275 + v276 = mul v274, v245 + enable_side_effects v272 + v277 = not v272 + enable_side_effects u1 1 + v278 = cast v272 as Field + v279 = cast v277 as Field + v280 = mul v278, v253 + v281 = mul v279, v262 + v282 = add v280, v281 + constrain v282 == Field 2 + return +} + +After Dead Instruction Elimination: +acir fn main f0 { + b0(v0: u32, v1: u32): + v241 = lt v0, v1 + enable_side_effects v241 + v242 = not v241 + enable_side_effects v242 + v243 = lt v1, v0 + v244 = mul v242, v243 + enable_side_effects v244 + v245 = not v243 + enable_side_effects v242 + v247 = cast v243 as Field + v248 = cast v245 as Field + v249 = mul v247, Field 2 + v250 = add v249, v248 + enable_side_effects u1 1 + v251 = cast v241 as Field + v252 = cast v242 as Field + v253 = mul v252, v250 + v255 = eq v253, Field 1 + enable_side_effects v255 + v256 = mul v255, v243 + enable_side_effects v256 + v257 = mul v255, v245 + enable_side_effects v257 + v258 = mul v257, v241 + enable_side_effects v258 + enable_side_effects v257 + v260 = mul v251, Field 2 + v261 = add v260, v252 + enable_side_effects v255 + v262 = mul v248, v261 + v263 = not v255 + enable_side_effects u1 1 + v264 = cast v255 as Field + v265 = cast v263 as Field + v266 = mul v264, v262 + v267 = mul v265, v253 + v268 = add v266, v267 + constrain v268 == Field 0 + enable_side_effects v245 + v269 = mul v245, v241 + enable_side_effects v269 + enable_side_effects u1 1 + v272 = eq v262, Field 1 + enable_side_effects v272 + v273 = mul v272, v241 + enable_side_effects v273 + v274 = mul v272, v242 + enable_side_effects v274 + v275 = mul v274, v243 + enable_side_effects v275 + enable_side_effects v272 + v277 = not v272 + enable_side_effects u1 1 + v278 = cast v272 as Field + v279 = cast v277 as Field + v280 = mul v278, v253 + v281 = mul v279, v262 + v282 = add v280, v281 + constrain v282 == Field 2 + return +} + +After Fill Internal Slice Dummy Data: +acir fn main f0 { + b0(v0: u32, v1: u32): + v283 = lt v0, v1 + enable_side_effects v283 + v284 = not v283 + enable_side_effects v284 + v285 = lt v1, v0 + v286 = mul v284, v285 + enable_side_effects v286 + v287 = not v285 + enable_side_effects v284 + v288 = cast v285 as Field + v289 = cast v287 as Field + v290 = mul v288, Field 2 + v291 = add v290, v289 + enable_side_effects u1 1 + v292 = cast v283 as Field + v293 = cast v284 as Field + v294 = mul v293, v291 + v295 = eq v294, Field 1 + enable_side_effects v295 + v296 = mul v295, v285 + enable_side_effects v296 + v297 = mul v295, v287 + enable_side_effects v297 + v298 = mul v297, v283 + enable_side_effects v297 + v299 = mul v292, Field 2 + v300 = add v299, v293 + enable_side_effects v295 + v301 = mul v289, v300 + v302 = not v295 + enable_side_effects u1 1 + v303 = cast v295 as Field + v304 = cast v302 as Field + v305 = mul v303, v301 + v306 = mul v304, v294 + v307 = add v305, v306 + constrain v307 == Field 0 + enable_side_effects v287 + v308 = mul v287, v283 + enable_side_effects u1 1 + v309 = eq v301, Field 1 + enable_side_effects v309 + v310 = mul v309, v283 + enable_side_effects v310 + v311 = mul v309, v284 + enable_side_effects v311 + v312 = mul v311, v285 + enable_side_effects v309 + v313 = not v309 + enable_side_effects u1 1 + v314 = cast v309 as Field + v315 = cast v313 as Field + v316 = mul v314, v294 + v317 = mul v315, v301 + v318 = add v316, v317 + constrain v318 == Field 2 + return +} + diff --git a/test_programs/execution_success/operator_overloading/src/main.nr b/test_programs/execution_success/operator_overloading/src/main.nr new file mode 100644 index 00000000000..2d3ac35f4a7 --- /dev/null +++ b/test_programs/execution_success/operator_overloading/src/main.nr @@ -0,0 +1,152 @@ + +// x = 3, y = 9 +fn main(x: u32, y: u32) { + let wx = Wrapper::new(x); + let wy = Wrapper::new(y); + + // expected x and expected y values + // let ex: u32 = 3; + // let ey: u32 = 9; + + // assert((wx + wy).inner == ex + ey); + // assert((wy - wx).inner == ey - ex); + // assert((wx * wy).inner == ex * ey); + // assert((wx / wy).inner == ex / ey); + // assert((wx % wy).inner == ex % ey); + + // assert((wx & wy).inner == (ex & ey)); + // assert((wx | wy).inner == (ex | ey)); + // assert((wx ^ wy).inner == (ex ^ ey)); + + // assert((wy << wx).inner == (ey << ex)); + // assert((wy >> wx).inner == (ey >> ex)); + + // assert((wx == wy) == (ex == ey)); + // assert((wx < wy) == (ex < ey)); + // assert((wx <= wy) == (ex <= ey)); + // assert((wx > wy) == (ex > ey)); + // assert((wx >= wy) == (ex >= ey)); + // assert(wx.cmp(wy) == ex.cmp(ey)); + + // // Ensure operator overloading still works with more complex types + let pair_ascending = Pair { x: wx, y: wy }; + let pair_descending = Pair { x: wy, y: wx }; + + // assert(pair_ascending != pair_descending); + + assert(pair_ascending < pair_descending); + // assert(pair_ascending <= pair_descending); + assert(pair_descending > pair_ascending); + // assert(pair_descending >= pair_ascending); + + // assert(pair_ascending.cmp(pair_descending) == Ordering::less()); +} + +struct Wrapper { + inner: u32 +} + +impl Wrapper { + fn new(inner: u32) -> Self { + Wrapper { inner } + } +} + +impl Add for Wrapper { + fn add(self, other: Self) -> Self { + Wrapper::new(self.inner + other.inner) + } +} + +impl Sub for Wrapper { + fn sub(self, other: Self) -> Self { + Wrapper::new(self.inner - other.inner) + } +} + +impl Mul for Wrapper { + fn mul(self, other: Self) -> Self { + Wrapper::new(self.inner * other.inner) + } +} + +impl Div for Wrapper { + fn div(self, other: Self) -> Self { + Wrapper::new(self.inner / other.inner) + } +} + +impl Rem for Wrapper { + fn rem(self, other: Self) -> Self { + Wrapper::new(self.inner % other.inner) + } +} + +impl BitAnd for Wrapper { + fn bitand(self, other: Self) -> Self { + Wrapper::new(self.inner & other.inner) + } +} + +impl BitOr for Wrapper { + fn bitor(self, other: Self) -> Self { + Wrapper::new(self.inner | other.inner) + } +} + +impl BitXor for Wrapper { + fn bitxor(self, other: Self) -> Self { + Wrapper::new(self.inner ^ other.inner) + } +} + +impl Shl for Wrapper { + fn shl(self, other: Self) -> Self { + Wrapper::new(self.inner << other.inner) + } +} + +impl Shr for Wrapper { + fn shr(self, other: Self) -> Self { + Wrapper::new(self.inner >> other.inner) + } +} + +impl Eq for Wrapper { + fn eq(self, other: Self) -> bool { + self.inner == other.inner + } +} + +impl Ord for Wrapper { + fn cmp(self, other: Self) -> Ordering { + self.inner.cmp(other.inner) + } +} + + + + + +struct Pair { + x: Wrapper, + y: Wrapper, +} + +impl Eq for Pair { + fn eq(self, o: Self) -> bool { + (self.x == o.x) & (self.y == o.y) + } +} + +impl Ord for Pair { + fn cmp(self, o: Self) -> Ordering { + let mut result = self.x.cmp(o.x); + + if result == Ordering::equal() { + result = self.y.cmp(o.y); + } + + result + } +} diff --git a/test_programs/execution_success/operator_overloading/success b/test_programs/execution_success/operator_overloading/success new file mode 100644 index 00000000000..5e2b241bf04 --- /dev/null +++ b/test_programs/execution_success/operator_overloading/success @@ -0,0 +1,664 @@ +Initial SSA: +acir fn main f0 { + b0(v0: u32, v1: u32): + v3 = call f1(v0) + v5 = call f1(v1) + v7 = call f2(v5, v3, v3, v5) + constrain v7 == Field 2 + return +} +acir fn new f1 { + b0(v0: u32): + return v0 +} +acir fn cmp f2 { + b0(v0: u32, v1: u32, v2: u32, v3: u32): + v5 = call f3(v0, v2) + v6 = allocate + store v5 at v6 + v8 = load v6 + v10 = call f5() + v11 = call f4(v8, v10) + jmpif v11 then: b1, else: b2 + b1(): + v13 = call f3(v1, v3) + store v13 at v6 + jmp b2() + b2(): + v14 = load v6 + return v14 +} +acir fn cmp f3 { + b0(v0: u32, v1: u32): + v3 = call f6(v0, v1) + return v3 +} +acir fn eq f4 { + b0(v0: Field, v1: Field): + v2 = eq v0, v1 + return v2 +} +acir fn equal f5 { + b0(): + return Field 1 +} +acir fn cmp f6 { + b0(v0: u32, v1: u32): + v2 = lt v0, v1 + jmpif v2 then: b1, else: b2 + b1(): + v4 = call f7() + jmp b3(v4) + b3(v11: Field): + return v11 + b2(): + v5 = lt v1, v0 + jmpif v5 then: b4, else: b5 + b4(): + v7 = call f8() + jmp b6(v7) + b6(v10: Field): + jmp b3(v10) + b5(): + v9 = call f5() + jmp b6(v9) +} +acir fn less f7 { + b0(): + return Field 0 +} +acir fn greater f8 { + b0(): + return Field 2 +} + +After Defunctionalization: +acir fn main f0 { + b0(v0: u32, v1: u32): + v3 = call f1(v0) + v5 = call f1(v1) + v7 = call f2(v5, v3, v3, v5) + constrain v7 == Field 2 + return +} +acir fn new f1 { + b0(v0: u32): + return v0 +} +acir fn cmp f2 { + b0(v0: u32, v1: u32, v2: u32, v3: u32): + v5 = call f3(v0, v2) + v6 = allocate + store v5 at v6 + v8 = load v6 + v10 = call f5() + v11 = call f4(v8, v10) + jmpif v11 then: b1, else: b2 + b1(): + v13 = call f3(v1, v3) + store v13 at v6 + jmp b2() + b2(): + v14 = load v6 + return v14 +} +acir fn cmp f3 { + b0(v0: u32, v1: u32): + v3 = call f6(v0, v1) + return v3 +} +acir fn eq f4 { + b0(v0: Field, v1: Field): + v2 = eq v0, v1 + return v2 +} +acir fn equal f5 { + b0(): + return Field 1 +} +acir fn cmp f6 { + b0(v0: u32, v1: u32): + v2 = lt v0, v1 + jmpif v2 then: b1, else: b2 + b1(): + v4 = call f7() + jmp b3(v4) + b3(v11: Field): + return v11 + b2(): + v5 = lt v1, v0 + jmpif v5 then: b4, else: b5 + b4(): + v7 = call f8() + jmp b6(v7) + b6(v10: Field): + jmp b3(v10) + b5(): + v9 = call f5() + jmp b6(v9) +} +acir fn less f7 { + b0(): + return Field 0 +} +acir fn greater f8 { + b0(): + return Field 2 +} + +After Inlining: +acir fn main f0 { + b0(v0: u32, v1: u32): + v7 = lt v1, v0 + jmpif v7 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v17 = allocate + store v12 at v17 + v18 = load v17 + v21 = eq v18, Field 1 + jmpif v21 then: b7, else: b8 + b7(): + v25 = lt v0, v1 + jmpif v25 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v17 + jmp b8() + b8(): + v22 = load v17 + constrain v22 == Field 2 + return + b10(): + v26 = lt v1, v0 + jmpif v26 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v8 = lt v0, v1 + jmpif v8 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v32 = lt v1, v0 + jmpif v32 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v34 = allocate + store v12 at v34 + v36 = eq v12, Field 1 + jmpif v36 then: b7, else: b8 + b7(): + v37 = lt v0, v1 + jmpif v37 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v34 + jmp b8() + b8(): + v39 = load v34 + constrain v39 == Field 2 + return + b10(): + v38 = lt v1, v0 + jmpif v38 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v33 = lt v0, v1 + jmpif v33 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Assert Constant: +acir fn main f0 { + b0(v0: u32, v1: u32): + v32 = lt v1, v0 + jmpif v32 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v34 = allocate + store v12 at v34 + v36 = eq v12, Field 1 + jmpif v36 then: b7, else: b8 + b7(): + v37 = lt v0, v1 + jmpif v37 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v34 + jmp b8() + b8(): + v39 = load v34 + constrain v39 == Field 2 + return + b10(): + v38 = lt v1, v0 + jmpif v38 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v33 = lt v0, v1 + jmpif v33 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Unrolling: +acir fn main f0 { + b0(v0: u32, v1: u32): + v32 = lt v1, v0 + jmpif v32 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v34 = allocate + store v12 at v34 + v36 = eq v12, Field 1 + jmpif v36 then: b7, else: b8 + b7(): + v37 = lt v0, v1 + jmpif v37 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v34 + jmp b8() + b8(): + v39 = load v34 + constrain v39 == Field 2 + return + b10(): + v38 = lt v1, v0 + jmpif v38 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v33 = lt v0, v1 + jmpif v33 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Simplifying: +acir fn main f0 { + b0(v0: u32, v1: u32): + v32 = lt v1, v0 + jmpif v32 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v34 = allocate + store v12 at v34 + v36 = eq v12, Field 1 + jmpif v36 then: b7, else: b8 + b7(): + v37 = lt v0, v1 + jmpif v37 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v34 + jmp b8() + b8(): + v39 = load v34 + constrain v39 == Field 2 + return + b10(): + v38 = lt v1, v0 + jmpif v38 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v33 = lt v0, v1 + jmpif v33 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v41 = lt v1, v0 + jmpif v41 then: b1, else: b2 + b1(): + jmp b6(Field 0) + b6(v12: Field): + v43 = allocate + store v12 at v43 + v44 = eq v12, Field 1 + jmpif v44 then: b7, else: b8 + b7(): + v45 = lt v0, v1 + jmpif v45 then: b9, else: b10 + b9(): + jmp b14(Field 0) + b14(v29: Field): + store v29 at v43 + jmp b8() + b8(): + v47 = load v43 + constrain v47 == Field 2 + return + b10(): + v46 = lt v1, v0 + jmpif v46 then: b11, else: b12 + b11(): + jmp b13(Field 2) + b13(v28: Field): + jmp b14(v28) + b12(): + jmp b13(Field 1) + b2(): + v42 = lt v0, v1 + jmpif v42 then: b3, else: b4 + b3(): + jmp b5(Field 2) + b5(v11: Field): + jmp b6(v11) + b4(): + jmp b5(Field 1) +} + +After Flattening: +acir fn main f0 { + b0(v0: u32, v1: u32): + v41 = lt v1, v0 + enable_side_effects v41 + v49 = not v41 + enable_side_effects v49 + v51 = lt v0, v1 + v52 = mul v49, v51 + enable_side_effects v52 + v53 = not v51 + v54 = mul v49, v53 + enable_side_effects v49 + v55 = cast v51 as Field + v56 = cast v53 as Field + v57 = mul v55, Field 2 + v58 = add v57, v56 + enable_side_effects u1 1 + v59 = cast v41 as Field + v60 = cast v49 as Field + v61 = mul v60, v58 + v62 = allocate + store v61 at v62 + v63 = eq v61, Field 1 + enable_side_effects v63 + v64 = lt v0, v1 + v65 = mul v63, v64 + enable_side_effects v65 + v66 = not v64 + v67 = mul v63, v66 + enable_side_effects v67 + v68 = lt v1, v0 + v69 = mul v67, v68 + enable_side_effects v69 + v70 = not v68 + v71 = mul v67, v70 + enable_side_effects v67 + v72 = cast v68 as Field + v73 = cast v70 as Field + v74 = mul v72, Field 2 + v75 = add v74, v73 + enable_side_effects v63 + v76 = cast v64 as Field + v77 = cast v66 as Field + v78 = mul v77, v75 + v79 = load v62 + store v78 at v62 + v80 = not v63 + store v79 at v62 + enable_side_effects u1 1 + v81 = cast v63 as Field + v82 = cast v80 as Field + v83 = mul v81, v78 + v84 = mul v82, v79 + v85 = add v83, v84 + store v85 at v62 + v86 = load v62 + constrain v86 == Field 2 + return +} + +After Mem2Reg: +acir fn main f0 { + b0(v0: u32, v1: u32): + v87 = lt v1, v0 + enable_side_effects v87 + v88 = not v87 + enable_side_effects v88 + v89 = lt v0, v1 + v90 = mul v88, v89 + enable_side_effects v90 + v91 = not v89 + v92 = mul v88, v91 + enable_side_effects v88 + v93 = cast v89 as Field + v94 = cast v91 as Field + v95 = mul v93, Field 2 + v96 = add v95, v94 + enable_side_effects u1 1 + v97 = cast v87 as Field + v98 = cast v88 as Field + v99 = mul v98, v96 + v100 = allocate + v101 = eq v99, Field 1 + enable_side_effects v101 + v102 = lt v0, v1 + v103 = mul v101, v102 + enable_side_effects v103 + v104 = not v102 + v105 = mul v101, v104 + enable_side_effects v105 + v106 = lt v1, v0 + v107 = mul v105, v106 + enable_side_effects v107 + v108 = not v106 + v109 = mul v105, v108 + enable_side_effects v105 + v110 = cast v106 as Field + v111 = cast v108 as Field + v112 = mul v110, Field 2 + v113 = add v112, v111 + enable_side_effects v101 + v114 = cast v102 as Field + v115 = cast v104 as Field + v116 = mul v115, v113 + v118 = not v101 + enable_side_effects u1 1 + v119 = cast v101 as Field + v120 = cast v118 as Field + v121 = mul v119, v116 + v122 = mul v120, v99 + v123 = add v121, v122 + constrain v123 == Field 2 + return +} + +After Constant Folding: +acir fn main f0 { + b0(v0: u32, v1: u32): + v125 = lt v1, v0 + enable_side_effects v125 + v126 = not v125 + enable_side_effects v126 + v127 = lt v0, v1 + v128 = mul v126, v127 + enable_side_effects v128 + v129 = not v127 + v130 = mul v126, v129 + enable_side_effects v126 + v131 = cast v127 as Field + v132 = cast v129 as Field + v133 = mul v131, Field 2 + v134 = add v133, v132 + enable_side_effects u1 1 + v135 = cast v125 as Field + v136 = cast v126 as Field + v137 = mul v136, v134 + v138 = allocate + v139 = eq v137, Field 1 + enable_side_effects v139 + v140 = mul v139, v127 + enable_side_effects v140 + v141 = mul v139, v129 + enable_side_effects v141 + v142 = mul v141, v125 + enable_side_effects v142 + v143 = mul v141, v126 + enable_side_effects v141 + v144 = mul v135, Field 2 + v145 = add v144, v136 + enable_side_effects v139 + v146 = mul v132, v145 + v147 = not v139 + enable_side_effects u1 1 + v148 = cast v139 as Field + v149 = cast v147 as Field + v150 = mul v148, v146 + v151 = mul v149, v137 + v152 = add v150, v151 + constrain v152 == Field 2 + return +} + +After Dead Instruction Elimination: +acir fn main f0 { + b0(v0: u32, v1: u32): + v125 = lt v1, v0 + enable_side_effects v125 + v126 = not v125 + enable_side_effects v126 + v127 = lt v0, v1 + v128 = mul v126, v127 + enable_side_effects v128 + v129 = not v127 + enable_side_effects v126 + v131 = cast v127 as Field + v132 = cast v129 as Field + v133 = mul v131, Field 2 + v134 = add v133, v132 + enable_side_effects u1 1 + v135 = cast v125 as Field + v136 = cast v126 as Field + v137 = mul v136, v134 + v139 = eq v137, Field 1 + enable_side_effects v139 + v140 = mul v139, v127 + enable_side_effects v140 + v141 = mul v139, v129 + enable_side_effects v141 + v142 = mul v141, v125 + enable_side_effects v142 + enable_side_effects v141 + v144 = mul v135, Field 2 + v145 = add v144, v136 + enable_side_effects v139 + v146 = mul v132, v145 + v147 = not v139 + enable_side_effects u1 1 + v148 = cast v139 as Field + v149 = cast v147 as Field + v150 = mul v148, v146 + v151 = mul v149, v137 + v152 = add v150, v151 + constrain v152 == Field 2 + return +} + +After Fill Internal Slice Dummy Data: +acir fn main f0 { + b0(v0: u32, v1: u32): + v153 = lt v1, v0 + enable_side_effects v153 + v154 = not v153 + enable_side_effects v154 + v155 = lt v0, v1 + v156 = mul v154, v155 + enable_side_effects v156 + v157 = not v155 + enable_side_effects v154 + v158 = cast v155 as Field + v159 = cast v157 as Field + v160 = mul v158, Field 2 + v161 = add v160, v159 + enable_side_effects u1 1 + v162 = cast v153 as Field + v163 = cast v154 as Field + v164 = mul v163, v161 + v165 = eq v164, Field 1 + enable_side_effects v165 + v166 = mul v165, v155 + enable_side_effects v166 + v167 = mul v165, v157 + enable_side_effects v167 + v168 = mul v167, v153 + enable_side_effects v167 + v169 = mul v162, Field 2 + v170 = add v169, v163 + enable_side_effects v165 + v171 = mul v159, v170 + v172 = not v165 + enable_side_effects u1 1 + v173 = cast v165 as Field + v174 = cast v172 as Field + v175 = mul v173, v171 + v176 = mul v174, v164 + v177 = add v175, v176 + constrain v177 == Field 2 + return +} + +[operator_overloading] Circuit witness successfully solved From 5e2743a6eea43aee6a9fc249b26e6695c067f436 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 10:59:07 -0500 Subject: [PATCH 06/21] Fix bad merge --- compiler/noirc_frontend/src/hir/type_check/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 575cdc4683a..04d1ee78786 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -1105,7 +1105,7 @@ impl<'interner> TypeChecker<'interner> { span, }); } - Ok(Integer(*sign_x, *bit_width_x)) + Ok((Integer(*sign_x, *bit_width_x), false)) } // The result of two Fields is always a witness (FieldElement, FieldElement) => { From 6be81eb7363433a5c0a2b99107b9e79ca17e3f3b Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 13:36:47 -0500 Subject: [PATCH 07/21] Fix return type of Cmp --- .../noirc_frontend/src/hir/resolution/traits.rs | 11 +++++++---- .../noirc_frontend/src/monomorphization/ast.rs | 2 +- .../noirc_frontend/src/monomorphization/mod.rs | 16 ++++++++++++++-- compiler/noirc_frontend/src/node_interner.rs | 17 +++++++++++++++++ noir_stdlib/src/cmp.nr | 3 +++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index f2fad112bf8..40041b0fd00 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -52,6 +52,13 @@ pub(crate) fn resolve_traits( context.def_interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); }); + + // This check needs to be after the trait's methods are set since + // the interner may set `interner.ordering_type` based on the result type + // of the Cmp trait, if this is it. + if crate_id.is_stdlib() { + context.def_interner.try_add_operator_trait(trait_id); + } } res } @@ -88,10 +95,6 @@ fn resolve_trait_methods( }); let file = def_maps[&crate_id].file_id(unresolved_trait.module_id); - if crate_id.is_stdlib() { - interner.try_add_operator_trait(trait_id); - } - let mut functions = vec![]; let mut resolver_errors = vec![]; diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 5a5f07b0a38..42a618e7d77 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -352,7 +352,7 @@ impl std::fmt::Display for Type { }; write!(f, "fn({}) -> {}{}", args.join(", "), ret, closure_env_text) } - Type::Slice(element) => write!(f, "[{element}"), + Type::Slice(element) => write!(f, "[{element}]"), Type::MutableReference(element) => write!(f, "&mut {element}"), } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index fb7445c2414..e4cd0552402 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -367,7 +367,16 @@ impl<'interner> Monomorphizer<'interner> { // with a method call to the appropriate trait impl method. let lhs_type = self.interner.id_type(infix.lhs); let args = vec![lhs_type.clone(), lhs_type]; - let ret = self.interner.id_type(expr); + + // If this is a comparison operator, the result is a boolean but + // the actual method call returns an Ordering + use crate::BinaryOpKind::*; + let ret = if matches!(operator, Less | LessEqual | Greater | GreaterEqual) { + self.interner.ordering_type() + } else { + self.interner.id_type(expr) + }; + let env = Box::new(Type::Unit); let function_type = Type::Function(args, Box::new(ret.clone()), env); @@ -1470,6 +1479,9 @@ impl<'interner> Monomorphizer<'interner> { // (a > b) => a.cmp(b) == Ordering::Greater // (a >= b) => a.cmp(b) != Ordering::Less Less | LessEqual | Greater | GreaterEqual => { + // Comparing an Ordering directly to a field value in this way takes advantage + // of the fact the Ordering struct contains a single Field type, and our SSA + // pass will automatically unpack tuple values. let ordering_value = if matches!(operator.kind, Less | GreaterEqual) { FieldElement::zero() // Ordering::Less } else { @@ -1481,7 +1493,7 @@ impl<'interner> Monomorphizer<'interner> { let int_value = ast::Literal::Integer(ordering_value, ast::Type::Field, location); let rhs = Box::new(ast::Expression::Literal(int_value)); - let lhs = Box::new(result); + let lhs = Box::new(ast::Expression::ExtractTupleField(Box::new(result), 0)); result = ast::Expression::Binary(ast::Binary { lhs, operator, rhs, location }); } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 8c3181f9420..b5a238c9473 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -113,6 +113,9 @@ pub struct NodeInterner { /// Holds the trait ids of the traits used for operator overloading operator_traits: HashMap, + /// The `Ordering` type is a semi-builtin type that is the result of the comparison traits. + ordering_type: Option, + /// Map from ExprId (referring to a Function/Method call) to its corresponding TypeBindings, /// filled out during type checking from instantiated variables. Used during monomorphization /// to map call site types back onto function parameter types, and undo this binding as needed. @@ -427,6 +430,7 @@ impl Default for NodeInterner { trait_implementation_map: HashMap::new(), selected_trait_implementations: HashMap::new(), operator_traits: HashMap::new(), + ordering_type: None, instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: std::cell::Cell::new(0), @@ -1393,6 +1397,15 @@ impl NodeInterner { self.operator_traits.insert(BinaryOpKind::LessEqual, trait_id); self.operator_traits.insert(BinaryOpKind::Greater, trait_id); self.operator_traits.insert(BinaryOpKind::GreaterEqual, trait_id); + + let the_trait = self.get_trait(trait_id); + self.ordering_type = match &the_trait.methods[0].typ { + Type::Forall(_, typ) => match typ.as_ref() { + Type::Function(_, return_type, _) => Some(return_type.as_ref().clone()), + other => unreachable!("Expected function type for `cmp`, found {}", other), + }, + other => unreachable!("Expected Forall type for `cmp`, found {}", other), + }; } _ => (), } @@ -1420,6 +1433,10 @@ impl NodeInterner { self.operator_traits.insert(BinaryOpKind::ShiftLeft, dummy_trait); self.operator_traits.insert(BinaryOpKind::ShiftRight, dummy_trait); } + + pub(crate) fn ordering_type(&self) -> Type { + self.ordering_type.clone().expect("Expected ordering_type to be set in the NodeInterner") + } } impl Methods { diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index 21fd017b6c4..11127494c18 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -74,6 +74,9 @@ struct Ordering { } impl Ordering { + // Implementation note: 0, 1, and 2 for Lt, Eq, and Gt are built + // into the compiler, do not change these without also updating + // the compiler itself! pub fn less() -> Ordering { Ordering { result: 0 } } From 4544a1ae505cc70d9c5790035bf3a20cfb38ef81 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 14:03:10 -0500 Subject: [PATCH 08/21] Fix tests --- .../noirc_frontend/src/hir/type_check/expr.rs | 16 +++++- .../impl_with_where_clause/src/main.nr | 18 +++--- .../trait_default_implementation/src/main.nr | 11 ++-- .../trait_override_implementation/src/main.nr | 22 ++++---- .../compile_success_empty/traits/src/main.nr | 10 ++-- .../operator_overloading/src/main.nr | 56 +++++++++---------- 6 files changed, 73 insertions(+), 60 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 04d1ee78786..10016c8193b 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -844,6 +844,20 @@ impl<'interner> TypeChecker<'interner> { // <= and friends are technically valid for booleans, just not very useful (Bool, Bool) => Ok((Bool, false)), + // Special-case == and != for arrays + (Array(x_size, x_type), Array(y_size, y_type)) + if matches!(op.kind, BinaryOpKind::Equal | BinaryOpKind::NotEqual) => + { + self.unify(x_size, y_size, || TypeCheckError::TypeMismatchWithSource { + expected: lhs_type.clone(), + actual: rhs_type.clone(), + source: Source::ArrayLen, + span: op.location.span, + }); + + self.comparator_operand_type_rules(x_type, y_type, op, span) + } + (String(x_size), String(y_size)) => { self.unify(x_size, y_size, || TypeCheckError::TypeMismatchWithSource { expected: *x_size.clone(), @@ -852,7 +866,7 @@ impl<'interner> TypeChecker<'interner> { source: Source::StringLen, }); - Ok((Bool, true)) + Ok((Bool, false)) } (lhs, rhs) => { self.unify(lhs, rhs, || TypeCheckError::TypeMismatchWithSource { diff --git a/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr b/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr index de3078be8ba..780512f04dc 100644 --- a/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr +++ b/test_programs/compile_success_empty/impl_with_where_clause/src/main.nr @@ -1,27 +1,27 @@ fn main() { let array: [Field; 3] = [1, 2, 3]; - assert(array.eq(array)); + assert(array.my_eq(array)); // Ensure this still works if we have to infer the type of the integer literals let array = [1, 2, 3]; - assert(array.eq(array)); + assert(array.my_eq(array)); } -trait Eq { - fn eq(self, other: Self) -> bool; +trait MyEq { + fn my_eq(self, other: Self) -> bool; } -impl Eq for [T; 3] where T: Eq { - fn eq(self, other: Self) -> bool { +impl MyEq for [T; 3] where T: MyEq { + fn my_eq(self, other: Self) -> bool { let mut ret = true; for i in 0 .. self.len() { - ret &= self[i].eq(other[i]); + ret &= self[i].my_eq(other[i]); } ret } } -impl Eq for Field { - fn eq(self, other: Field) -> bool { +impl MyEq for Field { + fn my_eq(self, other: Field) -> bool { self == other } } diff --git a/test_programs/compile_success_empty/trait_default_implementation/src/main.nr b/test_programs/compile_success_empty/trait_default_implementation/src/main.nr index e1f29ce3f48..2f5bff8c40c 100644 --- a/test_programs/compile_success_empty/trait_default_implementation/src/main.nr +++ b/test_programs/compile_success_empty/trait_default_implementation/src/main.nr @@ -1,12 +1,11 @@ use dep::std; -trait Default { - fn default(x: Field, y: Field) -> Self; +trait MyDefault { + fn my_default(x: Field, y: Field) -> Self; fn method2(x: Field) -> Field { - x + x } - } struct Foo { @@ -14,8 +13,8 @@ struct Foo { array: [Field; 2], } -impl Default for Foo { - fn default(x: Field,y: Field) -> Self { +impl MyDefault for Foo { + fn my_default(x: Field,y: Field) -> Self { Self { bar: x, array: [x,y] } } } diff --git a/test_programs/compile_success_empty/trait_override_implementation/src/main.nr b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr index a385efc63fd..85528291870 100644 --- a/test_programs/compile_success_empty/trait_override_implementation/src/main.nr +++ b/test_programs/compile_success_empty/trait_override_implementation/src/main.nr @@ -1,7 +1,7 @@ use dep::std; -trait Default { - fn default(x: Field, y: Field) -> Self; +trait MyDefault { + fn my_default(x: Field, y: Field) -> Self; fn method2(x: Field) -> Field { x @@ -13,8 +13,8 @@ struct Foo { array: [Field; 2], } -impl Default for Foo { - fn default(x: Field,y: Field) -> Self { +impl MyDefault for Foo { + fn my_default(x: Field,y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -25,18 +25,18 @@ impl Default for Foo { trait F { fn f1(self) -> Field; - fn f2(self) -> Field { 2 } - fn f3(self) -> Field { 3 } - fn f4(self) -> Field { 4 } - fn f5(self) -> Field { 5 } + fn f2(_self: Self) -> Field { 2 } + fn f3(_self: Self) -> Field { 3 } + fn f4(_self: Self) -> Field { 4 } + fn f5(_self: Self) -> Field { 5 } } struct Bar {} impl F for Bar { - fn f5(self) -> Field { 50 } - fn f1(self) -> Field { 10 } - fn f3(self) -> Field { 30 } + fn f5(_self: Self) -> Field { 50 } + fn f1(_self: Self) -> Field { 10 } + fn f3(_self: Self) -> Field { 30 } } // Impls on mutable references are temporarily disabled // impl F for &mut Bar { diff --git a/test_programs/compile_success_empty/traits/src/main.nr b/test_programs/compile_success_empty/traits/src/main.nr index 784ff01a883..ed804559fed 100644 --- a/test_programs/compile_success_empty/traits/src/main.nr +++ b/test_programs/compile_success_empty/traits/src/main.nr @@ -1,7 +1,7 @@ use dep::std; -trait Default { - fn default(x: Field, y: Field) -> Self; +trait MyDefault { + fn my_default(x: Field, y: Field) -> Self; } struct Foo { @@ -9,13 +9,13 @@ struct Foo { array: [Field; 2], } -impl Default for Foo { - fn default(x: Field,y: Field) -> Self { +impl MyDefault for Foo { + fn my_default(x: Field,y: Field) -> Self { Self { bar: x, array: [x,y] } } } fn main(x: Field, y: Field) { - let first = Foo::default(x, y); + let first = Foo::my_default(x, y); assert(first.bar == x); } diff --git a/test_programs/execution_success/operator_overloading/src/main.nr b/test_programs/execution_success/operator_overloading/src/main.nr index 2d3ac35f4a7..02caa377998 100644 --- a/test_programs/execution_success/operator_overloading/src/main.nr +++ b/test_programs/execution_success/operator_overloading/src/main.nr @@ -5,41 +5,41 @@ fn main(x: u32, y: u32) { let wy = Wrapper::new(y); // expected x and expected y values - // let ex: u32 = 3; - // let ey: u32 = 9; - - // assert((wx + wy).inner == ex + ey); - // assert((wy - wx).inner == ey - ex); - // assert((wx * wy).inner == ex * ey); - // assert((wx / wy).inner == ex / ey); - // assert((wx % wy).inner == ex % ey); - - // assert((wx & wy).inner == (ex & ey)); - // assert((wx | wy).inner == (ex | ey)); - // assert((wx ^ wy).inner == (ex ^ ey)); - - // assert((wy << wx).inner == (ey << ex)); - // assert((wy >> wx).inner == (ey >> ex)); - - // assert((wx == wy) == (ex == ey)); - // assert((wx < wy) == (ex < ey)); - // assert((wx <= wy) == (ex <= ey)); - // assert((wx > wy) == (ex > ey)); - // assert((wx >= wy) == (ex >= ey)); - // assert(wx.cmp(wy) == ex.cmp(ey)); - - // // Ensure operator overloading still works with more complex types + let ex: u32 = 3; + let ey: u32 = 9; + + assert((wx + wy).inner == ex + ey); + assert((wy - wx).inner == ey - ex); + assert((wx * wy).inner == ex * ey); + assert((wx / wy).inner == ex / ey); + assert((wx % wy).inner == ex % ey); + + assert((wx & wy).inner == (ex & ey)); + assert((wx | wy).inner == (ex | ey)); + assert((wx ^ wy).inner == (ex ^ ey)); + + assert((wy << wx).inner == (ey << ex)); + assert((wy >> wx).inner == (ey >> ex)); + + assert((wx == wy) == (ex == ey)); + assert((wx < wy) == (ex < ey)); + assert((wx <= wy) == (ex <= ey)); + assert((wx > wy) == (ex > ey)); + assert((wx >= wy) == (ex >= ey)); + assert(wx.cmp(wy) == ex.cmp(ey)); + + // Ensure operator overloading still works with more complex types let pair_ascending = Pair { x: wx, y: wy }; let pair_descending = Pair { x: wy, y: wx }; - // assert(pair_ascending != pair_descending); + assert(pair_ascending != pair_descending); assert(pair_ascending < pair_descending); - // assert(pair_ascending <= pair_descending); + assert(pair_ascending <= pair_descending); assert(pair_descending > pair_ascending); - // assert(pair_descending >= pair_ascending); + assert(pair_descending >= pair_ascending); - // assert(pair_ascending.cmp(pair_descending) == Ordering::less()); + assert(pair_ascending.cmp(pair_descending) == Ordering::less()); } struct Wrapper { From f2a9380e51b4845f22702495b0d706dde6c4fefb Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 14:14:03 -0500 Subject: [PATCH 09/21] Fix clippy warning --- compiler/noirc_frontend/src/hir/type_check/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 10016c8193b..8896d3fe734 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -1212,7 +1212,7 @@ impl<'interner> TypeChecker<'interner> { // The first (and only) method of each operator trait should be the operator method let method = &the_trait.methods[0]; - let (method_type, mut bindings) = method.typ.instantiate(&self.interner); + let (method_type, mut bindings) = method.typ.instantiate(self.interner); match method_type { Type::Function(args, _, _) => { From 960e850b94f29f2c22f520e3ee7033a71255e04b Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 14:18:14 -0500 Subject: [PATCH 10/21] Move docs files --- docs/docs/noir/{syntax => concepts}/_category_.json | 4 ++-- docs/docs/noir/{syntax => concepts}/assert.md | 0 docs/docs/noir/{syntax => concepts}/comments.md | 0 docs/docs/noir/{syntax => concepts}/control_flow.md | 0 docs/docs/noir/{syntax => concepts}/data_bus.md | 0 .../docs/noir/{syntax => concepts}/data_types/_category_.json | 0 docs/docs/noir/{syntax => concepts}/data_types/arrays.md | 0 docs/docs/noir/{syntax => concepts}/data_types/booleans.md | 0 docs/docs/noir/{syntax => concepts}/data_types/fields.md | 0 .../noir/{syntax => concepts}/data_types/function_types.md | 0 docs/docs/noir/{syntax => concepts}/data_types/index.md | 0 docs/docs/noir/{syntax => concepts}/data_types/integers.md | 0 docs/docs/noir/{syntax => concepts}/data_types/references.md | 0 docs/docs/noir/{syntax => concepts}/data_types/slices.mdx | 0 docs/docs/noir/{syntax => concepts}/data_types/strings.md | 0 docs/docs/noir/{syntax => concepts}/data_types/structs.md | 0 docs/docs/noir/{syntax => concepts}/data_types/tuples.md | 0 docs/docs/noir/{syntax => concepts}/data_types/vectors.mdx | 0 docs/docs/noir/{syntax => concepts}/distinct.md | 0 docs/docs/noir/{syntax => concepts}/functions.md | 0 docs/docs/noir/{syntax => concepts}/generics.md | 0 docs/docs/noir/{syntax => concepts}/lambdas.md | 0 docs/docs/noir/{syntax => concepts}/mutability.md | 0 docs/docs/noir/{syntax => concepts}/ops.md | 0 docs/docs/noir/{syntax => concepts}/shadowing.md | 0 docs/docs/{explanations/noir => noir/concepts}/traits.md | 0 docs/docs/noir/{syntax => concepts}/unconstrained.md | 0 docs/docs/{explanations => noir}/standard_library/traits.md | 0 28 files changed, 2 insertions(+), 2 deletions(-) rename docs/docs/noir/{syntax => concepts}/_category_.json (72%) rename docs/docs/noir/{syntax => concepts}/assert.md (100%) rename docs/docs/noir/{syntax => concepts}/comments.md (100%) rename docs/docs/noir/{syntax => concepts}/control_flow.md (100%) rename docs/docs/noir/{syntax => concepts}/data_bus.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/_category_.json (100%) rename docs/docs/noir/{syntax => concepts}/data_types/arrays.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/booleans.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/fields.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/function_types.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/index.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/integers.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/references.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/slices.mdx (100%) rename docs/docs/noir/{syntax => concepts}/data_types/strings.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/structs.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/tuples.md (100%) rename docs/docs/noir/{syntax => concepts}/data_types/vectors.mdx (100%) rename docs/docs/noir/{syntax => concepts}/distinct.md (100%) rename docs/docs/noir/{syntax => concepts}/functions.md (100%) rename docs/docs/noir/{syntax => concepts}/generics.md (100%) rename docs/docs/noir/{syntax => concepts}/lambdas.md (100%) rename docs/docs/noir/{syntax => concepts}/mutability.md (100%) rename docs/docs/noir/{syntax => concepts}/ops.md (100%) rename docs/docs/noir/{syntax => concepts}/shadowing.md (100%) rename docs/docs/{explanations/noir => noir/concepts}/traits.md (100%) rename docs/docs/noir/{syntax => concepts}/unconstrained.md (100%) rename docs/docs/{explanations => noir}/standard_library/traits.md (100%) diff --git a/docs/docs/noir/syntax/_category_.json b/docs/docs/noir/concepts/_category_.json similarity index 72% rename from docs/docs/noir/syntax/_category_.json rename to docs/docs/noir/concepts/_category_.json index 666b691ae91..7da08f8a8c5 100644 --- a/docs/docs/noir/syntax/_category_.json +++ b/docs/docs/noir/concepts/_category_.json @@ -1,6 +1,6 @@ { - "label": "Syntax", + "label": "Concepts", "position": 0, "collapsible": true, "collapsed": true -} +} \ No newline at end of file diff --git a/docs/docs/noir/syntax/assert.md b/docs/docs/noir/concepts/assert.md similarity index 100% rename from docs/docs/noir/syntax/assert.md rename to docs/docs/noir/concepts/assert.md diff --git a/docs/docs/noir/syntax/comments.md b/docs/docs/noir/concepts/comments.md similarity index 100% rename from docs/docs/noir/syntax/comments.md rename to docs/docs/noir/concepts/comments.md diff --git a/docs/docs/noir/syntax/control_flow.md b/docs/docs/noir/concepts/control_flow.md similarity index 100% rename from docs/docs/noir/syntax/control_flow.md rename to docs/docs/noir/concepts/control_flow.md diff --git a/docs/docs/noir/syntax/data_bus.md b/docs/docs/noir/concepts/data_bus.md similarity index 100% rename from docs/docs/noir/syntax/data_bus.md rename to docs/docs/noir/concepts/data_bus.md diff --git a/docs/docs/noir/syntax/data_types/_category_.json b/docs/docs/noir/concepts/data_types/_category_.json similarity index 100% rename from docs/docs/noir/syntax/data_types/_category_.json rename to docs/docs/noir/concepts/data_types/_category_.json diff --git a/docs/docs/noir/syntax/data_types/arrays.md b/docs/docs/noir/concepts/data_types/arrays.md similarity index 100% rename from docs/docs/noir/syntax/data_types/arrays.md rename to docs/docs/noir/concepts/data_types/arrays.md diff --git a/docs/docs/noir/syntax/data_types/booleans.md b/docs/docs/noir/concepts/data_types/booleans.md similarity index 100% rename from docs/docs/noir/syntax/data_types/booleans.md rename to docs/docs/noir/concepts/data_types/booleans.md diff --git a/docs/docs/noir/syntax/data_types/fields.md b/docs/docs/noir/concepts/data_types/fields.md similarity index 100% rename from docs/docs/noir/syntax/data_types/fields.md rename to docs/docs/noir/concepts/data_types/fields.md diff --git a/docs/docs/noir/syntax/data_types/function_types.md b/docs/docs/noir/concepts/data_types/function_types.md similarity index 100% rename from docs/docs/noir/syntax/data_types/function_types.md rename to docs/docs/noir/concepts/data_types/function_types.md diff --git a/docs/docs/noir/syntax/data_types/index.md b/docs/docs/noir/concepts/data_types/index.md similarity index 100% rename from docs/docs/noir/syntax/data_types/index.md rename to docs/docs/noir/concepts/data_types/index.md diff --git a/docs/docs/noir/syntax/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md similarity index 100% rename from docs/docs/noir/syntax/data_types/integers.md rename to docs/docs/noir/concepts/data_types/integers.md diff --git a/docs/docs/noir/syntax/data_types/references.md b/docs/docs/noir/concepts/data_types/references.md similarity index 100% rename from docs/docs/noir/syntax/data_types/references.md rename to docs/docs/noir/concepts/data_types/references.md diff --git a/docs/docs/noir/syntax/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx similarity index 100% rename from docs/docs/noir/syntax/data_types/slices.mdx rename to docs/docs/noir/concepts/data_types/slices.mdx diff --git a/docs/docs/noir/syntax/data_types/strings.md b/docs/docs/noir/concepts/data_types/strings.md similarity index 100% rename from docs/docs/noir/syntax/data_types/strings.md rename to docs/docs/noir/concepts/data_types/strings.md diff --git a/docs/docs/noir/syntax/data_types/structs.md b/docs/docs/noir/concepts/data_types/structs.md similarity index 100% rename from docs/docs/noir/syntax/data_types/structs.md rename to docs/docs/noir/concepts/data_types/structs.md diff --git a/docs/docs/noir/syntax/data_types/tuples.md b/docs/docs/noir/concepts/data_types/tuples.md similarity index 100% rename from docs/docs/noir/syntax/data_types/tuples.md rename to docs/docs/noir/concepts/data_types/tuples.md diff --git a/docs/docs/noir/syntax/data_types/vectors.mdx b/docs/docs/noir/concepts/data_types/vectors.mdx similarity index 100% rename from docs/docs/noir/syntax/data_types/vectors.mdx rename to docs/docs/noir/concepts/data_types/vectors.mdx diff --git a/docs/docs/noir/syntax/distinct.md b/docs/docs/noir/concepts/distinct.md similarity index 100% rename from docs/docs/noir/syntax/distinct.md rename to docs/docs/noir/concepts/distinct.md diff --git a/docs/docs/noir/syntax/functions.md b/docs/docs/noir/concepts/functions.md similarity index 100% rename from docs/docs/noir/syntax/functions.md rename to docs/docs/noir/concepts/functions.md diff --git a/docs/docs/noir/syntax/generics.md b/docs/docs/noir/concepts/generics.md similarity index 100% rename from docs/docs/noir/syntax/generics.md rename to docs/docs/noir/concepts/generics.md diff --git a/docs/docs/noir/syntax/lambdas.md b/docs/docs/noir/concepts/lambdas.md similarity index 100% rename from docs/docs/noir/syntax/lambdas.md rename to docs/docs/noir/concepts/lambdas.md diff --git a/docs/docs/noir/syntax/mutability.md b/docs/docs/noir/concepts/mutability.md similarity index 100% rename from docs/docs/noir/syntax/mutability.md rename to docs/docs/noir/concepts/mutability.md diff --git a/docs/docs/noir/syntax/ops.md b/docs/docs/noir/concepts/ops.md similarity index 100% rename from docs/docs/noir/syntax/ops.md rename to docs/docs/noir/concepts/ops.md diff --git a/docs/docs/noir/syntax/shadowing.md b/docs/docs/noir/concepts/shadowing.md similarity index 100% rename from docs/docs/noir/syntax/shadowing.md rename to docs/docs/noir/concepts/shadowing.md diff --git a/docs/docs/explanations/noir/traits.md b/docs/docs/noir/concepts/traits.md similarity index 100% rename from docs/docs/explanations/noir/traits.md rename to docs/docs/noir/concepts/traits.md diff --git a/docs/docs/noir/syntax/unconstrained.md b/docs/docs/noir/concepts/unconstrained.md similarity index 100% rename from docs/docs/noir/syntax/unconstrained.md rename to docs/docs/noir/concepts/unconstrained.md diff --git a/docs/docs/explanations/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md similarity index 100% rename from docs/docs/explanations/standard_library/traits.md rename to docs/docs/noir/standard_library/traits.md From db46352eb2b7519f1cae76e5404e051637d9135d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 2 Jan 2024 14:49:12 -0500 Subject: [PATCH 11/21] Add to traits section of stdlib --- docs/docs/noir/standard_library/traits.md | 154 +++++++++++++++++++++- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/docs/docs/noir/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md index 63b4f3d6f0b..50b0e6816ab 100644 --- a/docs/docs/noir/standard_library/traits.md +++ b/docs/docs/noir/standard_library/traits.md @@ -52,16 +52,17 @@ impl Default for (A, B, C, D, E) For primitive integer types, the return value of `default` is `0`. Container types such as arrays are filled with default values of their element type. -## `std::ops` +## `std::cmp` -### `std::ops::Eq` +### `std::cmp::Eq` ```rust trait Eq { fn eq(self, other: Self) -> bool; } ``` -Returns `true` if `self` is equal to `other`. +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. Implementations: ```rust @@ -96,11 +97,58 @@ impl Eq for (A, B, C, D, E) where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } ``` +### `std::cmp::Cmp` + +```rust +trait Cmp { + fn cmp(self, other: Self) -> Ordering; +} +``` + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + ### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` These traits abstract over addition, subtraction, multiplication, and division respectively. -Although Noir does not currently have operator overloading, in the future implementing these -traits for a given type will also allow that type to be used with the corresponding operator +Implementing these traits for a given type will also allow that type to be used with the corresponding operator for that trait (`+` for Add, etc) in addition to the normal method names. ```rust @@ -138,3 +186,99 @@ impl Add for u16 { .. } impl Add for u32 { .. } impl Add for u64 { .. } ``` + +### `std::ops::Rem` + +```rust +trait Rem { + fn rem(self, other: Self) -> Self; +} +``` + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust +trait BitOr { + fn bitor(self, other: Self) -> Self; +} + +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} + +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust +trait Shl { + fn shl(self, other: Self) -> Self; +} + +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` \ No newline at end of file From d841040be05bfc43460a7b90b6279bbd835513e6 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 2 Jan 2024 19:56:04 +0000 Subject: [PATCH 12/21] chore: remove unwanted files --- .../operator_overloading/fail | 1064 ----------------- .../operator_overloading/success | 664 ---------- 2 files changed, 1728 deletions(-) delete mode 100644 test_programs/execution_success/operator_overloading/fail delete mode 100644 test_programs/execution_success/operator_overloading/success diff --git a/test_programs/execution_success/operator_overloading/fail b/test_programs/execution_success/operator_overloading/fail deleted file mode 100644 index f3ead5e705e..00000000000 --- a/test_programs/execution_success/operator_overloading/fail +++ /dev/null @@ -1,1064 +0,0 @@ -Initial SSA: -acir fn main f0 { - b0(v0: u32, v1: u32): - v3 = call f1(v0) - v5 = call f1(v1) - v7 = call f2(v3, v5, v5, v3) - constrain v7 == Field 0 - v10 = call f2(v5, v3, v3, v5) - constrain v10 == Field 2 - return -} -acir fn new f1 { - b0(v0: u32): - return v0 -} -acir fn cmp f2 { - b0(v0: u32, v1: u32, v2: u32, v3: u32): - v5 = call f3(v0, v2) - v6 = allocate - store v5 at v6 - v8 = load v6 - v10 = call f5() - v11 = call f4(v8, v10) - jmpif v11 then: b1, else: b2 - b1(): - v13 = call f3(v1, v3) - store v13 at v6 - jmp b2() - b2(): - v14 = load v6 - return v14 -} -acir fn cmp f3 { - b0(v0: u32, v1: u32): - v3 = call f6(v0, v1) - return v3 -} -acir fn eq f4 { - b0(v0: Field, v1: Field): - v2 = eq v0, v1 - return v2 -} -acir fn equal f5 { - b0(): - return Field 1 -} -acir fn cmp f6 { - b0(v0: u32, v1: u32): - v2 = lt v0, v1 - jmpif v2 then: b1, else: b2 - b1(): - v4 = call f7() - jmp b3(v4) - b3(v11: Field): - return v11 - b2(): - v5 = lt v1, v0 - jmpif v5 then: b4, else: b5 - b4(): - v7 = call f8() - jmp b6(v7) - b6(v10: Field): - jmp b3(v10) - b5(): - v9 = call f5() - jmp b6(v9) -} -acir fn less f7 { - b0(): - return Field 0 -} -acir fn greater f8 { - b0(): - return Field 2 -} - -After Defunctionalization: -acir fn main f0 { - b0(v0: u32, v1: u32): - v3 = call f1(v0) - v5 = call f1(v1) - v7 = call f2(v3, v5, v5, v3) - constrain v7 == Field 0 - v10 = call f2(v5, v3, v3, v5) - constrain v10 == Field 2 - return -} -acir fn new f1 { - b0(v0: u32): - return v0 -} -acir fn cmp f2 { - b0(v0: u32, v1: u32, v2: u32, v3: u32): - v5 = call f3(v0, v2) - v6 = allocate - store v5 at v6 - v8 = load v6 - v10 = call f5() - v11 = call f4(v8, v10) - jmpif v11 then: b1, else: b2 - b1(): - v13 = call f3(v1, v3) - store v13 at v6 - jmp b2() - b2(): - v14 = load v6 - return v14 -} -acir fn cmp f3 { - b0(v0: u32, v1: u32): - v3 = call f6(v0, v1) - return v3 -} -acir fn eq f4 { - b0(v0: Field, v1: Field): - v2 = eq v0, v1 - return v2 -} -acir fn equal f5 { - b0(): - return Field 1 -} -acir fn cmp f6 { - b0(v0: u32, v1: u32): - v2 = lt v0, v1 - jmpif v2 then: b1, else: b2 - b1(): - v4 = call f7() - jmp b3(v4) - b3(v11: Field): - return v11 - b2(): - v5 = lt v1, v0 - jmpif v5 then: b4, else: b5 - b4(): - v7 = call f8() - jmp b6(v7) - b6(v10: Field): - jmp b3(v10) - b5(): - v9 = call f5() - jmp b6(v9) -} -acir fn less f7 { - b0(): - return Field 0 -} -acir fn greater f8 { - b0(): - return Field 2 -} - -After Inlining: -acir fn main f0 { - b0(v0: u32, v1: u32): - v7 = lt v0, v1 - jmpif v7 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v17 = allocate - store v12 at v17 - v18 = load v17 - v21 = eq v18, Field 1 - jmpif v21 then: b7, else: b8 - b7(): - v25 = lt v1, v0 - jmpif v25 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v17 - jmp b8() - b8(): - v22 = load v17 - constrain v22 == Field 0 - v35 = lt v1, v0 - jmpif v35 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v42 = allocate - store v39 at v42 - v43 = load v42 - v46 = eq v43, Field 1 - jmpif v46 then: b21, else: b22 - b21(): - v50 = lt v0, v1 - jmpif v50 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v42 - jmp b22() - b22(): - v47 = load v42 - constrain v47 == Field 2 - return - b24(): - v51 = lt v1, v0 - jmpif v51 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v36 = lt v0, v1 - jmpif v36 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v26 = lt v0, v1 - jmpif v26 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v8 = lt v1, v0 - jmpif v8 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v57 = lt v0, v1 - jmpif v57 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v59 = allocate - store v12 at v59 - v61 = eq v12, Field 1 - jmpif v61 then: b7, else: b8 - b7(): - v62 = lt v1, v0 - jmpif v62 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v59 - jmp b8() - b8(): - v64 = load v59 - constrain v64 == Field 0 - v65 = lt v1, v0 - jmpif v65 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v67 = allocate - store v39 at v67 - v69 = eq v39, Field 1 - jmpif v69 then: b21, else: b22 - b21(): - v70 = lt v0, v1 - jmpif v70 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v67 - jmp b22() - b22(): - v72 = load v67 - constrain v72 == Field 2 - return - b24(): - v71 = lt v1, v0 - jmpif v71 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v66 = lt v0, v1 - jmpif v66 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v63 = lt v0, v1 - jmpif v63 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v58 = lt v1, v0 - jmpif v58 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Assert Constant: -acir fn main f0 { - b0(v0: u32, v1: u32): - v57 = lt v0, v1 - jmpif v57 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v59 = allocate - store v12 at v59 - v61 = eq v12, Field 1 - jmpif v61 then: b7, else: b8 - b7(): - v62 = lt v1, v0 - jmpif v62 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v59 - jmp b8() - b8(): - v64 = load v59 - constrain v64 == Field 0 - v65 = lt v1, v0 - jmpif v65 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v67 = allocate - store v39 at v67 - v69 = eq v39, Field 1 - jmpif v69 then: b21, else: b22 - b21(): - v70 = lt v0, v1 - jmpif v70 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v67 - jmp b22() - b22(): - v72 = load v67 - constrain v72 == Field 2 - return - b24(): - v71 = lt v1, v0 - jmpif v71 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v66 = lt v0, v1 - jmpif v66 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v63 = lt v0, v1 - jmpif v63 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v58 = lt v1, v0 - jmpif v58 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Unrolling: -acir fn main f0 { - b0(v0: u32, v1: u32): - v57 = lt v0, v1 - jmpif v57 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v59 = allocate - store v12 at v59 - v61 = eq v12, Field 1 - jmpif v61 then: b7, else: b8 - b7(): - v62 = lt v1, v0 - jmpif v62 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v59 - jmp b8() - b8(): - v64 = load v59 - constrain v64 == Field 0 - v65 = lt v1, v0 - jmpif v65 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v67 = allocate - store v39 at v67 - v69 = eq v39, Field 1 - jmpif v69 then: b21, else: b22 - b21(): - v70 = lt v0, v1 - jmpif v70 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v67 - jmp b22() - b22(): - v72 = load v67 - constrain v72 == Field 2 - return - b24(): - v71 = lt v1, v0 - jmpif v71 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v66 = lt v0, v1 - jmpif v66 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v63 = lt v0, v1 - jmpif v63 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v58 = lt v1, v0 - jmpif v58 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Simplifying: -acir fn main f0 { - b0(v0: u32, v1: u32): - v57 = lt v0, v1 - jmpif v57 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v59 = allocate - store v12 at v59 - v61 = eq v12, Field 1 - jmpif v61 then: b7, else: b8 - b7(): - v62 = lt v1, v0 - jmpif v62 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v59 - jmp b8() - b8(): - v64 = load v59 - constrain v64 == Field 0 - v65 = lt v1, v0 - jmpif v65 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v67 = allocate - store v39 at v67 - v69 = eq v39, Field 1 - jmpif v69 then: b21, else: b22 - b21(): - v70 = lt v0, v1 - jmpif v70 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v67 - jmp b22() - b22(): - v72 = load v67 - constrain v72 == Field 2 - return - b24(): - v71 = lt v1, v0 - jmpif v71 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v66 = lt v0, v1 - jmpif v66 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v63 = lt v0, v1 - jmpif v63 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v58 = lt v1, v0 - jmpif v58 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v74 = lt v0, v1 - jmpif v74 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v76 = allocate - store v12 at v76 - v77 = eq v12, Field 1 - jmpif v77 then: b7, else: b8 - b7(): - v78 = lt v1, v0 - jmpif v78 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v76 - jmp b8() - b8(): - v80 = load v76 - constrain v80 == Field 0 - v81 = lt v1, v0 - jmpif v81 then: b15, else: b16 - b15(): - jmp b20(Field 0) - b20(v39: Field): - v83 = allocate - store v39 at v83 - v84 = eq v39, Field 1 - jmpif v84 then: b21, else: b22 - b21(): - v85 = lt v0, v1 - jmpif v85 then: b23, else: b24 - b23(): - jmp b28(Field 0) - b28(v54: Field): - store v54 at v83 - jmp b22() - b22(): - v87 = load v83 - constrain v87 == Field 2 - return - b24(): - v86 = lt v1, v0 - jmpif v86 then: b25, else: b26 - b25(): - jmp b27(Field 2) - b27(v53: Field): - jmp b28(v53) - b26(): - jmp b27(Field 1) - b16(): - v82 = lt v0, v1 - jmpif v82 then: b17, else: b18 - b17(): - jmp b19(Field 2) - b19(v38: Field): - jmp b20(v38) - b18(): - jmp b19(Field 1) - b10(): - v79 = lt v0, v1 - jmpif v79 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v75 = lt v1, v0 - jmpif v75 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Flattening: -acir fn main f0 { - b0(v0: u32, v1: u32): - v74 = lt v0, v1 - enable_side_effects v74 - v89 = not v74 - enable_side_effects v89 - v91 = lt v1, v0 - v92 = mul v89, v91 - enable_side_effects v92 - v93 = not v91 - v94 = mul v89, v93 - enable_side_effects v89 - v95 = cast v91 as Field - v96 = cast v93 as Field - v97 = mul v95, Field 2 - v98 = add v97, v96 - enable_side_effects u1 1 - v99 = cast v74 as Field - v100 = cast v89 as Field - v101 = mul v100, v98 - v102 = allocate - store v101 at v102 - v103 = eq v101, Field 1 - enable_side_effects v103 - v104 = lt v1, v0 - v105 = mul v103, v104 - enable_side_effects v105 - v106 = not v104 - v107 = mul v103, v106 - enable_side_effects v107 - v108 = lt v0, v1 - v109 = mul v107, v108 - enable_side_effects v109 - v110 = not v108 - v111 = mul v107, v110 - enable_side_effects v107 - v112 = cast v108 as Field - v113 = cast v110 as Field - v114 = mul v112, Field 2 - v115 = add v114, v113 - enable_side_effects v103 - v116 = cast v104 as Field - v117 = cast v106 as Field - v118 = mul v117, v115 - v119 = load v102 - store v118 at v102 - v120 = not v103 - store v119 at v102 - enable_side_effects u1 1 - v121 = cast v103 as Field - v122 = cast v120 as Field - v123 = mul v121, v118 - v124 = mul v122, v119 - v125 = add v123, v124 - store v125 at v102 - v126 = load v102 - constrain v126 == Field 0 - v127 = lt v1, v0 - enable_side_effects v127 - v128 = not v127 - enable_side_effects v128 - v129 = lt v0, v1 - v130 = mul v128, v129 - enable_side_effects v130 - v131 = not v129 - v132 = mul v128, v131 - enable_side_effects v128 - v133 = cast v129 as Field - v134 = cast v131 as Field - v135 = mul v133, Field 2 - v136 = add v135, v134 - enable_side_effects u1 1 - v137 = cast v127 as Field - v138 = cast v128 as Field - v139 = mul v138, v136 - v140 = allocate - store v139 at v140 - v141 = eq v139, Field 1 - enable_side_effects v141 - v142 = lt v0, v1 - v143 = mul v141, v142 - enable_side_effects v143 - v144 = not v142 - v145 = mul v141, v144 - enable_side_effects v145 - v146 = lt v1, v0 - v147 = mul v145, v146 - enable_side_effects v147 - v148 = not v146 - v149 = mul v145, v148 - enable_side_effects v145 - v150 = cast v146 as Field - v151 = cast v148 as Field - v152 = mul v150, Field 2 - v153 = add v152, v151 - enable_side_effects v141 - v154 = cast v142 as Field - v155 = cast v144 as Field - v156 = mul v155, v153 - v157 = load v140 - store v156 at v140 - v158 = not v141 - store v157 at v140 - enable_side_effects u1 1 - v159 = cast v141 as Field - v160 = cast v158 as Field - v161 = mul v159, v156 - v162 = mul v160, v157 - v163 = add v161, v162 - store v163 at v140 - v164 = load v140 - constrain v164 == Field 2 - return -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v165 = lt v0, v1 - enable_side_effects v165 - v166 = not v165 - enable_side_effects v166 - v167 = lt v1, v0 - v168 = mul v166, v167 - enable_side_effects v168 - v169 = not v167 - v170 = mul v166, v169 - enable_side_effects v166 - v171 = cast v167 as Field - v172 = cast v169 as Field - v173 = mul v171, Field 2 - v174 = add v173, v172 - enable_side_effects u1 1 - v175 = cast v165 as Field - v176 = cast v166 as Field - v177 = mul v176, v174 - v178 = allocate - v179 = eq v177, Field 1 - enable_side_effects v179 - v180 = lt v1, v0 - v181 = mul v179, v180 - enable_side_effects v181 - v182 = not v180 - v183 = mul v179, v182 - enable_side_effects v183 - v184 = lt v0, v1 - v185 = mul v183, v184 - enable_side_effects v185 - v186 = not v184 - v187 = mul v183, v186 - enable_side_effects v183 - v188 = cast v184 as Field - v189 = cast v186 as Field - v190 = mul v188, Field 2 - v191 = add v190, v189 - enable_side_effects v179 - v192 = cast v180 as Field - v193 = cast v182 as Field - v194 = mul v193, v191 - v196 = not v179 - enable_side_effects u1 1 - v197 = cast v179 as Field - v198 = cast v196 as Field - v199 = mul v197, v194 - v200 = mul v198, v177 - v201 = add v199, v200 - constrain v201 == Field 0 - v203 = lt v1, v0 - enable_side_effects v203 - v204 = not v203 - enable_side_effects v204 - v205 = lt v0, v1 - v206 = mul v204, v205 - enable_side_effects v206 - v207 = not v205 - v208 = mul v204, v207 - enable_side_effects v204 - v209 = cast v205 as Field - v210 = cast v207 as Field - v211 = mul v209, Field 2 - v212 = add v211, v210 - enable_side_effects u1 1 - v213 = cast v203 as Field - v214 = cast v204 as Field - v215 = mul v214, v212 - v216 = allocate - v217 = eq v215, Field 1 - enable_side_effects v217 - v218 = lt v0, v1 - v219 = mul v217, v218 - enable_side_effects v219 - v220 = not v218 - v221 = mul v217, v220 - enable_side_effects v221 - v222 = lt v1, v0 - v223 = mul v221, v222 - enable_side_effects v223 - v224 = not v222 - v225 = mul v221, v224 - enable_side_effects v221 - v226 = cast v222 as Field - v227 = cast v224 as Field - v228 = mul v226, Field 2 - v229 = add v228, v227 - enable_side_effects v217 - v230 = cast v218 as Field - v231 = cast v220 as Field - v232 = mul v231, v229 - v234 = not v217 - enable_side_effects u1 1 - v235 = cast v217 as Field - v236 = cast v234 as Field - v237 = mul v235, v232 - v238 = mul v236, v215 - v239 = add v237, v238 - constrain v239 == Field 2 - return -} - -After Constant Folding: -acir fn main f0 { - b0(v0: u32, v1: u32): - v241 = lt v0, v1 - enable_side_effects v241 - v242 = not v241 - enable_side_effects v242 - v243 = lt v1, v0 - v244 = mul v242, v243 - enable_side_effects v244 - v245 = not v243 - v246 = mul v242, v245 - enable_side_effects v242 - v247 = cast v243 as Field - v248 = cast v245 as Field - v249 = mul v247, Field 2 - v250 = add v249, v248 - enable_side_effects u1 1 - v251 = cast v241 as Field - v252 = cast v242 as Field - v253 = mul v252, v250 - v254 = allocate - v255 = eq v253, Field 1 - enable_side_effects v255 - v256 = mul v255, v243 - enable_side_effects v256 - v257 = mul v255, v245 - enable_side_effects v257 - v258 = mul v257, v241 - enable_side_effects v258 - v259 = mul v257, v242 - enable_side_effects v257 - v260 = mul v251, Field 2 - v261 = add v260, v252 - enable_side_effects v255 - v262 = mul v248, v261 - v263 = not v255 - enable_side_effects u1 1 - v264 = cast v255 as Field - v265 = cast v263 as Field - v266 = mul v264, v262 - v267 = mul v265, v253 - v268 = add v266, v267 - constrain v268 == Field 0 - enable_side_effects v245 - v269 = mul v245, v241 - enable_side_effects v269 - v270 = mul v245, v242 - enable_side_effects u1 1 - v271 = allocate - v272 = eq v262, Field 1 - enable_side_effects v272 - v273 = mul v272, v241 - enable_side_effects v273 - v274 = mul v272, v242 - enable_side_effects v274 - v275 = mul v274, v243 - enable_side_effects v275 - v276 = mul v274, v245 - enable_side_effects v272 - v277 = not v272 - enable_side_effects u1 1 - v278 = cast v272 as Field - v279 = cast v277 as Field - v280 = mul v278, v253 - v281 = mul v279, v262 - v282 = add v280, v281 - constrain v282 == Field 2 - return -} - -After Dead Instruction Elimination: -acir fn main f0 { - b0(v0: u32, v1: u32): - v241 = lt v0, v1 - enable_side_effects v241 - v242 = not v241 - enable_side_effects v242 - v243 = lt v1, v0 - v244 = mul v242, v243 - enable_side_effects v244 - v245 = not v243 - enable_side_effects v242 - v247 = cast v243 as Field - v248 = cast v245 as Field - v249 = mul v247, Field 2 - v250 = add v249, v248 - enable_side_effects u1 1 - v251 = cast v241 as Field - v252 = cast v242 as Field - v253 = mul v252, v250 - v255 = eq v253, Field 1 - enable_side_effects v255 - v256 = mul v255, v243 - enable_side_effects v256 - v257 = mul v255, v245 - enable_side_effects v257 - v258 = mul v257, v241 - enable_side_effects v258 - enable_side_effects v257 - v260 = mul v251, Field 2 - v261 = add v260, v252 - enable_side_effects v255 - v262 = mul v248, v261 - v263 = not v255 - enable_side_effects u1 1 - v264 = cast v255 as Field - v265 = cast v263 as Field - v266 = mul v264, v262 - v267 = mul v265, v253 - v268 = add v266, v267 - constrain v268 == Field 0 - enable_side_effects v245 - v269 = mul v245, v241 - enable_side_effects v269 - enable_side_effects u1 1 - v272 = eq v262, Field 1 - enable_side_effects v272 - v273 = mul v272, v241 - enable_side_effects v273 - v274 = mul v272, v242 - enable_side_effects v274 - v275 = mul v274, v243 - enable_side_effects v275 - enable_side_effects v272 - v277 = not v272 - enable_side_effects u1 1 - v278 = cast v272 as Field - v279 = cast v277 as Field - v280 = mul v278, v253 - v281 = mul v279, v262 - v282 = add v280, v281 - constrain v282 == Field 2 - return -} - -After Fill Internal Slice Dummy Data: -acir fn main f0 { - b0(v0: u32, v1: u32): - v283 = lt v0, v1 - enable_side_effects v283 - v284 = not v283 - enable_side_effects v284 - v285 = lt v1, v0 - v286 = mul v284, v285 - enable_side_effects v286 - v287 = not v285 - enable_side_effects v284 - v288 = cast v285 as Field - v289 = cast v287 as Field - v290 = mul v288, Field 2 - v291 = add v290, v289 - enable_side_effects u1 1 - v292 = cast v283 as Field - v293 = cast v284 as Field - v294 = mul v293, v291 - v295 = eq v294, Field 1 - enable_side_effects v295 - v296 = mul v295, v285 - enable_side_effects v296 - v297 = mul v295, v287 - enable_side_effects v297 - v298 = mul v297, v283 - enable_side_effects v297 - v299 = mul v292, Field 2 - v300 = add v299, v293 - enable_side_effects v295 - v301 = mul v289, v300 - v302 = not v295 - enable_side_effects u1 1 - v303 = cast v295 as Field - v304 = cast v302 as Field - v305 = mul v303, v301 - v306 = mul v304, v294 - v307 = add v305, v306 - constrain v307 == Field 0 - enable_side_effects v287 - v308 = mul v287, v283 - enable_side_effects u1 1 - v309 = eq v301, Field 1 - enable_side_effects v309 - v310 = mul v309, v283 - enable_side_effects v310 - v311 = mul v309, v284 - enable_side_effects v311 - v312 = mul v311, v285 - enable_side_effects v309 - v313 = not v309 - enable_side_effects u1 1 - v314 = cast v309 as Field - v315 = cast v313 as Field - v316 = mul v314, v294 - v317 = mul v315, v301 - v318 = add v316, v317 - constrain v318 == Field 2 - return -} - diff --git a/test_programs/execution_success/operator_overloading/success b/test_programs/execution_success/operator_overloading/success deleted file mode 100644 index 5e2b241bf04..00000000000 --- a/test_programs/execution_success/operator_overloading/success +++ /dev/null @@ -1,664 +0,0 @@ -Initial SSA: -acir fn main f0 { - b0(v0: u32, v1: u32): - v3 = call f1(v0) - v5 = call f1(v1) - v7 = call f2(v5, v3, v3, v5) - constrain v7 == Field 2 - return -} -acir fn new f1 { - b0(v0: u32): - return v0 -} -acir fn cmp f2 { - b0(v0: u32, v1: u32, v2: u32, v3: u32): - v5 = call f3(v0, v2) - v6 = allocate - store v5 at v6 - v8 = load v6 - v10 = call f5() - v11 = call f4(v8, v10) - jmpif v11 then: b1, else: b2 - b1(): - v13 = call f3(v1, v3) - store v13 at v6 - jmp b2() - b2(): - v14 = load v6 - return v14 -} -acir fn cmp f3 { - b0(v0: u32, v1: u32): - v3 = call f6(v0, v1) - return v3 -} -acir fn eq f4 { - b0(v0: Field, v1: Field): - v2 = eq v0, v1 - return v2 -} -acir fn equal f5 { - b0(): - return Field 1 -} -acir fn cmp f6 { - b0(v0: u32, v1: u32): - v2 = lt v0, v1 - jmpif v2 then: b1, else: b2 - b1(): - v4 = call f7() - jmp b3(v4) - b3(v11: Field): - return v11 - b2(): - v5 = lt v1, v0 - jmpif v5 then: b4, else: b5 - b4(): - v7 = call f8() - jmp b6(v7) - b6(v10: Field): - jmp b3(v10) - b5(): - v9 = call f5() - jmp b6(v9) -} -acir fn less f7 { - b0(): - return Field 0 -} -acir fn greater f8 { - b0(): - return Field 2 -} - -After Defunctionalization: -acir fn main f0 { - b0(v0: u32, v1: u32): - v3 = call f1(v0) - v5 = call f1(v1) - v7 = call f2(v5, v3, v3, v5) - constrain v7 == Field 2 - return -} -acir fn new f1 { - b0(v0: u32): - return v0 -} -acir fn cmp f2 { - b0(v0: u32, v1: u32, v2: u32, v3: u32): - v5 = call f3(v0, v2) - v6 = allocate - store v5 at v6 - v8 = load v6 - v10 = call f5() - v11 = call f4(v8, v10) - jmpif v11 then: b1, else: b2 - b1(): - v13 = call f3(v1, v3) - store v13 at v6 - jmp b2() - b2(): - v14 = load v6 - return v14 -} -acir fn cmp f3 { - b0(v0: u32, v1: u32): - v3 = call f6(v0, v1) - return v3 -} -acir fn eq f4 { - b0(v0: Field, v1: Field): - v2 = eq v0, v1 - return v2 -} -acir fn equal f5 { - b0(): - return Field 1 -} -acir fn cmp f6 { - b0(v0: u32, v1: u32): - v2 = lt v0, v1 - jmpif v2 then: b1, else: b2 - b1(): - v4 = call f7() - jmp b3(v4) - b3(v11: Field): - return v11 - b2(): - v5 = lt v1, v0 - jmpif v5 then: b4, else: b5 - b4(): - v7 = call f8() - jmp b6(v7) - b6(v10: Field): - jmp b3(v10) - b5(): - v9 = call f5() - jmp b6(v9) -} -acir fn less f7 { - b0(): - return Field 0 -} -acir fn greater f8 { - b0(): - return Field 2 -} - -After Inlining: -acir fn main f0 { - b0(v0: u32, v1: u32): - v7 = lt v1, v0 - jmpif v7 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v17 = allocate - store v12 at v17 - v18 = load v17 - v21 = eq v18, Field 1 - jmpif v21 then: b7, else: b8 - b7(): - v25 = lt v0, v1 - jmpif v25 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v17 - jmp b8() - b8(): - v22 = load v17 - constrain v22 == Field 2 - return - b10(): - v26 = lt v1, v0 - jmpif v26 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v8 = lt v0, v1 - jmpif v8 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v32 = lt v1, v0 - jmpif v32 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v34 = allocate - store v12 at v34 - v36 = eq v12, Field 1 - jmpif v36 then: b7, else: b8 - b7(): - v37 = lt v0, v1 - jmpif v37 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v34 - jmp b8() - b8(): - v39 = load v34 - constrain v39 == Field 2 - return - b10(): - v38 = lt v1, v0 - jmpif v38 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v33 = lt v0, v1 - jmpif v33 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Assert Constant: -acir fn main f0 { - b0(v0: u32, v1: u32): - v32 = lt v1, v0 - jmpif v32 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v34 = allocate - store v12 at v34 - v36 = eq v12, Field 1 - jmpif v36 then: b7, else: b8 - b7(): - v37 = lt v0, v1 - jmpif v37 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v34 - jmp b8() - b8(): - v39 = load v34 - constrain v39 == Field 2 - return - b10(): - v38 = lt v1, v0 - jmpif v38 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v33 = lt v0, v1 - jmpif v33 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Unrolling: -acir fn main f0 { - b0(v0: u32, v1: u32): - v32 = lt v1, v0 - jmpif v32 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v34 = allocate - store v12 at v34 - v36 = eq v12, Field 1 - jmpif v36 then: b7, else: b8 - b7(): - v37 = lt v0, v1 - jmpif v37 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v34 - jmp b8() - b8(): - v39 = load v34 - constrain v39 == Field 2 - return - b10(): - v38 = lt v1, v0 - jmpif v38 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v33 = lt v0, v1 - jmpif v33 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Simplifying: -acir fn main f0 { - b0(v0: u32, v1: u32): - v32 = lt v1, v0 - jmpif v32 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v34 = allocate - store v12 at v34 - v36 = eq v12, Field 1 - jmpif v36 then: b7, else: b8 - b7(): - v37 = lt v0, v1 - jmpif v37 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v34 - jmp b8() - b8(): - v39 = load v34 - constrain v39 == Field 2 - return - b10(): - v38 = lt v1, v0 - jmpif v38 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v33 = lt v0, v1 - jmpif v33 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v41 = lt v1, v0 - jmpif v41 then: b1, else: b2 - b1(): - jmp b6(Field 0) - b6(v12: Field): - v43 = allocate - store v12 at v43 - v44 = eq v12, Field 1 - jmpif v44 then: b7, else: b8 - b7(): - v45 = lt v0, v1 - jmpif v45 then: b9, else: b10 - b9(): - jmp b14(Field 0) - b14(v29: Field): - store v29 at v43 - jmp b8() - b8(): - v47 = load v43 - constrain v47 == Field 2 - return - b10(): - v46 = lt v1, v0 - jmpif v46 then: b11, else: b12 - b11(): - jmp b13(Field 2) - b13(v28: Field): - jmp b14(v28) - b12(): - jmp b13(Field 1) - b2(): - v42 = lt v0, v1 - jmpif v42 then: b3, else: b4 - b3(): - jmp b5(Field 2) - b5(v11: Field): - jmp b6(v11) - b4(): - jmp b5(Field 1) -} - -After Flattening: -acir fn main f0 { - b0(v0: u32, v1: u32): - v41 = lt v1, v0 - enable_side_effects v41 - v49 = not v41 - enable_side_effects v49 - v51 = lt v0, v1 - v52 = mul v49, v51 - enable_side_effects v52 - v53 = not v51 - v54 = mul v49, v53 - enable_side_effects v49 - v55 = cast v51 as Field - v56 = cast v53 as Field - v57 = mul v55, Field 2 - v58 = add v57, v56 - enable_side_effects u1 1 - v59 = cast v41 as Field - v60 = cast v49 as Field - v61 = mul v60, v58 - v62 = allocate - store v61 at v62 - v63 = eq v61, Field 1 - enable_side_effects v63 - v64 = lt v0, v1 - v65 = mul v63, v64 - enable_side_effects v65 - v66 = not v64 - v67 = mul v63, v66 - enable_side_effects v67 - v68 = lt v1, v0 - v69 = mul v67, v68 - enable_side_effects v69 - v70 = not v68 - v71 = mul v67, v70 - enable_side_effects v67 - v72 = cast v68 as Field - v73 = cast v70 as Field - v74 = mul v72, Field 2 - v75 = add v74, v73 - enable_side_effects v63 - v76 = cast v64 as Field - v77 = cast v66 as Field - v78 = mul v77, v75 - v79 = load v62 - store v78 at v62 - v80 = not v63 - store v79 at v62 - enable_side_effects u1 1 - v81 = cast v63 as Field - v82 = cast v80 as Field - v83 = mul v81, v78 - v84 = mul v82, v79 - v85 = add v83, v84 - store v85 at v62 - v86 = load v62 - constrain v86 == Field 2 - return -} - -After Mem2Reg: -acir fn main f0 { - b0(v0: u32, v1: u32): - v87 = lt v1, v0 - enable_side_effects v87 - v88 = not v87 - enable_side_effects v88 - v89 = lt v0, v1 - v90 = mul v88, v89 - enable_side_effects v90 - v91 = not v89 - v92 = mul v88, v91 - enable_side_effects v88 - v93 = cast v89 as Field - v94 = cast v91 as Field - v95 = mul v93, Field 2 - v96 = add v95, v94 - enable_side_effects u1 1 - v97 = cast v87 as Field - v98 = cast v88 as Field - v99 = mul v98, v96 - v100 = allocate - v101 = eq v99, Field 1 - enable_side_effects v101 - v102 = lt v0, v1 - v103 = mul v101, v102 - enable_side_effects v103 - v104 = not v102 - v105 = mul v101, v104 - enable_side_effects v105 - v106 = lt v1, v0 - v107 = mul v105, v106 - enable_side_effects v107 - v108 = not v106 - v109 = mul v105, v108 - enable_side_effects v105 - v110 = cast v106 as Field - v111 = cast v108 as Field - v112 = mul v110, Field 2 - v113 = add v112, v111 - enable_side_effects v101 - v114 = cast v102 as Field - v115 = cast v104 as Field - v116 = mul v115, v113 - v118 = not v101 - enable_side_effects u1 1 - v119 = cast v101 as Field - v120 = cast v118 as Field - v121 = mul v119, v116 - v122 = mul v120, v99 - v123 = add v121, v122 - constrain v123 == Field 2 - return -} - -After Constant Folding: -acir fn main f0 { - b0(v0: u32, v1: u32): - v125 = lt v1, v0 - enable_side_effects v125 - v126 = not v125 - enable_side_effects v126 - v127 = lt v0, v1 - v128 = mul v126, v127 - enable_side_effects v128 - v129 = not v127 - v130 = mul v126, v129 - enable_side_effects v126 - v131 = cast v127 as Field - v132 = cast v129 as Field - v133 = mul v131, Field 2 - v134 = add v133, v132 - enable_side_effects u1 1 - v135 = cast v125 as Field - v136 = cast v126 as Field - v137 = mul v136, v134 - v138 = allocate - v139 = eq v137, Field 1 - enable_side_effects v139 - v140 = mul v139, v127 - enable_side_effects v140 - v141 = mul v139, v129 - enable_side_effects v141 - v142 = mul v141, v125 - enable_side_effects v142 - v143 = mul v141, v126 - enable_side_effects v141 - v144 = mul v135, Field 2 - v145 = add v144, v136 - enable_side_effects v139 - v146 = mul v132, v145 - v147 = not v139 - enable_side_effects u1 1 - v148 = cast v139 as Field - v149 = cast v147 as Field - v150 = mul v148, v146 - v151 = mul v149, v137 - v152 = add v150, v151 - constrain v152 == Field 2 - return -} - -After Dead Instruction Elimination: -acir fn main f0 { - b0(v0: u32, v1: u32): - v125 = lt v1, v0 - enable_side_effects v125 - v126 = not v125 - enable_side_effects v126 - v127 = lt v0, v1 - v128 = mul v126, v127 - enable_side_effects v128 - v129 = not v127 - enable_side_effects v126 - v131 = cast v127 as Field - v132 = cast v129 as Field - v133 = mul v131, Field 2 - v134 = add v133, v132 - enable_side_effects u1 1 - v135 = cast v125 as Field - v136 = cast v126 as Field - v137 = mul v136, v134 - v139 = eq v137, Field 1 - enable_side_effects v139 - v140 = mul v139, v127 - enable_side_effects v140 - v141 = mul v139, v129 - enable_side_effects v141 - v142 = mul v141, v125 - enable_side_effects v142 - enable_side_effects v141 - v144 = mul v135, Field 2 - v145 = add v144, v136 - enable_side_effects v139 - v146 = mul v132, v145 - v147 = not v139 - enable_side_effects u1 1 - v148 = cast v139 as Field - v149 = cast v147 as Field - v150 = mul v148, v146 - v151 = mul v149, v137 - v152 = add v150, v151 - constrain v152 == Field 2 - return -} - -After Fill Internal Slice Dummy Data: -acir fn main f0 { - b0(v0: u32, v1: u32): - v153 = lt v1, v0 - enable_side_effects v153 - v154 = not v153 - enable_side_effects v154 - v155 = lt v0, v1 - v156 = mul v154, v155 - enable_side_effects v156 - v157 = not v155 - enable_side_effects v154 - v158 = cast v155 as Field - v159 = cast v157 as Field - v160 = mul v158, Field 2 - v161 = add v160, v159 - enable_side_effects u1 1 - v162 = cast v153 as Field - v163 = cast v154 as Field - v164 = mul v163, v161 - v165 = eq v164, Field 1 - enable_side_effects v165 - v166 = mul v165, v155 - enable_side_effects v166 - v167 = mul v165, v157 - enable_side_effects v167 - v168 = mul v167, v153 - enable_side_effects v167 - v169 = mul v162, Field 2 - v170 = add v169, v163 - enable_side_effects v165 - v171 = mul v159, v170 - v172 = not v165 - enable_side_effects u1 1 - v173 = cast v165 as Field - v174 = cast v172 as Field - v175 = mul v173, v171 - v176 = mul v174, v164 - v177 = add v175, v176 - constrain v177 == Field 2 - return -} - -[operator_overloading] Circuit witness successfully solved From 0925fc593fc52a999223297df4207b2ef89c758f Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 2 Jan 2024 23:19:51 +0000 Subject: [PATCH 13/21] fix: include stdlib when preparing a package from source string --- tooling/lsp/src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tooling/lsp/src/lib.rs b/tooling/lsp/src/lib.rs index 9887e5b8e96..90e8e563cd3 100644 --- a/tooling/lsp/src/lib.rs +++ b/tooling/lsp/src/lib.rs @@ -21,14 +21,12 @@ use fm::codespan_files as files; use lsp_types::CodeLens; use nargo::workspace::Workspace; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; +use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::{ graph::{CrateId, CrateName}, hir::{Context, FunctionNameMatch}, }; -use fm::FileManager; - use notifications::{ on_did_change_configuration, on_did_change_text_document, on_did_close_text_document, on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, @@ -223,14 +221,14 @@ pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result (Context<'static>, CrateId) { let root = Path::new(""); - let mut file_manager = FileManager::new(root); - let root_file_id = file_manager.add_file_with_source(Path::new("main.nr"), source).expect( + let file_name = Path::new("main.nr"); + let mut file_manager = file_manager_with_stdlib(root); + file_manager.add_file_with_source(file_name, source).expect( "Adding source buffer to file manager should never fail when file manager is empty", ); let mut context = Context::new(file_manager); - - let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + let root_crate_id = prepare_crate(&mut context, file_name); (context, root_crate_id) } From ff174ba99adacdb7fc30ff7cf69607b53a7a001d Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 3 Jan 2024 08:28:47 -0500 Subject: [PATCH 14/21] Update compiler/noirc_frontend/src/monomorphization/mod.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_frontend/src/monomorphization/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index e4cd0552402..83499d0e585 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -380,6 +380,7 @@ impl<'interner> Monomorphizer<'interner> { let env = Box::new(Type::Unit); let function_type = Type::Function(args, Box::new(ret.clone()), env); + // We assume that the first method in the trait definition implements the operator being overloaded. let method = TraitMethodId { trait_id: infix.trait_id, method_index: 0 }; let func = self.resolve_trait_method_reference(expr, function_type, method); self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location) From 5514654d38bb31134d18706fb979027d45c32097 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 3 Jan 2024 11:34:06 -0500 Subject: [PATCH 15/21] PR feedback --- .../noirc_frontend/src/hir/resolution/resolver.rs | 4 ++-- compiler/noirc_frontend/src/hir/type_check/expr.rs | 14 +++++++------- compiler/noirc_frontend/src/hir/type_check/mod.rs | 5 +++-- compiler/noirc_frontend/src/hir_def/expr.rs | 10 ++++++---- .../noirc_frontend/src/monomorphization/mod.rs | 3 +-- compiler/noirc_frontend/src/node_interner.rs | 7 +++++-- noir_stdlib/src/prelude.nr | 3 +-- 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index d3175f3d5c3..91cfa5c6058 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1315,12 +1315,12 @@ impl<'a> Resolver<'a> { ExpressionKind::Infix(infix) => { let lhs = self.resolve_expression(infix.lhs); let rhs = self.resolve_expression(infix.rhs); - let trait_id = self.interner.get_operator_trait(infix.operator.contents); + let trait_id = self.interner.get_operator_trait_method(infix.operator.contents); HirExpression::Infix(HirInfixExpression { lhs, operator: HirBinaryOp::new(infix.operator, self.file), - trait_id, + trait_method_id: trait_id, rhs, }) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 8896d3fe734..7f6697629a2 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -140,9 +140,9 @@ impl<'interner> TypeChecker<'interner> { match self.infix_operand_type_rules(&lhs_type, operator, &rhs_type, span) { Ok((typ, use_impl)) => { if use_impl { - let trait_id = infix_expr.trait_id; - self.verify_trait_constraint(&lhs_type, trait_id, *expr_id, span); - self.typecheck_operator_method(*expr_id, trait_id, &lhs_type, span); + let id = infix_expr.trait_method_id; + self.verify_trait_constraint(&lhs_type, id.trait_id, *expr_id, span); + self.typecheck_operator_method(*expr_id, id, &lhs_type, span); } typ } @@ -1204,14 +1204,14 @@ impl<'interner> TypeChecker<'interner> { fn typecheck_operator_method( &mut self, expr_id: ExprId, - trait_id: TraitId, + trait_method_id: TraitMethodId, object_type: &Type, span: Span, ) { - let the_trait = self.interner.get_trait(trait_id); + let the_trait = self.interner.get_trait(trait_method_id.trait_id); // The first (and only) method of each operator trait should be the operator method - let method = &the_trait.methods[0]; + let method = &the_trait.methods[trait_method_id.method_index]; let (method_type, mut bindings) = method.typ.instantiate(self.interner); match method_type { @@ -1234,7 +1234,7 @@ impl<'interner> TypeChecker<'interner> { // referenced by the selected trait impl, if one has yet to be selected. let impl_kind = self.interner.get_selected_impl_for_expression(expr_id); if let Some(TraitImplKind::Assumed { object_type }) = impl_kind { - let the_trait = self.interner.get_trait(trait_id); + let the_trait = self.interner.get_trait(trait_method_id.trait_id); let object_type = object_type.substitute(&bindings); bindings.insert( the_trait.self_type_typevar_id, diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 638b2c9d95a..092e8631f1b 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -241,7 +241,7 @@ mod test { function::{FuncMeta, HirFunction}, stmt::HirStatement, }; - use crate::node_interner::{DefinitionKind, FuncId, NodeInterner, TraitId}; + use crate::node_interner::{DefinitionKind, FuncId, NodeInterner, TraitId, TraitMethodId}; use crate::{ hir::{ def_map::{CrateDefMap, LocalModuleId, ModuleDefId}, @@ -286,7 +286,8 @@ mod test { // Create Infix let operator = HirBinaryOp { location, kind: BinaryOpKind::Add }; let trait_id = TraitId(ModuleId::dummy_id()); - let expr = HirInfixExpression { lhs: x_expr_id, operator, rhs: y_expr_id, trait_id }; + let trait_method_id = TraitMethodId { trait_id, method_index: 0 }; + let expr = HirInfixExpression { lhs: x_expr_id, operator, rhs: y_expr_id, trait_method_id }; let expr_id = interner.push_expr(HirExpression::Infix(expr)); interner.push_expr_location(expr_id, Span::single_char(0), file); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index ea6ecf30d12..d446e8783dd 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -3,7 +3,7 @@ use fm::FileId; use noirc_errors::Location; use crate::node_interner::{ - DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitId, TraitMethodId, + DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, }; use crate::{BinaryOp, BinaryOpKind, Ident, Shared, UnaryOp}; @@ -104,9 +104,11 @@ pub struct HirInfixExpression { pub operator: HirBinaryOp, pub rhs: ExprId, - /// The trait id for the operator trait that corresponds to this operator. - /// For derived operators like `!=`, this will lead to the derived trait (Eq in this case). - pub trait_id: TraitId, + /// The trait method id for the operator trait method that corresponds to this operator. + /// For derived operators like `!=`, this will lead to the method `Eq::eq`. For these + /// cases, it is up to the monomorphization pass to insert the appropriate `not` operation + /// after the call to `Eq::eq` to get the result of the `!=` operator. + pub trait_method_id: TraitMethodId, } /// This is always a struct field access `my_struct.field` diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 83499d0e585..5bdd41eafaa 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -380,8 +380,7 @@ impl<'interner> Monomorphizer<'interner> { let env = Box::new(Type::Unit); let function_type = Type::Function(args, Box::new(ret.clone()), env); - // We assume that the first method in the trait definition implements the operator being overloaded. - let method = TraitMethodId { trait_id: infix.trait_id, method_index: 0 }; + let method = infix.trait_method_id; let func = self.resolve_trait_method_reference(expr, function_type, method); self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location) } else { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b5a238c9473..ed612b7b8b3 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1361,8 +1361,11 @@ impl NodeInterner { /// to the same trait (such as `==` and `!=`). /// `self.operator_traits` is expected to be filled before name resolution, /// during definition collection. - pub fn get_operator_trait(&self, operator: BinaryOpKind) -> TraitId { - self.operator_traits[&operator] + pub fn get_operator_trait_method(&self, operator: BinaryOpKind) -> TraitMethodId { + let trait_id = self.operator_traits[&operator]; + + // Assume that the operator's method to be overloaded is the first method of the trait. + TraitMethodId { trait_id, method_index: 0 } } /// Add the given trait as an operator trait if its name matches one of the diff --git a/noir_stdlib/src/prelude.nr b/noir_stdlib/src/prelude.nr index e41b749ec75..56020509122 100644 --- a/noir_stdlib/src/prelude.nr +++ b/noir_stdlib/src/prelude.nr @@ -1,6 +1,5 @@ use crate::collections::vec::Vec; use crate::option::Option; use crate::{print, println, assert_constant}; -use crate::cmp::{Eq, Ord, Ordering}; +use crate::cmp::{Eq, Ord}; use crate::default::Default; -use crate::ops::{Add, Sub, Mul, Div, Rem, BitOr, BitAnd, BitXor, Shl, Shr}; From ff15a5587bbdda9c8a3371d32c0821bb640c869a Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 3 Jan 2024 11:34:18 -0500 Subject: [PATCH 16/21] fmt --- compiler/noirc_frontend/src/hir_def/expr.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index d446e8783dd..7c04398ca88 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -2,9 +2,7 @@ use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; -use crate::node_interner::{ - DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId, -}; +use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId}; use crate::{BinaryOp, BinaryOpKind, Ident, Shared, UnaryOp}; use super::stmt::HirPattern; From 6724584178bf0089d7ba4593a2b12d7f43f77d2d Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 3 Jan 2024 11:37:23 -0500 Subject: [PATCH 17/21] Add bitor and bitxor to dictionary --- cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.json b/cspell.json index 51e49c78e59..cd2e9019ee5 100644 --- a/cspell.json +++ b/cspell.json @@ -15,6 +15,8 @@ "bincode", "bindgen", "bitand", + "bitxor", + "bitor", "blackbox", "bridgekeeper", "brillig", From 0994938ebf7b33cc27cfc2b97579b1b4d5f5bffe Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 3 Jan 2024 12:00:19 -0500 Subject: [PATCH 18/21] Add newly required imports --- .../execution_success/operator_overloading/src/main.nr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_programs/execution_success/operator_overloading/src/main.nr b/test_programs/execution_success/operator_overloading/src/main.nr index 02caa377998..3867531abca 100644 --- a/test_programs/execution_success/operator_overloading/src/main.nr +++ b/test_programs/execution_success/operator_overloading/src/main.nr @@ -1,3 +1,5 @@ +use dep::std::ops::{ Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr }; +use dep::std::cmp::Ordering; // x = 3, y = 9 fn main(x: u32, y: u32) { From 62e164aea6760033fb7f0cbe69c789cfdd27f5d2 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 3 Jan 2024 12:40:05 -0500 Subject: [PATCH 19/21] Remove outdated comment --- compiler/noirc_frontend/src/hir/type_check/expr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 7f6697629a2..caa77852560 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -1210,7 +1210,6 @@ impl<'interner> TypeChecker<'interner> { ) { let the_trait = self.interner.get_trait(trait_method_id.trait_id); - // The first (and only) method of each operator trait should be the operator method let method = &the_trait.methods[trait_method_id.method_index]; let (method_type, mut bindings) = method.typ.instantiate(self.interner); From e8791e992399889035dbe36eeb72df0e3b66362b Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 3 Jan 2024 18:05:27 +0000 Subject: [PATCH 20/21] chore: fix broken links in docs --- docs/docs/getting_started/create_a_project.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/getting_started/create_a_project.md b/docs/docs/getting_started/create_a_project.md index f10916c39c5..26ff265c389 100644 --- a/docs/docs/getting_started/create_a_project.md +++ b/docs/docs/getting_started/create_a_project.md @@ -69,7 +69,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../noir/syntax/data_types/index.md) section. +[Data Types](../noir/concepts/data_types/index.md) section. The next line of the program specifies its body: @@ -79,7 +79,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../noir/syntax/comments.md) chapter. +For more Noir syntax, check the [Language Concepts](../noir/concepts/comments.md) chapter. ## Build In/Output Files From 3607666a17cf0e9587aa2541a23952211e7c3dad Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 3 Jan 2024 18:06:20 +0000 Subject: [PATCH 21/21] chore: more broken links --- docs/docs/migration_notes.md | 2 +- docs/docs/noir/concepts/data_types/function_types.md | 2 +- docs/docs/noir/concepts/data_types/index.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index 184ca283539..9f27230a1a0 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -16,7 +16,7 @@ To update, please make sure this field in `Nargo.toml` matches the output of `na ## ≥0.14 -The index of the [for loops](noir/syntax/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: ```rust for i in 0..10 { diff --git a/docs/docs/noir/concepts/data_types/function_types.md b/docs/docs/noir/concepts/data_types/function_types.md index 61e4076adaf..e224e860d59 100644 --- a/docs/docs/noir/concepts/data_types/function_types.md +++ b/docs/docs/noir/concepts/data_types/function_types.md @@ -23,4 +23,4 @@ fn main() { ``` A function type also has an optional capture environment - this is necessary to support closures. -See [Lambdas](@site/docs/noir/syntax/lambdas.md) for more details. +See [Lambdas](@site/docs/noir/concepts/lambdas.md) for more details. diff --git a/docs/docs/noir/concepts/data_types/index.md b/docs/docs/noir/concepts/data_types/index.md index 01cd0431a68..3c9cd4c2437 100644 --- a/docs/docs/noir/concepts/data_types/index.md +++ b/docs/docs/noir/concepts/data_types/index.md @@ -79,7 +79,7 @@ fn main() { } ``` -Type aliases can also be used with [generics](@site/docs/noir/syntax/generics.md): +Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): ```rust type Id = Size;