Skip to content

Commit

Permalink
feat(frontend): Where clause on impl (#5320)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #4508 

## Summary\*

This enables the simple functionality of being able to declare a where
clause directly on a struct implementation such as below:
```rust
impl<T> MyStruct<T> where T: MyEq {
    fn my_eq(self, other: Self) -> bool {
        (self.a == other.a) & self.b.my_eq(other.b)
    }
}
```
The code above is essentially syntactic sugar where we now every method
in the struct impl a where clause. As the logic for resolving trait
constraints already exists this PR really just updates the parser to
accept `where` clauses and during definition collection attaches them to
each method in an impl.

## Additional Context



## Documentation\*

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

# PR Checklist\*

- [X] I have tested the changes locally.
- [X] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

---------

Co-authored-by: jfecher <jake@aztecprotocol.com>
  • Loading branch information
vezenovm and jfecher authored Jun 24, 2024
1 parent f2f8ecc commit cf938bc
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 7 deletions.
1 change: 1 addition & 0 deletions aztec_macros/src/transforms/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn generate_selector_impl(structure: &mut NoirStruct) -> TypeImpl {
type_span: structure.span,
generics: vec![],
methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())],
where_clause: vec![],
}
}

Expand Down
1 change: 1 addition & 0 deletions aztec_macros/src/transforms/note_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt
type_span: note_struct.name.span(),
generics: vec![],
methods: vec![],
where_clause: vec![],
};
module.impls.push(default_impl.clone());
module.impls.last_mut().unwrap()
Expand Down
2 changes: 2 additions & 0 deletions aztec_macros/src/transforms/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ pub fn generate_storage_implementation(
generics: vec![generic_context_ident],

methods: vec![(init, Span::default())],

where_clause: vec![],
};
module.impls.push(storage_impl);

Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct TypeImpl {
pub object_type: UnresolvedType,
pub type_span: Span,
pub generics: UnresolvedGenerics,
pub where_clause: Vec<UnresolvedTraitConstraint>,
pub methods: Vec<(NoirFunction, Span)>,
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ impl<'a> ModCollector<'a> {
self_type: None,
};

for (method, _) in r#impl.methods {
for (mut method, _) in r#impl.methods {
let func_id = context.def_interner.push_empty_fn();
method.def.where_clause.extend(r#impl.where_clause.clone());
let location = Location::new(method.span(), self.file_id);
context.def_interner.push_function(func_id, &method.def, module_id, location);
unresolved_functions.push_fn(self.module_id, func_id, method);
Expand Down
13 changes: 11 additions & 2 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,20 @@ fn implementation() -> impl NoirParser<TopLevelStatement> {
keyword(Keyword::Impl)
.ignore_then(function::generics())
.then(parse_type().map_with_span(|typ, span| (typ, span)))
.then(where_clause())
.then_ignore(just(Token::LeftBrace))
.then(spanned(function::function_definition(true)).repeated())
.then_ignore(just(Token::RightBrace))
.map(|((generics, (object_type, type_span)), methods)| {
TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, methods })
.map(|args| {
let ((other_args, where_clause), methods) = args;
let (generics, (object_type, type_span)) = other_args;
TopLevelStatement::Impl(TypeImpl {
generics,
object_type,
type_span,
where_clause,
methods,
})
})
}

Expand Down
18 changes: 17 additions & 1 deletion docs/docs/noir/concepts/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ fn main() {

### Generic Trait Implementations With Where Clauses

Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way.
Where clauses can be placed on trait implementations themselves to restrict generics in a similar way.
For example, while `impl<T> Foo for T` implements the trait `Foo` for every type, `impl<T> Foo for T where T: Bar`
will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types.
For example, here is the implementation for array equality:
Expand All @@ -169,6 +169,22 @@ impl<T, N> Eq for [T; N] where T: Eq {
}
```

Where clauses can also be placed on struct implementations.
For example, here is a method utilizing a generic type that implements the equality trait.

```rust
struct Foo<T> {
a: u32,
b: T,
}

impl<T> Foo<T> where T: Eq {
fn eq(self, other: Self) -> bool {
(self.a == other.a) & self.b.eq(other.b)
}
}
```

## Generic Traits

Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "impl_where_clause"
type = "bin"
authors = [""]
compiler_version = ">=0.31.0"

[dependencies]
34 changes: 34 additions & 0 deletions test_programs/compile_success_empty/impl_where_clause/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
struct MyStruct<T> {
a: u32,
b: T,
}

struct InnerStruct {
a: Field,
b: Field,
}

trait MyEq {
fn my_eq(self, other: Self) -> bool;
}

impl MyEq for InnerStruct {
fn my_eq(self, other: InnerStruct) -> bool {
(self.a == other.a) & (self.b == other.b)
}
}

impl<T> MyStruct<T> where T: MyEq {
fn my_eq(self, other: Self) -> bool {
(self.a == other.a) & self.b.my_eq(other.b)
}
}

fn main() {
let inner = InnerStruct { a: 1, b: 2 };
let my_struct = MyStruct { a: 5, b: inner };
assert(my_struct.my_eq(my_struct));

let mut my_struct_new = MyStruct { a: 5, b: InnerStruct { a: 10, b: 15 } };
assert(my_struct_new.my_eq(my_struct_new));
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "impl_with_where_clause"
name = "trait_impl_with_where_clause"
type = "bin"
authors = [""]

Expand Down
4 changes: 2 additions & 2 deletions tooling/nargo_fmt/src/visitor/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ impl super::FmtVisitor<'_> {
continue;
}

let slice =
self.slice(self.last_position..impl_.object_type.span.unwrap().end());
let before_brace = self.span_before(span, Token::LeftBrace).start();
let slice = self.slice(self.last_position..before_brace).trim();
let after_brace = self.span_after(span, Token::LeftBrace).start();
self.last_position = after_brace;

Expand Down
6 changes: 6 additions & 0 deletions tooling/nargo_fmt/tests/expected/impl.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ impl MyType {
impl MyType {
fn method(self) {}
}

impl<T> MyStruct<T> where T: MyEq {
fn my_eq(self, other: Self) -> bool {
(self.a == other.a) & self.b.my_eq(other.b)
}
}
6 changes: 6 additions & 0 deletions tooling/nargo_fmt/tests/input/impl.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ fn method(self) {}
impl MyType {
fn method(self) {}
}

impl<T> MyStruct<T> where T: MyEq {
fn my_eq(self, other: Self) -> bool {
(self.a == other.a) & self.b.my_eq(other.b)
}
}

0 comments on commit cf938bc

Please sign in to comment.