Skip to content

Commit

Permalink
feat: Derive Eq & Default (#5667)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #4594 

First traits to derive in the stdlib!

## Summary\*

Lets us derive the `Eq` and `Default` traits for almost any type.

I anticipate there will be issues with deriving generic types. I'll fix
this in another PR.

## Additional Context

Since derive is in the stdlib and we can derive traits now, I'm
arbitrarily marking the epic for metaprogramming as completed. There are
still bugs to fix and many helper functions we can add but the bulk of
the work and infrastructure is all in.

## Documentation\*

Check one:
- [ ] No documentation needed.
- [ ] Documentation included in this PR.
- [x] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [ ] I have tested the changes locally.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
jfecher authored Aug 2, 2024
1 parent e18ed68 commit 507f5f7
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 2 deletions.
28 changes: 28 additions & 0 deletions noir_stdlib/src/cmp.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
use crate::meta::derive_via;

#[derive_via(derive_eq)]
// docs:start:eq-trait
trait Eq {
fn eq(self, other: Self) -> bool;
}
// docs:end:eq-trait

comptime fn derive_eq(s: StructDefinition) -> Quoted {
let typ = s.as_type();

let impl_generics = s.generics().join(quote {,});

let where_clause = s.generics().map(|name| quote { $name: Default }).join(quote {,});

// `(self.a == other.a) & (self.b == other.b) & ...`
let equalities = s.fields().map(
|f: (Quoted, Type)| {
let name = f.0;
quote { (self.$name == other.$name) }
}
);
let body = equalities.join(quote { & });

quote {
impl<$impl_generics> Eq for $typ where $where_clause {
fn eq(self, other: Self) -> bool {
$body
}
}
}
}

impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } }

impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } }
Expand Down
28 changes: 28 additions & 0 deletions noir_stdlib/src/default.nr
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
use crate::meta::derive_via;

#[derive_via(derive_default)]
// docs:start:default-trait
trait Default {
fn default() -> Self;
}
// docs:end:default-trait

comptime fn derive_default(s: StructDefinition) -> Quoted {
let typ = s.as_type();

let impl_generics = s.generics().join(quote {,});

let where_clause = s.generics().map(|name| quote { $name: Default }).join(quote {,});

// `foo: Default::default(), bar: Default::default(), ...`
let fields = s.fields().map(
|f: (Quoted, Type)| {
let name = f.0;
quote { $name: Default::default() }
}
);
let fields = fields.join(quote {,});

quote {
impl<$impl_generics> Default for $typ where $where_clause {
fn default() -> Self {
Self { $fields }
}
}
}
}

impl Default for Field { fn default() -> Field { 0 } }

impl Default for u8 { fn default() -> u8 { 0 } }
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/hash/poseidon2.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::hash::Hasher;
use crate::default::Default;

global RATE: u32 = 3;
comptime global RATE: u32 = 3;

struct Poseidon2 {
cache: [Field;3],
Expand Down
2 changes: 1 addition & 1 deletion noir_stdlib/src/meta/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use crate::collections::umap::UHashMap;
use crate::hash::BuildHasherDefault;
use crate::hash::poseidon2::Poseidon2Hasher;

mod struct_def;
mod trait_constraint;
mod trait_def;
mod type_def;
mod quoted;

/// Calling unquote as a macro (via `unquote!(arg)`) will unquote
Expand Down
File renamed without changes.
10 changes: 10 additions & 0 deletions test_programs/execution_success/derive/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ comptime fn derive_do_nothing(s: StructDefinition) -> Quoted {
}
}

// Test stdlib derive fns & multiple traits
#[derive(Eq, Default)]
struct MyOtherStruct {
field1: u32,
field2: u64,
}

fn main() {
let s = MyStruct { my_field: 1 };
s.do_nothing();

let o = MyOtherStruct::default();
assert_eq(o, o);
}

0 comments on commit 507f5f7

Please sign in to comment.