diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 797b8f3828a..d7632eda2ce 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1,7 +1,4 @@ -use std::{ - hash::{Hash, Hasher}, - rc::Rc, -}; +use std::rc::Rc; use acvm::{AcirField, FieldElement}; use builtin_helpers::{ @@ -43,7 +40,7 @@ use crate::{ Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; -use self::builtin_helpers::{get_array, get_str, get_u8}; +use self::builtin_helpers::{eq_item, get_array, get_str, get_u8, hash_item}; use super::Interpreter; pub(crate) mod builtin_helpers; @@ -104,9 +101,11 @@ impl<'local, 'context> Interpreter<'local, 'context> { "fresh_type_variable" => fresh_type_variable(interner), "function_def_add_attribute" => function_def_add_attribute(self, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), + "function_def_eq" => function_def_eq(arguments, location), "function_def_has_named_attribute" => { function_def_has_named_attribute(interner, arguments, location) } + "function_def_hash" => function_def_hash(arguments, location), "function_def_is_unconstrained" => { function_def_is_unconstrained(interner, arguments, location) } @@ -126,21 +125,24 @@ impl<'local, 'context> Interpreter<'local, 'context> { function_def_set_unconstrained(self, arguments, location) } "module_add_item" => module_add_item(self, arguments, location), + "module_eq" => module_eq(arguments, location), "module_functions" => module_functions(self, arguments, location), "module_has_named_attribute" => module_has_named_attribute(self, arguments, location), + "module_hash" => module_hash(arguments, location), "module_is_contract" => module_is_contract(self, arguments, location), "module_name" => module_name(interner, arguments, location), "module_structs" => module_structs(self, arguments, location), - "modulus_be_bits" => modulus_be_bits(interner, arguments, location), - "modulus_be_bytes" => modulus_be_bytes(interner, arguments, location), - "modulus_le_bits" => modulus_le_bits(interner, arguments, location), - "modulus_le_bytes" => modulus_le_bytes(interner, arguments, location), - "modulus_num_bits" => modulus_num_bits(interner, arguments, location), + "modulus_be_bits" => modulus_be_bits(arguments, location), + "modulus_be_bytes" => modulus_be_bytes(arguments, location), + "modulus_le_bits" => modulus_le_bits(arguments, location), + "modulus_le_bytes" => modulus_le_bytes(arguments, location), + "modulus_num_bits" => modulus_num_bits(arguments, location), "quoted_as_expr" => quoted_as_expr(arguments, return_type, location), "quoted_as_module" => quoted_as_module(self, arguments, return_type, location), "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), "quoted_as_type" => quoted_as_type(self, arguments, location), "quoted_eq" => quoted_eq(arguments, location), + "quoted_hash" => quoted_hash(arguments, location), "quoted_tokens" => quoted_tokens(arguments, location), "slice_insert" => slice_insert(interner, arguments, location), "slice_pop_back" => slice_pop_back(interner, arguments, location, call_stack), @@ -152,22 +154,24 @@ impl<'local, 'context> Interpreter<'local, 'context> { "struct_def_add_attribute" => struct_def_add_attribute(interner, arguments, location), "struct_def_add_generic" => struct_def_add_generic(interner, arguments, location), "struct_def_as_type" => struct_def_as_type(interner, arguments, location), + "struct_def_eq" => struct_def_eq(arguments, location), "struct_def_fields" => struct_def_fields(interner, arguments, location), "struct_def_generics" => struct_def_generics(interner, arguments, location), "struct_def_has_named_attribute" => { struct_def_has_named_attribute(interner, arguments, location) } + "struct_def_hash" => struct_def_hash(arguments, location), "struct_def_module" => struct_def_module(self, arguments, location), "struct_def_name" => struct_def_name(interner, arguments, location), "struct_def_set_fields" => struct_def_set_fields(interner, arguments, location), "to_le_radix" => to_le_radix(arguments, return_type, location), - "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), - "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), + "trait_constraint_eq" => trait_constraint_eq(arguments, location), + "trait_constraint_hash" => trait_constraint_hash(arguments, location), "trait_def_as_trait_constraint" => { trait_def_as_trait_constraint(interner, arguments, location) } - "trait_def_eq" => trait_def_eq(interner, arguments, location), - "trait_def_hash" => trait_def_hash(interner, arguments, location), + "trait_def_eq" => trait_def_eq(arguments, location), + "trait_def_hash" => trait_def_hash(arguments, location), "trait_impl_methods" => trait_impl_methods(interner, arguments, location), "trait_impl_trait_generic_args" => { trait_impl_trait_generic_args(interner, arguments, location) @@ -183,6 +187,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_get_trait_impl" => { type_get_trait_impl(interner, arguments, return_type, location) } + "type_hash" => type_hash(arguments, location), "type_implements" => type_implements(interner, arguments, location), "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), @@ -428,6 +433,14 @@ fn struct_def_generics( Ok(Value::Slice(generics.collect(), typ)) } +fn struct_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_struct) +} + +fn struct_def_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_struct) +} + // fn has_named_attribute(self, name: Quoted) -> bool fn struct_def_has_named_attribute( interner: &NodeInterner, @@ -904,12 +917,12 @@ where // fn type_eq(_first: Type, _second: Type) -> bool fn type_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { - let (self_type, other_type) = check_two_arguments(arguments, location)?; - - let self_type = get_type(self_type)?; - let other_type = get_type(other_type)?; + eq_item(arguments, location, get_type) +} - Ok(Value::Bool(self_type == other_type)) +// fn type_hash(_t: Type) -> Field +fn type_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_type) } // fn get_trait_impl(self, constraint: TraitConstraint) -> Option @@ -978,65 +991,23 @@ fn type_of(arguments: Vec<(Value, Location)>, location: Location) -> IResult Field -fn trait_constraint_hash( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let argument = check_one_argument(arguments, location)?; - - let bound = get_trait_constraint(argument)?; - - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - bound.hash(&mut hasher); - let hash = hasher.finish(); - - Ok(Value::Field((hash as u128).into())) +fn trait_constraint_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_trait_constraint) } // fn constraint_eq(constraint_a: TraitConstraint, constraint_b: TraitConstraint) -> bool -fn trait_constraint_eq( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let (value_a, value_b) = check_two_arguments(arguments, location)?; - - let constraint_a = get_trait_constraint(value_a)?; - let constraint_b = get_trait_constraint(value_b)?; - - Ok(Value::Bool(constraint_a == constraint_b)) +fn trait_constraint_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_trait_constraint) } // fn trait_def_hash(def: TraitDefinition) -> Field -fn trait_def_hash( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let argument = check_one_argument(arguments, location)?; - - let id = get_trait_def(argument)?; - - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - id.hash(&mut hasher); - let hash = hasher.finish(); - - Ok(Value::Field((hash as u128).into())) +fn trait_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_trait_def) } // fn trait_def_eq(def_a: TraitDefinition, def_b: TraitDefinition) -> bool -fn trait_def_eq( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let (id_a, id_b) = check_two_arguments(arguments, location)?; - - let id_a = get_trait_def(id_a)?; - let id_b = get_trait_def(id_b)?; - - Ok(Value::Bool(id_a == id_b)) +fn trait_def_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_trait_def) } // fn methods(self) -> [FunctionDefinition] @@ -2005,6 +1976,14 @@ fn function_def_has_named_attribute( Ok(Value::Bool(has_named_attribute(&name, attributes, location))) } +fn function_def_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_function_def) +} + +fn function_def_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_function_def) +} + // fn is_unconstrained(self) -> bool fn function_def_is_unconstrained( interner: &NodeInterner, @@ -2271,6 +2250,14 @@ fn module_add_item( Ok(Value::Unit) } +fn module_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_module) +} + +fn module_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + eq_item(arguments, location, get_module) +} + // fn functions(self) -> [FunctionDefinition] fn module_functions( interpreter: &Interpreter, @@ -2361,11 +2348,7 @@ fn module_name( Ok(Value::Quoted(tokens)) } -fn modulus_be_bits( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { +fn modulus_be_bits(arguments: Vec<(Value, Location)>, location: Location) -> IResult { check_argument_count(0, &arguments, location)?; let bits = FieldElement::modulus().to_radix_be(2); @@ -2376,11 +2359,7 @@ fn modulus_be_bits( Ok(Value::Slice(bits_vector, typ)) } -fn modulus_be_bytes( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { +fn modulus_be_bytes(arguments: Vec<(Value, Location)>, location: Location) -> IResult { check_argument_count(0, &arguments, location)?; let bytes = FieldElement::modulus().to_bytes_be(); @@ -2391,35 +2370,23 @@ fn modulus_be_bytes( Ok(Value::Slice(bytes_vector, typ)) } -fn modulus_le_bits( - interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let Value::Slice(bits, typ) = modulus_be_bits(interner, arguments, location)? else { +fn modulus_le_bits(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let Value::Slice(bits, typ) = modulus_be_bits(arguments, location)? else { unreachable!("modulus_be_bits must return slice") }; let reversed_bits = bits.into_iter().rev().collect(); Ok(Value::Slice(reversed_bits, typ)) } -fn modulus_le_bytes( - interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { - let Value::Slice(bytes, typ) = modulus_be_bytes(interner, arguments, location)? else { +fn modulus_le_bytes(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let Value::Slice(bytes, typ) = modulus_be_bytes(arguments, location)? else { unreachable!("modulus_be_bytes must return slice") }; let reversed_bytes = bytes.into_iter().rev().collect(); Ok(Value::Slice(reversed_bytes, typ)) } -fn modulus_num_bits( - _interner: &mut NodeInterner, - arguments: Vec<(Value, Location)>, - location: Location, -) -> IResult { +fn modulus_num_bits(arguments: Vec<(Value, Location)>, location: Location) -> IResult { check_argument_count(0, &arguments, location)?; let bits = FieldElement::max_num_bits().into(); Ok(Value::U64(bits)) @@ -2427,19 +2394,18 @@ fn modulus_num_bits( // fn quoted_eq(_first: Quoted, _second: Quoted) -> bool fn quoted_eq(arguments: Vec<(Value, Location)>, location: Location) -> IResult { - let (self_value, other_value) = check_two_arguments(arguments, location)?; - - let self_quoted = get_quoted(self_value)?; - let other_quoted = get_quoted(other_value)?; + eq_item(arguments, location, get_quoted) +} - Ok(Value::Bool(self_quoted == other_quoted)) +fn quoted_hash(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + hash_item(arguments, location, get_quoted) } fn trait_def_as_trait_constraint( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, -) -> Result { +) -> IResult { let argument = check_one_argument(arguments, location)?; let trait_id = get_trait_def(argument)?; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 6e72866bec0..e50bb14b2d6 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -1,4 +1,5 @@ -use std::rc::Rc; +use std::hash::Hash; +use std::{hash::Hasher, rc::Rc}; use acvm::FieldElement; use noirc_errors::Location; @@ -471,3 +472,28 @@ pub(super) fn has_named_attribute<'a>( false } + +pub(super) fn hash_item( + arguments: Vec<(Value, Location)>, + location: Location, + get_item: impl FnOnce((Value, Location)) -> IResult, +) -> IResult { + let argument = check_one_argument(arguments, location)?; + let item = get_item(argument)?; + + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + item.hash(&mut hasher); + let hash = hasher.finish(); + Ok(Value::Field((hash as u128).into())) +} + +pub(super) fn eq_item( + arguments: Vec<(Value, Location)>, + location: Location, + mut get_item: impl FnMut((Value, Location)) -> IResult, +) -> IResult { + let (self_arg, other_arg) = check_two_arguments(arguments, location)?; + let self_arg = get_item(self_arg)?; + let other_arg = get_item(other_arg)?; + Ok(Value::Bool(self_arg == other_arg)) +} diff --git a/docs/docs/noir/standard_library/meta/function_def.md b/docs/docs/noir/standard_library/meta/function_def.md index c9fa3b345f0..583771aeb73 100644 --- a/docs/docs/noir/standard_library/meta/function_def.md +++ b/docs/docs/noir/standard_library/meta/function_def.md @@ -101,3 +101,14 @@ This means any functions called at compile-time are invalid targets for this met Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). This is only valid on functions in the current crate which have not yet been resolved. This means any functions called at compile-time are invalid targets for this method. + +## Trait Implementations + +```rust +impl Eq for FunctionDefinition +impl Hash for FunctionDefinition +``` + +Note that each function is assigned a unique ID internally and this is what is used for +equality and hashing. So even functions with identical signatures and bodies may not +be equal in this sense if they were originally different items in the source program. diff --git a/docs/docs/noir/standard_library/meta/module.md b/docs/docs/noir/standard_library/meta/module.md index 1bc11e725a3..2765718dd7b 100644 --- a/docs/docs/noir/standard_library/meta/module.md +++ b/docs/docs/noir/standard_library/meta/module.md @@ -45,3 +45,14 @@ Returns the name of the module. #include_code structs noir_stdlib/src/meta/module.nr rust Returns each struct defined in the module. + +## Trait Implementations + +```rust +impl Eq for Module +impl Hash for Module +``` + +Note that each module is assigned a unique ID internally and this is what is used for +equality and hashing. So even modules with identical names and contents may not +be equal in this sense if they were originally different items in the source program. diff --git a/docs/docs/noir/standard_library/meta/op.md b/docs/docs/noir/standard_library/meta/op.md index d8b154edc02..55d2d244344 100644 --- a/docs/docs/noir/standard_library/meta/op.md +++ b/docs/docs/noir/standard_library/meta/op.md @@ -43,6 +43,13 @@ Returns `true` if this operator is `-`. Returns this operator as a `Quoted` value. +### Trait Implementations + +```rust +impl Eq for UnaryOp +impl Hash for UnaryOp +``` + ### BinaryOp Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. @@ -143,4 +150,11 @@ Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, ` #include_code binary_quoted noir_stdlib/src/meta/op.nr rust -Returns this operator as a `Quoted` value. \ No newline at end of file +Returns this operator as a `Quoted` value. + +### Trait Implementations + +```rust +impl Eq for BinaryOp +impl Hash for BinaryOp +``` diff --git a/docs/docs/noir/standard_library/meta/quoted.md b/docs/docs/noir/standard_library/meta/quoted.md index ac075d96648..11dacd51fcb 100644 --- a/docs/docs/noir/standard_library/meta/quoted.md +++ b/docs/docs/noir/standard_library/meta/quoted.md @@ -60,4 +60,5 @@ Returns a slice of the individual tokens that form this token stream. ```rust impl Eq for Quoted +impl Hash for Quoted ``` diff --git a/docs/docs/noir/standard_library/meta/struct_def.md b/docs/docs/noir/standard_library/meta/struct_def.md index c088e538fc9..535708e0353 100644 --- a/docs/docs/noir/standard_library/meta/struct_def.md +++ b/docs/docs/noir/standard_library/meta/struct_def.md @@ -117,3 +117,14 @@ comptime fn mangle_fields(s: StructDefinition) { ]); } ``` + +## Trait Implementations + +```rust +impl Eq for StructDefinition +impl Hash for StructDefinition +``` + +Note that each struct is assigned a unique ID internally and this is what is used for +equality and hashing. So even structs with identical generics and fields may not +be equal in this sense if they were originally different items in the source program. diff --git a/docs/docs/noir/standard_library/meta/typ.md b/docs/docs/noir/standard_library/meta/typ.md index 1334092a9fa..5a8b43b1dfa 100644 --- a/docs/docs/noir/standard_library/meta/typ.md +++ b/docs/docs/noir/standard_library/meta/typ.md @@ -150,7 +150,8 @@ fn foo() where T: Default { ```rust impl Eq for Type +impl Hash for Type ``` Note that this is syntactic equality, this is not the same as whether two types will type check to be the same type. Unless type inference or generics are being used however, users should not -typically have to worry about this distinction. +typically have to worry about this distinction unless `std::meta::typ::fresh_type_variable` is used. diff --git a/noir_stdlib/src/meta/function_def.nr b/noir_stdlib/src/meta/function_def.nr index 5d536d60ae1..6c68978a630 100644 --- a/noir_stdlib/src/meta/function_def.nr +++ b/noir_stdlib/src/meta/function_def.nr @@ -64,3 +64,21 @@ impl FunctionDefinition { comptime fn set_unconstrained(self, value: bool) {} // docs:end:set_unconstrained } + +impl crate::hash::Hash for FunctionDefinition { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(function_def_hash(self)) + } +} + +impl crate::cmp::Eq for FunctionDefinition { + comptime fn eq(self, other: Self) -> bool { + function_def_eq(self, other) + } +} + +#[builtin(function_def_eq)] +comptime fn function_def_eq(_first: FunctionDefinition, _second: FunctionDefinition) -> bool {} + +#[builtin(function_def_hash)] +comptime fn function_def_hash(_function: FunctionDefinition) -> Field {} diff --git a/noir_stdlib/src/meta/module.nr b/noir_stdlib/src/meta/module.nr index 5a7adab0d27..d983b8e3a8c 100644 --- a/noir_stdlib/src/meta/module.nr +++ b/noir_stdlib/src/meta/module.nr @@ -29,3 +29,21 @@ impl Module { comptime fn name(self) -> Quoted {} // docs:end:name } + +impl crate::hash::Hash for Module { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(module_hash(self)) + } +} + +impl crate::cmp::Eq for Module { + comptime fn eq(self, other: Self) -> bool { + module_eq(self, other) + } +} + +#[builtin(module_eq)] +comptime fn module_eq(_first: Module, _second: Module) -> bool {} + +#[builtin(module_hash)] +comptime fn module_hash(_module: Module) -> Field {} diff --git a/noir_stdlib/src/meta/op.nr b/noir_stdlib/src/meta/op.nr index 4b104486201..31a815f07ba 100644 --- a/noir_stdlib/src/meta/op.nr +++ b/noir_stdlib/src/meta/op.nr @@ -1,3 +1,4 @@ +#[derive(Eq, Hash)] struct UnaryOp { op: Field } @@ -45,6 +46,7 @@ impl UnaryOp { } } +#[derive(Eq, Hash)] struct BinaryOp { op: Field } diff --git a/noir_stdlib/src/meta/quoted.nr b/noir_stdlib/src/meta/quoted.nr index ff74580ce20..6e8d001c57c 100644 --- a/noir_stdlib/src/meta/quoted.nr +++ b/noir_stdlib/src/meta/quoted.nr @@ -34,6 +34,14 @@ impl Eq for Quoted { } } +impl crate::hash::Hash for Quoted { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(quoted_hash(self)) + } +} + #[builtin(quoted_eq)] comptime fn quoted_eq(_first: Quoted, _second: Quoted) -> bool {} +#[builtin(quoted_hash)] +comptime fn quoted_hash(_quoted: Quoted) -> Field {} diff --git a/noir_stdlib/src/meta/struct_def.nr b/noir_stdlib/src/meta/struct_def.nr index e3621b3482e..064891fda3f 100644 --- a/noir_stdlib/src/meta/struct_def.nr +++ b/noir_stdlib/src/meta/struct_def.nr @@ -53,3 +53,21 @@ impl StructDefinition { comptime fn set_fields(self, new_fields: [(Quoted, Type)]) {} // docs:end:set_fields } + +impl crate::hash::Hash for StructDefinition { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(struct_def_hash(self)) + } +} + +impl crate::cmp::Eq for StructDefinition { + comptime fn eq(self, other: Self) -> bool { + struct_def_eq(self, other) + } +} + +#[builtin(struct_def_eq)] +comptime fn struct_def_eq(_first: StructDefinition, _second: StructDefinition) -> bool {} + +#[builtin(struct_def_hash)] +comptime fn struct_def_hash(_struct: StructDefinition) -> Field {} diff --git a/noir_stdlib/src/meta/typ.nr b/noir_stdlib/src/meta/typ.nr index d692f6e5a7e..5a748c2c823 100644 --- a/noir_stdlib/src/meta/typ.nr +++ b/noir_stdlib/src/meta/typ.nr @@ -69,5 +69,14 @@ impl Eq for Type { } } +impl crate::hash::Hash for Type { + comptime fn hash(self, state: &mut H) where H: crate::hash::Hasher { + state.write(type_hash(self)) + } +} + #[builtin(type_eq)] comptime fn type_eq(_first: Type, _second: Type) -> bool {} + +#[builtin(type_hash)] +comptime fn type_hash(_typ: Type) -> Field {}