From df9f09eda62b7d09ed8ade8cad907453ea91d3e2 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov <52652109+yordanmadzhunkov@users.noreply.github.com> Date: Wed, 30 Aug 2023 18:31:37 +0300 Subject: [PATCH] feat: Basic implementation of traits (#2368) Co-authored-by: Yordan Madzhunkov Co-authored-by: Nikolay Nikolov Co-authored-by: jfecher --- .../dup_trait_declaration/Nargo.toml | 7 + .../dup_trait_declaration/Prover.toml | 2 + .../dup_trait_declaration/src/main.nr | 26 ++ .../dup_trait_implementation/Nargo.toml | 7 + .../dup_trait_implementation/Prover.toml | 2 + .../dup_trait_implementation/src/main.nr | 30 ++ .../impl_struct_not_trait/Nargo.toml | 7 + .../impl_struct_not_trait/Prover.toml | 2 + .../impl_struct_not_trait/src/main.nr | 23 ++ .../trait_missing_implementation/Nargo.toml | 7 + .../trait_missing_implementation/src/main.nr | 24 ++ .../trait_not_in_scope/Nargo.toml | 7 + .../trait_not_in_scope/Prover.toml | 2 + .../trait_not_in_scope/src/main.nr | 18 ++ .../trait_wrong_method_name/Nargo.toml | 7 + .../trait_wrong_method_name/Prover.toml | 2 + .../trait_wrong_method_name/src/main.nr | 22 ++ .../trait_wrong_method_return_type/Nargo.toml | 7 + .../Prover.toml | 2 + .../src/main.nr | 21 ++ .../trait_wrong_parameter/Nargo.toml | 7 + .../trait_wrong_parameter/Prover.toml | 2 + .../trait_wrong_parameter/src/main.nr | 21 ++ .../trait_wrong_parameter_type/Nargo.toml | 7 + .../trait_wrong_parameter_type/Prover.toml | 2 + .../trait_wrong_parameter_type/src/main.nr | 7 + .../trait_wrong_parameters_count/Nargo.toml | 7 + .../trait_wrong_parameters_count/Prover.toml | 2 + .../trait_wrong_parameters_count/src/main.nr | 21 ++ .../trait_default_implementation/Nargo.toml | 7 + .../trait_default_implementation/Prover.toml | 2 + .../trait_default_implementation/src/main.nr | 26 ++ .../trait_override_implementation/Nargo.toml | 7 + .../trait_override_implementation/Prover.toml | 2 + .../trait_override_implementation/src/main.nr | 30 ++ .../tests/execution_success/traits/Nargo.toml | 7 + .../execution_success/traits/Prover.toml | 2 + .../execution_success/traits/src/main.nr | 21 ++ crates/noirc_frontend/src/ast/expression.rs | 44 ++- .../src/hir/def_collector/dc_crate.rs | 146 +++++++++- .../src/hir/def_collector/dc_mod.rs | 263 +++++++++++++++++- .../src/hir/def_collector/errors.rs | 105 ++++++- .../src/hir/resolution/resolver.rs | 2 +- crates/noirc_frontend/src/hir_def/types.rs | 6 +- crates/noirc_frontend/src/node_interner.rs | 28 +- 45 files changed, 984 insertions(+), 15 deletions(-) create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_missing_implementation/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/src/main.nr create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Nargo.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Prover.toml create mode 100644 crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr create mode 100644 crates/nargo_cli/tests/execution_success/trait_default_implementation/Nargo.toml create mode 100644 crates/nargo_cli/tests/execution_success/trait_default_implementation/Prover.toml create mode 100644 crates/nargo_cli/tests/execution_success/trait_default_implementation/src/main.nr create mode 100644 crates/nargo_cli/tests/execution_success/trait_override_implementation/Nargo.toml create mode 100644 crates/nargo_cli/tests/execution_success/trait_override_implementation/Prover.toml create mode 100644 crates/nargo_cli/tests/execution_success/trait_override_implementation/src/main.nr create mode 100644 crates/nargo_cli/tests/execution_success/traits/Nargo.toml create mode 100644 crates/nargo_cli/tests/execution_success/traits/Prover.toml create mode 100644 crates/nargo_cli/tests/execution_success/traits/src/main.nr diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Nargo.toml b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Nargo.toml new file mode 100644 index 00000000000..214116185f4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dup_trait_declaration" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Prover.toml b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr new file mode 100644 index 00000000000..f4c246c786a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_declaration/src/main.nr @@ -0,0 +1,26 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +// Duplicate trait declarations should not compile +trait Default { + fn default(x: Field) -> Self; +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Nargo.toml b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Nargo.toml new file mode 100644 index 00000000000..708e26777d6 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dup_trait_implementation" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Prover.toml b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr new file mode 100644 index 00000000000..6bb6cedefb5 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/dup_trait_implementation/src/main.nr @@ -0,0 +1,30 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +// Duplicate trait implementations should not compile +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +// Duplicate trait implementations should not compile +impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: y, array: [y,x] } + } +} + + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Nargo.toml b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Nargo.toml new file mode 100644 index 00000000000..3c6943f1ce1 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "impl_struct_not_trait" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Prover.toml b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr new file mode 100644 index 00000000000..e25465378b1 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/impl_struct_not_trait/src/main.nr @@ -0,0 +1,23 @@ +use dep::std; + +struct Foo { + bar: Field, + array: [Field; 2], +} + +struct Default { + x: Field, + z: Field, +} + +// Default is struct not a trait +impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/Nargo.toml new file mode 100644 index 00000000000..75fb80c4bfa --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "traits" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr new file mode 100644 index 00000000000..bc74b328592 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_missing_implementation/src/main.nr @@ -0,0 +1,24 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field; + +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field) { + let first = Foo::method2(x); + assert(first == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Nargo.toml new file mode 100644 index 00000000000..22d31e22e29 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_not_in_scope" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] diff --git a/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr new file mode 100644 index 00000000000..9dc57ee395f --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_not_in_scope/src/main.nr @@ -0,0 +1,18 @@ +use dep::std; + +struct Foo { + bar: Field, + array: [Field; 2], +} + +// Default trait does not exist +impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Nargo.toml new file mode 100644 index 00000000000..c84f1f3c1c7 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_wrong_method_name" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr new file mode 100644 index 00000000000..0ba10815efa --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_name/src/main.nr @@ -0,0 +1,22 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +// wrong trait name method should not compile +impl Default for Foo { + fn default_wrong_name(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default_wrong_name(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Nargo.toml new file mode 100644 index 00000000000..95e3e222ca3 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_wrong_method_return_type" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr new file mode 100644 index 00000000000..acd930a6d49 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_method_return_type/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field, y: Field) -> Field { + x + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Nargo.toml new file mode 100644 index 00000000000..7299ec69e7a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_wrong_parameter" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr new file mode 100644 index 00000000000..2975aa6b1dd --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field, y: Foo) -> Self { + Self { bar: x, array: [x, y.bar] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Nargo.toml new file mode 100644 index 00000000000..95e3e222ca3 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_wrong_method_return_type" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/src/main.nr new file mode 100644 index 00000000000..2ba1ee13e70 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameter_type/src/main.nr @@ -0,0 +1,7 @@ +trait Default { + fn default(x: Field, y: NotAType) -> Field; +} + +fn main(x: Field, y: Field) { + assert(y == x); +} diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Nargo.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Nargo.toml new file mode 100644 index 00000000000..a60cf09e828 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_wrong_parameters_count" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Prover.toml b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Prover.toml new file mode 100644 index 00000000000..2c1854573a4 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/Prover.toml @@ -0,0 +1,2 @@ +x = 1 +y = 2 diff --git a/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr new file mode 100644 index 00000000000..92469ae8fdb --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/trait_wrong_parameters_count/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field) -> Self { + Self { bar: x, array: [x, x] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/nargo_cli/tests/execution_success/trait_default_implementation/Nargo.toml b/crates/nargo_cli/tests/execution_success/trait_default_implementation/Nargo.toml new file mode 100644 index 00000000000..7fcb4d0281e --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_default_implementation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_default_implementation" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/execution_success/trait_default_implementation/Prover.toml b/crates/nargo_cli/tests/execution_success/trait_default_implementation/Prover.toml new file mode 100644 index 00000000000..71805e71e8e --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_default_implementation/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "1" \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/trait_default_implementation/src/main.nr b/crates/nargo_cli/tests/execution_success/trait_default_implementation/src/main.nr new file mode 100644 index 00000000000..e1f29ce3f48 --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_default_implementation/src/main.nr @@ -0,0 +1,26 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field { + x + } + +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field) { + let first = Foo::method2(x); + assert(first == x); +} diff --git a/crates/nargo_cli/tests/execution_success/trait_override_implementation/Nargo.toml b/crates/nargo_cli/tests/execution_success/trait_override_implementation/Nargo.toml new file mode 100644 index 00000000000..7ab5a62b5ba --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_override_implementation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_override_implementation" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/execution_success/trait_override_implementation/Prover.toml b/crates/nargo_cli/tests/execution_success/trait_override_implementation/Prover.toml new file mode 100644 index 00000000000..71805e71e8e --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_override_implementation/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "1" \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/trait_override_implementation/src/main.nr b/crates/nargo_cli/tests/execution_success/trait_override_implementation/src/main.nr new file mode 100644 index 00000000000..92e19f97d50 --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/trait_override_implementation/src/main.nr @@ -0,0 +1,30 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; + + fn method2(x: Field) -> Field { + x + } + +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + + fn method2(x: Field) -> Field { + x * 3 + } +} + +fn main(x: Field) { + let first = Foo::method2(x); + assert(first == 3 * x); +} diff --git a/crates/nargo_cli/tests/execution_success/traits/Nargo.toml b/crates/nargo_cli/tests/execution_success/traits/Nargo.toml new file mode 100644 index 00000000000..75fb80c4bfa --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/traits/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "traits" +type = "bin" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/execution_success/traits/Prover.toml b/crates/nargo_cli/tests/execution_success/traits/Prover.toml new file mode 100644 index 00000000000..71805e71e8e --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/traits/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "1" \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/traits/src/main.nr b/crates/nargo_cli/tests/execution_success/traits/src/main.nr new file mode 100644 index 00000000000..2333c5da244 --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/traits/src/main.nr @@ -0,0 +1,21 @@ +use dep::std; + +trait Default { + fn default(x: Field, y: Field) -> Self; +} + +struct Foo { + bar: Field, + array: [Field; 2], +} + +impl Default for Foo { + fn default(x: Field,y: Field) -> Self { + Self { bar: x, array: [x,y] } + } +} + +fn main(x: Field, y: Field) { + let first = Foo::default(x,y); + assert(first.bar == x); +} diff --git a/crates/noirc_frontend/src/ast/expression.rs b/crates/noirc_frontend/src/ast/expression.rs index 170d3c569c9..6c29c89051f 100644 --- a/crates/noirc_frontend/src/ast/expression.rs +++ b/crates/noirc_frontend/src/ast/expression.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use crate::token::{Attribute, Token}; use crate::{ Distinctness, Ident, Path, Pattern, Recoverable, Statement, TraitConstraint, UnresolvedType, - Visibility, + UnresolvedTypeData, Visibility, }; use acvm::FieldElement; use iter_extended::vecmap; @@ -626,6 +626,39 @@ impl Display for Lambda { } } +impl FunctionDefinition { + pub fn normal( + name: &Ident, + generics: &UnresolvedGenerics, + parameters: &[(Ident, UnresolvedType)], + body: &BlockExpression, + where_clause: &[TraitConstraint], + return_type: &FunctionReturnType, + ) -> FunctionDefinition { + let p = parameters + .iter() + .map(|(ident, unresolved_type)| { + (Pattern::Identifier(ident.clone()), unresolved_type.clone(), Visibility::Private) + }) + .collect(); + FunctionDefinition { + name: name.clone(), + attribute: None, + is_open: false, + is_internal: false, + is_unconstrained: false, + generics: generics.clone(), + parameters: p, + body: body.clone(), + span: name.span(), + where_clause: where_clause.to_vec(), + return_type: return_type.clone(), + return_visibility: Visibility::Private, + return_distinctness: Distinctness::DuplicationAllowed, + } + } +} + impl Display for FunctionDefinition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(attribute) = &self.attribute { @@ -655,6 +688,15 @@ impl Display for FunctionDefinition { } } +impl FunctionReturnType { + pub fn get_type(&self) -> &UnresolvedTypeData { + match self { + FunctionReturnType::Default(_span) => &UnresolvedTypeData::Unit, + FunctionReturnType::Ty(typ, _span) => &typ.typ, + } + } +} + impl Display for FunctionReturnType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs index ac83e81ea71..03c0aad7c0f 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -3,6 +3,7 @@ use super::errors::{DefCollectorErrorKind, DuplicateType}; use crate::graph::CrateId; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; +use crate::hir::resolution::import::PathResolutionError; use crate::hir::resolution::resolver::Resolver; use crate::hir::resolution::{ import::{resolve_imports, ImportDirective}, @@ -10,11 +11,11 @@ use crate::hir::resolution::{ }; use crate::hir::type_check::{type_check_func, TypeChecker}; use crate::hir::Context; -use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TypeAliasId}; +use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::{ - ExpressionKind, Generics, Ident, LetStatement, Literal, NoirFunction, NoirStruct, - NoirTypeAlias, ParsedModule, Shared, StructType, Type, TypeBinding, UnresolvedGenerics, - UnresolvedType, + ExpressionKind, FunctionReturnType, Generics, Ident, LetStatement, Literal, NoirFunction, + NoirStruct, NoirTrait, NoirTypeAlias, ParsedModule, Shared, StructType, TraitItem, + TraitItemType, Type, TypeBinding, UnresolvedGenerics, UnresolvedType, }; use fm::FileId; use iter_extended::vecmap; @@ -22,6 +23,7 @@ use noirc_errors::Span; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; use std::collections::HashMap; use std::rc::Rc; +use std::vec; /// Stores all of the unresolved functions in a particular file/mod pub struct UnresolvedFunctions { @@ -41,6 +43,12 @@ pub struct UnresolvedStruct { pub struct_def: NoirStruct, } +pub struct UnresolvedTrait { + pub file_id: FileId, + pub module_id: LocalModuleId, + pub trait_def: NoirTrait, +} + #[derive(Clone)] pub struct UnresolvedTypeAlias { pub file_id: FileId, @@ -63,8 +71,10 @@ pub struct DefCollector { pub(crate) collected_functions: Vec, pub(crate) collected_types: HashMap, pub(crate) collected_type_aliases: HashMap, + pub(crate) collected_traits: HashMap, pub(crate) collected_globals: Vec, pub(crate) collected_impls: ImplMap, + pub(crate) collected_traits_impls: ImplMap, } /// Maps the type and the module id in which the impl is defined to the functions contained in that @@ -81,7 +91,9 @@ impl DefCollector { collected_functions: vec![], collected_types: HashMap::new(), collected_type_aliases: HashMap::new(), + collected_traits: HashMap::new(), collected_impls: HashMap::new(), + collected_traits_impls: HashMap::new(), collected_globals: vec![], } } @@ -173,6 +185,7 @@ impl DefCollector { resolve_type_aliases(context, def_collector.collected_type_aliases, crate_id, errors); + resolve_traits(context, def_collector.collected_traits, crate_id, errors); // Must resolve structs before we resolve globals. resolve_structs(context, def_collector.collected_types, crate_id, errors); @@ -188,6 +201,8 @@ impl DefCollector { // impl since that determines the module we should collect into. collect_impls(context, crate_id, &def_collector.collected_impls, errors); + collect_impls(context, crate_id, &def_collector.collected_traits_impls, errors); + // Lower each function in the crate. This is now possible since imports have been resolved let file_func_ids = resolve_free_functions( &mut context.def_interner, @@ -206,10 +221,19 @@ impl DefCollector { errors, ); + let file_trait_impls_ids = resolve_impls( + &mut context.def_interner, + crate_id, + &context.def_maps, + def_collector.collected_traits_impls, + errors, + ); + type_check_globals(&mut context.def_interner, file_global_ids, errors); // Type check all of the functions in the crate type_check_functions(&mut context.def_interner, file_func_ids, errors); + type_check_functions(&mut context.def_interner, file_trait_impls_ids, errors); type_check_functions(&mut context.def_interner, file_method_ids, errors); } } @@ -372,6 +396,119 @@ fn resolve_structs( } } +fn resolve_trait_types( + _context: &mut Context, + _crate_id: CrateId, + _unresolved_trait: &UnresolvedTrait, + _errors: &mut [FileDiagnostic], +) -> Vec { + // TODO + vec![] +} +fn resolve_trait_constants( + _context: &mut Context, + _crate_id: CrateId, + _unresolved_trait: &UnresolvedTrait, + _errors: &mut [FileDiagnostic], +) -> Vec { + // TODO + vec![] +} + +fn resolve_trait_methods( + context: &mut Context, + crate_id: CrateId, + unresolved_trait: &UnresolvedTrait, + errors: &mut Vec, +) -> Vec { + let interner = &mut context.def_interner; + let def_maps = &mut context.def_maps; + + let path_resolver = StandardPathResolver::new(ModuleId { + local_id: unresolved_trait.module_id, + krate: crate_id, + }); + let file = def_maps[&crate_id].file_id(unresolved_trait.module_id); + + let mut res = vec![]; + + for item in &unresolved_trait.trait_def.items { + if let TraitItem::Function { + name, + generics: _, + parameters, + return_type, + where_clause: _, + body: _, + } = item + { + let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); + let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); + let resolved_return_type = match return_type { + FunctionReturnType::Default(_) => None, + FunctionReturnType::Ty(unresolved_type, _span) => { + Some(resolver.resolve_type(unresolved_type.clone())) + } + }; + let name = name.clone(); + // TODO + let generics: Generics = vec![]; + let span: Span = name.span(); + let f = TraitItemType::Function { + name, + generics, + arguments, + return_type: resolved_return_type, + span, + }; + res.push(f); + let new_errors = take_errors_filter_self_not_resolved(resolver); + extend_errors(errors, file, new_errors); + } + } + res +} + +fn take_errors_filter_self_not_resolved(resolver: Resolver<'_>) -> Vec { + resolver + .take_errors() + .iter() + .cloned() + .filter(|resolution_error| match resolution_error { + ResolverError::PathResolutionError(PathResolutionError::Unresolved(ident)) => { + &ident.0.contents != "Self" + } + _ => true, + }) + .collect() +} + +/// Create the mappings from TypeId -> TraitType +/// so that expressions can access the elements of traits +fn resolve_traits( + context: &mut Context, + traits: HashMap, + crate_id: CrateId, + errors: &mut Vec, +) { + for (trait_id, unresolved_trait) in &traits { + context.def_interner.push_empty_trait(*trait_id, unresolved_trait); + } + for (trait_id, unresolved_trait) in traits { + let mut items: Vec = vec![]; + // Resolve order + // 1. Trait Types ( Trait contants can have a trait type, therefore types before constants) + items.append(&mut resolve_trait_types(context, crate_id, &unresolved_trait, errors)); + // 2. Trait Constants ( Trait's methods can use trait types & constants, threfore they should be after) + items.append(&mut resolve_trait_constants(context, crate_id, &unresolved_trait, errors)); + // 3. Trait Methods + items.append(&mut resolve_trait_methods(context, crate_id, &unresolved_trait, errors)); + context.def_interner.update_trait(trait_id, |trait_def| { + trait_def.set_items(items); + }); + } +} + fn resolve_struct_fields( context: &mut Context, krate: CrateId, @@ -442,7 +579,6 @@ fn resolve_impls( generics, errors, ); - if self_type != Type::Error { for (file_id, method_id) in &file_func_ids { let method_name = interner.function_name(method_id).to_owned(); diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs index 53ed397e647..f72b72f54df 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -2,16 +2,20 @@ use fm::FileId; use noirc_errors::{FileDiagnostic, Location}; use crate::{ - graph::CrateId, hir::def_collector::dc_crate::UnresolvedStruct, node_interner::StructId, - parser::SubModule, Ident, LetStatement, NoirFunction, NoirStruct, NoirTypeAlias, ParsedModule, - TypeImpl, + graph::CrateId, + hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, + node_interner::{StructId, TraitId}, + parser::SubModule, + FunctionDefinition, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, + NoirTrait, NoirTypeAlias, ParsedModule, TraitImpl, TraitImplItem, TraitItem, TypeImpl, + UnresolvedType, }; use super::{ dc_crate::{DefCollector, UnresolvedFunctions, UnresolvedGlobal, UnresolvedTypeAlias}, errors::{DefCollectorErrorKind, DuplicateType}, }; -use crate::hir::def_map::{parse_file, LocalModuleId, ModuleData, ModuleId}; +use crate::hir::def_map::{parse_file, LocalModuleId, ModuleData, ModuleDefId, ModuleId}; use crate::hir::resolution::import::ImportDirective; use crate::hir::Context; @@ -54,15 +58,100 @@ pub fn collect_defs( collector.collect_globals(context, ast.globals, errors); + collector.collect_traits(ast.traits, crate_id, errors); + collector.collect_structs(ast.types, crate_id, errors); collector.collect_type_aliases(context, ast.type_aliases, errors); collector.collect_functions(context, ast.functions, errors); + collector.collect_trait_impls(context, ast.trait_impls, errors); + collector.collect_impls(context, ast.impls); } +fn check_trait_method_implementation_parameters( + expected_parameters: &Vec<(Ident, UnresolvedType)>, + impl_method: &NoirFunction, + trait_name: &str, +) -> Result<(), DefCollectorErrorKind> { + let expected_num_parameters = expected_parameters.len(); + let actual_num_parameters = impl_method.def.parameters.len(); + if actual_num_parameters != expected_num_parameters { + return Err(DefCollectorErrorKind::MismatchTraitImplementationNumParameters { + actual_num_parameters, + expected_num_parameters, + trait_name: trait_name.to_owned(), + impl_ident: impl_method.name_ident().clone(), + }); + } + for (count, (parameter, typ, _abi_vis)) in impl_method.def.parameters.iter().enumerate() { + let (_expected_name, expected_type) = &expected_parameters[count]; + if typ.typ != expected_type.typ { + return Err(DefCollectorErrorKind::MismatchTraitImlementationParameter { + trait_name: trait_name.to_owned(), + expected_type: expected_type.clone(), + impl_method: impl_method.name().to_string(), + parameter: parameter.name_ident().clone(), + }); + } + } + Ok(()) +} + +fn check_trait_method_implementation_return_type( + expected_return_type: &FunctionReturnType, + impl_method: &NoirFunction, + trait_name: &str, +) -> Result<(), DefCollectorErrorKind> { + if expected_return_type.get_type() == impl_method.def.return_type.get_type() { + Ok(()) + } else { + Err(DefCollectorErrorKind::MismatchTraitImplementationReturnType { + trait_name: trait_name.to_owned(), + impl_ident: impl_method.name_ident().clone(), + }) + } +} + +fn check_trait_method_implementation( + r#trait: &NoirTrait, + impl_method: &NoirFunction, +) -> Result<(), DefCollectorErrorKind> { + for item in &r#trait.items { + if let TraitItem::Function { + name, + generics: _, + parameters, + return_type, + where_clause: _, + body: _, + } = item + { + if name.0.contents == impl_method.def.name.0.contents { + // name matches, check for parameters - count and type, return type + check_trait_method_implementation_parameters( + parameters, + impl_method, + &r#trait.name.0.contents, + )?; + check_trait_method_implementation_return_type( + return_type, + impl_method, + &r#trait.name.0.contents, + )?; + return Ok(()); + } + } + } + + Err(DefCollectorErrorKind::MethodNotInTrait { + trait_name: r#trait.name.clone(), + impl_method: impl_method.def.name.clone(), + }) +} + impl<'a> ModCollector<'a> { fn collect_globals( &mut self, @@ -116,6 +205,132 @@ impl<'a> ModCollector<'a> { } } + fn collect_trait_impls( + &mut self, + context: &mut Context, + impls: Vec, + errors: &mut Vec, + ) { + for trait_impl in impls { + let trait_name = trait_impl.trait_name.clone(); + let module = &self.def_collector.def_map.modules[self.module_id.0]; + match module.find_name(&trait_name).types { + Some((module_def_id, _visibility)) => { + if let Some(collected_trait) = self.get_unresolved_trait(module_def_id) { + let trait_def = collected_trait.trait_def.clone(); + let collected_implementations = self.collect_trait_implementations( + context, + &trait_impl, + &trait_def, + errors, + ); + + let impl_type_span = trait_impl.object_type_span; + let impl_generics = trait_impl.impl_generics.clone(); + let impl_object_type = trait_impl.object_type.clone(); + let key = (impl_object_type, self.module_id); + self.def_collector.collected_traits_impls.entry(key).or_default().push(( + impl_generics, + impl_type_span, + collected_implementations, + )); + } else { + let error = DefCollectorErrorKind::NotATrait { + not_a_trait_name: trait_name.clone(), + }; + errors.push(error.into_file_diagnostic(self.file_id)); + } + } + None => { + let error = DefCollectorErrorKind::TraitNotFound { + trait_name: trait_name.to_string(), + span: trait_name.span(), + }; + errors.push(error.into_file_diagnostic(self.file_id)); + } + } + } + } + + fn get_unresolved_trait(&self, module_def_id: ModuleDefId) -> Option<&UnresolvedTrait> { + match module_def_id { + ModuleDefId::TraitId(trait_id) => self.def_collector.collected_traits.get(&trait_id), + _ => None, + } + } + + fn collect_trait_implementations( + &mut self, + context: &mut Context, + trait_impl: &TraitImpl, + trait_def: &NoirTrait, + errors: &mut Vec, + ) -> UnresolvedFunctions { + let mut unresolved_functions = + UnresolvedFunctions { file_id: self.file_id, functions: Vec::new() }; + + for item in &trait_impl.items { + if let TraitImplItem::Function(impl_method) = item { + match check_trait_method_implementation(trait_def, impl_method) { + Ok(()) => { + let func_id = context.def_interner.push_empty_fn(); + context + .def_interner + .push_function_definition(impl_method.name().to_owned(), func_id); + unresolved_functions.push_fn(self.module_id, func_id, impl_method.clone()); + } + Err(error) => { + errors.push(error.into_file_diagnostic(self.file_id)); + } + } + } + } + + for item in &trait_def.items { + if let TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body, + } = item + { + let is_implemented = unresolved_functions + .functions + .iter() + .any(|(_, _, func_impl)| func_impl.name() == name.0.contents); + if !is_implemented { + match body { + Some(body) => { + let method_name = name.0.contents.clone(); + let func_id = context.def_interner.push_empty_fn(); + context.def_interner.push_function_definition(method_name, func_id); + let impl_method = NoirFunction::normal(FunctionDefinition::normal( + name, + generics, + parameters, + body, + where_clause, + return_type, + )); + unresolved_functions.push_fn(self.module_id, func_id, impl_method); + } + None => { + let error = DefCollectorErrorKind::TraitMissedMethodImplementation { + trait_name: trait_def.name.clone(), + method_name: name.clone(), + trait_impl_span: trait_impl.object_type_span, + }; + errors.push(error.into_file_diagnostic(self.file_id)); + } + } + } + } + } + unresolved_functions + } + fn collect_functions( &mut self, context: &mut Context, @@ -235,6 +450,46 @@ impl<'a> ModCollector<'a> { } } + /// Collect any traits definitions declared within the ast. + /// Returns a vector of errors if any traits were already defined. + fn collect_traits( + &mut self, + traits: Vec, + krate: CrateId, + errors: &mut Vec, + ) { + for trait_definition in traits { + let name = trait_definition.name.clone(); + + // Create the corresponding module for the trait namespace + let id = match self.push_child_module(&name, self.file_id, false, false, errors) { + Some(local_id) => TraitId(ModuleId { krate, local_id }), + None => continue, + }; + + // Add the trait to scope so its path can be looked up later + let result = + self.def_collector.def_map.modules[self.module_id.0].declare_trait(name, id); + + if let Err((first_def, second_def)) = result { + let err = DefCollectorErrorKind::Duplicate { + typ: DuplicateType::Trait, + first_def, + second_def, + }; + errors.push(err.into_file_diagnostic(self.file_id)); + } + + // And store the TraitId -> TraitType mapping somewhere it is reachable + let unresolved = UnresolvedTrait { + file_id: self.file_id, + module_id: self.module_id, + trait_def: trait_definition, + }; + self.def_collector.collected_traits.insert(id, unresolved); + } + } + fn collect_submodules( &mut self, context: &mut Context, diff --git a/crates/noirc_frontend/src/hir/def_collector/errors.rs b/crates/noirc_frontend/src/hir/def_collector/errors.rs index d47d985e12e..2aab7f514d9 100644 --- a/crates/noirc_frontend/src/hir/def_collector/errors.rs +++ b/crates/noirc_frontend/src/hir/def_collector/errors.rs @@ -1,5 +1,6 @@ use crate::hir::resolution::import::PathResolutionError; use crate::Ident; +use crate::UnresolvedType; use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::FileDiagnostic; @@ -15,6 +16,7 @@ pub enum DuplicateType { Global, TypeDefinition, Import, + Trait, } #[derive(Error, Debug)] @@ -29,6 +31,30 @@ pub enum DefCollectorErrorKind { NonStructTypeInImpl { span: Span }, #[error("Cannot `impl` a type defined outside the current crate")] ForeignImpl { span: Span, type_name: String }, + #[error("Mismatch signature of trait")] + MismatchTraitImlementationParameter { + trait_name: String, + impl_method: String, + parameter: Ident, + expected_type: UnresolvedType, + }, + #[error("Mismatch return type of trait implementation")] + MismatchTraitImplementationReturnType { trait_name: String, impl_ident: Ident }, + #[error("Mismatch number of parameters in of trait implementation")] + MismatchTraitImplementationNumParameters { + actual_num_parameters: usize, + expected_num_parameters: usize, + trait_name: String, + impl_ident: Ident, + }, + #[error("Method is not defined in trait")] + MethodNotInTrait { trait_name: Ident, impl_method: Ident }, + #[error("Only traits can be implemented")] + NotATrait { not_a_trait_name: Ident }, + #[error("Trait not found")] + TraitNotFound { trait_name: String, span: Span }, + #[error("Missing Trait method implementation")] + TraitMissedMethodImplementation { trait_name: Ident, method_name: Ident, trait_impl_span: Span }, } impl DefCollectorErrorKind { @@ -44,6 +70,7 @@ impl fmt::Display for DuplicateType { DuplicateType::Module => write!(f, "module"), DuplicateType::Global => write!(f, "global"), DuplicateType::TypeDefinition => write!(f, "type definition"), + DuplicateType::Trait => write!(f, "trait definition"), DuplicateType::Import => write!(f, "import"), } } @@ -62,10 +89,10 @@ impl From for Diagnostic { let second_span = second_def.0.span(); let mut diag = Diagnostic::simple_error( primary_message, - format!("first {:?} found here", &typ), + format!("first {} found here", &typ), first_span, ); - diag.add_secondary(format!("second {:?} found here", &typ), second_span); + diag.add_secondary(format!("second {} found here", &typ), second_span); diag } } @@ -90,6 +117,80 @@ impl From for Diagnostic { format!("{type_name} was defined outside the current crate"), span, ), + DefCollectorErrorKind::TraitNotFound { trait_name, span } => Diagnostic::simple_error( + format!("Trait {} not found", trait_name), + "".to_string(), + span, + ), + DefCollectorErrorKind::MismatchTraitImplementationReturnType { + trait_name, + impl_ident, + } => { + let span = impl_ident.span(); + let method_name = impl_ident.0.contents; + Diagnostic::simple_error( + format!("Mismatch return type of method with name {method_name} that implements trait {trait_name}"), + "".to_string(), + span, + ) + } + DefCollectorErrorKind::MismatchTraitImplementationNumParameters { + expected_num_parameters, + actual_num_parameters, + trait_name, + impl_ident, + } => { + let method_name = impl_ident.0.contents.clone(); + let primary_message = format!( + "Mismatch - expected {expected_num_parameters} arguments, but got {actual_num_parameters} of trait `{trait_name}` implementation `{method_name}`"); + Diagnostic::simple_error(primary_message, "".to_string(), impl_ident.span()) + } + DefCollectorErrorKind::MismatchTraitImlementationParameter { + trait_name, + impl_method, + parameter, + expected_type, + } => { + let primary_message = format!( + "Mismatch signature of method {impl_method} that implements trait {trait_name}" + ); + let secondary_message = + format!("`{}: {}` expected", parameter.0.contents, expected_type,); + let span = parameter.span(); + Diagnostic::simple_error(primary_message, secondary_message, span) + } + DefCollectorErrorKind::MethodNotInTrait { trait_name, impl_method } => { + let trait_name = trait_name.0.contents; + let impl_method_span = impl_method.span(); + let impl_method_name = impl_method.0.contents; + let primary_message = format!("method with name {impl_method_name} is not part of trait {trait_name}, therefore it can't be implemented"); + Diagnostic::simple_error(primary_message, "".to_owned(), impl_method_span) + } + DefCollectorErrorKind::TraitMissedMethodImplementation { + trait_name, + method_name, + trait_impl_span, + } => { + let trait_name = trait_name.0.contents; + let impl_method_name = method_name.0.contents; + let primary_message = format!( + "method `{impl_method_name}` from trait `{trait_name}` is not implemented" + ); + Diagnostic::simple_error( + primary_message, + format!("Please implement {impl_method_name} here"), + trait_impl_span, + ) + } + DefCollectorErrorKind::NotATrait { not_a_trait_name } => { + let span = not_a_trait_name.0.span(); + let name = ¬_a_trait_name.0.contents; + Diagnostic::simple_error( + format!("{name} is not a trait, therefore it can't be implemented"), + String::new(), + span, + ) + } } } } diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 7cde9ae231d..f73a3b85166 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -1537,7 +1537,7 @@ mod test { src: &str, ) -> (ParsedModule, NodeInterner, HashMap, FileId, TestPathResolver) { let (program, errors) = parse_program(src); - if !errors.is_empty() { + if errors.iter().any(|e| e.is_error()) { panic!("Unexpected parse errors in test code: {:?}", errors); } diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index 7988c20d244..8372f7a0355 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -130,7 +130,7 @@ pub enum TraitItemType { name: Ident, generics: Generics, arguments: Vec, - return_type: Type, + return_type: Option, span: Span, }, @@ -196,6 +196,10 @@ impl Trait { ) -> Trait { Trait { id, name, span, items, generics } } + + pub fn set_items(&mut self, items: Vec) { + self.items = items; + } } impl std::fmt::Display for Trait { diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 5a45cfee42b..ae53a71cba6 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -7,7 +7,7 @@ use noirc_errors::{Location, Span, Spanned}; use crate::ast::Ident; use crate::graph::CrateId; -use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTypeAlias}; +use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::StorageSlot; use crate::hir_def::stmt::HirLetStatement; @@ -70,6 +70,7 @@ pub struct NodeInterner { // TODO: We may be able to remove the Shared wrapper once traits are no longer types. // We'd just lookup their methods as needed through the NodeInterner. 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. @@ -326,6 +327,26 @@ impl NodeInterner { self.id_to_type.insert(expr_id.into(), typ); } + pub fn push_empty_trait(&mut self, type_id: TraitId, typ: &UnresolvedTrait) { + self.traits.insert( + type_id, + Shared::new(Trait::new( + type_id, + typ.trait_def.name.clone(), + typ.trait_def.span, + Vec::new(), + vecmap(&typ.trait_def.generics, |_| { + // Temporary type variable ids before the trait is resolved to its actual ids. + // This lets us record how many arguments the type expects so that other types + // can refer to it with generic arguments before the generic parameters themselves + // are resolved. + let id = TypeVariableId(0); + (id, Shared::new(TypeBinding::Unbound(id))) + }), + )), + ); + } + pub fn push_empty_struct(&mut self, type_id: StructId, typ: &UnresolvedStruct) { self.structs.insert( type_id, @@ -368,6 +389,11 @@ impl NodeInterner { f(&mut value); } + pub fn update_trait(&mut self, trait_id: TraitId, f: impl FnOnce(&mut Trait)) { + let mut value = self.traits.get_mut(&trait_id).unwrap().borrow_mut(); + f(&mut value); + } + pub fn set_type_alias(&mut self, type_id: TypeAliasId, typ: Type, generics: Generics) { let type_alias_type = &mut self.type_aliases[type_id.0]; type_alias_type.set_type_and_generics(typ, generics);