From 80b593cff87d36ba8036484c338b3c169d473b8d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 21 Nov 2024 19:23:04 -0500 Subject: [PATCH 1/4] require global types to be specified, add error that's thrown when unspecified and is_unspecified check for UnresolvedType(Data), update tests and add new unit tests for this behavior --- compiler/noirc_frontend/src/ast/mod.rs | 20 ++++++ .../src/elaborator/statements.rs | 9 +++ .../src/hir/resolution/errors.rs | 9 +++ compiler/noirc_frontend/src/tests.rs | 66 +++++++++++++++++-- .../noirc_frontend/src/tests/unused_items.rs | 6 +- docs/docs/noir/concepts/globals.md | 16 ++--- noir_stdlib/src/bigint.nr | 12 ++-- noir_stdlib/src/collections/map.nr | 4 +- noir_stdlib/src/ec/mod.nr | 8 +-- noir_stdlib/src/hash/sha256.nr | 18 ++--- .../assert_constant/src/main.nr | 10 +-- .../comptime_globals_regression/src/main.nr | 2 +- .../comptime_module/src/main.nr | 2 +- .../numeric_generics_explicit/src/main.nr | 2 +- .../src/main.nr | 4 +- .../raw_string/src/main.nr | 2 +- .../static_assert/src/main.nr | 10 +-- .../src/main.nr | 4 +- .../databus_mapping_regression/src/main.nr | 4 +- .../bench_2_to_17/src/main.nr | 2 +- .../execution_success/brillig_cow/src/main.nr | 2 +- .../brillig_cow_assign/src/main.nr | 2 +- .../brillig_cow_regression/src/main.nr | 2 +- .../fmtstr_with_global/src/main.nr | 2 +- .../fold_2_to_17/src/main.nr | 2 +- .../fold_call_witness_condition/src/main.nr | 2 +- .../fold_numeric_generic_poseidon/src/main.nr | 4 +- .../global_consts/src/foo.nr | 2 +- .../global_consts/src/main.nr | 4 +- .../execution_success/hashmap/src/main.nr | 12 ++-- .../src/main.nr | 2 +- .../ram_blowup_regression/src/main.nr | 2 +- .../regression_2660/src/main.nr | 2 +- .../regression_5252/src/main.nr | 4 +- .../sha256_var_size_regression/src/main.nr | 2 +- .../execution_success/strings/src/main.nr | 2 +- .../struct_inputs/src/foo/bar.nr | 2 +- .../execution_success/uhashmap/src/main.nr | 12 ++-- .../test_libraries/diamond_deps_2/src/lib.nr | 2 +- 39 files changed, 185 insertions(+), 89 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 3c6664dd569..154f29c4d7d 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -32,6 +32,7 @@ pub use traits::*; pub use type_alias::*; use crate::{ + elaborator::types::WILDCARD_TYPE, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, @@ -160,12 +161,31 @@ pub enum UnresolvedTypeData { Error, } +impl UnresolvedTypeData { + pub(crate) fn is_unspecified(&self) -> bool { + match self { + UnresolvedTypeData::Unspecified => true, + // '_' is unspecified + UnresolvedTypeData::Named(path, _, _) => { + path.to_ident().map(|ident| ident.0.contents) == Some(WILDCARD_TYPE.to_string()) + } + _ => false, + } + } +} + #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct UnresolvedType { pub typ: UnresolvedTypeData, pub span: Span, } +impl UnresolvedType { + pub(crate) fn is_unspecified(&self) -> bool { + self.typ.is_unspecified() + } +} + /// An argument to a generic type or trait. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum GenericTypeArg { diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 757def16a93..6e4c8dcc115 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -76,8 +76,17 @@ impl<'context> Elaborator<'context> { ) -> (HirStatement, Type) { let expr_span = let_stmt.expression.span; let (expression, expr_type) = self.elaborate_expression(let_stmt.expression); + let type_is_unspecified = let_stmt.r#type.is_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); + // Require the top-level of a global's type to be specified + if type_is_unspecified && global_id.is_some() { + let span = expr_span; + let expected_type = annotated_type.clone(); + let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; + self.push_err(error); + } + let definition = match global_id { None => DefinitionKind::Local(Some(expression)), Some(id) => DefinitionKind::Global(id), diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index b82eafa5b9d..81113923f13 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -101,6 +101,8 @@ pub enum ResolverError { JumpOutsideLoop { is_break: bool, span: Span }, #[error("Only `comptime` globals can be mutable")] MutableGlobal { span: Span }, + #[error("Globals must have a specified type (RHS inferred to have type `{expected_type}`)")] + UnspecifiedGlobalType { span: Span, expected_type: Type }, #[error("Self-referential structs are not supported")] SelfReferentialStruct { span: Span }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] @@ -431,6 +433,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, + ResolverError::UnspecifiedGlobalType { span, expected_type } => { + Diagnostic::simple_error( + format!("Globals must have a specified type (RHS inferred to have type `{expected_type}`)"), + String::new(), + *span, + ) + }, ResolverError::SelfReferentialStruct { span } => { Diagnostic::simple_error( "Self-referential structs are not supported".into(), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 20a5bac49f6..d717e5ce37c 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1304,7 +1304,21 @@ fn deny_cyclic_globals() { global B = A; fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[2].0, + CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) + )); } #[test] @@ -3210,10 +3224,10 @@ fn as_trait_path_syntax_no_impl() { } #[test] -fn infer_globals_to_u32_from_type_use() { +fn dont_infer_globals_to_u32_from_type_use() { let src = r#" global ARRAY_LEN = 3; - global STR_LEN = 2; + global STR_LEN: _ = 2; global FMT_STR_LEN = 2; fn main() { @@ -3223,6 +3237,50 @@ fn infer_globals_to_u32_from_type_use() { } "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + assert!(matches!( + errors[2].0, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); +} + +#[test] +fn infer_partial_global_types() { + let src = r#" + pub global ARRAY: [Field; _] = [0; 3]; + pub global STR: str<_> = "hi"; + pub global FMT_STR: fmtstr<_, _> = f"hi {ARRAY}"; + + fn main() { } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn u32_globals_as_sizes_in_types() { + let src = r#" + global ARRAY_LEN: u32 = 3; + global STR_LEN: u32 = 2; + global FMT_STR_LEN: u32 = 2; + + fn main() { + let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + let _b: str = "hi"; + let _c: fmtstr = f"hi"; + } + "#; + let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } @@ -3686,7 +3744,7 @@ fn allows_struct_with_generic_infix_type_as_main_input_3() { x: [u64; N * 2], } - global N = 9; + global N: u32 = 9; fn main(_x: Foo) {} "#; diff --git a/compiler/noirc_frontend/src/tests/unused_items.rs b/compiler/noirc_frontend/src/tests/unused_items.rs index 5f9fc887b27..9304209e501 100644 --- a/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/compiler/noirc_frontend/src/tests/unused_items.rs @@ -191,8 +191,8 @@ fn errors_on_unused_type_alias() { #[test] fn warns_on_unused_global() { let src = r#" - global foo = 1; - global bar = 1; + global foo: u32 = 1; + global bar: Field = 1; fn main() { let _ = bar; @@ -216,7 +216,7 @@ fn does_not_warn_on_unused_global_if_it_has_an_abi_attribute() { let src = r#" contract foo { #[abi(notes)] - global bar = 1; + global bar: u64 = 1; } fn main() {} diff --git a/docs/docs/noir/concepts/globals.md b/docs/docs/noir/concepts/globals.md index 6b8314399a2..c64b6c53746 100644 --- a/docs/docs/noir/concepts/globals.md +++ b/docs/docs/noir/concepts/globals.md @@ -10,12 +10,12 @@ sidebar_position: 8 ## Globals -Noir supports global variables. The global's type can be inferred by the compiler entirely: +Noir supports global variables. The global's type must be specified by the user: ```rust -global N = 5; // Same as `global N: Field = 5` +global N: Field = 5; -global TUPLE = (3, 2); +global TUPLE: (Field, Field) = (3, 2); fn main() { assert(N == 5); @@ -28,7 +28,7 @@ fn main() { Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: ```rust -global T = foo(T); // dependency error +global T: u32 = foo(T); // dependency error ``` ::: @@ -47,7 +47,7 @@ fn main(y : [Field; N]) { A global from another module can be imported or referenced externally like any other name: ```rust -global N = 20; +global N: Field = 20; fn main() { assert(my_submodule::N != N); @@ -62,7 +62,7 @@ When a global is used, Noir replaces the name with its definition on each occurr This means globals defined using function calls will repeat the call each time they're used: ```rust -global RESULT = foo(); +global RESULT: [Field; 100] = foo(); fn foo() -> [Field; 100] { ... } ``` @@ -78,5 +78,5 @@ to make the global public or `pub(crate)` to make it public to just its crate: ```rust // This global is now public -pub global N = 5; -``` \ No newline at end of file +pub global N: u32 = 5; +``` diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index be072257be3..c94a7a75f25 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,27 +1,27 @@ use crate::cmp::Eq; use crate::ops::{Add, Div, Mul, Sub}; -global bn254_fq = &[ +global bn254_fq: [u8] = &[ 0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30, ]; -global bn254_fr = &[ +global bn254_fr: [u8] = &[ 1, 0, 0, 240, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48, ]; -global secpk1_fr = &[ +global secpk1_fr: [u8] = &[ 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpk1_fq = &[ +global secpk1_fq: [u8] = &[ 0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpr1_fq = &[ +global secpr1_fq: [u8] = &[ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, ]; -global secpr1_fr = &[ +global secpr1_fr: [u8] = &[ 81, 37, 99, 252, 194, 202, 185, 243, 132, 158, 23, 167, 173, 250, 230, 188, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, ]; diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr index b46bfa837fb..bcce08faab4 100644 --- a/noir_stdlib/src/collections/map.nr +++ b/noir_stdlib/src/collections/map.nr @@ -7,8 +7,8 @@ use crate::option::Option; // We use load factor alpha_max = 0.75. // Upon exceeding it, assert will fail in order to inform the user // about performance degradation, so that he can adjust the capacity. -global MAX_LOAD_FACTOR_NUMERATOR = 3; -global MAX_LOAD_FACTOR_DEN0MINATOR = 4; +global MAX_LOAD_FACTOR_NUMERATOR: u32 = 3; +global MAX_LOAD_FACTOR_DEN0MINATOR: u32 = 4; /// `HashMap` is used to efficiently store and look up key-value pairs. /// diff --git a/noir_stdlib/src/ec/mod.nr b/noir_stdlib/src/ec/mod.nr index b62bc99d9c8..e2b2df1b794 100644 --- a/noir_stdlib/src/ec/mod.nr +++ b/noir_stdlib/src/ec/mod.nr @@ -122,12 +122,12 @@ pub mod consts; // Commonly used curve presets // Field-dependent constant ZETA = a non-square element of Field // Required for Elligator 2 map // TODO: Replace with built-in constant. -global ZETA = 5; +global ZETA: Field = 5; // Field-dependent constants for Tonelli-Shanks algorithm (see sqrt function below) // TODO: Possibly make this built-in. -global C1 = 28; -global C3 = 40770029410420498293352137776570907027550720424234931066070132305055; -global C5 = 19103219067921713944291392827692070036145651957329286315305642004821462161904; +global C1: u32 = 28; +global C3: Field = 40770029410420498293352137776570907027550720424234931066070132305055; +global C5: Field = 19103219067921713944291392827692070036145651957329286315305642004821462161904; // Higher-order version of scalar multiplication // TODO: Make this work so that the submodules' bit_mul may be defined in terms of it. //fn bit_mul(add: fn(T,T) -> T, e: T, bits: [u1; N], p: T) -> T { diff --git a/noir_stdlib/src/hash/sha256.nr b/noir_stdlib/src/hash/sha256.nr index d55044907ac..b9a2b02c9d9 100644 --- a/noir_stdlib/src/hash/sha256.nr +++ b/noir_stdlib/src/hash/sha256.nr @@ -4,27 +4,27 @@ use crate::runtime::is_unconstrained; // 32 bytes. // A message block is up to 64 bytes taken from the input. -global BLOCK_SIZE = 64; +global BLOCK_SIZE: u32 = 64; // The first index in the block where the 8 byte message size will be written. -global MSG_SIZE_PTR = 56; +global MSG_SIZE_PTR: u32 = 56; // Size of the message block when packed as 4-byte integer array. -global INT_BLOCK_SIZE = 16; +global INT_BLOCK_SIZE: u32 = 16; // A `u32` integer consists of 4 bytes. -global INT_SIZE = 4; +global INT_SIZE: u32 = 4; // Index of the integer in the `INT_BLOCK` where the length is written. -global INT_SIZE_PTR = MSG_SIZE_PTR / INT_SIZE; +global INT_SIZE_PTR: u32 = MSG_SIZE_PTR / INT_SIZE; // Magic numbers for bit shifting. // Works with actual bit shifting as well as the compiler turns them into * and / // but circuit execution appears to be 10% faster this way. -global TWO_POW_8 = 256; -global TWO_POW_16 = TWO_POW_8 * 256; -global TWO_POW_24 = TWO_POW_16 * 256; -global TWO_POW_32 = TWO_POW_24 as u64 * 256; +global TWO_POW_8: u32 = 256; +global TWO_POW_16: u32 = TWO_POW_8 * 256; +global TWO_POW_24: u32 = TWO_POW_16 * 256; +global TWO_POW_32: u64 = TWO_POW_24 as u64 * 256; // Index of a byte in a 64 byte block; ie. 0..=63 type BLOCK_BYTE_PTR = u32; diff --git a/test_programs/compile_success_empty/assert_constant/src/main.nr b/test_programs/compile_success_empty/assert_constant/src/main.nr index 978f668f611..42d66f88137 100644 --- a/test_programs/compile_success_empty/assert_constant/src/main.nr +++ b/test_programs/compile_success_empty/assert_constant/src/main.nr @@ -1,13 +1,13 @@ use std::static_assert; -global GLOBAL_ONE = 1; -global GLOBAL_TWO = 2; -global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; +global GLOBAL_ONE: Field = 1; +global GLOBAL_TWO: Field = 2; +global GLOBAL_THREE: Field = GLOBAL_ONE + GLOBAL_TWO; // contents known at compile time // length known at compile time -global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; -global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_ARRAY_PAIR: [Field; 2] = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR: [Field] = &[GLOBAL_ONE, GLOBAL_TWO]; struct Foo { field: Field, diff --git a/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr b/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr index 86b85fbc00a..45afef6d831 100644 --- a/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr +++ b/test_programs/compile_success_empty/comptime_globals_regression/src/main.nr @@ -1,4 +1,4 @@ -comptime mut global COUNTER = 0; +comptime mut global COUNTER: Field = 0; fn main() { comptime { increment() }; diff --git a/test_programs/compile_success_empty/comptime_module/src/main.nr b/test_programs/compile_success_empty/comptime_module/src/main.nr index 8114fa34555..20fd8053fbe 100644 --- a/test_programs/compile_success_empty/comptime_module/src/main.nr +++ b/test_programs/compile_success_empty/comptime_module/src/main.nr @@ -24,7 +24,7 @@ mod yet_another_module { #[outer_attribute_separate_module] mod separate_module; -comptime mut global counter = 0; +comptime mut global counter: u32 = 0; comptime fn increment_counter() { counter += 1; diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index c2eeeb37395..978a7fdf66b 100644 --- a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -1,5 +1,5 @@ // Regression that a global of the same name does not trigger a duplicate definition error -global N = 1000; +global N: u32 = 1000; fn main() { let a = id([1, 2]); diff --git a/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr b/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr index b596d331e7f..d4479ec933b 100644 --- a/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr +++ b/test_programs/compile_success_empty/parenthesized_expression_in_array_length/src/main.nr @@ -1,5 +1,5 @@ -global N = 100; -global BLOCK_SIZE = 10; +global N: u32 = 100; +global BLOCK_SIZE: u32 = 10; fn main() { let _: [Field; 110] = [0; ((N + BLOCK_SIZE) * BLOCK_SIZE) / BLOCK_SIZE]; diff --git a/test_programs/compile_success_empty/raw_string/src/main.nr b/test_programs/compile_success_empty/raw_string/src/main.nr index ad8dfe82ae5..a076a4ccfd6 100644 --- a/test_programs/compile_success_empty/raw_string/src/main.nr +++ b/test_programs/compile_success_empty/raw_string/src/main.nr @@ -1,4 +1,4 @@ -global D = r#####"Hello "world""#####; +global D: str<_> = r#####"Hello "world""#####; fn main() { let a = "Hello \"world\""; diff --git a/test_programs/compile_success_empty/static_assert/src/main.nr b/test_programs/compile_success_empty/static_assert/src/main.nr index 873efe734e1..fda310ba7eb 100644 --- a/test_programs/compile_success_empty/static_assert/src/main.nr +++ b/test_programs/compile_success_empty/static_assert/src/main.nr @@ -1,13 +1,13 @@ use std::static_assert; -global GLOBAL_ONE = 1; -global GLOBAL_TWO = 2; -global GLOBAL_THREE = GLOBAL_ONE + GLOBAL_TWO; +global GLOBAL_ONE: Field = 1; +global GLOBAL_TWO: Field = 2; +global GLOBAL_THREE: Field = GLOBAL_ONE + GLOBAL_TWO; // contents known at compile time // length known at compile time -global GLOBAL_ARRAY_PAIR = [GLOBAL_ONE, GLOBAL_TWO]; -global GLOBAL_SLICE_PAIR = &[GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_ARRAY_PAIR: [Field; 2] = [GLOBAL_ONE, GLOBAL_TWO]; +global GLOBAL_SLICE_PAIR: [Field] = &[GLOBAL_ONE, GLOBAL_TWO]; pub struct Foo { field: Field, diff --git a/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr index 11d50fc2ab5..591c03de905 100644 --- a/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr +++ b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr @@ -8,7 +8,7 @@ fn main() { comptime fn foo(_: StructDefinition) -> Quoted { quote { - global ONE = 1; - global TWO = 2; + global ONE: Field = 1; + global TWO: u32 = 2; } } diff --git a/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr b/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr index ff74c82f2ee..9b6ad264a9e 100644 --- a/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr +++ b/test_programs/compile_success_no_bug/databus_mapping_regression/src/main.nr @@ -23,8 +23,8 @@ pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec wh BoundedVec { storage: array, len } } -global TX_SIZE = 5; -global APP_CALL_SIZE = 2; +global TX_SIZE: u32 = 5; +global APP_CALL_SIZE: u32 = 2; fn main( a: call_data(0) [Field; TX_SIZE], diff --git a/test_programs/execution_success/bench_2_to_17/src/main.nr b/test_programs/execution_success/bench_2_to_17/src/main.nr index ae80dfcf0b4..204fbc38a16 100644 --- a/test_programs/execution_success/bench_2_to_17/src/main.nr +++ b/test_programs/execution_success/bench_2_to_17/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global len = 2450 * 2; +global len: u32 = 2450 * 2; fn main(x: Field) { let ped_input = [x; len]; let mut val = poseidon2::Poseidon2::hash(ped_input, len); diff --git a/test_programs/execution_success/brillig_cow/src/main.nr b/test_programs/execution_success/brillig_cow/src/main.nr index 1d4c7f3172e..2dd0d4b3411 100644 --- a/test_programs/execution_success/brillig_cow/src/main.nr +++ b/test_programs/execution_success/brillig_cow/src/main.nr @@ -1,5 +1,5 @@ // Tests the copy on write optimization for arrays. We look for cases where we are modifying an array in place when we shouldn't. -global ARRAY_SIZE = 5; +global ARRAY_SIZE: u32 = 5; struct ExecutionResult { original: [Field; ARRAY_SIZE], diff --git a/test_programs/execution_success/brillig_cow_assign/src/main.nr b/test_programs/execution_success/brillig_cow_assign/src/main.nr index 73b91e24bea..cfa228b3a96 100644 --- a/test_programs/execution_success/brillig_cow_assign/src/main.nr +++ b/test_programs/execution_success/brillig_cow_assign/src/main.nr @@ -1,4 +1,4 @@ -global N = 10; +global N: u32 = 10; unconstrained fn main() { let mut arr = [0; N]; diff --git a/test_programs/execution_success/brillig_cow_regression/src/main.nr b/test_programs/execution_success/brillig_cow_regression/src/main.nr index ad2a291f87d..69273bc3dca 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -7,7 +7,7 @@ global MAX_NEW_CONTRACTS_PER_TX: u32 = 1; global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: u32 = 1; global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: u32 = 1; global NUM_FIELDS_PER_SHA256: u32 = 2; -global TX_EFFECT_HASH_INPUT_SIZE = 169; +global TX_EFFECT_HASH_INPUT_SIZE: u32 = 169; global TX_EFFECT_HASH_LOG_FIELDS: u32 = 4; global TX_EFFECT_HASH_FULL_FIELDS: u32 = 165; diff --git a/test_programs/execution_success/fmtstr_with_global/src/main.nr b/test_programs/execution_success/fmtstr_with_global/src/main.nr index 8b9c9635015..4ca118f856f 100644 --- a/test_programs/execution_success/fmtstr_with_global/src/main.nr +++ b/test_programs/execution_success/fmtstr_with_global/src/main.nr @@ -1,4 +1,4 @@ -global FOO = 1; +global FOO: Field = 1; fn main() { println(f"foo = {FOO}"); diff --git a/test_programs/execution_success/fold_2_to_17/src/main.nr b/test_programs/execution_success/fold_2_to_17/src/main.nr index a3a747e4aee..d54dff4617a 100644 --- a/test_programs/execution_success/fold_2_to_17/src/main.nr +++ b/test_programs/execution_success/fold_2_to_17/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global len = 2450 * 2 - 240; // for just under 2^17 gates +global len: u32 = 2450 * 2 - 240; // for just under 2^17 gates fn main(x: Field) { let ped_input = [x; len]; let mut val = poseidon2::Poseidon2::hash(ped_input, len); diff --git a/test_programs/execution_success/fold_call_witness_condition/src/main.nr b/test_programs/execution_success/fold_call_witness_condition/src/main.nr index 5dc75e4a99f..5b9a5db62c5 100644 --- a/test_programs/execution_success/fold_call_witness_condition/src/main.nr +++ b/test_programs/execution_success/fold_call_witness_condition/src/main.nr @@ -1,4 +1,4 @@ -global NUM_RESULTS = 2; +global NUM_RESULTS: u32 = 2; fn main(x: Field, y: pub Field, enable: bool) -> pub [Field; NUM_RESULTS] { let mut result = [0; NUM_RESULTS]; for i in 0..NUM_RESULTS { diff --git a/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr b/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr index c5993cf6523..15b9dd26195 100644 --- a/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr +++ b/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr @@ -1,7 +1,7 @@ -use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; +use std::hash::poseidon2::Poseidon2; global NUM_HASHES: u32 = 2; -global HASH_LENGTH = 10; +global HASH_LENGTH: u32 = 10; #[fold] pub fn poseidon_hash(inputs: [Field; N]) -> Field { diff --git a/test_programs/execution_success/global_consts/src/foo.nr b/test_programs/execution_success/global_consts/src/foo.nr index 50e331493dc..2c39b534259 100644 --- a/test_programs/execution_success/global_consts/src/foo.nr +++ b/test_programs/execution_success/global_consts/src/foo.nr @@ -2,7 +2,7 @@ mod bar; global N: u32 = 5; global MAGIC_NUMBER: u32 = 3; -global TYPE_INFERRED = 42; +global TYPE_INFERRED: u32 = 42; pub fn from_foo(x: [Field; bar::N]) { for i in 0..bar::N { diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr index 30c5f7167f3..2eaab810d6a 100644 --- a/test_programs/execution_success/global_consts/src/main.nr +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -18,7 +18,7 @@ struct Test { v: Field, } global VALS: [Test; 1] = [Test { v: 100 }]; -global NESTED = [VALS, VALS]; +global NESTED: [[Test; 1]; 2] = [VALS, VALS]; unconstrained fn calculate_global_value() -> Field { 42 @@ -121,4 +121,4 @@ impl Bar { } // Regression for #1440 -global foo = Foo { a: Bar::get_a() }; +global foo: Foo = Foo { a: Bar::get_a() }; diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr index 964b900dce5..cfd4e4a9136 100644 --- a/test_programs/execution_success/hashmap/src/main.nr +++ b/test_programs/execution_success/hashmap/src/main.nr @@ -16,15 +16,15 @@ struct Entry { } global HASHMAP_CAP: u32 = 8; -global HASHMAP_LEN = 6; +global HASHMAP_LEN: u32 = 6; -global FIELD_CMP = |a: Field, b: Field| a.lt(b); +global FIELD_CMP: fn(Field, Field) -> bool = |a: Field, b: Field| a.lt(b); -global K_CMP = FIELD_CMP; -global V_CMP = FIELD_CMP; -global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); +global K_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global V_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global KV_CMP: fn((K, V), (K, V)) -> bool = |a: (K, V), b: (K, V)| a.0.lt(b.0); -global ALLOCATE_HASHMAP = +global ALLOCATE_HASHMAP: fn() -> HashMap> = || -> HashMap> HashMap::default(); fn main(input: [Entry; HASHMAP_LEN]) { diff --git a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr index aa1106132ff..82a868f3ffb 100644 --- a/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr +++ b/test_programs/execution_success/no_predicates_numeric_generic_poseidon/src/main.nr @@ -1,7 +1,7 @@ use std::hash::poseidon2::Poseidon2; global NUM_HASHES: u32 = 2; -global HASH_LENGTH = 10; +global HASH_LENGTH: u32 = 10; #[no_predicates] pub fn poseidon_hash(inputs: [Field; N]) -> Field { diff --git a/test_programs/execution_success/ram_blowup_regression/src/main.nr b/test_programs/execution_success/ram_blowup_regression/src/main.nr index 59843c368ec..6deb54dd21d 100644 --- a/test_programs/execution_success/ram_blowup_regression/src/main.nr +++ b/test_programs/execution_success/ram_blowup_regression/src/main.nr @@ -1,4 +1,4 @@ -global TX_EFFECTS_HASH_INPUT_FIELDS = 256; +global TX_EFFECTS_HASH_INPUT_FIELDS: u32 = 256; // Convert a 32 byte array to a field element by truncating the final byte pub fn field_from_bytes_32_trunc(bytes32: [u8; 32]) -> Field { diff --git a/test_programs/execution_success/regression_2660/src/main.nr b/test_programs/execution_success/regression_2660/src/main.nr index f32bc924e3a..92aa15abb43 100644 --- a/test_programs/execution_success/regression_2660/src/main.nr +++ b/test_programs/execution_success/regression_2660/src/main.nr @@ -1,4 +1,4 @@ -global foo = -1; +global foo: i32 = -1; fn main(x: i32) { let y = x + foo; diff --git a/test_programs/execution_success/regression_5252/src/main.nr b/test_programs/execution_success/regression_5252/src/main.nr index 6ab4157e7a5..5f56b7f7f35 100644 --- a/test_programs/execution_success/regression_5252/src/main.nr +++ b/test_programs/execution_success/regression_5252/src/main.nr @@ -1,7 +1,7 @@ use std::hash::{poseidon, poseidon2::Poseidon2}; -global NUM_HASHES = 3; -global HASH_LENGTH = 20; +global NUM_HASHES: u32 = 3; +global HASH_LENGTH: u32 = 20; pub fn poseidon_hash(inputs: [Field; N]) -> Field { Poseidon2::hash(inputs, inputs.len()) diff --git a/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/test_programs/execution_success/sha256_var_size_regression/src/main.nr index de1c2b23c5f..4278cdda8a3 100644 --- a/test_programs/execution_success/sha256_var_size_regression/src/main.nr +++ b/test_programs/execution_success/sha256_var_size_regression/src/main.nr @@ -1,4 +1,4 @@ -global NUM_HASHES = 2; +global NUM_HASHES: u32 = 2; fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { let mut result = [[0; 32]; NUM_HASHES]; diff --git a/test_programs/execution_success/strings/src/main.nr b/test_programs/execution_success/strings/src/main.nr index d28a9f483ac..f3e24709fc8 100644 --- a/test_programs/execution_success/strings/src/main.nr +++ b/test_programs/execution_success/strings/src/main.nr @@ -1,5 +1,5 @@ // Test global string literals -global HELLO_WORLD = "hello world"; +global HELLO_WORLD: str<_> = "hello world"; fn main(message: pub str<11>, y: Field, hex_as_string: str<4>, hex_as_field: Field) { let mut bad_message = "hello world"; diff --git a/test_programs/execution_success/struct_inputs/src/foo/bar.nr b/test_programs/execution_success/struct_inputs/src/foo/bar.nr index 6d879326677..7a79528f8ab 100644 --- a/test_programs/execution_success/struct_inputs/src/foo/bar.nr +++ b/test_programs/execution_success/struct_inputs/src/foo/bar.nr @@ -1,4 +1,4 @@ -global N = 2; +global N: Field = 2; struct barStruct { val: Field, diff --git a/test_programs/execution_success/uhashmap/src/main.nr b/test_programs/execution_success/uhashmap/src/main.nr index e917a83c5fd..b56a4fe1747 100644 --- a/test_programs/execution_success/uhashmap/src/main.nr +++ b/test_programs/execution_success/uhashmap/src/main.nr @@ -11,15 +11,15 @@ struct Entry { value: Field, } -global HASHMAP_LEN = 6; +global HASHMAP_LEN: u32 = 6; -global FIELD_CMP = |a: Field, b: Field| a.lt(b); +global FIELD_CMP: fn(Field, Field) -> bool = |a: Field, b: Field| a.lt(b); -global K_CMP = FIELD_CMP; -global V_CMP = FIELD_CMP; -global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); +global K_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global V_CMP: fn(Field, Field) -> bool = FIELD_CMP; +global KV_CMP: fn((K, V), (K, V)) -> bool = |a: (K, V), b: (K, V)| a.0.lt(b.0); -global ALLOCATE_HASHMAP = +global ALLOCATE_HASHMAP: fn() -> UHashMap> = || -> UHashMap> UHashMap::default(); unconstrained fn main(input: [Entry; HASHMAP_LEN]) { diff --git a/test_programs/test_libraries/diamond_deps_2/src/lib.nr b/test_programs/test_libraries/diamond_deps_2/src/lib.nr index 46dce3d5600..23de4d4c0f3 100644 --- a/test_programs/test_libraries/diamond_deps_2/src/lib.nr +++ b/test_programs/test_libraries/diamond_deps_2/src/lib.nr @@ -1,4 +1,4 @@ -global RESOLVE_THIS = 3; +global RESOLVE_THIS: Field = 3; pub fn call_dep2(x: Field, y: Field) -> Field { x + y From 280c27fbe0cbdc5915204af9cd4809543ce89702 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 21 Nov 2024 21:34:17 -0500 Subject: [PATCH 2/4] add types to globals in test_programs/benchmark --- test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr | 2 +- test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr | 2 +- .../bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr | 2 +- .../bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr | 2 +- test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr | 2 +- test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr | 2 +- test_programs/benchmarks/bench_sha256_100/src/main.nr | 2 +- test_programs/benchmarks/bench_sha256_30/src/main.nr | 2 +- test_programs/benchmarks/bench_sha256_long/src/main.nr | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr b/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr index 39c714e524f..66a785f446a 100644 --- a/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon2_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr b/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr index d1251a4c853..2e72ebc3519 100644 --- a/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon2_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash::poseidon2; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr index 1c9bbfe61bf..75d853941e5 100644 --- a/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr index 3edb47e9f72..d4f357e11f9 100644 --- a/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash/bench_poseidon_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr index 1c9bbfe61bf..75d853941e5 100644 --- a/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash_100/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr index 3edb47e9f72..d4f357e11f9 100644 --- a/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr +++ b/test_programs/benchmarks/bench_poseidon_hash_30/src/main.nr @@ -1,6 +1,6 @@ use std::hash; -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[Field; 2]; SIZE]) -> pub [Field; SIZE] { let mut results: [Field; SIZE] = [0; SIZE]; diff --git a/test_programs/benchmarks/bench_sha256_100/src/main.nr b/test_programs/benchmarks/bench_sha256_100/src/main.nr index 6df856a83fc..6e4bfc27c8f 100644 --- a/test_programs/benchmarks/bench_sha256_100/src/main.nr +++ b/test_programs/benchmarks/bench_sha256_100/src/main.nr @@ -1,4 +1,4 @@ -global SIZE = 100; +global SIZE: u32 = 100; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; diff --git a/test_programs/benchmarks/bench_sha256_30/src/main.nr b/test_programs/benchmarks/bench_sha256_30/src/main.nr index 220c1cfbbed..0a4288114e3 100644 --- a/test_programs/benchmarks/bench_sha256_30/src/main.nr +++ b/test_programs/benchmarks/bench_sha256_30/src/main.nr @@ -1,4 +1,4 @@ -global SIZE = 30; +global SIZE: u32 = 30; fn main(input: [[u8; 2]; SIZE]) -> pub [[u8; 32]; SIZE] { let mut results: [[u8; 32]; SIZE] = [[0; 32]; SIZE]; diff --git a/test_programs/benchmarks/bench_sha256_long/src/main.nr b/test_programs/benchmarks/bench_sha256_long/src/main.nr index 17129275371..c47bdc2a561 100644 --- a/test_programs/benchmarks/bench_sha256_long/src/main.nr +++ b/test_programs/benchmarks/bench_sha256_long/src/main.nr @@ -1,6 +1,6 @@ // Input size long enough that we have to compress a few times // and then pad the last block out. -global INPUT_SIZE = 2 * 64 + 60; +global INPUT_SIZE: u32 = 2 * 64 + 60; fn main(input: [u8; INPUT_SIZE]) -> pub [u8; 32] { std::hash::sha256(input) From dd9bd0998fcc6aaadc09555f2dbd13aca6d5e469 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 22 Nov 2024 13:55:04 -0500 Subject: [PATCH 3/4] use 'contains_unspecified' instead of is_unspecified to recursively check for Unspecified, update tests, move WILDCARD_TYPE to ast/statements where it's now used with Path --- compiler/noirc_frontend/src/ast/mod.rs | 86 ++++++++++++++----- compiler/noirc_frontend/src/ast/statement.rs | 7 ++ .../src/elaborator/statements.rs | 6 +- .../noirc_frontend/src/elaborator/types.rs | 3 +- compiler/noirc_frontend/src/tests.rs | 13 ++- .../raw_string/src/main.nr | 2 +- .../execution_success/strings/src/main.nr | 2 +- 7 files changed, 90 insertions(+), 29 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 154f29c4d7d..35e57cd4528 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -32,7 +32,6 @@ pub use traits::*; pub use type_alias::*; use crate::{ - elaborator::types::WILDCARD_TYPE, node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, @@ -161,31 +160,12 @@ pub enum UnresolvedTypeData { Error, } -impl UnresolvedTypeData { - pub(crate) fn is_unspecified(&self) -> bool { - match self { - UnresolvedTypeData::Unspecified => true, - // '_' is unspecified - UnresolvedTypeData::Named(path, _, _) => { - path.to_ident().map(|ident| ident.0.contents) == Some(WILDCARD_TYPE.to_string()) - } - _ => false, - } - } -} - #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct UnresolvedType { pub typ: UnresolvedTypeData, pub span: Span, } -impl UnresolvedType { - pub(crate) fn is_unspecified(&self) -> bool { - self.typ.is_unspecified() - } -} - /// An argument to a generic type or trait. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum GenericTypeArg { @@ -220,6 +200,14 @@ impl GenericTypeArgs { pub fn is_empty(&self) -> bool { self.ordered_args.is_empty() && self.named_args.is_empty() } + + fn contains_unspecified(&self) -> bool { + let ordered_args_contains_unspecified = + self.ordered_args.iter().any(|ordered_arg| ordered_arg.contains_unspecified()); + let named_args_contains_unspecified = + self.named_args.iter().any(|(_name, named_arg)| named_arg.contains_unspecified()); + ordered_args_contains_unspecified || named_args_contains_unspecified + } } impl From> for GenericTypeArgs { @@ -395,6 +383,10 @@ impl UnresolvedType { let typ = UnresolvedTypeData::Named(path, generic_type_args, true); UnresolvedType { typ, span } } + + pub(crate) fn contains_unspecified(&self) -> bool { + self.typ.contains_unspecified() + } } impl UnresolvedTypeData { @@ -415,6 +407,47 @@ impl UnresolvedTypeData { pub fn with_span(&self, span: Span) -> UnresolvedType { UnresolvedType { typ: self.clone(), span } } + + fn contains_unspecified(&self) -> bool { + match self { + UnresolvedTypeData::Array(typ, length) => { + typ.contains_unspecified() || length.contains_unspecified() + } + UnresolvedTypeData::Slice(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Expression(expr) => expr.contains_unspecified(), + UnresolvedTypeData::String(length) => length.contains_unspecified(), + UnresolvedTypeData::FormatString(typ, length) => { + typ.contains_unspecified() || length.contains_unspecified() + } + UnresolvedTypeData::Parenthesized(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Named(path, args, _is_synthesized) => { + // '_' is unspecified + let path_is_wildcard = path.is_wildcard(); + let an_arg_is_unresolved = args.contains_unspecified(); + path_is_wildcard || an_arg_is_unresolved + } + UnresolvedTypeData::TraitAsType(_path, args) => args.contains_unspecified(), + UnresolvedTypeData::MutableReference(typ) => typ.contains_unspecified(), + UnresolvedTypeData::Tuple(args) => args.iter().any(|arg| arg.contains_unspecified()), + UnresolvedTypeData::Function(args, ret, env, _unconstrained) => { + let args_contains_unspecified = args.iter().any(|arg| arg.contains_unspecified()); + args_contains_unspecified + || ret.contains_unspecified() + || env.contains_unspecified() + } + UnresolvedTypeData::Unspecified => true, + + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::AsTraitPath(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Interned(_) + | UnresolvedTypeData::Error => false, + } + } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)] @@ -514,6 +547,19 @@ impl UnresolvedTypeExpression { | BinaryOpKind::Modulo ) } + + fn contains_unspecified(&self) -> bool { + match self { + // '_' is unspecified + UnresolvedTypeExpression::Variable(path) => path.is_wildcard(), + UnresolvedTypeExpression::BinaryOperation(lhs, _op, rhs, _span) => { + lhs.contains_unspecified() || rhs.contains_unspecified() + } + UnresolvedTypeExpression::Constant(_, _) | UnresolvedTypeExpression::AsTraitPath(_) => { + false + } + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 7244be371af..c77fe7513a1 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -27,6 +27,9 @@ use crate::token::{SecondaryAttribute, Token}; /// for an identifier that already failed to parse. pub const ERROR_IDENT: &str = "$error"; +/// This is used to represent an UnresolvedTypeData::Unspecified in a Path +pub const WILDCARD_TYPE: &str = "_"; + #[derive(Debug, PartialEq, Eq, Clone)] pub struct Statement { pub kind: StatementKind, @@ -483,6 +486,10 @@ impl Path { self.segments.first().cloned().map(|segment| segment.ident) } + pub(crate) fn is_wildcard(&self) -> bool { + self.to_ident().map(|ident| ident.0.contents) == Some(WILDCARD_TYPE.to_string()) + } + pub fn is_empty(&self) -> bool { self.segments.is_empty() && self.kind == PathKind::Plain } diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index b7cd378ad65..6ed8fee753c 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -76,11 +76,11 @@ impl<'context> Elaborator<'context> { ) -> (HirStatement, Type) { let expr_span = let_stmt.expression.span; let (expression, expr_type) = self.elaborate_expression(let_stmt.expression); - let type_is_unspecified = let_stmt.r#type.is_unspecified(); + let type_contains_unspecified = let_stmt.r#type.contains_unspecified(); let annotated_type = self.resolve_inferred_type(let_stmt.r#type); - // Require the top-level of a global's type to be specified - if type_is_unspecified && global_id.is_some() { + // Require the top-level of a global's type to be fully-specified + if type_contains_unspecified && global_id.is_some() { let span = expr_span; let expected_type = annotated_type.clone(); let error = ResolverError::UnspecifiedGlobalType { span, expected_type }; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ae2bb942f48..7e06964b563 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -9,7 +9,7 @@ use crate::{ ast::{ AsTraitPath, BinaryOpKind, GenericTypeArgs, Ident, IntegerBitSize, Path, PathKind, Signedness, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, - UnresolvedTypeData, UnresolvedTypeExpression, + UnresolvedTypeData, UnresolvedTypeExpression, WILDCARD_TYPE, }, hir::{ comptime::{Interpreter, Value}, @@ -40,7 +40,6 @@ use crate::{ use super::{lints, path_resolution::PathResolutionItem, Elaborator}; pub const SELF_TYPE_NAME: &str = "Self"; -pub const WILDCARD_TYPE: &str = "_"; pub(super) struct TraitPathResolution { pub(super) method: TraitMethod, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index d717e5ce37c..b7e8e7ded7b 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -3254,17 +3254,26 @@ fn dont_infer_globals_to_u32_from_type_use() { } #[test] -fn infer_partial_global_types() { +fn dont_infer_partial_global_types() { let src = r#" pub global ARRAY: [Field; _] = [0; 3]; + pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; pub global STR: str<_> = "hi"; + pub global NESTED_STR: [str<_>] = &["hi"]; pub global FMT_STR: fmtstr<_, _> = f"hi {ARRAY}"; + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); fn main() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_eq!(errors.len(), 6); + for (error, _file_id) in errors { + assert!(matches!( + error, + CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) + )); + } } #[test] diff --git a/test_programs/compile_success_empty/raw_string/src/main.nr b/test_programs/compile_success_empty/raw_string/src/main.nr index a076a4ccfd6..6bed1cfecc9 100644 --- a/test_programs/compile_success_empty/raw_string/src/main.nr +++ b/test_programs/compile_success_empty/raw_string/src/main.nr @@ -1,4 +1,4 @@ -global D: str<_> = r#####"Hello "world""#####; +global D: str<13> = r#####"Hello "world""#####; fn main() { let a = "Hello \"world\""; diff --git a/test_programs/execution_success/strings/src/main.nr b/test_programs/execution_success/strings/src/main.nr index f3e24709fc8..c4fa0539745 100644 --- a/test_programs/execution_success/strings/src/main.nr +++ b/test_programs/execution_success/strings/src/main.nr @@ -1,5 +1,5 @@ // Test global string literals -global HELLO_WORLD: str<_> = "hello world"; +global HELLO_WORLD: str<11> = "hello world"; fn main(message: pub str<11>, y: Field, hex_as_string: str<4>, hex_as_field: Field) { let mut bad_message = "hello world"; From 33b4597b2e65115930f2489864590985085892d6 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 22 Nov 2024 14:55:11 -0500 Subject: [PATCH 4/4] update UnspecifiedGlobalType error message, annotate types in dependency cycle test --- .../noirc_frontend/src/hir/resolution/errors.rs | 6 +++--- compiler/noirc_frontend/src/tests.rs | 14 +++----------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 81113923f13..80bd5247ee6 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -101,7 +101,7 @@ pub enum ResolverError { JumpOutsideLoop { is_break: bool, span: Span }, #[error("Only `comptime` globals can be mutable")] MutableGlobal { span: Span }, - #[error("Globals must have a specified type (RHS inferred to have type `{expected_type}`)")] + #[error("Globals must have a specified type")] UnspecifiedGlobalType { span: Span, expected_type: Type }, #[error("Self-referential structs are not supported")] SelfReferentialStruct { span: Span }, @@ -435,8 +435,8 @@ impl<'a> From<&'a ResolverError> for Diagnostic { }, ResolverError::UnspecifiedGlobalType { span, expected_type } => { Diagnostic::simple_error( - format!("Globals must have a specified type (RHS inferred to have type `{expected_type}`)"), - String::new(), + "Globals must have a specified type".to_string(), + format!("Inferred type is `{expected_type}`"), *span, ) }, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index b7e8e7ded7b..cb45bf37c83 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1300,23 +1300,15 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { #[test] fn deny_cyclic_globals() { let src = r#" - global A = B; - global B = A; + global A: u32 = B; + global B: u32 = A; fn main() {} "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); + assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - assert!(matches!( - errors[2].0, CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) )); }