Skip to content

Commit

Permalink
fix: error on incorrect generic count for impl and type alias (#5623)
Browse files Browse the repository at this point in the history
# Description

## Problem

Resolves #5583

## Summary

There's a call, `verify_generics_count`, that's supposed to do this
check. The problem is that the `args` given to it are the results of
zipping the struct's generics with the given generics. That will always
produce an `args` the size of the smallest of the two. So, if a struct
doesn't have generics, `args` will end up empty and no error is
produced. However, if a struct has more generics than given, an error
was correctly produced. The solution is to get the actual and expected
numbers before shadowing `args`.

## Additional Context

In Rust this program gives two errors:

```rust
struct Foo {}

impl Foo<T> {}

fn main() { }
```

1. cannot find T in this scope
2. struct takes 0 generic arguments but 1 generic argument was supplied

With this PR, in Noir we'll be just giving one error (the second one).
The reason is that only generics that match the struct generics count
are checked. I thought about changing the code to produce the same
number of errors as Rust... but I didn't know if it was worth it. Here
you'll get the "incorrect generics count" error, so you'll have to fix
that by either removing the generic (solved) or by adding a generic to
`struct Foo` (likely not what you are going to do), at which point
you'll get the other error... so I thought that with just one error it's
good enough.

## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] 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.
  • Loading branch information
asterite authored Jul 29, 2024
1 parent b33495d commit 1f5d000
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 8 deletions.
28 changes: 20 additions & 8 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ impl<'context> Elaborator<'context> {

if let Some(type_alias) = self.lookup_type_alias(path.clone()) {
let type_alias = type_alias.borrow();
let actual_generic_count = args.len();
let expected_generic_count = type_alias.generics.len();
let type_alias_string = type_alias.to_string();
let id = type_alias.id;
Expand All @@ -247,9 +248,13 @@ impl<'context> Elaborator<'context> {
self.resolve_type_inner(arg, &generic.kind)
});

self.verify_generics_count(expected_generic_count, &mut args, span, || {
type_alias_string
});
self.verify_generics_count(
expected_generic_count,
actual_generic_count,
&mut args,
span,
|| type_alias_string,
);

if let Some(item) = self.current_item {
self.interner.add_type_alias_dependency(item, id);
Expand Down Expand Up @@ -279,6 +284,8 @@ impl<'context> Elaborator<'context> {
}

let expected_generic_count = struct_type.borrow().generics.len();
let actual_generic_count = args.len();

if !self.in_contract()
&& self
.interner
Expand All @@ -296,9 +303,13 @@ impl<'context> Elaborator<'context> {
self.resolve_type_inner(arg, &generic.kind)
});

self.verify_generics_count(expected_generic_count, &mut args, span, || {
struct_type.borrow().to_string()
});
self.verify_generics_count(
expected_generic_count,
actual_generic_count,
&mut args,
span,
|| struct_type.borrow().to_string(),
);

if let Some(current_item) = self.current_item {
let dependency_id = struct_type.borrow().id;
Expand Down Expand Up @@ -333,15 +344,16 @@ impl<'context> Elaborator<'context> {
fn verify_generics_count(
&mut self,
expected_count: usize,
actual_count: usize,
args: &mut Vec<Type>,
span: Span,
type_name: impl FnOnce() -> String,
) {
if args.len() != expected_count {
if actual_count != expected_count {
self.push_err(ResolverError::IncorrectGenericCount {
span,
item_name: type_name(),
actual: args.len(),
actual: actual_count,
expected: expected_count,
});

Expand Down
48 changes: 48 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2681,3 +2681,51 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() {
assert_eq!(*expected, 1);
assert_eq!(*found, 2);
}

#[test]
fn incorrect_generic_count_on_struct_impl() {
let src = r#"
struct Foo {}
impl <T> Foo<T> {}
fn main() {}
"#;

let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

let CompilationError::ResolverError(ResolverError::IncorrectGenericCount {
actual,
expected,
..
}) = errors[0].0
else {
panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0);
};

assert_eq!(actual, 1);
assert_eq!(expected, 0);
}

#[test]
fn incorrect_generic_count_on_type_alias() {
let src = r#"
struct Foo {}
type Bar = Foo<i32>;
fn main() {}
"#;

let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

let CompilationError::ResolverError(ResolverError::IncorrectGenericCount {
actual,
expected,
..
}) = errors[0].0
else {
panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0);
};

assert_eq!(actual, 1);
assert_eq!(expected, 0);
}

0 comments on commit 1f5d000

Please sign in to comment.