diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index a9571a6669d..e3f198e68bd 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -11,7 +11,7 @@ use crate::{ def_map::ModuleDefId, resolution::{ errors::ResolverError, - resolver::{verify_mutable_reference, SELF_TYPE_NAME}, + resolver::{verify_mutable_reference, SELF_TYPE_NAME, WILDCARD_TYPE}, }, type_check::{Source, TypeCheckError}, }, @@ -146,6 +146,8 @@ impl<'context> Elaborator<'context> { } return self_type; } + } else if name == WILDCARD_TYPE { + return self.interner.next_type_variable(); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 4583125d2a2..f8db9730f88 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -64,6 +64,7 @@ use super::errors::{PubPosition, ResolverError}; use super::import::PathResolution; pub const SELF_TYPE_NAME: &str = "Self"; +pub const WILDCARD_TYPE: &str = "_"; type Scope = GenericScope; type ScopeTree = GenericScopeTree; diff --git a/docs/docs/noir/concepts/data_types/index.md b/docs/docs/noir/concepts/data_types/index.md index 357813c147a..3eadb2dc8a4 100644 --- a/docs/docs/noir/concepts/data_types/index.md +++ b/docs/docs/noir/concepts/data_types/index.md @@ -105,6 +105,14 @@ type Bad2 = Bad1; // ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 ``` +## Wildcard Type +Noir can usually infer the type of the variable from the context, so specifying the type of a variable is only required when it cannot be inferred. However, specifying a complex type can be tedious, especially when it has multiple generic arguments. Often some of the generic types can be inferred from the context, and Noir only needs a hint to properly infer the other types. We can partially specify a variable's type by using `_` as a marker, indicating where we still want the compiler to infer the type. + +```rust +let a: [_; 4] = foo(b); +``` + + ### BigInt You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/test_programs/execution_success/wildcard_type/Nargo.toml b/test_programs/execution_success/wildcard_type/Nargo.toml new file mode 100644 index 00000000000..e3d7fc636af --- /dev/null +++ b/test_programs/execution_success/wildcard_type/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_type" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/wildcard_type/Prover.toml b/test_programs/execution_success/wildcard_type/Prover.toml new file mode 100644 index 00000000000..c7c8371dfa4 --- /dev/null +++ b/test_programs/execution_success/wildcard_type/Prover.toml @@ -0,0 +1 @@ +enable = [4,7] diff --git a/test_programs/execution_success/wildcard_type/src/main.nr b/test_programs/execution_success/wildcard_type/src/main.nr new file mode 100644 index 00000000000..c27f9987c48 --- /dev/null +++ b/test_programs/execution_success/wildcard_type/src/main.nr @@ -0,0 +1,23 @@ +struct bar { + enable: [bool; 4], + data: [Field; 2], + pad: u32, +} + +fn main(enable: [Field; 2]) -> pub [Field; 4] { + let mut result = [0; 4]; + let a: [_; 4] = foo(enable[1]); + for i in 0..4 { + result[i] = a[i].data[i % 2]; + } + result +} + +fn foo(x: Field) -> [bar; 4] { + [ + bar { enable: [true, true, false, false], data: [x, x + 1], pad: 0 }, + bar { enable: [true, false, false, false], data: [x + 2, x + 7], pad: 0 }, + bar { enable: [true, true, false, true], data: [x + 3, x + 5], pad: 0 }, + bar { enable: [false, false, false, false], data: [x + 4, x - 1], pad: 0 } + ] +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index d2822ba11ab..ec750236806 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -58,10 +58,10 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ "is_unconstrained", ]; -/// Certain comptime features are only available in the elaborator. +/// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_COMPTIME_TESTS: [&str; 1] = ["macros"]; +const IGNORED_NEW_FEATURE_TESTS: [&str; 2] = ["macros", "wildcard_type"]; fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; @@ -82,11 +82,16 @@ fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) let brillig_ignored = if IGNORED_BRILLIG_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; + let new_features_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; write!( test_file, r#" -#[test] +#[test]{new_features_ignored} fn execution_success_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); @@ -286,8 +291,11 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); - let comptime_ignored = - if IGNORED_COMPTIME_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" }; + let comptime_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; write!( test_file,