From 19cf0c321f13367b724102e6c81beb2632559b56 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 27 Aug 2024 11:10:01 -0400 Subject: [PATCH 01/45] chore: wip removing implicit numeric generics --- compiler/noirc_frontend/src/elaborator/mod.rs | 10 ++++++++-- compiler/noirc_frontend/src/hir/resolution/errors.rs | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 53b46536078..a6996a2df87 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -898,6 +898,8 @@ impl<'context> Elaborator<'context> { } for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { + // TODO: cleanup comments + // // Declare any generics to let users use numeric generics in scope. // Don't issue a warning if these are unused // @@ -907,6 +909,8 @@ impl<'context> Elaborator<'context> { if let Some(ResolvedGeneric { name, span, kind, .. }) = self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) { + // TODO: cleanup comments + // let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); if value.is_some() { @@ -921,8 +925,10 @@ impl<'context> Elaborator<'context> { } *kind = Kind::Numeric(Box::new(Type::default_int_type())); let ident = Ident::new(name.to_string(), *span); - let definition = DefinitionKind::GenericType(type_variable); - self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + + // TODO + // let definition = DefinitionKind::GenericType(type_variable); + // self.add_variable_decl_inner(ident.clone(), false, false, false, definition); self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 0aad50d13b2..12f52c37a85 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -417,8 +417,8 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ResolverError::UseExplicitNumericGeneric { ident } => { let name = &ident.0.contents; - Diagnostic::simple_warning( - String::from("Noir now supports explicit numeric generics. Support for implicit numeric generics will be removed in the following release."), + Diagnostic::simple_error( + String::from("Noir requires explicit numeric generics."), format!("Numeric generic `{name}` should now be specified with `let {name}: `"), ident.0.span(), ) From 136ef8d275a69f8ae36594183e5598d444479b98 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 28 Aug 2024 11:55:43 -0400 Subject: [PATCH 02/45] wip removing support for implicit generics, updating tests --- compiler/noirc_frontend/src/elaborator/mod.rs | 132 +++++++++--------- .../noirc_frontend/src/elaborator/types.rs | 29 ++-- compiler/noirc_frontend/src/hir_def/types.rs | 79 ++++++----- compiler/noirc_frontend/src/tests.rs | 10 +- 4 files changed, 136 insertions(+), 114 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index a6996a2df87..0d46af9be7d 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -415,7 +415,8 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), true); } - self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); + // TODO + // self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); self.add_trait_constraints_to_scope(&func_meta); let (hir_func, body_type) = match kind { @@ -891,49 +892,50 @@ impl<'context> Elaborator<'context> { } } - // TODO(https://github.com/noir-lang/noir/issues/5156): Remove implicit numeric generics - fn declare_numeric_generics(&mut self, params: &Parameters, return_type: &Type) { - if self.generics.is_empty() { - return; - } - - for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { - // TODO: cleanup comments - // - // Declare any generics to let users use numeric generics in scope. - // Don't issue a warning if these are unused - // - // We can fail to find the generic in self.generics if it is an implicit one created - // by the compiler. This can happen when, e.g. eliding array lengths using the slice - // syntax [T]. - if let Some(ResolvedGeneric { name, span, kind, .. }) = - self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) - { - // TODO: cleanup comments - // - let scope = self.scopes.get_mut_scope(); - let value = scope.find(&name_to_find); - if value.is_some() { - // With the addition of explicit numeric generics we do not want to introduce numeric generics in this manner - // However, this is going to be a big breaking change so for now we simply issue a warning while users have time - // to transition to the new syntax - // e.g. this code would break with a duplicate definition error: - // ``` - // fn foo(arr: [Field; N]) { } - // ``` - continue; - } - *kind = Kind::Numeric(Box::new(Type::default_int_type())); - let ident = Ident::new(name.to_string(), *span); - - // TODO - // let definition = DefinitionKind::GenericType(type_variable); - // self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - - self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); - } - } - } + // // TODO: cleanup + // fn declare_numeric_generics(&mut self, params: &Parameters, return_type: &Type) { + // // TODO + // // if self.generics.is_empty() { + // // return; + // // } + // // + // // for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { + // // // TODO: cleanup comments + // // // + // // // Declare any generics to let users use numeric generics in scope. + // // // Don't issue a warning if these are unused + // // // + // // // We can fail to find the generic in self.generics if it is an implicit one created + // // // by the compiler. This can happen when, e.g. eliding array lengths using the slice + // // // syntax [T]. + // // if let Some(ResolvedGeneric { name, span, kind, .. }) = + // // self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) + // // { + // // // TODO: cleanup comments + // // // + // // let scope = self.scopes.get_mut_scope(); + // // let value = scope.find(&name_to_find); + // // if value.is_some() { + // // // With the addition of explicit numeric generics we do not want to introduce numeric generics in this manner + // // // However, this is going to be a big breaking change so for now we simply issue a warning while users have time + // // // to transition to the new syntax + // // // e.g. this code would break with a duplicate definition error: + // // // ``` + // // // fn foo(arr: [Field; N]) { } + // // // ``` + // // continue; + // // } + // // *kind = Kind::Numeric(Box::new(Type::default_int_type())); + // // let ident = Ident::new(name.to_string(), *span); + // // + // // // TODO + // // // let definition = DefinitionKind::GenericType(type_variable); + // // // self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + // // + // // self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + // // } + // // } + // } fn add_trait_constraints_to_scope(&mut self, func_meta: &FuncMeta) { for constraint in &func_meta.trait_constraints { @@ -1189,27 +1191,27 @@ impl<'context> Elaborator<'context> { self.interner.update_struct(*type_id, |struct_def| { struct_def.set_fields(fields); - // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this with implicit numeric generics - // This is only necessary for resolving named types when implicit numeric generics are used. - let mut found_names = Vec::new(); - struct_def.find_numeric_generics_in_fields(&mut found_names); - for generic in struct_def.generics.iter_mut() { - for found_generic in found_names.iter() { - if found_generic == generic.name.as_str() { - if matches!(generic.kind, Kind::Normal) { - let ident = Ident::new(generic.name.to_string(), generic.span); - self.errors.push(( - CompilationError::ResolverError( - ResolverError::UseExplicitNumericGeneric { ident }, - ), - self.file, - )); - generic.kind = Kind::Numeric(Box::new(Type::default_int_type())); - } - break; - } - } - } + // // TODO cleanup + // // This is only necessary for resolving named types when implicit numeric generics are used. + // let mut found_names = Vec::new(); + // struct_def.find_numeric_generics_in_fields(&mut found_names); + // for generic in struct_def.generics.iter_mut() { + // for found_generic in found_names.iter() { + // if found_generic == generic.name.as_str() { + // if matches!(generic.kind, Kind::Normal) { + // let ident = Ident::new(generic.name.to_string(), generic.span); + // self.errors.push(( + // CompilationError::ResolverError( + // ResolverError::UseExplicitNumericGeneric { ident }, + // ), + // self.file, + // )); + // generic.kind = Kind::Numeric(Box::new(Type::default_int_type())); + // } + // break; + // } + // } + // } }); for field_index in 0..fields_len { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 44bded6b92f..ae61d86a841 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -78,11 +78,11 @@ impl<'context> Elaborator<'context> { let elem = Box::new(self.resolve_type_inner(*elem, kind)); let mut size = self.convert_expression_type(size); // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics - if let Type::NamedGeneric(type_var, name, _) = size { + if let Type::NamedGeneric(type_var, name, kind) = size { size = Type::NamedGeneric( type_var, name, - Kind::Numeric(Box::new(Type::default_int_type())), + kind, // Kind::Numeric(Box::new(Type::default_int_type())), ); } Type::Array(Box::new(size), elem) @@ -97,11 +97,11 @@ impl<'context> Elaborator<'context> { String(size) => { let mut resolved_size = self.convert_expression_type(size); // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics - if let Type::NamedGeneric(type_var, name, _) = resolved_size { + if let Type::NamedGeneric(type_var, name, kind) = resolved_size { resolved_size = Type::NamedGeneric( type_var, name, - Kind::Numeric(Box::new(Type::default_int_type())), + kind, ); } Type::String(Box::new(resolved_size)) @@ -193,12 +193,25 @@ impl<'context> Elaborator<'context> { // return Type::Error; // } if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { - let expected_typ_err = - ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; - self.push_err(expected_typ_err); + if resolved_type.kind() != kind.clone() { + + // TODO: import CompilationError + let expected_typ_err = crate::hir::def_collector::dc_crate::CompilationError::TypeError(TypeCheckError::TypeKindMismatch { + expected_kind: kind.to_string(), + expr_kind: resolved_type.kind().to_string(), + // TODO + expr_span: span, // .expect("Type should have span"), + }); + self.errors.push((expected_typ_err, self.file)); return Type::Error; } + + // if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { + // let expected_typ_err = + // ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; + // self.push_err(expected_typ_err); + // return Type::Error; + // } } resolved_type diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 807666f9af9..649b35f9579 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -207,6 +207,10 @@ impl ResolvedGeneric { pub fn as_named_generic(self) -> Type { Type::NamedGeneric(self.type_var, self.name, self.kind) } + + fn is_numeric(&self) -> bool { + matches!(self.kind, Kind::Numeric { .. }) + } } enum FunctionCoercionResult { @@ -330,8 +334,9 @@ impl StructType { /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. /// TODO(https://github.com/noir-lang/noir/issues/5156): This is outdated and we should remove this implicit searching for numeric generics pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - let target_id = self.generics[index_of_generic].type_var.id(); - self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) + self.generics[index_of_generic].is_numeric() + // let target_id = self.generics[index_of_generic].type_var.id(); + // self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) } /// Instantiate this struct type, returning a Vec of the new generic args (in @@ -418,11 +423,12 @@ impl TypeAlias { } /// True if the given index is the same index as a generic type of this alias - /// which is expected to be a numeric generic. - /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. + /// which is a numeric generic. pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - let target_id = self.generics[index_of_generic].type_var.id(); - self.typ.contains_numeric_typevar(target_id) + self.generics[index_of_generic].is_numeric() + // TODO + // let target_id = self.generics[index_of_generic].type_var.id(); + // self.typ.contains_numeric_typevar(target_id) } } @@ -904,7 +910,8 @@ impl Type { } } - /// TODO(https://github.com/noir-lang/noir/issues/5156): Remove with explicit numeric generics + // TODO(https://github.com/noir-lang/noir/issues/5156) + // TODO: fix for no implicit numeric generics pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { // Return whether the named generic has a TypeKind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { @@ -1171,35 +1178,35 @@ impl Type { } } - // TODO(https://github.com/noir-lang/noir/issues/5156): Bring back this method when we remove implicit numeric generics - // It has been commented out as to not trigger clippy for an unused method - // pub(crate) fn kind(&self) -> Kind { - // match self { - // Type::NamedGeneric(_, _, kind) => kind.clone(), - // Type::Constant(_) => Kind::Numeric(Box::new(Type::Integer( - // Signedness::Unsigned, - // IntegerBitSize::ThirtyTwo, - // ))), - // Type::FieldElement - // | Type::Array(_, _) - // | Type::Slice(_) - // | Type::Integer(_, _) - // | Type::Bool - // | Type::String(_) - // | Type::FmtString(_, _) - // | Type::Unit - // | Type::Tuple(_) - // | Type::Struct(_, _) - // | Type::Alias(_, _) - // | Type::TypeVariable(_, _) - // | Type::TraitAsType(_, _, _) - // | Type::Function(_, _, _) - // | Type::MutableReference(_) - // | Type::Forall(_, _) - // | Type::Quoted(_) - // | Type::Error => Kind::Normal, - // } - // } + // TODO: where to use this? + pub(crate) fn kind(&self) -> Kind { + match self { + Type::NamedGeneric(_, _, kind) => kind.clone(), + Type::Constant(..) => Kind::Numeric(Box::new(Type::Integer( + Signedness::Unsigned, + IntegerBitSize::ThirtyTwo, + ))), + Type::FieldElement + | Type::Array(..) + | Type::Slice(..) + | Type::Integer(..) + | Type::Bool + | Type::String(..) + | Type::FmtString(..) + | Type::Unit + | Type::Tuple(..) + | Type::Struct(..) + | Type::Alias(..) + | Type::TypeVariable(..) + | Type::TraitAsType(..) + | Type::Function(..) + | Type::MutableReference(..) + | Type::Forall(..) + | Type::Quoted(..) + | Type::InfixExpr(..) + | Type::Error => Kind::Normal, + } + } /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self) -> u32 { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index cc4aae7f447..8a88737e7c1 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1658,8 +1658,6 @@ fn normal_generic_as_array_length() { "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - // TODO(https://github.com/noir-lang/noir/issues/5156): This should be switched to a hard type error rather than - // the `UseExplicitNumericGeneric` once implicit numeric generics are removed. assert!(matches!( errors[0].0, CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), @@ -1724,8 +1722,11 @@ fn normal_generic_used_in_nested_array_length_fail() { } "#; let errors = get_program_errors(src); - // TODO(https://github.com/noir-lang/noir/issues/5156): This should be switched to a hard type error once implicit numeric generics are removed. - assert_eq!(errors.len(), 0); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + )); } #[test] @@ -1895,7 +1896,6 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { )); } -// TODO(https://github.com/noir-lang/noir/issues/5156): Remove this test once we ban implicit numeric generics #[test] fn implicit_numeric_generics_elaborator() { let src = r#" From 2fb72f0169979e10caecb1dae6a170dca05e7bcd Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 28 Aug 2024 14:59:06 -0400 Subject: [PATCH 03/45] updated tests to use explicit numeric generics, added tests to prevent implicit numeric generics, cleanup --- compiler/noirc_frontend/src/elaborator/mod.rs | 47 +--------- .../noirc_frontend/src/elaborator/types.rs | 94 +++++++++---------- compiler/noirc_frontend/src/hir_def/types.rs | 79 ++-------------- .../implicit_numeric_generics/Nargo.toml | 7 ++ .../implicit_numeric_generics/src/main.nr | 5 + .../implicit_numeric_generics_type/Nargo.toml | 7 ++ .../src/main.nr | 5 + .../closure_explicit_types/src/main.nr | 2 +- .../numeric_generics/src/main.nr | 6 +- .../trait_generics/src/main.nr | 4 +- .../execution_success/7_function/src/main.nr | 2 +- .../aes128_encrypt/src/main.nr | 2 +- .../execution_success/array_len/src/main.nr | 6 +- .../array_to_slice/src/main.nr | 2 +- .../brillig_keccak/src/main.nr | 2 +- .../execution_success/debug_logs/src/main.nr | 4 +- .../execution_success/generics/src/main.nr | 6 +- .../execution_success/hashmap/src/utils.nr | 2 +- .../execution_success/regression/src/main.nr | 4 +- .../regression_4088/src/main.nr | 2 +- .../regression_4124/src/main.nr | 2 +- .../execution_success/schnorr/src/main.nr | 4 +- .../noir_test_success/bounded_vec/src/main.nr | 2 +- 23 files changed, 103 insertions(+), 193 deletions(-) create mode 100644 test_programs/compile_failure/implicit_numeric_generics/Nargo.toml create mode 100644 test_programs/compile_failure/implicit_numeric_generics/src/main.nr create mode 100644 test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml create mode 100644 test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 0d46af9be7d..f905ba9b057 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -17,7 +17,7 @@ use crate::{ }, hir_def::{ expr::{HirCapturedVar, HirIdent}, - function::{FunctionBody, Parameters}, + function::FunctionBody, traits::TraitConstraint, types::{Generics, Kind, ResolvedGeneric}, }, @@ -892,51 +892,6 @@ impl<'context> Elaborator<'context> { } } - // // TODO: cleanup - // fn declare_numeric_generics(&mut self, params: &Parameters, return_type: &Type) { - // // TODO - // // if self.generics.is_empty() { - // // return; - // // } - // // - // // for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { - // // // TODO: cleanup comments - // // // - // // // Declare any generics to let users use numeric generics in scope. - // // // Don't issue a warning if these are unused - // // // - // // // We can fail to find the generic in self.generics if it is an implicit one created - // // // by the compiler. This can happen when, e.g. eliding array lengths using the slice - // // // syntax [T]. - // // if let Some(ResolvedGeneric { name, span, kind, .. }) = - // // self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) - // // { - // // // TODO: cleanup comments - // // // - // // let scope = self.scopes.get_mut_scope(); - // // let value = scope.find(&name_to_find); - // // if value.is_some() { - // // // With the addition of explicit numeric generics we do not want to introduce numeric generics in this manner - // // // However, this is going to be a big breaking change so for now we simply issue a warning while users have time - // // // to transition to the new syntax - // // // e.g. this code would break with a duplicate definition error: - // // // ``` - // // // fn foo(arr: [Field; N]) { } - // // // ``` - // // continue; - // // } - // // *kind = Kind::Numeric(Box::new(Type::default_int_type())); - // // let ident = Ident::new(name.to_string(), *span); - // // - // // // TODO - // // // let definition = DefinitionKind::GenericType(type_variable); - // // // self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - // // - // // self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); - // // } - // // } - // } - fn add_trait_constraints_to_scope(&mut self, func_meta: &FuncMeta) { for constraint in &func_meta.trait_constraints { let object = constraint.typ.clone(); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ae61d86a841..ec3e900e153 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -76,14 +76,21 @@ impl<'context> Elaborator<'context> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); - let mut size = self.convert_expression_type(size); + let size = self.convert_expression_type(size); // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics - if let Type::NamedGeneric(type_var, name, kind) = size { - size = Type::NamedGeneric( - type_var, - name, - kind, // Kind::Numeric(Box::new(Type::default_int_type())), - ); + if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = size { + // TODO remove + // size = Type::NamedGeneric( + // type_var, + // name, + // kind, // Kind::Numeric(Box::new(Type::default_int_type())), + // ); + + if !kind.is_numeric() { + let ident = Ident::new(name.to_string(), span); + self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + return Type::Error; + } } Type::Array(Box::new(size), elem) } @@ -95,25 +102,32 @@ impl<'context> Elaborator<'context> { Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let mut resolved_size = self.convert_expression_type(size); + let resolved_size = self.convert_expression_type(size); // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics - if let Type::NamedGeneric(type_var, name, kind) = resolved_size { - resolved_size = Type::NamedGeneric( - type_var, - name, - kind, - ); + if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_size { + // TODO remove + // resolved_size = Type::NamedGeneric( + // type_var, + // name, + // kind, + // ); + + if !kind.is_numeric() { + let ident = Ident::new(name.to_string(), span); + self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + return Type::Error; + } } Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let mut resolved_size = self.convert_expression_type(size); - if let Type::NamedGeneric(type_var, name, _) = resolved_size { - resolved_size = Type::NamedGeneric( - type_var, - name, - Kind::Numeric(Box::new(Type::default_int_type())), - ); + let resolved_size = self.convert_expression_type(size); + if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_size { + if !kind.is_numeric() { + let ident = Ident::new(name.to_string(), span); + self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + return Type::Error; + } } let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) @@ -179,39 +193,21 @@ impl<'context> Elaborator<'context> { _ => (), } - // Check that any types with a type kind match the expected type kind supplied to this function - // TODO(https://github.com/noir-lang/noir/issues/5156): make this named generic check more general with `*resolved_kind != kind` - // as implicit numeric generics still existing makes this check more challenging to enforce - // An example of a more general check that we should switch to: - // if resolved_type.kind() != kind.clone() { - // let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - // expected_kind: kind.to_string(), - // expr_kind: resolved_type.kind().to_string(), - // expr_span: span.expect("Type should have span"), - // }); - // self.errors.push((expected_typ_err, self.file)); - // return Type::Error; - // } - if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { + if let Type::NamedGeneric(_type_var, _name, resolved_kind) = &resolved_type { + assert_eq!(&resolved_type.kind(), resolved_kind); if resolved_type.kind() != kind.clone() { - // TODO: import CompilationError - let expected_typ_err = crate::hir::def_collector::dc_crate::CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - // TODO - expr_span: span, // .expect("Type should have span"), - }); + let expected_typ_err = + crate::hir::def_collector::dc_crate::CompilationError::TypeError( + TypeCheckError::TypeKindMismatch { + expected_kind: kind.to_string(), + expr_kind: resolved_type.kind().to_string(), + expr_span: span, + }, + ); self.errors.push((expected_typ_err, self.file)); return Type::Error; } - - // if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { - // let expected_typ_err = - // ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; - // self.push_err(expected_typ_err); - // return Type::Error; - // } } resolved_type diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 649b35f9579..a9f005ed9a9 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -136,6 +136,12 @@ pub enum Kind { Numeric(Box), } +impl Kind { + pub(crate) fn is_numeric(&self) -> bool { + matches!(self, Self::Numeric { .. }) + } +} + impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -209,7 +215,7 @@ impl ResolvedGeneric { } fn is_numeric(&self) -> bool { - matches!(self.kind, Kind::Numeric { .. }) + self.kind.is_numeric() } } @@ -839,77 +845,6 @@ impl Type { ) } - fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { - // True if the given type is a NamedGeneric with the target_id - let named_generic_id_matches_target = |typ: &Type| { - if let Type::NamedGeneric(type_variable, _, _) = typ { - match &*type_variable.borrow() { - TypeBinding::Bound(_) => { - unreachable!("Named generics should not be bound until monomorphization") - } - TypeBinding::Unbound(id) => target_id == *id, - } - } else { - false - } - }; - - match self { - Type::FieldElement - | Type::Integer(_, _) - | Type::Bool - | Type::Unit - | Type::Error - | Type::TypeVariable(_, _) - | Type::Constant(_) - | Type::NamedGeneric(_, _, _) - | Type::Forall(_, _) - | Type::Quoted(_) => false, - - Type::TraitAsType(_, _, generics) => { - generics.ordered.iter().any(|generic| generic.contains_numeric_typevar(target_id)) - || generics.named.iter().any(|typ| typ.typ.contains_numeric_typevar(target_id)) - } - Type::Array(length, elem) => { - elem.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) - } - Type::Slice(elem) => elem.contains_numeric_typevar(target_id), - Type::Tuple(fields) => { - fields.iter().any(|field| field.contains_numeric_typevar(target_id)) - } - Type::Function(parameters, return_type, env, _unconstrained) => { - parameters.iter().any(|parameter| parameter.contains_numeric_typevar(target_id)) - || return_type.contains_numeric_typevar(target_id) - || env.contains_numeric_typevar(target_id) - } - Type::Struct(struct_type, generics) => { - generics.iter().enumerate().any(|(i, generic)| { - if named_generic_id_matches_target(generic) { - struct_type.borrow().generic_is_numeric(i) - } else { - generic.contains_numeric_typevar(target_id) - } - }) - } - Type::Alias(alias, generics) => generics.iter().enumerate().any(|(i, generic)| { - if named_generic_id_matches_target(generic) { - alias.borrow().generic_is_numeric(i) - } else { - generic.contains_numeric_typevar(target_id) - } - }), - Type::MutableReference(element) => element.contains_numeric_typevar(target_id), - Type::String(length) => named_generic_id_matches_target(length), - Type::FmtString(length, elements) => { - elements.contains_numeric_typevar(target_id) - || named_generic_id_matches_target(length) - } - Type::InfixExpr(lhs, _op, rhs) => { - lhs.contains_numeric_typevar(target_id) || rhs.contains_numeric_typevar(target_id) - } - } - } - // TODO(https://github.com/noir-lang/noir/issues/5156) // TODO: fix for no implicit numeric generics pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { diff --git a/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml b/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml new file mode 100644 index 00000000000..e2e79e2065c --- /dev/null +++ b/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "implicit_numeric_generics" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/implicit_numeric_generics/src/main.nr b/test_programs/compile_failure/implicit_numeric_generics/src/main.nr new file mode 100644 index 00000000000..be45c7443ec --- /dev/null +++ b/test_programs/compile_failure/implicit_numeric_generics/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + foo([]) +} + +fn foo(bytes: [u8; N]) { } diff --git a/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml b/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml new file mode 100644 index 00000000000..a2fd2fcf069 --- /dev/null +++ b/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "implicit_numeric_generics_type" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr b/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr new file mode 100644 index 00000000000..56303411283 --- /dev/null +++ b/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr @@ -0,0 +1,5 @@ +struct Foo { + inner: [Field; N], +} + +fn main() { } diff --git a/test_programs/compile_success_empty/closure_explicit_types/src/main.nr b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr index b6c8a6b7b3c..c43fc9cb34b 100644 --- a/test_programs/compile_success_empty/closure_explicit_types/src/main.nr +++ b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr @@ -35,7 +35,7 @@ fn add_results(f1: fn[Env1]() -> Field, f2: fn[Env2]() -> Field) -> f1() + f2() } // a *really* generic function -fn map(arr: [T; N], f: fn[Env](T) -> U) -> [U; N] { +fn map(arr: [T; N], f: fn[Env](T) -> U) -> [U; N] { let first_elem = f(arr[0]); let mut ret = [first_elem; N]; diff --git a/test_programs/compile_success_empty/numeric_generics/src/main.nr b/test_programs/compile_success_empty/numeric_generics/src/main.nr index 340c18c2a1d..04f170aef58 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -14,15 +14,15 @@ fn main() { assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); } -fn id(x: [Field; I]) -> [Field; I] { +fn id(x: [Field; I]) -> [Field; I] { x } -struct MyStruct { +struct MyStruct { data: [Field; S], } -impl MyStruct { +impl MyStruct { fn insert(mut self: Self, index: Field, elem: Field) -> Self { // Regression test for numeric generics on impls assert(index as u64 < S as u64); diff --git a/test_programs/compile_success_empty/trait_generics/src/main.nr b/test_programs/compile_success_empty/trait_generics/src/main.nr index 15591f2f2ea..77309ca4bee 100644 --- a/test_programs/compile_success_empty/trait_generics/src/main.nr +++ b/test_programs/compile_success_empty/trait_generics/src/main.nr @@ -44,13 +44,13 @@ impl Serializable<2> for Data { } } -fn sum(data: T) -> Field where T: Serializable { +fn sum(data: T) -> Field where T: Serializable { let serialized = data.serialize(); serialized.fold(0, |acc, elem| acc + elem) } // Test static trait method syntax -fn sum_static(data: T) -> Field where T: Serializable { +fn sum_static(data: T) -> Field where T: Serializable { let serialized = Serializable::serialize(data); serialized.fold(0, |acc, elem| acc + elem) } diff --git a/test_programs/execution_success/7_function/src/main.nr b/test_programs/execution_success/7_function/src/main.nr index 95568dd4ccd..a866567d722 100644 --- a/test_programs/execution_success/7_function/src/main.nr +++ b/test_programs/execution_success/7_function/src/main.nr @@ -82,7 +82,7 @@ fn test_multiple6(a: my2, b: my_struct, c: (my2, my_struct)) { assert(c.0.aa.a == c.1.a); } -fn foo(a: [Field; N]) -> [Field; N] { +fn foo(a: [Field; N]) -> [Field; N] { a } diff --git a/test_programs/execution_success/aes128_encrypt/src/main.nr b/test_programs/execution_success/aes128_encrypt/src/main.nr index 9cf07841b9e..b937c801860 100644 --- a/test_programs/execution_success/aes128_encrypt/src/main.nr +++ b/test_programs/execution_success/aes128_encrypt/src/main.nr @@ -8,7 +8,7 @@ unconstrained fn decode_ascii(ascii: u8) -> u8 { } } -unconstrained fn decode_hex(s: str) -> [u8; M] { +unconstrained fn decode_hex(s: str) -> [u8; M] { let mut result: [u8; M] = [0; M]; let as_bytes = s.as_bytes(); for i in 0..N { diff --git a/test_programs/execution_success/array_len/src/main.nr b/test_programs/execution_success/array_len/src/main.nr index 45c09b8a282..877a9007b1f 100644 --- a/test_programs/execution_success/array_len/src/main.nr +++ b/test_programs/execution_success/array_len/src/main.nr @@ -1,12 +1,12 @@ -fn len_plus_1(array: [T; N]) -> u32 { +fn len_plus_1(array: [T; N]) -> u32 { array.len() + 1 } -fn add_lens(a: [T; N], b: [Field; M]) -> u32 { +fn add_lens(a: [T; N], b: [Field; M]) -> u32 { a.len() + b.len() } -fn nested_call(b: [Field; N]) -> u32 { +fn nested_call(b: [Field; N]) -> u32 { len_plus_1(b) } diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index 0d0f9562d7b..3ca8bfff2ae 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -1,5 +1,5 @@ // Converts an array into a slice. -fn as_slice_push(xs: [T; N]) -> [T] { +fn as_slice_push(xs: [T; N]) -> [T] { let mut slice = &[]; for elem in xs { slice = slice.push_back(elem); diff --git a/test_programs/execution_success/brillig_keccak/src/main.nr b/test_programs/execution_success/brillig_keccak/src/main.nr index dd339208659..9674ed92942 100644 --- a/test_programs/execution_success/brillig_keccak/src/main.nr +++ b/test_programs/execution_success/brillig_keccak/src/main.nr @@ -21,6 +21,6 @@ fn main(x: Field, result: [u8; 32]) { } } -unconstrained fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { +unconstrained fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { std::hash::keccak256(data, msg_len) } diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index c7fd01ebbc5..d1314406068 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -79,11 +79,11 @@ fn string_identity(string: fmtstr<14, (Field, Field)>) -> fmtstr<14, (Field, Fie string } -fn string_with_generics(string: fmtstr) -> fmtstr { +fn string_with_generics(string: fmtstr) -> fmtstr { string } -fn string_with_partial_generics(string: fmtstr) -> fmtstr { +fn string_with_partial_generics(string: fmtstr) -> fmtstr { string } diff --git a/test_programs/execution_success/generics/src/main.nr b/test_programs/execution_success/generics/src/main.nr index 75a7f8a3154..329759caea0 100644 --- a/test_programs/execution_success/generics/src/main.nr +++ b/test_programs/execution_success/generics/src/main.nr @@ -8,11 +8,11 @@ fn foo(bar: Bar) { assert(bar.one == bar.two); } -struct BigInt { +struct BigInt { limbs: [u32; N], } -impl BigInt { +impl BigInt { // `N` is in scope of all methods in the impl fn first(first: BigInt, second: BigInt) -> Self { assert(first.limbs != second.limbs); @@ -69,7 +69,7 @@ fn main(x: Field, y: Field) { let _ = regression_2055([1, 2, 3]); } -fn regression_2055(bytes: [u8; LEN]) -> Field { +fn regression_2055(bytes: [u8; LEN]) -> Field { let mut f = 0; let mut b = 1; let mut len = LEN - 1; // FAILS diff --git a/test_programs/execution_success/hashmap/src/utils.nr b/test_programs/execution_success/hashmap/src/utils.nr index ee73245a902..de6c78f5adf 100644 --- a/test_programs/execution_success/hashmap/src/utils.nr +++ b/test_programs/execution_success/hashmap/src/utils.nr @@ -1,5 +1,5 @@ // Compile-time: cuts the M first elements from the BoundedVec. -pub(crate) fn cut(input: BoundedVec) -> [T; M] { +pub(crate) fn cut(input: BoundedVec) -> [T; M] { assert(M < N, "M should be less than N."); let mut new = BoundedVec::new(); diff --git a/test_programs/execution_success/regression/src/main.nr b/test_programs/execution_success/regression/src/main.nr index e60791aface..e6226de29ef 100644 --- a/test_programs/execution_success/regression/src/main.nr +++ b/test_programs/execution_success/regression/src/main.nr @@ -20,7 +20,7 @@ impl Eq for U4 { } } -fn compact_decode(input: [u8; N], length: Field) -> ([U4; NIBBLE_LENGTH], Field) { +fn compact_decode(input: [u8; N], length: Field) -> ([U4; NIBBLE_LENGTH], Field) { assert(2 * input.len() <= NIBBLE_LENGTH); assert(length as u32 <= input.len()); @@ -53,7 +53,7 @@ fn compact_decode(input: [u8; N], length: Field) -> ([U4; NIBBLE_LENGTH], Fie out } -fn enc(value: [u8; N], value_length: Field) -> ([u8; 32], Field) { +fn enc(value: [u8; N], value_length: Field) -> ([u8; 32], Field) { assert(value.len() as u8 >= value_length as u8); let mut out_value = [0; 32]; if value_length == 0 { diff --git a/test_programs/execution_success/regression_4088/src/main.nr b/test_programs/execution_success/regression_4088/src/main.nr index 12a7afca68c..b2a050b5db3 100644 --- a/test_programs/execution_success/regression_4088/src/main.nr +++ b/test_programs/execution_success/regression_4088/src/main.nr @@ -16,7 +16,7 @@ fn check(serialized_note: [Field; N]) { assert(serialized_note[0] == 0); } -fn oopsie(note: Note) where Note: Serialize { +fn oopsie(note: Note) where Note: Serialize { let serialized_note = Note::serialize(note); check(serialized_note) diff --git a/test_programs/execution_success/regression_4124/src/main.nr b/test_programs/execution_success/regression_4124/src/main.nr index 7b5060062da..b0e1a394c32 100644 --- a/test_programs/execution_success/regression_4124/src/main.nr +++ b/test_programs/execution_success/regression_4124/src/main.nr @@ -24,7 +24,7 @@ impl PublicMutable { PublicMutable { storage_slot } } - pub fn read(_self: Self) -> T where T: MyDeserialize { + pub fn read(_self: Self) -> T where T: MyDeserialize { // storage_read returns slice here let fields: [Field; T_SERIALIZED_LEN] = storage_read(); T::deserialize(fields) diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index cf22fd371d1..12e10367b0a 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -36,7 +36,7 @@ fn main( // Meanwhile, you have to use a message with 32 additional bytes: // If you want to verify a signature on a message of 10 bytes, you need to pass a message of length 42, // where the first 10 bytes are the one from the original message (the other bytes are not used) -pub fn verify_signature_noir(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) -> bool { +pub fn verify_signature_noir(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) -> bool { let N = message.len() - 32; //scalar lo/hi from bytes @@ -85,7 +85,7 @@ pub fn bytes_to_scalar(bytes: [u8; 64], offset: u32) -> embedded_curve_ops::Embe sig_s } -pub fn assert_valid_signature(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) { +pub fn assert_valid_signature(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) { let N = message.len() - 32; //scalar lo/hi from bytes let sig_s = bytes_to_scalar(signature, 0); diff --git a/test_programs/noir_test_success/bounded_vec/src/main.nr b/test_programs/noir_test_success/bounded_vec/src/main.nr index e5aa5f88a94..b7339eab8cd 100644 --- a/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -52,7 +52,7 @@ fn test_vec_get_unchecked() { } // docs:start:get_unchecked_example -fn sum_of_first_three(v: BoundedVec) -> u32 { +fn sum_of_first_three(v: BoundedVec) -> u32 { // Always ensure the length is larger than the largest // index passed to get_unchecked assert(v.len() > 2); From 0ec783cea634288434d0a1bc3de24e998b407304 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 28 Aug 2024 15:15:33 -0400 Subject: [PATCH 04/45] removing TODO's and wip comments, removing implicit numeric generics examples from docs, adding small explanation of explicit numeric generics --- .../noirc_frontend/src/elaborator/types.rs | 30 ++++--------------- compiler/noirc_frontend/src/hir_def/types.rs | 9 ------ docs/docs/noir/concepts/generics.md | 28 +++++++++++------ docs/docs/noir/concepts/globals.md | 2 +- docs/docs/noir/concepts/traits.md | 2 +- .../cryptographic_primitives/hashes.mdx | 2 +- 6 files changed, 28 insertions(+), 45 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ec3e900e153..323ef889976 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -12,6 +12,7 @@ use crate::{ }, hir::{ comptime::{Interpreter, Value}, + def_collector::dc_crate::CompilationError, def_map::ModuleDefId, resolution::errors::ResolverError, type_check::{ @@ -77,15 +78,7 @@ impl<'context> Elaborator<'context> { Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); let size = self.convert_expression_type(size); - // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = size { - // TODO remove - // size = Type::NamedGeneric( - // type_var, - // name, - // kind, // Kind::Numeric(Box::new(Type::default_int_type())), - // ); - if !kind.is_numeric() { let ident = Ident::new(name.to_string(), span); self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); @@ -103,15 +96,7 @@ impl<'context> Elaborator<'context> { Bool => Type::Bool, String(size) => { let resolved_size = self.convert_expression_type(size); - // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this once we only have explicit numeric generics if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_size { - // TODO remove - // resolved_size = Type::NamedGeneric( - // type_var, - // name, - // kind, - // ); - if !kind.is_numeric() { let ident = Ident::new(name.to_string(), span); self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); @@ -196,15 +181,12 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(_type_var, _name, resolved_kind) = &resolved_type { assert_eq!(&resolved_type.kind(), resolved_kind); if resolved_type.kind() != kind.clone() { - // TODO: import CompilationError let expected_typ_err = - crate::hir::def_collector::dc_crate::CompilationError::TypeError( - TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - expr_span: span, - }, - ); + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { + expected_kind: kind.to_string(), + expr_kind: resolved_type.kind().to_string(), + expr_span: span, + }); self.errors.push((expected_typ_err, self.file)); return Type::Error; } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a9f005ed9a9..9e6baec301f 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -338,11 +338,8 @@ impl StructType { /// True if the given index is the same index as a generic type of this struct /// which is expected to be a numeric generic. /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. - /// TODO(https://github.com/noir-lang/noir/issues/5156): This is outdated and we should remove this implicit searching for numeric generics pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { self.generics[index_of_generic].is_numeric() - // let target_id = self.generics[index_of_generic].type_var.id(); - // self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) } /// Instantiate this struct type, returning a Vec of the new generic args (in @@ -432,9 +429,6 @@ impl TypeAlias { /// which is a numeric generic. pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { self.generics[index_of_generic].is_numeric() - // TODO - // let target_id = self.generics[index_of_generic].type_var.id(); - // self.typ.contains_numeric_typevar(target_id) } } @@ -845,8 +839,6 @@ impl Type { ) } - // TODO(https://github.com/noir-lang/noir/issues/5156) - // TODO: fix for no implicit numeric generics pub fn find_numeric_type_vars(&self, found_names: &mut Vec) { // Return whether the named generic has a TypeKind::Numeric and save its name let named_generic_is_numeric = |typ: &Type, found_names: &mut Vec| { @@ -1113,7 +1105,6 @@ impl Type { } } - // TODO: where to use this? pub(crate) fn kind(&self) -> Kind { match self { Type::NamedGeneric(_, _, kind) => kind.clone(), diff --git a/docs/docs/noir/concepts/generics.md b/docs/docs/noir/concepts/generics.md index 3e416eee093..5538d98173d 100644 --- a/docs/docs/noir/concepts/generics.md +++ b/docs/docs/noir/concepts/generics.md @@ -122,15 +122,8 @@ fn main() { let array = slice.as_array::<2>(); } ``` -```rust -fn double() -> u32 { - N * 2 -} -fn example() { - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); -} -``` + + ```rust trait MyTrait { fn ten() -> Self; @@ -161,3 +154,20 @@ fn example() { assert(10 as u32 == foo.generic_method::()); } ``` + +### Numeric Generics + +Numeric generics are specified using the `let NAME: TYPE` syntax, and are +required when a generic `N` is used for the size of an array (`[T; N]`) or string (`str`): + +```rust +fn double() -> u32 { + N * 2 +} + +fn example() { + assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); +} +``` + diff --git a/docs/docs/noir/concepts/globals.md b/docs/docs/noir/concepts/globals.md index 063a3d89248..97a92a86e72 100644 --- a/docs/docs/noir/concepts/globals.md +++ b/docs/docs/noir/concepts/globals.md @@ -37,7 +37,7 @@ global T = foo(T); // dependency error If they are initialized to a literal integer, globals can be used to specify an array's length: ```rust -global N: Field = 2; +global N: u32 = 2; fn main(y : [Field; N]) { assert(y[0] == y[1]) diff --git a/docs/docs/noir/concepts/traits.md b/docs/docs/noir/concepts/traits.md index 597c62c737c..c134c37a1e0 100644 --- a/docs/docs/noir/concepts/traits.md +++ b/docs/docs/noir/concepts/traits.md @@ -153,7 +153,7 @@ will implement `Foo` only for types that also implement `Bar`. This is often use For example, here is the implementation for array equality: ```rust -impl Eq for [T; N] where T: Eq { +impl Eq for [T; let N: u32] where T: Eq { // Test if two arrays have the same elements. // Because both arrays must have length N, we know their lengths already match. fn eq(self, other: Self) -> bool { diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index 6ff47a77df9..d2a8204bccb 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -144,7 +144,7 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field otherwise, use the `mimc_bn254` method: ```rust -fn mimc_bn254(array: [Field; N]) -> Field +fn mimc_bn254(array: [Field; N]) -> Field ``` example: From 776d19402e7b4518faf3c057ab6857e6c1676543 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 28 Aug 2024 16:22:37 -0400 Subject: [PATCH 05/45] debug noirc_frontend tests --- compiler/noirc_frontend/src/tests.rs | 76 ++++++++++++++++++---------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 21bc6dfea5b..1227378b083 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -14,6 +14,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; +use crate::ast::IntegerBitSize; use crate::hir::comptime::InterpreterError; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; @@ -1330,12 +1331,15 @@ fn for_loop_over_array() { } "#; let errors = get_program_errors(src); - assert_eq!(get_program_errors(src).len(), 1); - + assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }) )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }) + )); } // Regression for #4545 @@ -1639,7 +1643,7 @@ fn numeric_generic_in_function_signature() { } #[test] -fn numeric_generic_as_struct_field_type() { +fn numeric_generic_as_struct_field_type_fails() { let src = r#" struct Foo { a: Field, @@ -1650,7 +1654,7 @@ fn numeric_generic_as_struct_field_type() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } @@ -1680,31 +1684,32 @@ fn numeric_generic_as_param_type() { "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 3); + // Error from the parameter type assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); // Error from the let statement annotated type assert!(matches!( errors[1].0, - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); // Error from the return type assert!(matches!( errors[2].0, - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } #[test] -fn numeric_generic_used_in_nested_type_fail() { +fn numeric_generic_used_in_nested_type_fails() { let src = r#" struct Foo { a: Field, b: Bar, } - struct Bar { + struct Bar { inner: N } "#; @@ -1712,7 +1717,7 @@ fn numeric_generic_used_in_nested_type_fail() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } @@ -1731,7 +1736,7 @@ fn normal_generic_used_in_nested_array_length_fail() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } @@ -1876,9 +1881,10 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); + assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); let src = r#" @@ -1895,9 +1901,18 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 3); assert!(matches!( errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + )); + // N + assert!(matches!( + errors[2].0, CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), )); } @@ -1905,14 +1920,12 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { #[test] fn implicit_numeric_generics_elaborator() { let src = r#" - struct BoundedVec { + struct BoundedVec { storage: [T; MaxLen], len: u64, } - impl BoundedVec { - - // Test that we have an implicit numeric generic for "Len" as well as "MaxLen" + impl BoundedVec { pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { // We do this to avoid an unused variable warning on `self` let _ = self.len; @@ -1927,17 +1940,24 @@ fn implicit_numeric_generics_elaborator() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - - for error in errors.iter() { - if let CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident }) = - &errors[0].0 - { - assert!(matches!(ident.0.contents.as_str(), "MaxLen" | "Len")); - } else { - panic!("Expected ResolverError::UseExplicitNumericGeneric but got {:?}", error); - } - } + assert_eq!(errors.len(), 3); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + )); + // N + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::IntegerBitWidth { + bit_width_x: IntegerBitSize::SixtyFour, + bit_width_y: IntegerBitSize::ThirtyTwo, + .. + }), + )); } #[test] From b331952fa7be448753e1c38ab84972a2bcca9d08 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Thu, 29 Aug 2024 12:26:13 -0400 Subject: [PATCH 06/45] Update compiler/noirc_frontend/src/elaborator/mod.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/elaborator/mod.rs | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 6fefadc9e56..cb0c6582ceb 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1142,27 +1142,6 @@ impl<'context> Elaborator<'context> { self.interner.update_struct(*type_id, |struct_def| { struct_def.set_fields(fields); - // // TODO cleanup - // // This is only necessary for resolving named types when implicit numeric generics are used. - // let mut found_names = Vec::new(); - // struct_def.find_numeric_generics_in_fields(&mut found_names); - // for generic in struct_def.generics.iter_mut() { - // for found_generic in found_names.iter() { - // if found_generic == generic.name.as_str() { - // if matches!(generic.kind, Kind::Normal) { - // let ident = Ident::new(generic.name.to_string(), generic.span); - // self.errors.push(( - // CompilationError::ResolverError( - // ResolverError::UseExplicitNumericGeneric { ident }, - // ), - // self.file, - // )); - // generic.kind = Kind::Numeric(Box::new(Type::default_int_type())); - // } - // break; - // } - // } - // } }); for field_index in 0..fields_len { From 484694e99d809a1c697d0784c9d451c1d6918463 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Thu, 29 Aug 2024 12:26:21 -0400 Subject: [PATCH 07/45] Update compiler/noirc_frontend/src/elaborator/mod.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/elaborator/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index cb0c6582ceb..e9f815f84cc 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -415,8 +415,6 @@ impl<'context> Elaborator<'context> { self.add_existing_variable_to_scope(name, parameter.clone(), true); } - // TODO - // self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); self.add_trait_constraints_to_scope(&func_meta); let (hir_func, body_type) = match kind { From 8d9ae0b7181967e4dabe8a96172b84a1dfaf28e7 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Thu, 29 Aug 2024 13:54:56 -0400 Subject: [PATCH 08/45] Update compiler/noirc_frontend/src/elaborator/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/elaborator/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index a1801caff7d..aa953064336 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -184,7 +184,7 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(_type_var, _name, resolved_kind) = &resolved_type { assert_eq!(&resolved_type.kind(), resolved_kind); - if resolved_type.kind() != kind.clone() { + if resolved_type.kind() != *kind { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), From a1538e5d9313b1ebd77dd73b8f08963bb3432f65 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 14:00:46 -0400 Subject: [PATCH 09/45] remove generic_is_numeric methods --- compiler/noirc_frontend/src/elaborator/types.rs | 4 ++-- compiler/noirc_frontend/src/hir_def/types.rs | 15 +-------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index aa953064336..7e24ea83c5e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1719,7 +1719,7 @@ impl<'context> Elaborator<'context> { Type::Struct(struct_type, generics) => { for (i, generic) in generics.iter().enumerate() { if let Type::NamedGeneric(type_variable, name, _) = generic { - if struct_type.borrow().generic_is_numeric(i) { + if struct_type.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } } else { @@ -1730,7 +1730,7 @@ impl<'context> Elaborator<'context> { Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { if let Type::NamedGeneric(type_variable, name, _) = generic { - if alias.borrow().generic_is_numeric(i) { + if alias.borrow().generics[i].is_numeric() { found.insert(name.to_string(), type_variable.clone()); } } else { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index d0f7a7e5e9f..0b18877f3c8 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -216,7 +216,7 @@ impl ResolvedGeneric { Type::NamedGeneric(self.type_var, self.name, self.kind) } - fn is_numeric(&self) -> bool { + pub(crate) fn is_numeric(&self) -> bool { self.kind.is_numeric() } } @@ -337,13 +337,6 @@ impl StructType { } } - /// True if the given index is the same index as a generic type of this struct - /// which is expected to be a numeric generic. - /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. - pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - self.generics[index_of_generic].is_numeric() - } - /// Instantiate this struct type, returning a Vec of the new generic args (in /// the same order as self.generics) pub fn instantiate(&self, interner: &mut NodeInterner) -> Vec { @@ -426,12 +419,6 @@ impl TypeAlias { self.typ.substitute(&substitutions) } - - /// True if the given index is the same index as a generic type of this alias - /// which is a numeric generic. - pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - self.generics[index_of_generic].is_numeric() - } } /// A shared, mutable reference to some T. From 7011bca8df5351509c4dc4fa79bbca0a666b8306 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 14:56:06 -0400 Subject: [PATCH 10/45] move generic kind check to convert_expression_type, update test for additional error thrown --- .../src/elaborator/expressions.rs | 2 +- .../noirc_frontend/src/elaborator/types.rs | 48 +++++++------------ compiler/noirc_frontend/src/tests.rs | 8 +++- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index beede7a3a30..1b2a1ddad2c 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -189,7 +189,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeExpression::Constant(0, span) }); - let length = self.convert_expression_type(length); + let length = self.convert_expression_type(length, span); let (repeated_element, elem_type) = self.elaborate_expression(*repeated_element); let length_clone = length.clone(); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 7e24ea83c5e..36b8e9cd83f 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -77,43 +77,22 @@ impl<'context> Elaborator<'context> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); - let size = self.convert_expression_type(size); - if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = size { - if !kind.is_numeric() { - let ident = Ident::new(name.to_string(), span); - self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); - return Type::Error; - } - } + let size = self.convert_expression_type(size, span); Type::Array(Box::new(size), elem) } Slice(elem) => { let elem = Box::new(self.resolve_type_inner(*elem, kind)); Type::Slice(elem) } - Expression(expr) => self.convert_expression_type(expr), + Expression(expr) => self.convert_expression_type(expr, span), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let resolved_size = self.convert_expression_type(size); - if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_size { - if !kind.is_numeric() { - let ident = Ident::new(name.to_string(), span); - self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); - return Type::Error; - } - } + let resolved_size = self.convert_expression_type(size, span); Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size); - if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_size { - if !kind.is_numeric() { - let ident = Ident::new(name.to_string(), span); - self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); - return Type::Error; - } - } + let resolved_size = self.convert_expression_type(size, span); let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } @@ -431,19 +410,28 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn convert_expression_type(&mut self, length: UnresolvedTypeExpression) -> Type { + pub(super) fn convert_expression_type(&mut self, length: UnresolvedTypeExpression, span: Span) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { - self.lookup_generic_or_global_type(&path).unwrap_or_else(|| { + let resolved_length = self.lookup_generic_or_global_type(&path).unwrap_or_else(|| { self.push_err(ResolverError::NoSuchNumericTypeVariable { path }); Type::Constant(0) - }) + }); + + if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_length { + if !kind.is_numeric() { + let ident = Ident::new(name.to_string(), span); + self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + return Type::Error; + } + } + resolved_length } UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); - let lhs = self.convert_expression_type(*lhs); - let rhs = self.convert_expression_type(*rhs); + let lhs = self.convert_expression_type(*lhs, lhs_span); + let rhs = self.convert_expression_type(*rhs, rhs_span); match (lhs, rhs) { (Type::Constant(lhs), Type::Constant(rhs)) => { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1227378b083..5c39d19b0d8 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1901,7 +1901,7 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); + assert_eq!(errors.len(), 4); assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), @@ -1910,9 +1910,13 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { errors[1].0, CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), )); - // N assert!(matches!( errors[2].0, + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + )); + // N + assert!(matches!( + errors[3].0, CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), )); } From 197f037f8a03554116bce3fb0533a874130735ef Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 15:01:35 -0400 Subject: [PATCH 11/45] update array/slice/generics docs with explicit numeric generics, move numeric generics section earlier in generics docs --- docs/docs/noir/concepts/data_types/slices.mdx | 2 +- docs/docs/noir/concepts/generics.md | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index a0c87c29259..cfee564a302 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -175,7 +175,7 @@ Make sure to specify the size of the resulting array. Panics if the resulting array length is different than the slice's length. ```rust -fn as_array(self) -> [T; N] +fn as_array(self) -> [T; N] ``` Example: diff --git a/docs/docs/noir/concepts/generics.md b/docs/docs/noir/concepts/generics.md index 5538d98173d..cc6e51f53fd 100644 --- a/docs/docs/noir/concepts/generics.md +++ b/docs/docs/noir/concepts/generics.md @@ -18,6 +18,23 @@ fn id(x: T) -> T { } ``` +## Numeric Generics + +Numeric generics are specified using the `let NAME: TYPE` syntax, and are +required when a generic `N` is used for the size of an array (`[T; N]`) or string (`str`): + +```rust +fn double() -> u32 { + N * 2 +} + +fn example() { + assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); +} +``` + + ## In Structs Generics are useful for specifying types in structs. For example, we can specify that a field in a @@ -51,11 +68,11 @@ integers at compile-time, rather than resolving to types. Here's an example of a generic over the size of the array it contains internally: ```rust -struct BigInt { +struct BigInt { limbs: [u32; N], } -impl BigInt { +impl BigInt { // `N` is in scope of all methods in the impl fn first(first: BigInt, second: BigInt) -> Self { assert(first.limbs != second.limbs); @@ -77,7 +94,7 @@ This is what [traits](../concepts/traits.md) are for in Noir. Here's an example any type `T` that implements the `Eq` trait for equality: ```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool where T: Eq { if (array1.len() == 0) | (array2.len() == 0) { @@ -155,19 +172,3 @@ fn example() { } ``` -### Numeric Generics - -Numeric generics are specified using the `let NAME: TYPE` syntax, and are -required when a generic `N` is used for the size of an array (`[T; N]`) or string (`str`): - -```rust -fn double() -> u32 { - N * 2 -} - -fn example() { - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); -} -``` - From 93e33b83edd579ed7087403f18cd53d05e35260b Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 15:18:19 -0400 Subject: [PATCH 12/45] use TypeKindMismatch instead of UseExplicitNumericGeneric --- compiler/noirc_frontend/src/elaborator/types.rs | 9 ++++++--- compiler/noirc_frontend/src/hir/resolution/errors.rs | 11 ----------- compiler/noirc_frontend/src/tests.rs | 9 ++++----- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 36b8e9cd83f..36213b8687e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -418,10 +418,13 @@ impl<'context> Elaborator<'context> { Type::Constant(0) }); - if let Type::NamedGeneric(ref _type_var, ref name, ref kind) = resolved_length { + if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { - let ident = Ident::new(name.to_string(), span); - self.push_err(ResolverError::UseExplicitNumericGeneric { ident }); + self.push_err(TypeCheckError::TypeKindMismatch { + expected_kind: Kind::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))).to_string(), + expr_kind: kind.to_string(), + expr_span: span, + }); return Type::Error; } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 67413906bcc..a44f26acf94 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -102,8 +102,6 @@ pub enum ResolverError { FoldAttributeOnUnconstrained { ident: Ident }, #[error("The only supported types of numeric generics are integers, fields, and booleans")] UnsupportedNumericGenericType { ident: Ident, typ: Type }, - #[error("Numeric generics should be explicit")] - UseExplicitNumericGeneric { ident: Ident }, #[error("expected type, found numeric generic parameter")] NumericGenericUsedForType { name: String, span: Span }, #[error("Invalid array length construction")] @@ -427,15 +425,6 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ident.0.span(), ) } - ResolverError::UseExplicitNumericGeneric { ident } => { - let name = &ident.0.contents; - - Diagnostic::simple_error( - String::from("Noir requires explicit numeric generics."), - format!("Numeric generic `{name}` should now be specified with `let {name}: `"), - ident.0.span(), - ) - } ResolverError::NumericGenericUsedForType { name, span } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 5c39d19b0d8..d148864af86 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1334,7 +1334,7 @@ fn for_loop_over_array() { assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }) + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) )); assert!(matches!( errors[1].0, @@ -1670,7 +1670,7 @@ fn normal_generic_as_array_length() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } @@ -1881,7 +1881,6 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); - assert!(matches!( errors[0].0, CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), @@ -1908,11 +1907,11 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { )); assert!(matches!( errors[1].0, - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); assert!(matches!( errors[2].0, - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }), + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); // N assert!(matches!( From a75631b20f494fe50caad15acd9d21feb0d77e9a Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 16:09:16 -0400 Subject: [PATCH 13/45] cargo clippy/fmt --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 - .../noirc_frontend/src/elaborator/types.rs | 21 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 353b02a99eb..042bd5b47a6 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1144,7 +1144,6 @@ impl<'context> Elaborator<'context> { let fields_len = fields.len(); self.interner.update_struct(*type_id, |struct_def| { struct_def.set_fields(fields); - }); for field_index in 0..fields_len { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 36213b8687e..2cb59ce7b09 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -410,18 +410,27 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn convert_expression_type(&mut self, length: UnresolvedTypeExpression, span: Span) -> Type { + pub(super) fn convert_expression_type( + &mut self, + length: UnresolvedTypeExpression, + span: Span, + ) -> Type { match length { UnresolvedTypeExpression::Variable(path) => { - let resolved_length = self.lookup_generic_or_global_type(&path).unwrap_or_else(|| { - self.push_err(ResolverError::NoSuchNumericTypeVariable { path }); - Type::Constant(0) - }); + let resolved_length = + self.lookup_generic_or_global_type(&path).unwrap_or_else(|| { + self.push_err(ResolverError::NoSuchNumericTypeVariable { path }); + Type::Constant(0) + }); if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: Kind::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))).to_string(), + expected_kind: Kind::Numeric(Box::new(Type::Integer( + Signedness::Unsigned, + IntegerBitSize::ThirtyTwo, + ))) + .to_string(), expr_kind: kind.to_string(), expr_span: span, }); From 6ee01d2f04fc0392fc37f3bcc34fe577b704f1b6 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 29 Aug 2024 16:48:07 -0400 Subject: [PATCH 14/45] ./test_programs/format.sh --- test_programs/execution_success/schnorr/src/main.nr | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index 12e10367b0a..48235f51754 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -36,7 +36,11 @@ fn main( // Meanwhile, you have to use a message with 32 additional bytes: // If you want to verify a signature on a message of 10 bytes, you need to pass a message of length 42, // where the first 10 bytes are the one from the original message (the other bytes are not used) -pub fn verify_signature_noir(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) -> bool { +pub fn verify_signature_noir( + public_key: embedded_curve_ops::EmbeddedCurvePoint, + signature: [u8; 64], + message: [u8; M] +) -> bool { let N = message.len() - 32; //scalar lo/hi from bytes @@ -85,7 +89,11 @@ pub fn bytes_to_scalar(bytes: [u8; 64], offset: u32) -> embedded_curve_ops::Embe sig_s } -pub fn assert_valid_signature(public_key: embedded_curve_ops::EmbeddedCurvePoint, signature: [u8; 64], message: [u8; M]) { +pub fn assert_valid_signature( + public_key: embedded_curve_ops::EmbeddedCurvePoint, + signature: [u8; 64], + message: [u8; M] +) { let N = message.len() - 32; //scalar lo/hi from bytes let sig_s = bytes_to_scalar(signature, 0); From f1a9b951480c16c8e4db91cd33d9086c08e9ed75 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 3 Sep 2024 12:40:08 -0400 Subject: [PATCH 15/45] add test for #5447 --- .../compile_failure/type_kind_mismatch/Nargo.toml | 7 +++++++ .../type_kind_mismatch/src/main.nr | 15 +++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test_programs/compile_failure/type_kind_mismatch/Nargo.toml create mode 100644 test_programs/compile_failure/type_kind_mismatch/src/main.nr diff --git a/test_programs/compile_failure/type_kind_mismatch/Nargo.toml b/test_programs/compile_failure/type_kind_mismatch/Nargo.toml new file mode 100644 index 00000000000..27874d17838 --- /dev/null +++ b/test_programs/compile_failure/type_kind_mismatch/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_kind_mismatch" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/type_kind_mismatch/src/main.nr b/test_programs/compile_failure/type_kind_mismatch/src/main.nr new file mode 100644 index 00000000000..e5c5cd7efdb --- /dev/null +++ b/test_programs/compile_failure/type_kind_mismatch/src/main.nr @@ -0,0 +1,15 @@ +fn foo() -> u64 { + N as u64 +} + +global J: u64 = 10; + +fn bar() -> u64 { + foo::() +} + +global M: u64 = 3; + +fn main() { + let _ = bar::(); +} From ea8a61b61217ae7510076f2330eb7099cdc36fd5 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 3 Sep 2024 12:58:57 -0400 Subject: [PATCH 16/45] use explicit numeric generics in noir_codegen test --- tooling/noir_codegen/test/test_lib/src/lib.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/noir_codegen/test/test_lib/src/lib.nr b/tooling/noir_codegen/test/test_lib/src/lib.nr index 4915b0a2c49..4229de06157 100644 --- a/tooling/noir_codegen/test/test_lib/src/lib.nr +++ b/tooling/noir_codegen/test/test_lib/src/lib.nr @@ -1,10 +1,10 @@ -struct MyStruct { +struct MyStruct { foo: bool, bar: [str; BAR_SIZE], baz: Field } -struct NestedStruct { +struct NestedStruct { foo: MyStruct, bar: [MyStruct; BAR_SIZE], baz: BAZ_TYP From 543c7134fdfd718d9407b8f1f5d6fbe9230b079c Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 9 Sep 2024 16:21:02 -0400 Subject: [PATCH 17/45] move type_kind_mismatch and implicit_numeric_generics_type compile_failure tests to noirc_frontend unit tests, fix test name and avoid redundant match on Type::NamedGeneric --- .../noirc_frontend/src/elaborator/types.rs | 20 +++++------- compiler/noirc_frontend/src/tests.rs | 31 +++++++++++++++++-- .../implicit_numeric_generics_type/Nargo.toml | 7 ----- .../src/main.nr | 5 --- .../type_kind_mismatch/Nargo.toml | 7 ----- .../type_kind_mismatch/src/main.nr | 15 --------- 6 files changed, 37 insertions(+), 48 deletions(-) delete mode 100644 test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml delete mode 100644 test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr delete mode 100644 test_programs/compile_failure/type_kind_mismatch/Nargo.toml delete mode 100644 test_programs/compile_failure/type_kind_mismatch/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index e900ac34601..e44219cba2a 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -161,18 +161,14 @@ impl<'context> Elaborator<'context> { _ => (), } - if let Type::NamedGeneric(_type_var, _name, resolved_kind) = &resolved_type { - assert_eq!(&resolved_type.kind(), resolved_kind); - if resolved_type.kind() != *kind { - let expected_typ_err = - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { - expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), - expr_span: span, - }); - self.errors.push((expected_typ_err, self.file)); - return Type::Error; - } + if resolved_type.kind() != *kind { + let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { + expected_kind: kind.to_string(), + expr_kind: resolved_type.kind().to_string(), + expr_span: span, + }); + self.errors.push((expected_typ_err, self.file)); + return Type::Error; } resolved_type diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index d1ba491683b..59c9bc0af09 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1938,13 +1938,40 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } #[test] -fn implicit_numeric_generics_elaborator() { +fn implicit_numeric_generics_type_kind_mismatch() { + let src = r#" + fn foo() -> u16 { + N as u16 + } + + global J: u16 = 10; + + fn bar() -> u16 { + foo::() + } + + global M: u16 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn implicit_numeric_generics_value_kind_mismatch() { let src = r#" struct BoundedVec { storage: [T; MaxLen], len: u64, } - + impl BoundedVec { pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { // We do this to avoid an unused variable warning on `self` diff --git a/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml b/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml deleted file mode 100644 index a2fd2fcf069..00000000000 --- a/test_programs/compile_failure/implicit_numeric_generics_type/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "implicit_numeric_generics_type" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr b/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr deleted file mode 100644 index 56303411283..00000000000 --- a/test_programs/compile_failure/implicit_numeric_generics_type/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -struct Foo { - inner: [Field; N], -} - -fn main() { } diff --git a/test_programs/compile_failure/type_kind_mismatch/Nargo.toml b/test_programs/compile_failure/type_kind_mismatch/Nargo.toml deleted file mode 100644 index 27874d17838..00000000000 --- a/test_programs/compile_failure/type_kind_mismatch/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "type_kind_mismatch" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/type_kind_mismatch/src/main.nr b/test_programs/compile_failure/type_kind_mismatch/src/main.nr deleted file mode 100644 index e5c5cd7efdb..00000000000 --- a/test_programs/compile_failure/type_kind_mismatch/src/main.nr +++ /dev/null @@ -1,15 +0,0 @@ -fn foo() -> u64 { - N as u64 -} - -global J: u64 = 10; - -fn bar() -> u64 { - foo::() -} - -global M: u64 = 3; - -fn main() { - let _ = bar::(); -} From dae8ce72c3cc8920be44c7bca57fd0f311ee6eb9 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 9 Sep 2024 18:40:58 -0400 Subject: [PATCH 18/45] Update compiler/noirc_frontend/src/tests.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 59c9bc0af09..383ec68d0bb 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1965,7 +1965,7 @@ fn implicit_numeric_generics_type_kind_mismatch() { } #[test] -fn implicit_numeric_generics_value_kind_mismatch() { +fn numeric_generics_value_kind_mismatch() { let src = r#" struct BoundedVec { storage: [T; MaxLen], From b8690f9655c854ad37ce184b326acf9a7e5703a8 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 9 Sep 2024 18:41:07 -0400 Subject: [PATCH 19/45] Update compiler/noirc_frontend/src/tests.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 383ec68d0bb..67cf0a948f6 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1996,7 +1996,6 @@ fn numeric_generics_value_kind_mismatch() { errors[1].0, CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), )); - // N assert!(matches!( errors[2].0, CompilationError::TypeError(TypeCheckError::IntegerBitWidth { From be8f23551e7d6a5c296aa2bb33f9d62004a35680 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Mon, 9 Sep 2024 18:41:24 -0400 Subject: [PATCH 20/45] Update compiler/noirc_frontend/src/tests.rs Co-authored-by: Maxim Vezenov --- compiler/noirc_frontend/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 67cf0a948f6..5ad9afd412c 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1938,7 +1938,7 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } #[test] -fn implicit_numeric_generics_type_kind_mismatch() { +fn numeric_generics_type_kind_mismatch() { let src = r#" fn foo() -> u16 { N as u16 From 84c6e38ed6976e7f3aaf75d8daac31dfec31f04a Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 11 Sep 2024 01:49:05 -0400 Subject: [PATCH 21/45] remove redundant test, restore regression unit test, clarify error in type-kind mismatch tests, include other cases in type-kind mismatch tests --- compiler/noirc_frontend/src/tests.rs | 64 +++++++++++++------ .../implicit_numeric_generics/Nargo.toml | 7 -- .../implicit_numeric_generics/src/main.nr | 5 -- 3 files changed, 43 insertions(+), 33 deletions(-) delete mode 100644 test_programs/compile_failure/implicit_numeric_generics/Nargo.toml delete mode 100644 test_programs/compile_failure/implicit_numeric_generics/src/main.nr diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index ed47c657c55..cf574e6340f 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1338,7 +1338,7 @@ fn break_and_continue_outside_loop() { #[test] fn for_loop_over_array() { let src = r#" - fn hello(_array: [u1; N]) { + fn hello(_array: [u1; N]) { for _ in 0..N {} } @@ -1348,15 +1348,7 @@ fn for_loop_over_array() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }) - )); + assert_eq!(errors.len(), 0); } // Regression for #4545 @@ -1965,15 +1957,17 @@ fn numeric_generics_type_kind_mismatch() { } #[test] -fn numeric_generics_value_kind_mismatch() { +fn numeric_generics_value_kind_mismatch_u32_u64() { let src = r#" struct BoundedVec { storage: [T; MaxLen], + // can't be compared to MaxLen: u32 + // can't be used to index self.storage len: u64, } impl BoundedVec { - pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { + pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { // We do this to avoid an unused variable warning on `self` let _ = self.len; for _ in 0..Len { } @@ -1987,17 +1981,9 @@ fn numeric_generics_value_kind_mismatch() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); + assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); - assert!(matches!( - errors[2].0, CompilationError::TypeError(TypeCheckError::IntegerBitWidth { bit_width_x: IntegerBitSize::SixtyFour, bit_width_y: IntegerBitSize::ThirtyTwo, @@ -2006,6 +1992,42 @@ fn numeric_generics_value_kind_mismatch() { )); } +#[test] +fn numeric_generics_value_kind_mismatch_field_u32() { + let src = r#" + struct BoundedVec { + storage: [T; MaxLen], + // can't be compared to MaxLen: u32 + // can't be used to index self.storage + len: u32, + } + + impl BoundedVec { + pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { + // We do this to avoid an unused variable warning on `self` + let _ = self.len; + for _ in 0..Len { } + } + + pub fn push(&mut self, elem: T) { + assert(self.len < MaxLen, "push out of bounds"); + self.storage[self.len] = elem; + self.len += 1; + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + #[test] fn quote_code_fragments() { // This test ensures we can quote (and unquote/splice) code fragments diff --git a/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml b/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml deleted file mode 100644 index e2e79e2065c..00000000000 --- a/test_programs/compile_failure/implicit_numeric_generics/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "implicit_numeric_generics" -type = "bin" -authors = [""] -compiler_version = ">=0.33.0" - -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/implicit_numeric_generics/src/main.nr b/test_programs/compile_failure/implicit_numeric_generics/src/main.nr deleted file mode 100644 index be45c7443ec..00000000000 --- a/test_programs/compile_failure/implicit_numeric_generics/src/main.nr +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - foo([]) -} - -fn foo(bytes: [u8; N]) { } From ee458aa63158e578271ae9b004e0a87234195eb4 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Wed, 11 Sep 2024 12:34:42 -0400 Subject: [PATCH 22/45] deduplicating docs --- docs/docs/noir/concepts/generics.md | 51 ++++++++++------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/docs/docs/noir/concepts/generics.md b/docs/docs/noir/concepts/generics.md index 84e8ac8700a..8925666aa20 100644 --- a/docs/docs/noir/concepts/generics.md +++ b/docs/docs/noir/concepts/generics.md @@ -20,21 +20,30 @@ fn id(x: T) -> T { ## Numeric Generics -Numeric generics are specified using the `let NAME: TYPE` syntax, and are -required when a generic `N` is used for the size of an array (`[T; N]`) or string (`str`): +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks similar to using regular generics, but introducing them into scope +requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal +generic is declared. Instead of types, these generics resolve to integers at compile-time. +Here's an example of a struct that is generic over the size of the array it contains internally: ```rust -fn double() -> u32 { - N * 2 +struct BigInt { + limbs: [u32; N], } -fn example() { - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } } ``` - ## In Structs Generics are useful for specifying types in structs. For example, we can specify that a field in a @@ -62,32 +71,6 @@ fn main() { The `print` function will print `Hello!` an arbitrary number of times, twice in this case. -## Numeric Generics - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks similar to using regular generics, but introducing them into scope -requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal -generic is declared. Instead of types, these generics resolve to integers at compile-time. -Here's an example of a struct that is generic over the size of the array it contains internally: - -```rust -struct BigInt { - limbs: [u32; N], -} - -impl BigInt { - // `N` is in scope of all methods in the impl - fn first(first: BigInt, second: BigInt) -> Self { - assert(first.limbs != second.limbs); - first - - fn second(first: BigInt, second: Self) -> Self { - assert(first.limbs != second.limbs); - second - } -} -``` - ## Calling functions on generic parameters Since a generic type `T` can represent any type, how can we call functions on the underlying type? From 43ee2ccc8eee03f90e5a532cf7f20ca4295892e6 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 11 Sep 2024 13:13:40 -0400 Subject: [PATCH 23/45] remove redundant test --- compiler/noirc_frontend/src/tests.rs | 36 ---------------------------- 1 file changed, 36 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index cf574e6340f..2bc5655fa24 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1992,42 +1992,6 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { )); } -#[test] -fn numeric_generics_value_kind_mismatch_field_u32() { - let src = r#" - struct BoundedVec { - storage: [T; MaxLen], - // can't be compared to MaxLen: u32 - // can't be used to index self.storage - len: u32, - } - - impl BoundedVec { - pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { - // We do this to avoid an unused variable warning on `self` - let _ = self.len; - for _ in 0..Len { } - } - - pub fn push(&mut self, elem: T) { - assert(self.len < MaxLen, "push out of bounds"); - self.storage[self.len] = elem; - self.len += 1; - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - #[test] fn quote_code_fragments() { // This test ensures we can quote (and unquote/splice) code fragments From 626fe197d2c5dfb88a002f090bf5b9a4bf82a0e6 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 12 Sep 2024 16:21:06 -0400 Subject: [PATCH 24/45] wip debugging failing kind errors, remove unused constant_variable function, update Type::kind to return None when the kind is unknown, make None: Option be treated as any Kind, use explicitly-Field kind for macros_in_comptime test, annotate associated types with ': u32' --- .../noirc_frontend/src/elaborator/types.rs | 11 ++++- compiler/noirc_frontend/src/hir_def/types.rs | 42 ++++++++++++++----- .../macros_in_comptime/src/main.nr | 5 ++- .../serialize/src/main.nr | 6 +-- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ea069fc1f3c..bdd855827ef 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -169,10 +169,14 @@ impl<'context> Elaborator<'context> { _ => (), } - if resolved_type.kind() != *kind { + // When Type::kind() is None, it matches every Kind + if resolved_type.kind().map(|resolved_type_kind| resolved_type_kind != *kind).unwrap_or(false) { + + // TODO cleanup + dbg!("resolve_type_inner", &resolved_type, format!("{:?}", &resolved_type), &resolved_type.kind(), &kind, matches!(&resolved_type, Type::Constant(..))); let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().to_string(), + expr_kind: resolved_type.kind().as_ref().map(Kind::to_string).unwrap_or("unknown".to_string()), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -429,6 +433,9 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { + + // TODO: cleanup + dbg!("convert_expression_type", &kind); self.push_err(TypeCheckError::TypeKindMismatch { expected_kind: Kind::Numeric(Box::new(Type::Integer( Signedness::Unsigned, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index b232abf40b7..c4ea1429dea 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -769,9 +769,11 @@ impl Type { pub fn type_variable(id: TypeVariableId) -> Type { let var = TypeVariable::unbound(id); - Type::TypeVariable(var, TypeVariableKind::Normal) + let type_var_kind = TypeVariableKind::Normal; + Type::TypeVariable(var, type_var_kind) } + // TODO: constant_variable along with TypeVariableKind::Constant are unused /// Returns a TypeVariable(_, TypeVariableKind::Constant(length)) to bind to /// a constant integer for e.g. an array length. pub fn constant_variable(length: u32, interner: &mut NodeInterner) -> Type { @@ -1111,13 +1113,35 @@ impl Type { } } - pub(crate) fn kind(&self) -> Kind { + pub(crate) fn kind(&self) -> Option { match self { - Type::NamedGeneric(_, _, kind) => kind.clone(), - Type::Constant(..) => Kind::Numeric(Box::new(Type::Integer( - Signedness::Unsigned, - IntegerBitSize::ThirtyTwo, - ))), + Type::NamedGeneric(_, _, kind) => Some(kind.clone()), + Type::Constant(..) + | Type::TypeVariable(..) => None, + + // TODO: ensure kinds for InfixExpr are checked, e.g. + // + // Type::InfixExpr(lhs, _, rhs) => + // { + // let lhs_kind_opt = lhs.kind(); + // let rhs_kind_opt = rhs.kind(); + // if let Some(lhs_kind) = lhs_kind_opt { + // if let Some(rhs_kind) = rhs_kind_opt { + // if lhs_kind == rhs_kind { + // Some(lhs_kind) + // } else { + // fail with TypeKindMismatch or + // Kind::Numeric(Box::new(Type::Error)) + // } + // } else { + // Some(lhs_kind) + // } + // } else { + // rhs_kind + // } + // } + Type::InfixExpr(..) => None, + Type::FieldElement | Type::Array(..) | Type::Slice(..) @@ -1129,14 +1153,12 @@ impl Type { | Type::Tuple(..) | Type::Struct(..) | Type::Alias(..) - | Type::TypeVariable(..) | Type::TraitAsType(..) | Type::Function(..) | Type::MutableReference(..) | Type::Forall(..) | Type::Quoted(..) - | Type::InfixExpr(..) - | Type::Error => Kind::Normal, + | Type::Error => Some(Kind::Normal), } } diff --git a/test_programs/compile_success_empty/macros_in_comptime/src/main.nr b/test_programs/compile_success_empty/macros_in_comptime/src/main.nr index c5cc7880112..197f4e87f0b 100644 --- a/test_programs/compile_success_empty/macros_in_comptime/src/main.nr +++ b/test_programs/compile_success_empty/macros_in_comptime/src/main.nr @@ -1,11 +1,14 @@ use std::field::modulus_num_bits; use std::meta::unquote; +// Numeric generics default to u32 +global three_field: Field = 3; + fn main() { comptime { unsafe { - foo::<3>(5) + foo::(5) }; submodule::bar(); } diff --git a/test_programs/compile_success_empty/serialize/src/main.nr b/test_programs/compile_success_empty/serialize/src/main.nr index 79114c5b567..1e9cd05aaa2 100644 --- a/test_programs/compile_success_empty/serialize/src/main.nr +++ b/test_programs/compile_success_empty/serialize/src/main.nr @@ -7,7 +7,7 @@ trait Serialize { impl Serialize for (A, B) where A: Serialize, B: Serialize { // let Size = ::Size + ::Size; - let Size = AS + BS; + let Size: u32 = AS + BS; fn serialize(self: Self) -> [Field; Self::Size] { let mut array: [Field; Self::Size] = std::mem::zeroed(); @@ -26,7 +26,7 @@ impl Serialize for (A, B) where A: Serialize Serialize for [T; N] where T: Serialize { // let Size = ::Size * N; - let Size = TS * N; + let Size: u32 = TS * N; fn serialize(self: Self) -> [Field; Self::Size] { let mut array: [Field; Self::Size] = std::mem::zeroed(); @@ -46,7 +46,7 @@ impl Serialize for [T; N] where T: Serialize [Field; Self::Size] { [self] From 25be3a03d225fc45a534349ac4e523d316088e3f Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 12 Sep 2024 16:24:48 -0400 Subject: [PATCH 25/45] cargo clippy/fmt --- .../noirc_frontend/src/elaborator/types.rs | 23 +++++++++++++++---- compiler/noirc_frontend/src/hir_def/types.rs | 3 +-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index bdd855827ef..737162deff8 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -170,13 +170,27 @@ impl<'context> Elaborator<'context> { } // When Type::kind() is None, it matches every Kind - if resolved_type.kind().map(|resolved_type_kind| resolved_type_kind != *kind).unwrap_or(false) { - + if resolved_type + .kind() + .map(|resolved_type_kind| resolved_type_kind != *kind) + .unwrap_or(false) + { // TODO cleanup - dbg!("resolve_type_inner", &resolved_type, format!("{:?}", &resolved_type), &resolved_type.kind(), &kind, matches!(&resolved_type, Type::Constant(..))); + dbg!( + "resolve_type_inner", + &resolved_type, + format!("{:?}", &resolved_type), + &resolved_type.kind(), + &kind, + matches!(&resolved_type, Type::Constant(..)) + ); let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), - expr_kind: resolved_type.kind().as_ref().map(Kind::to_string).unwrap_or("unknown".to_string()), + expr_kind: resolved_type + .kind() + .as_ref() + .map(Kind::to_string) + .unwrap_or("unknown".to_string()), expr_span: span, }); self.errors.push((expected_typ_err, self.file)); @@ -433,7 +447,6 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { - // TODO: cleanup dbg!("convert_expression_type", &kind); self.push_err(TypeCheckError::TypeKindMismatch { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c4ea1429dea..bde866dc119 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1116,8 +1116,7 @@ impl Type { pub(crate) fn kind(&self) -> Option { match self { Type::NamedGeneric(_, _, kind) => Some(kind.clone()), - Type::Constant(..) - | Type::TypeVariable(..) => None, + Type::Constant(..) | Type::TypeVariable(..) => None, // TODO: ensure kinds for InfixExpr are checked, e.g. // From bd4d51598c298c814ab662bec2b4f5291b21e5f6 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 12 Sep 2024 17:20:36 -0400 Subject: [PATCH 26/45] wip debugging kind mismatch errors: Type::Constant needs kind information now --- .../noirc_frontend/src/elaborator/types.rs | 12 + .../noirc_frontend/src/parser/parser.rs:28:9 | 45 - compiler/noirc_frontend/src/tests.rs | 6713 +++++++++-------- .../src/tests/name_shadowing.rs | 831 +- 4 files changed, 3787 insertions(+), 3814 deletions(-) delete mode 100644 compiler/noirc_frontend/src/parser/parser.rs:28:9 diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 737162deff8..9464c147e80 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -169,6 +169,9 @@ impl<'context> Elaborator<'context> { _ => (), } + // TODO cleanup + dbg!("resolve_type_inner", &resolved_type, &kind); + // When Type::kind() is None, it matches every Kind if resolved_type .kind() @@ -426,6 +429,15 @@ impl<'context> Elaborator<'context> { let reference_location = Location::new(path.span(), self.file); self.interner.add_global_reference(id, reference_location); + // TODO cleanup + dbg!( + "lookup_generic_or_global_type", + &path, + self.eval_global_as_array_length(id, path), + self.interner.get_global_let_statement(id), + self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), + ); + panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); Some(Type::Constant(self.eval_global_as_array_length(id, path))) } _ => None, diff --git a/compiler/noirc_frontend/src/parser/parser.rs:28:9 b/compiler/noirc_frontend/src/parser/parser.rs:28:9 deleted file mode 100644 index 47dfb32b53b..00000000000 --- a/compiler/noirc_frontend/src/parser/parser.rs:28:9 +++ /dev/null @@ -1,45 +0,0 @@ -[?1049h[?1h[?2004h[?2026$p[?u[?12h[?25h[?25l(B[38:2:235:219:178m[48:2:168:153:132m [No Name]  (B[38:2:168:153:132m(B[38:2:235:219:178m (B[38:2:80:73:69m(B[38:2:168:153:132m[48:2:80:73:69m buffers -(B[38:2:124:111:100m 1 (B[38:2:235:219:178m -(B[38:2:80:73:69m~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -~ -(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:235:219:178m[48:2:168:153:132mNORMAL(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:168:153:132m[48:2:80:73:69m  jf/quoted-as-type (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m (B[38:2:60:56:54m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m  (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:80:73:69m(B[38:2:235:219:178m[48:2:168:153:132m 100% (B[38:2:235:219:178m[48:2:168:153:132m☰ 0/1 (B[38:2:235:219:178m[48:2:168:153:132m : 1 (B[38:2:254:128:25m[48:2:168:153:132m(B[38:2:235:219:178mNVIM v0.10.0Nvim is open source and freely distributablehttps://neovim.io/#chattype :help nvim(B[38:2:80:73:69m(B[38:2:235:219:178m if you are new! type :checkhealth(B[38:2:80:73:69m(B[38:2:235:219:178m to optimize Nvimtype :q(B[38:2:80:73:69m(B[38:2:235:219:178m to exit type :help(B[38:2:80:73:69m(B[38:2:235:219:178m for help type :help news(B[38:2:80:73:69m(B[38:2:235:219:178m to see changes in v0.10Help poor children in Uganda!type :help iccf(B[38:2:80:73:69m(B[38:2:235:219:178m for information ]112[2 q]112[2 q[?1002h[?1006h(B[38:2:235:219:178m[48:2:168:153:132m [No Name]  (B[38:2:168:153:132m(B[38:2:235:219:178m (B[38:2:80:73:69m(B[38:2:168:153:132m[48:2:80:73:69m buffers -(B[38:2:124:111:100m 1 (B[38:2:235:219:178m -(B[38:2:80:73:69m~ -~ -~ (B[38:2:235:219:178mNVIM v0.10.0(B[38:2:80:73:69m -~ -~ (B[38:2:235:219:178mNvim is open source and freely distributable(B[38:2:80:73:69m -~ (B[38:2:235:219:178mhttps://neovim.io/#chat(B[38:2:80:73:69m -~ -~ (B[38:2:235:219:178mtype :help nvim(B[38:2:80:73:69m(B[38:2:235:219:178m if you are new! (B[38:2:80:73:69m -~ (B[38:2:235:219:178mtype :checkhealth(B[38:2:80:73:69m(B[38:2:235:219:178m to optimize Nvim(B[38:2:80:73:69m -~ (B[38:2:235:219:178mtype :q(B[38:2:80:73:69m(B[38:2:235:219:178m to exit (B[38:2:80:73:69m -~ (B[38:2:235:219:178mtype :help(B[38:2:80:73:69m(B[38:2:235:219:178m for help (B[38:2:80:73:69m -~ -~ (B[38:2:235:219:178mtype :help news(B[38:2:80:73:69m(B[38:2:235:219:178m to see changes in v0.10(B[38:2:80:73:69m -~ -~ (B[38:2:235:219:178mHelp poor children in Uganda!(B[38:2:80:73:69m -~ (B[38:2:235:219:178mtype :help iccf(B[38:2:80:73:69m(B[38:2:235:219:178m for information (B[38:2:80:73:69m -~ -~ -~ -~ -(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:235:219:178m[48:2:168:153:132mNORMAL(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:168:153:132m[48:2:80:73:69m  jf/quoted-as-type (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m (B[38:2:60:56:54m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m  (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:80:73:69m(B[38:2:235:219:178m[48:2:168:153:132m 100% (B[38:2:235:219:178m[48:2:168:153:132m☰ 0/1 (B[38:2:235:219:178m[48:2:168:153:132m : 1 (B[38:2:254:128:25m[48:2:168:153:132m(B[38:2:235:219:178m[?12h[?25h[?25l[?1004h[?12h[?25h \ No newline at end of file diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 2bc5655fa24..9613c840fcc 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -125,3375 +125,3380 @@ fn assert_no_errors(src: &str) { } } -#[test] -fn check_trait_implemented_for_all_t() { - let src = " - trait Default { - fn default() -> Self; - } - - trait Eq { - fn eq(self, other: Self) -> bool; - } - - trait IsDefault { - fn is_default(self) -> bool; - } - - impl IsDefault for T where T: Default + Eq { - fn is_default(self) -> bool { - self.eq(T::default()) - } - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - impl Default for u64 { - fn default() -> Self { - 0 - } - } - - impl Default for Foo { - fn default() -> Self { - Foo { a: Default::default() } - } - } - - fn main(a: Foo) -> pub bool { - a.is_default() - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_implementation_duplicate_method() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Field; - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - impl Default for Foo { - // Duplicate trait methods should not compile - fn default(x: Field, y: Field) -> Field { - y + 2 * x - } - // Duplicate trait methods should not compile - fn default(x: Field, y: Field) -> Field { - x + 2 * y - } - } - - fn main() {}"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_return_type() { - let src = " - trait Default { - fn default() -> Self; - } - - struct Foo { - } - - impl Default for Foo { - fn default() -> Field { - 0 - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_return_type2() { - let src = " - 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() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_span: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_missing_implementation() { - let src = " - 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() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_span: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_not_in_scope() { - let src = " - 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() { - } - - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_method_name() { - let src = " - trait Default { - } - - struct Foo { - bar: Field, - array: [Field; 2], - } - - // wrong trait name method should not compile - impl Default for Foo { - fn does_not_exist(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() { - }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for (err, _file_id) in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter() { - let src = " - trait Default { - fn default(x: Field) -> Self; - } - - struct Foo { - bar: u32, - } - - impl Default for Foo { - fn default(x: u32) -> Self { - Foo {bar: x} - } - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter2() { - let src = " - 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() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameter_type() { - let src = " - trait Default { - fn default(x: Field, y: NotAType) -> Field; - } - - fn main(x: Field, y: Field) { - assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_wrong_parameters_count() { - let src = " - 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() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_impl_for_non_type() { - let src = " - trait Default { - fn default(x: Field, y: Field) -> Field; - } - - impl Default for main { - fn default(x: Field, y: Field) -> Field { - x + y - } - } - - fn main() {} - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(expected, "type"); - assert_eq!(got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_impl_struct_not_trait() { - let src = " - struct Foo { - bar: Field, - array: [Field; 2], - } - - struct Default { - x: Field, - z: Field, - } - - // Default is a struct not a trait - impl Default for Foo { - fn default(x: Field, y: Field) -> Self { - Self { bar: x, array: [x,y] } - } - } - - fn main() {} - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_declaration() { - let src = " - 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] } - } - } - - - trait Default { - fn default(x: Field) -> Self; - } - - fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_implementation() { - let src = " - trait Default { - } - struct Foo { - bar: Field, - } - - impl Default for Foo { - } - impl Default for Foo { - } - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn check_trait_duplicate_implementation_with_alias() { - let src = " - trait Default { - } - - struct MyStruct { - } - - type MyType = MyStruct; - - impl Default for MyStruct { - } - - impl Default for MyType { - } - - fn main() { - } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); - for (err, _file_id) in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { - .. - }) => (), - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { - .. - }) => (), - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } -} - -#[test] -fn test_impl_self_within_default_def() { - let src = " - trait Bar { - fn ok(self) -> Self; - - fn ref_ok(self) -> Self { - self.ok() - } - } - - impl Bar for (T, T) where T: Bar { - fn ok(self) -> Self { - self - } - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_as_type_as_fn_parameter() { - let src = " - trait Eq { - fn eq(self, other: Self) -> bool; - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - fn test_eq(x: impl Eq) -> bool { - x.eq(x) - } - - fn main(a: Foo) -> pub bool { - test_eq(a) - }"; - assert_no_errors(src); -} - -#[test] -fn check_trait_as_type_as_two_fn_parameters() { - let src = " - trait Eq { - fn eq(self, other: Self) -> bool; - } - - trait Test { - fn test(self) -> bool; - } - - struct Foo { - a: u64, - } - - impl Eq for Foo { - fn eq(self, other: Foo) -> bool { self.a == other.a } - } - - impl Test for u64 { - fn test(self) -> bool { self == self } - } - - fn test_eq(x: impl Eq, y: impl Test) -> bool { - x.eq(x) == y.test() - } - - fn main(a: Foo, b: u64) -> pub bool { - test_eq(a, b) - }"; - assert_no_errors(src); -} - -fn get_program_captures(src: &str) -> Vec> { - let (program, context, _errors) = get_program(src); - let interner = context.def_interner; - let mut all_captures: Vec> = Vec::new(); - for func in program.into_sorted().functions { - let func_id = interner.find_function(func.item.name()).unwrap(); - let hir_func = interner.function(&func_id); - // Iterate over function statements and apply filtering function - find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); - } - all_captures -} - -fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { - for stmt_id in stmts.iter() { - let hir_stmt = interner.statement(stmt_id); - let expr_id = match hir_stmt { - HirStatement::Expression(expr_id) => expr_id, - HirStatement::Let(let_stmt) => let_stmt.expression, - HirStatement::Assign(assign_stmt) => assign_stmt.expression, - HirStatement::Constrain(constr_stmt) => constr_stmt.0, - HirStatement::Semi(semi_expr) => semi_expr, - HirStatement::For(for_loop) => for_loop.block, - HirStatement::Error => panic!("Invalid HirStatement!"), - HirStatement::Break => panic!("Unexpected break"), - HirStatement::Continue => panic!("Unexpected continue"), - HirStatement::Comptime(_) => panic!("Unexpected comptime"), - }; - let expr = interner.expression(&expr_id); - - get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter - } -} - -fn get_lambda_captures( - expr: HirExpression, - interner: &NodeInterner, - result: &mut Vec>, -) { - if let HirExpression::Lambda(lambda_expr) = expr { - let mut cur_capture = Vec::new(); - - for capture in lambda_expr.captures.iter() { - cur_capture.push(interner.definition(capture.ident.id).name.clone()); - } - result.push(cur_capture); - - // Check for other captures recursively within the lambda body - let hir_body_expr = interner.expression(&lambda_expr.body); - if let HirExpression::Block(block_expr) = hir_body_expr { - find_lambda_captures(block_expr.statements(), interner, result); - } - } -} - -#[test] -fn resolve_empty_function() { - let src = " - fn main() { - - } - "; - assert_no_errors(src); -} -#[test] -fn resolve_basic_function() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(y == x); - } - "#; - assert_no_errors(src); -} -#[test] -fn resolve_unused_var() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(x == x); - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } -} - -#[test] -fn resolve_unresolved_var() { - let src = r#" - fn main(x : Field) { - let y = x + x; - assert(y == z); - } - "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0].0 { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } -} - -#[test] -fn unresolved_path() { - let src = " - fn main(x : Field) { - let _z = some::path::to::a::func(x); - } - "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } -} - -#[test] -fn resolve_literal_expr() { - let src = r#" - fn main(x : Field) { - let y = 5; - assert(y == x); - } - "#; - assert_no_errors(src); -} - -#[test] -fn multiple_resolution_errors() { - let src = r#" - fn main(x : Field) { - let y = foo::bar(x); - let z = y + a; - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for (compilation_error, _file_id) in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } -} - -#[test] -fn resolve_prefix_expr() { - let src = r#" - fn main(x : Field) { - let _y = -x; - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_for_expr() { - let src = r#" - fn main(x : u64) { - for i in 1..20 { - let _z = x + i; - }; - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_call_expr() { - let src = r#" - fn main(x : Field) { - let _z = foo(x); - } - - fn foo(x : Field) -> Field { - x - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_shadowing() { - let src = r#" - fn main(x : Field) { - let x = foo(x); - let x = x; - let (x, x) = (x, x); - let _ = x; - } - - fn foo(x : Field) -> Field { - x - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_basic_closure() { - let src = r#" - fn main(x : Field) -> pub Field { - let closure = |y| y + x; - closure(x) - } - "#; - assert_no_errors(src); -} - -#[test] -fn resolve_simplified_closure() { - // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { - let y = x; - let ret_capture = || { - y - }; - ret_capture() - } - - fn main(x: Field) { - assert(do_closure(x) == 100); - } - - "#; - let parsed_captures = get_program_captures(src); - let expected_captures = vec![vec!["y".to_string()]]; - assert_eq!(expected_captures, parsed_captures); -} - -#[test] -fn resolve_complex_closures() { - let src = r#" - fn main(x: Field) -> pub Field { - let closure_without_captures = |x: Field| -> Field { x + x }; - let a = closure_without_captures(1); - - let closure_capturing_a_param = |y: Field| -> Field { y + x }; - let b = closure_capturing_a_param(2); - - let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; - let c = closure_capturing_a_local_var(3); - - let closure_with_transitive_captures = |y: Field| -> Field { - let d = 5; - let nested_closure = |z: Field| -> Field { - let doubly_nested_closure = |w: Field| -> Field { w + x + b }; - a + z + y + d + x + doubly_nested_closure(4) + x + y - }; - let res = nested_closure(5); - res - }; - - a + b + c + closure_with_transitive_captures(6) - } - "#; - assert_no_errors(src); - - let expected_captures = vec![ - vec![], - vec!["x".to_string()], - vec!["b".to_string()], - vec!["x".to_string(), "b".to_string(), "a".to_string()], - vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], - vec!["x".to_string(), "b".to_string()], - ]; - - let parsed_captures = get_program_captures(src); - - assert_eq!(expected_captures, parsed_captures); -} - -#[test] -fn resolve_fmt_strings() { - let src = r#" - fn main() { - let string = f"this is i: {i}"; - println(string); - - println(f"I want to print {0}"); - - let new_val = 10; - println(f"random_string{new_val}{new_val}"); - } - fn println(x : T) -> T { - x - } - "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); - - for (err, _file_id) in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { - name, - .. - }) => { - assert_eq!(name, "0"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_span, - }) => { - let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); - assert!( - a == "println(string)" - || a == "println(f\"I want to print {0}\")" - || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } -} - -fn check_rewrite(src: &str, expected: &str) { - let (_program, mut context, _errors) = get_program(src); - let main_func_id = context.def_interner.find_function("main").unwrap(); - let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); - assert!(format!("{}", program) == expected); -} - -#[test] -fn simple_closure_with_no_captured_variables() { - let src = r#" - fn main() -> pub Field { - let x = 1; - let closure = || x; - closure() - } - "#; - - let expected_rewrite = r#"fn main$f0() -> Field { - let x$0 = 1; - let closure$3 = { - let closure_variable$2 = { - let env$1 = (x$l0); - (env$l1, lambda$f1) - }; - closure_variable$l2 - }; - { - let tmp$4 = closure$l3; - tmp$l4.1(tmp$l4.0) - } -} -fn lambda$f1(mut env$l1: (Field)) -> Field { - env$l1.0 -} -"#; - check_rewrite(src, expected_rewrite); -} - -#[test] -fn deny_cyclic_globals() { - let src = r#" - global A = B; - global B = A; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn deny_cyclic_type_aliases() { - let src = r#" - type A = B; - type B = A; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn ensure_nested_type_aliases_type_check() { - let src = r#" - type A = B; - type B = u8; - fn main() { - let _a: A = 0 as u16; - } - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn type_aliases_in_entry_point() { - let src = r#" - type Foo = u8; - fn main(_x: Foo) {} - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn operators_in_global_used_in_type() { - let src = r#" - global ONE = 1; - global COUNT = ONE + 2; - fn main() { - let _array: [Field; COUNT] = [1, 2, 3]; - } - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn break_and_continue_in_constrained_fn() { - let src = r#" - fn main() { - for i in 0 .. 10 { - if i == 2 { - continue; - } - if i == 5 { - break; - } - } - } - "#; - assert_eq!(get_program_errors(src).len(), 2); -} - -#[test] -fn break_and_continue_outside_loop() { - let src = r#" - unconstrained fn main() { - continue; - break; - } - "#; - assert_eq!(get_program_errors(src).len(), 2); -} - -// Regression for #2540 -#[test] -fn for_loop_over_array() { - let src = r#" - fn hello(_array: [u1; N]) { - for _ in 0..N {} - } - - fn main() { - let array: [u1; 2] = [0, 1]; - hello(array); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -// Regression for #4545 -#[test] -fn type_aliases_in_main() { - let src = r#" - type Outer = [u8; N]; - fn main(_arg: Outer<1>) {} - "#; - assert_eq!(get_program_errors(src).len(), 0); -} - -#[test] -fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context - let src = r#" - mut global FOO: Field = 0; - fn main() {} - "#; - assert_eq!(get_program_errors(src).len(), 1); -} - -#[test] -fn deny_inline_attribute_on_unconstrained() { - let src = r#" - #[no_predicates] - unconstrained pub fn foo(x: Field, y: Field) { - assert(x != y); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); -} - -#[test] -fn deny_fold_attribute_on_unconstrained() { - let src = r#" - #[fold] - unconstrained pub fn foo(x: Field, y: Field) { - assert(x != y); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); -} - -#[test] -fn specify_function_types_with_turbofish() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - impl Default for u64 { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - fn generic_func() -> (T, U) where T: Default, U: Default { - (T::default(), U::default()) - } - - fn main() { - let _ = generic_func::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn specify_method_types_with_turbofish() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - struct Foo { - inner: T - } - - impl Foo { - fn generic_method(_self: Self) -> U where U: Default { - U::default() - } - } - - fn main() { - let foo: Foo = Foo { inner: 1 }; - let _ = foo.generic_method::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn incorrect_turbofish_count_function_call() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - impl Default for u64 { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - fn generic_func() -> (T, U) where T: Default, U: Default { - (T::default(), U::default()) - } - - fn main() { - let _ = generic_func::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); -} - -#[test] -fn incorrect_turbofish_count_method_call() { - let src = r#" - trait Default { - fn default() -> Self; - } - - impl Default for Field { - fn default() -> Self { 0 } - } - - // Need the above as we don't have access to the stdlib here. - // We also need to construct a concrete value of `U` without giving away its type - // as otherwise the unspecified type is ignored. - - struct Foo { - inner: T - } - - impl Foo { - fn generic_method(_self: Self) -> U where U: Default { - U::default() - } - } - - fn main() { - let foo: Foo = Foo { inner: 1 }; - let _ = foo.generic_method::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); -} - -#[test] -fn struct_numeric_generic_in_function() { - let src = r#" - struct Foo { - inner: u64 - } - - pub fn bar() { } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} - -#[test] -fn struct_numeric_generic_in_struct() { - let src = r#" - struct Foo { - inner: u64 - } - - struct Bar { } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::UnsupportedNumericGenericType { .. } - ), - )); -} - -#[test] -fn bool_numeric_generic() { - let src = r#" - pub fn read() -> Field { - if N { - 0 - } else { - 1 - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); -} - -#[test] -fn numeric_generic_binary_operation_type_mismatch() { - let src = r#" - pub fn foo() -> bool { - let mut check: bool = true; - check = N; - check - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); -} - -#[test] -fn bool_generic_as_loop_bound() { - let src = r#" - pub fn read() { - let mut fields = [0; N]; - for i in 0..N { - fields[i] = i + 1; - } - assert(fields[0] == 1); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[1].0 - else { - panic!("Got an error other than a type mismatch"); - }; - - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); -} - -#[test] -fn numeric_generic_in_function_signature() { - let src = r#" - pub fn foo(arr: [Field; N]) -> [Field; N] { arr } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_as_struct_field_type_fails() { - let src = r#" - struct Foo { - a: Field, - b: N, - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn normal_generic_as_array_length() { - let src = r#" - struct Foo { - a: Field, - b: [Field; N], - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_as_param_type() { - let src = r#" - pub fn foo(x: I) -> I { - let _q: I = 5; - x - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_used_in_nested_type_fails() { - let src = r#" - struct Foo { - a: Field, - b: Bar, - } - struct Bar { - inner: N - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn normal_generic_used_in_nested_array_length_fail() { - let src = r#" - struct Foo { - a: Field, - b: Bar, - } - struct Bar { - inner: [Field; N] - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generic_used_in_nested_type_pass() { - // The order of these structs should not be changed to make sure - // that we are accurately resolving all struct generics before struct fields - let src = r#" - struct NestedNumeric { - a: Field, - b: InnerNumeric - } - struct InnerNumeric { - inner: [u64; N], - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_trait() { - // We want to make sure that `N` in `impl Deserialize` does - // not trigger `expected type, found numeric generic parameter N` as the trait - // does in fact expect a numeric generic. - let src = r#" - struct MyType { - a: Field, - b: Field, - c: Field, - d: T, - } - - impl Deserialize for MyType { - fn deserialize(fields: [Field; N], other: T) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2], d: other } - } - } - - trait Deserialize { - fn deserialize(fields: [Field; N], other: T) -> Self; - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_in_trait_impl_with_extra_impl_generics() { - let src = r#" - trait Default { - fn default() -> Self; - } - - struct MyType { - a: Field, - b: Field, - c: Field, - d: T, - } - - // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. - // `N` is used first in the trait impl generics (`Deserialize for MyType`). - // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind - // while `T` has a normal kind. - impl Deserialize for MyType where T: Default { - fn deserialize(fields: [Field; N]) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } - } - } - - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_where_clause() { - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } - "#; - assert_no_errors(src); -} - -#[test] -fn numeric_generic_used_in_turbofish() { - let src = r#" - pub fn double() -> u32 { - // Used as an expression - N * 2 - } - - pub fn double_numeric_generics_test() { - // Example usage of a numeric generic arguments. - assert(double::<9>() == 18); - assert(double::<7 + 8>() == 30); - } - "#; - assert_no_errors(src); -} - -#[test] -fn constant_used_with_numeric_generic() { - let src = r#" - struct ValueNote { - value: Field, - } - - trait Serialize { - fn serialize(self) -> [Field; N]; - } - - impl Serialize<1> for ValueNote { - fn serialize(self) -> [Field; 1] { - [self.value] - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn normal_generic_used_when_numeric_expected_in_where_clause() { - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - T::deserialize([0, 1]) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let src = r#" - trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; - } - - pub fn read() -> T where T: Deserialize { - let mut fields: [Field; N] = [0; N]; - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); -} - -#[test] -fn numeric_generics_type_kind_mismatch() { - let src = r#" - fn foo() -> u16 { - N as u16 - } - - global J: u16 = 10; - - fn bar() -> u16 { - foo::() - } - - global M: u16 = 3; - - fn main() { - let _ = bar::(); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); -} - -#[test] -fn numeric_generics_value_kind_mismatch_u32_u64() { - let src = r#" - struct BoundedVec { - storage: [T; MaxLen], - // can't be compared to MaxLen: u32 - // can't be used to index self.storage - len: u64, - } - - impl BoundedVec { - pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { - // We do this to avoid an unused variable warning on `self` - let _ = self.len; - for _ in 0..Len { } - } - - pub fn push(&mut self, elem: T) { - assert(self.len < MaxLen, "push out of bounds"); - self.storage[self.len] = elem; - self.len += 1; - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); -} - -#[test] -fn quote_code_fragments() { - // This test ensures we can quote (and unquote/splice) code fragments - // which by themselves are not valid code. They only need to be valid - // by the time they are unquoted into the macro's call site. - let src = r#" - fn main() { - comptime { - concat!(quote { assert( }, quote { false); }); - } - } - - comptime fn concat(a: Quoted, b: Quoted) -> Quoted { - quote { $a $b } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); -} - -#[test] -fn impl_stricter_than_trait_no_trait_method_constraints() { - // This test ensures that the error we get from the where clause on the trait impl method - // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. - let src = r#" - trait Serialize { - // We want to make sure we trigger the error when override a trait method - // which itself has no trait constraints. - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } - - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() - } - - struct MyType { - a: T, - b: T, - } - - impl Serialize<2> for MyType { - fn serialize(self) -> [Field; 2] where T: ToField { - [ self.a.to_field(), self.b.to_field() ] - } - } - - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); -} - -#[test] -fn impl_stricter_than_trait_different_generics() { - let src = r#" - trait Default { } - - // Object type of the trait constraint differs - trait Foo { - fn foo_good() where T: Default; - - fn foo_bad() where T: Default; - } - - impl Foo for () { - fn foo_good() where A: Default {} - - fn foo_bad() where B: Default {} - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_object_generics() { - let src = r#" - trait MyTrait { } - - trait OtherTrait {} - - struct Option { - inner: T - } - - struct OtherOption { - inner: Option, - } - - trait Bar { - fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; - - fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; - - fn array_good() where [T; 8]: MyTrait; - - fn array_bad() where [T; 8]: MyTrait; - - fn tuple_good() where (Option, Option): MyTrait; - - fn tuple_bad() where (Option, Option): MyTrait; - } - - impl Bar for () { - fn bar_good() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn bar_bad() - where - OtherOption>: OtherTrait, - Option: MyTrait { } - - fn array_good() where [A; 8]: MyTrait { } - - fn array_bad() where [B; 8]: MyTrait { } - - fn tuple_good() where (Option, Option): MyTrait { } - - fn tuple_bad() where (Option, Option): MyTrait { } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_trait() { - let src = r#" - trait Default { } - - trait OtherDefault { } - - struct Option { - inner: T - } - - trait Bar { - fn bar() where Option: Default; - } - - impl Bar for () { - // Trait constraint differs due to the trait even though the constraint - // types are the same. - fn bar() where Option: OtherDefault {} - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn trait_impl_where_clause_stricter_pass() { - let src = r#" - trait MyTrait { - fn good_foo() where H: OtherTrait; - - fn bad_foo() where H: OtherTrait; - } - - trait OtherTrait {} - - struct Option { - inner: T - } - - impl MyTrait for [T] where Option: MyTrait { - fn good_foo() where B: OtherTrait { } - - fn bad_foo() where A: OtherTrait { } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_stricter_than_trait_different_trait_generics() { - let src = r#" - trait Foo { - fn foo() where T: T2; - } - - impl Foo for () { - // Should be A: T2 - fn foo() where A: T2 {} - } - - trait T2 {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0].0 - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); - } -} - -#[test] -fn impl_not_found_for_inner_impl() { - // We want to guarantee that we get a no impl found error - let src = r#" - trait Serialize { - fn serialize(self) -> [Field; N]; - } - - trait ToField { - fn to_field(self) -> Field; - } - - fn process_array(array: [Field; N]) -> Field { - array[0] - } - - fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { - thing.serialize() - } - - struct MyType { - a: T, - b: T, - } - - impl Serialize<2> for MyType where T: ToField { - fn serialize(self) -> [Field; 2] { - [ self.a.to_field(), self.b.to_field() ] - } - } - - impl MyType { - fn do_thing_with_serialization_with_extra_steps(self) -> Field { - process_array(serialize_thing(self)) - } - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); -} - -// Regression for #5388 -#[test] -fn comptime_let() { - let src = r#"fn main() { - comptime let my_var = 2; - assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn overflowing_u8() { - let src = r#" - fn main() { - let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁸` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_u8() { - let src = r#" - fn main() { - let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn overflowing_i8() { - let src = r#" - fn main() { - let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_i8() { - let src = r#" - fn main() { - let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls - let src = r#" - fn foo() -> [u8; N] { - [0; N] - } - - fn bar() -> [u8; N] { - foo::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); - - // Check for turbofish numeric generics used with method calls - let src = r#" - struct Foo { - a: T - } - - impl Foo { - fn static_method() -> [u8; N] { - [0; N] - } - - fn impl_method(self) -> [T; N] { - [self.a; N] - } - } - - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super() { - let src = r#" - fn some_func() {} - - mod foo { - use super::some_func; - - pub fn bar() { - some_func(); - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super_in_path() { - let src = r#" - fn some_func() {} - - mod foo { - pub fn func() { - super::some_func(); - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(span), - )) = &errors[0].0 - else { - panic!("Expected a 'no super' error, got {:?}", errors[0].0); - }; - - assert_eq!(span.start(), 4); - assert_eq!(span.end(), 9); -} - -#[test] -fn cannot_call_unconstrained_function_outside_of_unsafe() { - let src = r#" - fn main() { - foo(); - } - - unconstrained fn foo() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { - let src = r#" - fn main() { - let func = foo; - // Warning should trigger here - func(); - inner(func); - } - - fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here - x(); - } - - unconstrained fn foo() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; - } -} - -#[test] -fn missing_unsafe_block_when_needing_type_annotations() { - // This test is a regression check that even when an unsafe block is missing - // that we still appropriately continue type checking and infer type annotations. - let src = r#" - fn main() { - let z = BigNum { limbs: [2, 0, 0] }; - assert(z.__is_zero() == false); - } - - struct BigNum { - limbs: [u64; N], - } - - impl BigNum { - unconstrained fn __is_zero_impl(self) -> bool { - let mut result: bool = true; - for i in 0..N { - result = result & (self.limbs[i] == 0); - } - result - } - } - - trait BigNumTrait { - fn __is_zero(self) -> bool; - } - - impl BigNumTrait for BigNum { - fn __is_zero(self) -> bool { - self.__is_zero_impl() - } - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_pass_unconstrained_function_to_regular_function() { - let src = r#" - fn main() { - let func = foo; - expect_regular(func); - } - - unconstrained fn foo() {} - - fn expect_regular(_func: fn() -> ()) { - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; -} - -#[test] -fn cannot_assign_unconstrained_and_regular_fn_to_variable() { - let src = r#" - fn main() { - let _func = if true { foo } else { bar }; - } - - fn foo() {} - unconstrained fn bar() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { - panic!("Expected a context error, got {:?}", errors[0].0); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; -} - -#[test] -fn can_pass_regular_function_to_unconstrained_function() { - let src = r#" - fn main() { - let func = foo; - expect_unconstrained(func); - } - - fn foo() {} - - fn expect_unconstrained(_func: unconstrained fn() -> ()) {} - "#; - assert_no_errors(src); -} - -#[test] -fn cannot_pass_unconstrained_function_to_constrained_function() { - let src = r#" - fn main() { - let func = foo; - expect_regular(func); - } - - unconstrained fn foo() {} - - fn expect_regular(_func: fn() -> ()) {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); - }; -} - -#[test] -fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { - let src = r#" - fn main() { - let _func: unconstrained fn() -> () = foo; - } - - fn foo() {} - "#; - assert_no_errors(src); -} - -#[test] -fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { - let src = r#" - fn main() { - let _ = Foo { func: foo }; - } - - fn foo() {} - - struct Foo { - func: unconstrained fn() -> (), - } - "#; - assert_no_errors(src); -} - -#[test] -fn trait_impl_generics_count_mismatch() { - let src = r#" - trait Foo {} - - impl Foo<()> for Field {} - - fn main() {}"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0].0 - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(item, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); -} - -#[test] -fn bit_not_on_untyped_integer() { - let src = r#" - fn main() { - let _: u32 = 3 & !1; - } - "#; - assert_no_errors(src); -} - -#[test] -fn duplicate_struct_field() { - let src = r#" - struct Foo { - x: i32, - x: i32, - } - - fn main() {} - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { - first_def, - second_def, - }) = &errors[0].0 - else { - panic!("Expected a duplicate field error, got {:?}", errors[0].0); - }; - - assert_eq!(first_def.to_string(), "x"); - assert_eq!(second_def.to_string(), "x"); - - assert_eq!(first_def.span().start(), 26); - assert_eq!(second_def.span().start(), 42); -} - -#[test] -fn trait_constraint_on_tuple_type() { - let src = r#" - trait Foo { - fn foo(self, x: A) -> bool; - } - - pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { - x.foo(y) - } - - fn main() {}"#; - assert_no_errors(src); -} - -#[test] -fn turbofish_in_constructor_generics_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let _ = Foo:: { x: 1 }; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); -} - -#[test] -fn turbofish_in_constructor() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let x: Field = 0; - let _ = Foo:: { x: x }; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "Field"); -} - -#[test] -fn turbofish_in_middle_of_variable_unsupported_yet() { - let src = r#" - struct Foo { - x: T - } - - impl Foo { - fn new(x: T) -> Self { - Foo { x } - } - } - - fn main() { - let _ = Foo::::new(1); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), - )); -} - -#[test] -fn turbofish_in_struct_pattern() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - assert_no_errors(src); -} - -#[test] -fn turbofish_in_struct_pattern_errors_if_type_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; -} - -#[test] -fn turbofish_in_struct_pattern_generic_count_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0].0 - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(item, "struct Foo"); - assert_eq!(*expected, 1); - assert_eq!(*found, 2); -} - -#[test] -fn incorrect_generic_count_on_struct_impl() { - let src = r#" - struct Foo {} - impl Foo {} - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); -} - -#[test] -fn incorrect_generic_count_on_type_alias() { - let src = r#" - struct Foo {} - type Bar = Foo; - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0].0 - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); -} - -#[test] -fn uses_self_type_for_struct_function_call() { - let src = r#" - struct S { } - - impl S { - fn one() -> Field { - 1 - } - - fn two() -> Field { - Self::one() + Self::one() - } - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn uses_self_type_inside_trait() { - let src = r#" - trait Foo { - fn foo() -> Self { - Self::bar() - } - - fn bar() -> Self; - } - - impl Foo for Field { - fn bar() -> Self { - 1 - } - } - - fn main() { - let _: Field = Foo::foo(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn uses_self_type_in_trait_where_clause() { - let src = r#" - trait Trait { - fn trait_func() -> bool; - } - - trait Foo where Self: Trait { - fn foo(self) -> bool { - self.trait_func() - } - } - - struct Bar { - - } - - impl Foo for Bar { - - } - - fn main() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[0].0 - else { - panic!("Expected an unresolved method call error, got {:?}", errors[0].0); - }; - - assert_eq!(method_name, "trait_func"); -} - -#[test] -fn do_not_eagerly_error_on_cast_on_type_variable() { - let src = r#" - pub fn foo(x: T, f: fn(T) -> U) -> U { - f(x) - } - - fn main() { - let x: u8 = 1; - let _: Field = foo(x, |x| x as Field); - } - "#; - assert_no_errors(src); -} - -#[test] -fn error_on_cast_over_type_variable() { - let src = r#" - pub fn foo(x: T, f: fn(T) -> U) -> U { - f(x) - } - - fn main() { - let x = "a"; - let _: Field = foo(x, |x| x as Field); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); -} - -#[test] -fn trait_impl_for_a_type_that_implements_another_trait() { - let src = r#" - trait One { - fn one(self) -> i32; - } - - impl One for i32 { - fn one(self) -> i32 { - self - } - } - - trait Two { - fn two(self) -> i32; - } - - impl Two for T where T: One { - fn two(self) -> i32 { - self.one() + 1 - } - } - - pub fn use_it(t: T) -> i32 where T: Two { - Two::two(t) - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { - let src = r#" - trait One { - fn one(self) -> i32; - } - - impl One for i32 { - fn one(self) -> i32 { - let _ = self; - 1 - } - } - - trait Two { - fn two(self) -> i32; - } - - impl Two for T where T: One { - fn two(self) -> i32 { - self.one() + 1 - } - } - - impl Two for u32 { - fn two(self) -> i32 { - let _ = self; - 0 - } - } - - pub fn use_it(t: u32) -> i32 { - Two::two(t) - } - - fn main() {} - "#; - assert_no_errors(src); -} - -#[test] -fn impl_missing_associated_type() { - let src = r#" - trait Foo { - type Assoc; - } - - impl Foo for () {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); -} - -#[test] -fn as_trait_path_syntax_resolves_outside_impl() { - let src = r#" - trait Foo { - type Assoc; - } - - struct Bar {} - - impl Foo for Bar { - type Assoc = i32; - } - - fn main() { - // AsTraitPath syntax is a bit silly when associated types - // are explicitly specified - let _: i64 = 1 as >::Assoc; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); -} - -#[test] -fn as_trait_path_syntax_no_impl() { - let src = r#" - trait Foo { - type Assoc; - } - - struct Bar {} - - impl Foo for Bar { - type Assoc = i32; - } - - fn main() { - let _: i64 = 1 as >::Assoc; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); -} - -#[test] -fn errors_on_unused_private_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - trait Foo { - } - } - - use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn errors_on_unused_pub_crate_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - trait Foo { - } - } - - pub(crate) use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn warns_on_use_of_private_exported_item() { - let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - use bar::baz; - - pub fn qux() { - baz(); - } - } - - fn main() { - foo::baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated - - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); -} +// TODO re-enable +// #[test] +// fn check_trait_implemented_for_all_t() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// trait IsDefault { +// fn is_default(self) -> bool; +// } +// +// impl IsDefault for T where T: Default + Eq { +// fn is_default(self) -> bool { +// self.eq(T::default()) +// } +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// impl Default for u64 { +// fn default() -> Self { +// 0 +// } +// } +// +// impl Default for Foo { +// fn default() -> Self { +// Foo { a: Default::default() } +// } +// } +// +// fn main(a: Foo) -> pub bool { +// a.is_default() +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_implementation_duplicate_method() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Field; +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// impl Default for Foo { +// // Duplicate trait methods should not compile +// fn default(x: Field, y: Field) -> Field { +// y + 2 * x +// } +// // Duplicate trait methods should not compile +// fn default(x: Field, y: Field) -> Field { +// x + 2 * y +// } +// } +// +// fn main() {}"; +// +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { +// typ, +// first_def, +// second_def, +// }) => { +// assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); +// assert_eq!(first_def, "default"); +// assert_eq!(second_def, "default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_return_type() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// struct Foo { +// } +// +// impl Default for Foo { +// fn default() -> Field { +// 0 +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, +// expr_typ, +// expr_span: _, +// }) => { +// assert_eq!(expected_typ, "Foo"); +// assert_eq!(expr_typ, "Field"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_return_type2() { +// let src = " +// 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() { +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, +// expr_typ, +// expr_span: _, +// }) => { +// assert_eq!(expected_typ, "Foo"); +// assert_eq!(expr_typ, "Field"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_missing_implementation() { +// let src = " +// 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() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { +// trait_name, +// method_name, +// trait_impl_span: _, +// }) => { +// assert_eq!(trait_name, "Default"); +// assert_eq!(method_name, "method2"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_not_in_scope() { +// let src = " +// 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() { +// } +// +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { +// trait_path, +// }) => { +// assert_eq!(trait_path.as_string(), "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_method_name() { +// let src = " +// trait Default { +// } +// +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// // wrong trait name method should not compile +// impl Default for Foo { +// fn does_not_exist(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() { +// }"; +// let compilation_errors = get_program_errors(src); +// assert!(!has_parser_error(&compilation_errors)); +// assert!( +// compilation_errors.len() == 1, +// "Expected 1 compilation error, got: {:?}", +// compilation_errors +// ); +// +// for (err, _file_id) in compilation_errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { +// trait_name, +// impl_method, +// }) => { +// assert_eq!(trait_name, "Default"); +// assert_eq!(impl_method, "does_not_exist"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter() { +// let src = " +// trait Default { +// fn default(x: Field) -> Self; +// } +// +// struct Foo { +// bar: u32, +// } +// +// impl Default for Foo { +// fn default(x: u32) -> Self { +// Foo {bar: x} +// } +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { +// method_name, +// expected_typ, +// actual_typ, +// .. +// }) => { +// assert_eq!(method_name, "default"); +// assert_eq!(expected_typ, "Field"); +// assert_eq!(actual_typ, "u32"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter2() { +// let src = " +// 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() { +// }"; +// +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { +// method_name, +// expected_typ, +// actual_typ, +// .. +// }) => { +// assert_eq!(method_name, "default"); +// assert_eq!(expected_typ, "Field"); +// assert_eq!(actual_typ, "Foo"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameter_type() { +// let src = " +// trait Default { +// fn default(x: Field, y: NotAType) -> Field; +// } +// +// fn main(x: Field, y: Field) { +// assert(y == x); +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// +// // This is a duplicate error in the name resolver & type checker. +// // In the elaborator there is no duplicate and only 1 error is issued +// assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::PathResolutionError( +// PathResolutionError::Unresolved(ident), +// )) => { +// assert_eq!(ident, "NotAType"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_wrong_parameters_count() { +// let src = " +// 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() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { +// actual_num_parameters, +// expected_num_parameters, +// trait_name, +// method_name, +// .. +// }) => { +// assert_eq!(actual_num_parameters, &1_usize); +// assert_eq!(expected_num_parameters, &2_usize); +// assert_eq!(method_name, "default"); +// assert_eq!(trait_name, "Default"); +// } +// _ => { +// panic!("No other errors are expected in this test case! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_impl_for_non_type() { +// let src = " +// trait Default { +// fn default(x: Field, y: Field) -> Field; +// } +// +// impl Default for main { +// fn default(x: Field, y: Field) -> Field { +// x + y +// } +// } +// +// fn main() {} +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { +// assert_eq!(expected, "type"); +// assert_eq!(got, "function"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_impl_struct_not_trait() { +// let src = " +// struct Foo { +// bar: Field, +// array: [Field; 2], +// } +// +// struct Default { +// x: Field, +// z: Field, +// } +// +// // Default is a struct not a trait +// impl Default for Foo { +// fn default(x: Field, y: Field) -> Self { +// Self { bar: x, array: [x,y] } +// } +// } +// +// fn main() {} +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { +// not_a_trait_name, +// }) => { +// assert_eq!(not_a_trait_name.to_string(), "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_declaration() { +// let src = " +// 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] } +// } +// } +// +// +// trait Default { +// fn default(x: Field) -> Self; +// } +// +// fn main() { +// }"; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { +// typ, +// first_def, +// second_def, +// }) => { +// assert_eq!(typ, &DuplicateType::Trait); +// assert_eq!(first_def, "Default"); +// assert_eq!(second_def, "Default"); +// } +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_implementation() { +// let src = " +// trait Default { +// } +// struct Foo { +// bar: Field, +// } +// +// impl Default for Foo { +// } +// impl Default for Foo { +// } +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { +// .. +// }) => (), +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { +// .. +// }) => (), +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn check_trait_duplicate_implementation_with_alias() { +// let src = " +// trait Default { +// } +// +// struct MyStruct { +// } +// +// type MyType = MyStruct; +// +// impl Default for MyStruct { +// } +// +// impl Default for MyType { +// } +// +// fn main() { +// } +// "; +// let errors = get_program_errors(src); +// assert!(!has_parser_error(&errors)); +// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); +// for (err, _file_id) in errors { +// match &err { +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { +// .. +// }) => (), +// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { +// .. +// }) => (), +// _ => { +// panic!("No other errors are expected! Found = {:?}", err); +// } +// }; +// } +// } +// +// #[test] +// fn test_impl_self_within_default_def() { +// let src = " +// trait Bar { +// fn ok(self) -> Self; +// +// fn ref_ok(self) -> Self { +// self.ok() +// } +// } +// +// impl Bar for (T, T) where T: Bar { +// fn ok(self) -> Self { +// self +// } +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_as_type_as_fn_parameter() { +// let src = " +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// fn test_eq(x: impl Eq) -> bool { +// x.eq(x) +// } +// +// fn main(a: Foo) -> pub bool { +// test_eq(a) +// }"; +// assert_no_errors(src); +// } +// +// #[test] +// fn check_trait_as_type_as_two_fn_parameters() { +// let src = " +// trait Eq { +// fn eq(self, other: Self) -> bool; +// } +// +// trait Test { +// fn test(self) -> bool; +// } +// +// struct Foo { +// a: u64, +// } +// +// impl Eq for Foo { +// fn eq(self, other: Foo) -> bool { self.a == other.a } +// } +// +// impl Test for u64 { +// fn test(self) -> bool { self == self } +// } +// +// fn test_eq(x: impl Eq, y: impl Test) -> bool { +// x.eq(x) == y.test() +// } +// +// fn main(a: Foo, b: u64) -> pub bool { +// test_eq(a, b) +// }"; +// assert_no_errors(src); +// } +// +// fn get_program_captures(src: &str) -> Vec> { +// let (program, context, _errors) = get_program(src); +// let interner = context.def_interner; +// let mut all_captures: Vec> = Vec::new(); +// for func in program.into_sorted().functions { +// let func_id = interner.find_function(func.item.name()).unwrap(); +// let hir_func = interner.function(&func_id); +// // Iterate over function statements and apply filtering function +// find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); +// } +// all_captures +// } +// +// fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { +// for stmt_id in stmts.iter() { +// let hir_stmt = interner.statement(stmt_id); +// let expr_id = match hir_stmt { +// HirStatement::Expression(expr_id) => expr_id, +// HirStatement::Let(let_stmt) => let_stmt.expression, +// HirStatement::Assign(assign_stmt) => assign_stmt.expression, +// HirStatement::Constrain(constr_stmt) => constr_stmt.0, +// HirStatement::Semi(semi_expr) => semi_expr, +// HirStatement::For(for_loop) => for_loop.block, +// HirStatement::Error => panic!("Invalid HirStatement!"), +// HirStatement::Break => panic!("Unexpected break"), +// HirStatement::Continue => panic!("Unexpected continue"), +// HirStatement::Comptime(_) => panic!("Unexpected comptime"), +// }; +// let expr = interner.expression(&expr_id); +// +// get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter +// } +// } +// +// fn get_lambda_captures( +// expr: HirExpression, +// interner: &NodeInterner, +// result: &mut Vec>, +// ) { +// if let HirExpression::Lambda(lambda_expr) = expr { +// let mut cur_capture = Vec::new(); +// +// for capture in lambda_expr.captures.iter() { +// cur_capture.push(interner.definition(capture.ident.id).name.clone()); +// } +// result.push(cur_capture); +// +// // Check for other captures recursively within the lambda body +// let hir_body_expr = interner.expression(&lambda_expr.body); +// if let HirExpression::Block(block_expr) = hir_body_expr { +// find_lambda_captures(block_expr.statements(), interner, result); +// } +// } +// } +// +// #[test] +// fn resolve_empty_function() { +// let src = " +// fn main() { +// +// } +// "; +// assert_no_errors(src); +// } +// #[test] +// fn resolve_basic_function() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(y == x); +// } +// "#; +// assert_no_errors(src); +// } +// #[test] +// fn resolve_unused_var() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(x == x); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unused variable +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { +// assert_eq!(&ident.0.contents, "y"); +// } +// _ => unreachable!("we should only have an unused var error"), +// } +// } +// +// #[test] +// fn resolve_unresolved_var() { +// let src = r#" +// fn main(x : Field) { +// let y = x + x; +// assert(y == z); +// } +// "#; +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) +// match &errors[0].0 { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { +// assert_eq!(name, "z"); +// } +// _ => unimplemented!("we should only have an unresolved variable"), +// } +// } +// +// #[test] +// fn unresolved_path() { +// let src = " +// fn main(x : Field) { +// let _z = some::path::to::a::func(x); +// } +// "; +// let errors = get_program_errors(src); +// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "some"); +// } +// _ => unimplemented!("we should only have an unresolved function"), +// }; +// } +// _ => unimplemented!(), +// } +// } +// } +// +// #[test] +// fn resolve_literal_expr() { +// let src = r#" +// fn main(x : Field) { +// let y = 5; +// assert(y == x); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn multiple_resolution_errors() { +// let src = r#" +// fn main(x : Field) { +// let y = foo::bar(x); +// let z = y + a; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); +// +// // Errors are: +// // `a` is undeclared +// // `z` is unused +// // `foo::bar` does not exist +// for (compilation_error, _file_id) in errors { +// match compilation_error { +// CompilationError::ResolverError(err) => { +// match err { +// ResolverError::UnusedVariable { ident } => { +// assert_eq!(&ident.0.contents, "z"); +// } +// ResolverError::VariableNotDeclared { name, .. } => { +// assert_eq!(name, "a"); +// } +// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { +// assert_eq!(name.to_string(), "foo"); +// } +// _ => unimplemented!(), +// }; +// } +// _ => unimplemented!(), +// } +// } +// } +// +// #[test] +// fn resolve_prefix_expr() { +// let src = r#" +// fn main(x : Field) { +// let _y = -x; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_for_expr() { +// let src = r#" +// fn main(x : u64) { +// for i in 1..20 { +// let _z = x + i; +// }; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_call_expr() { +// let src = r#" +// fn main(x : Field) { +// let _z = foo(x); +// } +// +// fn foo(x : Field) -> Field { +// x +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_shadowing() { +// let src = r#" +// fn main(x : Field) { +// let x = foo(x); +// let x = x; +// let (x, x) = (x, x); +// let _ = x; +// } +// +// fn foo(x : Field) -> Field { +// x +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_basic_closure() { +// let src = r#" +// fn main(x : Field) -> pub Field { +// let closure = |y| y + x; +// closure(x) +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn resolve_simplified_closure() { +// // based on bug https://github.com/noir-lang/noir/issues/1088 +// +// let src = r#"fn do_closure(x: Field) -> Field { +// let y = x; +// let ret_capture = || { +// y +// }; +// ret_capture() +// } +// +// fn main(x: Field) { +// assert(do_closure(x) == 100); +// } +// +// "#; +// let parsed_captures = get_program_captures(src); +// let expected_captures = vec![vec!["y".to_string()]]; +// assert_eq!(expected_captures, parsed_captures); +// } +// +// #[test] +// fn resolve_complex_closures() { +// let src = r#" +// fn main(x: Field) -> pub Field { +// let closure_without_captures = |x: Field| -> Field { x + x }; +// let a = closure_without_captures(1); +// +// let closure_capturing_a_param = |y: Field| -> Field { y + x }; +// let b = closure_capturing_a_param(2); +// +// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; +// let c = closure_capturing_a_local_var(3); +// +// let closure_with_transitive_captures = |y: Field| -> Field { +// let d = 5; +// let nested_closure = |z: Field| -> Field { +// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; +// a + z + y + d + x + doubly_nested_closure(4) + x + y +// }; +// let res = nested_closure(5); +// res +// }; +// +// a + b + c + closure_with_transitive_captures(6) +// } +// "#; +// assert_no_errors(src); +// +// let expected_captures = vec![ +// vec![], +// vec!["x".to_string()], +// vec!["b".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string()], +// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], +// vec!["x".to_string(), "b".to_string()], +// ]; +// +// let parsed_captures = get_program_captures(src); +// +// assert_eq!(expected_captures, parsed_captures); +// } +// +// #[test] +// fn resolve_fmt_strings() { +// let src = r#" +// fn main() { +// let string = f"this is i: {i}"; +// println(string); +// +// println(f"I want to print {0}"); +// +// let new_val = 10; +// println(f"random_string{new_val}{new_val}"); +// } +// fn println(x : T) -> T { +// x +// } +// "#; +// +// let errors = get_program_errors(src); +// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); +// +// for (err, _file_id) in errors { +// match &err { +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { +// name, .. +// }) => { +// assert_eq!(name, "i"); +// } +// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { +// name, +// .. +// }) => { +// assert_eq!(name, "0"); +// } +// CompilationError::TypeError(TypeCheckError::UnusedResultError { +// expr_type: _, +// expr_span, +// }) => { +// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); +// assert!( +// a == "println(string)" +// || a == "println(f\"I want to print {0}\")" +// || a == "println(f\"random_string{new_val}{new_val}\")" +// ); +// } +// _ => unimplemented!(), +// }; +// } +// } +// +// fn check_rewrite(src: &str, expected: &str) { +// let (_program, mut context, _errors) = get_program(src); +// let main_func_id = context.def_interner.find_function("main").unwrap(); +// let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); +// assert!(format!("{}", program) == expected); +// } +// +// #[test] +// fn simple_closure_with_no_captured_variables() { +// let src = r#" +// fn main() -> pub Field { +// let x = 1; +// let closure = || x; +// closure() +// } +// "#; +// +// let expected_rewrite = r#"fn main$f0() -> Field { +// let x$0 = 1; +// let closure$3 = { +// let closure_variable$2 = { +// let env$1 = (x$l0); +// (env$l1, lambda$f1) +// }; +// closure_variable$l2 +// }; +// { +// let tmp$4 = closure$l3; +// tmp$l4.1(tmp$l4.0) +// } +// } +// fn lambda$f1(mut env$l1: (Field)) -> Field { +// env$l1.0 +// } +// "#; +// check_rewrite(src, expected_rewrite); +// } +// +// #[test] +// fn deny_cyclic_globals() { +// let src = r#" +// global A = B; +// global B = A; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn deny_cyclic_type_aliases() { +// let src = r#" +// type A = B; +// type B = A; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn ensure_nested_type_aliases_type_check() { +// let src = r#" +// type A = B; +// type B = u8; +// fn main() { +// let _a: A = 0 as u16; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn type_aliases_in_entry_point() { +// let src = r#" +// type Foo = u8; +// fn main(_x: Foo) {} +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn operators_in_global_used_in_type() { +// let src = r#" +// global ONE = 1; +// global COUNT = ONE + 2; +// fn main() { +// let _array: [Field; COUNT] = [1, 2, 3]; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn break_and_continue_in_constrained_fn() { +// let src = r#" +// fn main() { +// for i in 0 .. 10 { +// if i == 2 { +// continue; +// } +// if i == 5 { +// break; +// } +// } +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 2); +// } +// +// #[test] +// fn break_and_continue_outside_loop() { +// let src = r#" +// unconstrained fn main() { +// continue; +// break; +// } +// "#; +// assert_eq!(get_program_errors(src).len(), 2); +// } +// +// // Regression for #2540 +// #[test] +// fn for_loop_over_array() { +// let src = r#" +// fn hello(_array: [u1; N]) { +// for _ in 0..N {} +// } +// +// fn main() { +// let array: [u1; 2] = [0, 1]; +// hello(array); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// // Regression for #4545 +// #[test] +// fn type_aliases_in_main() { +// let src = r#" +// type Outer = [u8; N]; +// fn main(_arg: Outer<1>) {} +// "#; +// assert_eq!(get_program_errors(src).len(), 0); +// } +// +// #[test] +// fn ban_mutable_globals() { +// // Mutable globals are only allowed in a comptime context +// let src = r#" +// mut global FOO: Field = 0; +// fn main() {} +// "#; +// assert_eq!(get_program_errors(src).len(), 1); +// } +// +// #[test] +// fn deny_inline_attribute_on_unconstrained() { +// let src = r#" +// #[no_predicates] +// unconstrained pub fn foo(x: Field, y: Field) { +// assert(x != y); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) +// )); +// } +// +// #[test] +// fn deny_fold_attribute_on_unconstrained() { +// let src = r#" +// #[fold] +// unconstrained pub fn foo(x: Field, y: Field) { +// assert(x != y); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) +// )); +// } +// +// #[test] +// fn specify_function_types_with_turbofish() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// impl Default for u64 { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// fn generic_func() -> (T, U) where T: Default, U: Default { +// (T::default(), U::default()) +// } +// +// fn main() { +// let _ = generic_func::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn specify_method_types_with_turbofish() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// struct Foo { +// inner: T +// } +// +// impl Foo { +// fn generic_method(_self: Self) -> U where U: Default { +// U::default() +// } +// } +// +// fn main() { +// let foo: Foo = Foo { inner: 1 }; +// let _ = foo.generic_method::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn incorrect_turbofish_count_function_call() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// impl Default for u64 { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// fn generic_func() -> (T, U) where T: Default, U: Default { +// (T::default(), U::default()) +// } +// +// fn main() { +// let _ = generic_func::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), +// )); +// } +// +// #[test] +// fn incorrect_turbofish_count_method_call() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for Field { +// fn default() -> Self { 0 } +// } +// +// // Need the above as we don't have access to the stdlib here. +// // We also need to construct a concrete value of `U` without giving away its type +// // as otherwise the unspecified type is ignored. +// +// struct Foo { +// inner: T +// } +// +// impl Foo { +// fn generic_method(_self: Self) -> U where U: Default { +// U::default() +// } +// } +// +// fn main() { +// let foo: Foo = Foo { inner: 1 }; +// let _ = foo.generic_method::(); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), +// )); +// } +// +// #[test] +// fn struct_numeric_generic_in_function() { +// let src = r#" +// struct Foo { +// inner: u64 +// } +// +// pub fn bar() { } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } +// +// #[test] +// fn struct_numeric_generic_in_struct() { +// let src = r#" +// struct Foo { +// inner: u64 +// } +// +// struct Bar { } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::DefinitionError( +// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } +// ), +// )); +// } +// +// #[test] +// fn bool_numeric_generic() { +// let src = r#" +// pub fn read() -> Field { +// if N { +// 0 +// } else { +// 1 +// } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_binary_operation_type_mismatch() { +// let src = r#" +// pub fn foo() -> bool { +// let mut check: bool = true; +// check = N; +// check +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), +// )); +// } +// +// #[test] +// fn bool_generic_as_loop_bound() { +// let src = r#" +// pub fn read() { +// let mut fields = [0; N]; +// for i in 0..N { +// fields[i] = i + 1; +// } +// assert(fields[0] == 1); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 2); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), +// )); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, expr_typ, .. +// }) = &errors[1].0 +// else { +// panic!("Got an error other than a type mismatch"); +// }; +// +// assert_eq!(expected_typ, "Field"); +// assert_eq!(expr_typ, "bool"); +// } +// +// #[test] +// fn numeric_generic_in_function_signature() { +// let src = r#" +// pub fn foo(arr: [Field; N]) -> [Field; N] { arr } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_as_struct_field_type_fails() { +// let src = r#" +// struct Foo { +// a: Field, +// b: N, +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn normal_generic_as_array_length() { +// let src = r#" +// struct Foo { +// a: Field, +// b: [Field; N], +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_as_param_type() { +// let src = r#" +// pub fn foo(x: I) -> I { +// let _q: I = 5; +// x +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 3); +// +// // Error from the parameter type +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// // Error from the let statement annotated type +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// // Error from the return type +// assert!(matches!( +// errors[2].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_used_in_nested_type_fails() { +// let src = r#" +// struct Foo { +// a: Field, +// b: Bar, +// } +// struct Bar { +// inner: N +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn normal_generic_used_in_nested_array_length_fail() { +// let src = r#" +// struct Foo { +// a: Field, +// b: Bar, +// } +// struct Bar { +// inner: [Field; N] +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// } +// +// #[test] +// fn numeric_generic_used_in_nested_type_pass() { +// // The order of these structs should not be changed to make sure +// // that we are accurately resolving all struct generics before struct fields +// let src = r#" +// struct NestedNumeric { +// a: Field, +// b: InnerNumeric +// } +// struct InnerNumeric { +// inner: [u64; N], +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_trait() { +// // We want to make sure that `N` in `impl Deserialize` does +// // not trigger `expected type, found numeric generic parameter N` as the trait +// // does in fact expect a numeric generic. +// let src = r#" +// struct MyType { +// a: Field, +// b: Field, +// c: Field, +// d: T, +// } +// +// impl Deserialize for MyType { +// fn deserialize(fields: [Field; N], other: T) -> Self { +// MyType { a: fields[0], b: fields[1], c: fields[2], d: other } +// } +// } +// +// trait Deserialize { +// fn deserialize(fields: [Field; N], other: T) -> Self; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_in_trait_impl_with_extra_impl_generics() { +// let src = r#" +// trait Default { +// fn default() -> Self; +// } +// +// struct MyType { +// a: Field, +// b: Field, +// c: Field, +// d: T, +// } +// +// // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. +// // `N` is used first in the trait impl generics (`Deserialize for MyType`). +// // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind +// // while `T` has a normal kind. +// impl Deserialize for MyType where T: Default { +// fn deserialize(fields: [Field; N]) -> Self { +// MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } +// } +// } +// +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_where_clause() { +// let src = r#" +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// +// pub fn read() -> T where T: Deserialize { +// let mut fields: [Field; N] = [0; N]; +// for i in 0..N { +// fields[i] = i as Field + 1; +// } +// T::deserialize(fields) +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn numeric_generic_used_in_turbofish() { +// let src = r#" +// pub fn double() -> u32 { +// // Used as an expression +// N * 2 +// } +// +// pub fn double_numeric_generics_test() { +// // Example usage of a numeric generic arguments. +// assert(double::<9>() == 18); +// assert(double::<7 + 8>() == 30); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn constant_used_with_numeric_generic() { +// let src = r#" +// struct ValueNote { +// value: Field, +// } +// +// trait Serialize { +// fn serialize(self) -> [Field; N]; +// } +// +// impl Serialize<1> for ValueNote { +// fn serialize(self) -> [Field; 1] { +// [self.value] +// } +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn normal_generic_used_when_numeric_expected_in_where_clause() { +// let src = r#" +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// +// pub fn read() -> T where T: Deserialize { +// T::deserialize([0, 1]) +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// +// let src = r#" +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } +// +// pub fn read() -> T where T: Deserialize { +// let mut fields: [Field; N] = [0; N]; +// for i in 0..N { +// fields[i] = i as Field + 1; +// } +// T::deserialize(fields) +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 4); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// assert!(matches!( +// errors[1].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// assert!(matches!( +// errors[2].0, +// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), +// )); +// // N +// assert!(matches!( +// errors[3].0, +// CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), +// )); +// } #[test] -fn can_use_pub_use_item() { +fn numeric_generics_type_kind_mismatch() { let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - pub use bar::baz; - } - - fn main() { - foo::baz(); + fn foo() -> u16 { + N as u16 } - "#; - assert_no_errors(src); -} -#[test] -fn warns_on_re_export_of_item_with_less_visibility() { - let src = r#" - mod foo { - mod bar { - pub(crate) fn baz() {} - } + global J: u16 = 10; - pub use bar::baz; + fn bar() -> u16 { + foo::() } + global M: u16 = 3; + fn main() { - foo::baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); -} - -#[test] -fn unquoted_integer_as_integer_token() { - let src = r#" - trait Serialize { - fn serialize() {} - } - - #[attr] - pub fn foobar() {} - - comptime fn attr(_f: FunctionDefinition) -> Quoted { - let serialized_len = 1; - // We are testing that when we unquote $serialized_len, it's unquoted - // as the token `1` and not as something else that later won't be parsed correctly - // in the context of a generic argument. - quote { - impl Serialize<$serialized_len> for Field { - fn serialize() { } - } - } - } - - fn main() {} - "#; - - assert_no_errors(src); -} - -#[test] -fn errors_on_unused_function() { - let src = r#" - contract some_contract { - // This function is unused, but it's a contract entrypoint - // so it should not produce a warning - fn foo() -> pub Field { - 1 - } - } - - - fn foo() { - bar(); - } - - fn bar() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(*item_type, "function"); -} - -#[test] -fn constrained_reference_to_unconstrained() { - let src = r#" - fn main(mut x: u32, y: pub u32) { - let x_ref = &mut x; - if x == 5 { - unsafe { - mut_ref_input(x_ref, y); - } - } - - assert(x == 10); - } - - unconstrained fn mut_ref_input(x: &mut u32, y: u32) { - *x = y; + let _ = bar::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; -} -#[test] -fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); + // TODO cleanup + dbg!(&errors); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), )); } + +// TODO re-enable +// #[test] +// fn numeric_generics_value_kind_mismatch_u32_u64() { +// let src = r#" +// struct BoundedVec { +// storage: [T; MaxLen], +// // can't be compared to MaxLen: u32 +// // can't be used to index self.storage +// len: u64, +// } +// +// impl BoundedVec { +// pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { +// // We do this to avoid an unused variable warning on `self` +// let _ = self.len; +// for _ in 0..Len { } +// } +// +// pub fn push(&mut self, elem: T) { +// assert(self.len < MaxLen, "push out of bounds"); +// self.storage[self.len] = elem; +// self.len += 1; +// } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::IntegerBitWidth { +// bit_width_x: IntegerBitSize::SixtyFour, +// bit_width_y: IntegerBitSize::ThirtyTwo, +// .. +// }), +// )); +// } +// +// #[test] +// fn quote_code_fragments() { +// // This test ensures we can quote (and unquote/splice) code fragments +// // which by themselves are not valid code. They only need to be valid +// // by the time they are unquoted into the macro's call site. +// let src = r#" +// fn main() { +// comptime { +// concat!(quote { assert( }, quote { false); }); +// } +// } +// +// comptime fn concat(a: Quoted, b: Quoted) -> Quoted { +// quote { $a $b } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use InterpreterError::FailingConstraint; +// assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +// } +// +// #[test] +// fn impl_stricter_than_trait_no_trait_method_constraints() { +// // This test ensures that the error we get from the where clause on the trait impl method +// // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. +// let src = r#" +// trait Serialize { +// // We want to make sure we trigger the error when override a trait method +// // which itself has no trait constraints. +// fn serialize(self) -> [Field; N]; +// } +// +// trait ToField { +// fn to_field(self) -> Field; +// } +// +// fn process_array(array: [Field; N]) -> Field { +// array[0] +// } +// +// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { +// thing.serialize() +// } +// +// struct MyType { +// a: T, +// b: T, +// } +// +// impl Serialize<2> for MyType { +// fn serialize(self) -> [Field; 2] where T: ToField { +// [ self.a.to_field(), self.b.to_field() ] +// } +// } +// +// impl MyType { +// fn do_thing_with_serialization_with_extra_steps(self) -> Field { +// process_array(serialize_thing(self)) +// } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// &errors[0].0, +// CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) +// )); +// } +// +// #[test] +// fn impl_stricter_than_trait_different_generics() { +// let src = r#" +// trait Default { } +// +// // Object type of the trait constraint differs +// trait Foo { +// fn foo_good() where T: Default; +// +// fn foo_bad() where T: Default; +// } +// +// impl Foo for () { +// fn foo_good() where A: Default {} +// +// fn foo_bad() where B: Default {} +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "B")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_object_generics() { +// let src = r#" +// trait MyTrait { } +// +// trait OtherTrait {} +// +// struct Option { +// inner: T +// } +// +// struct OtherOption { +// inner: Option, +// } +// +// trait Bar { +// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; +// +// fn array_good() where [T; 8]: MyTrait; +// +// fn array_bad() where [T; 8]: MyTrait; +// +// fn tuple_good() where (Option, Option): MyTrait; +// +// fn tuple_bad() where (Option, Option): MyTrait; +// } +// +// impl Bar for () { +// fn bar_good() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn bar_bad() +// where +// OtherOption>: OtherTrait, +// Option: MyTrait { } +// +// fn array_good() where [A; 8]: MyTrait { } +// +// fn array_bad() where [B; 8]: MyTrait { } +// +// fn tuple_good() where (Option, Option): MyTrait { } +// +// fn tuple_bad() where (Option, Option): MyTrait { } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 3); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[1].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[2].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); +// assert!(matches!(constraint_name.as_str(), "MyTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_trait() { +// let src = r#" +// trait Default { } +// +// trait OtherDefault { } +// +// struct Option { +// inner: T +// } +// +// trait Bar { +// fn bar() where Option: Default; +// } +// +// impl Bar for () { +// // Trait constraint differs due to the trait even though the constraint +// // types are the same. +// fn bar() where Option: OtherDefault {} +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); +// assert!(matches!(constraint_name.as_str(), "OtherDefault")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn trait_impl_where_clause_stricter_pass() { +// let src = r#" +// trait MyTrait { +// fn good_foo() where H: OtherTrait; +// +// fn bad_foo() where H: OtherTrait; +// } +// +// trait OtherTrait {} +// +// struct Option { +// inner: T +// } +// +// impl MyTrait for [T] where Option: MyTrait { +// fn good_foo() where B: OtherTrait { } +// +// fn bad_foo() where A: OtherTrait { } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "A")); +// assert!(matches!(constraint_name.as_str(), "OtherTrait")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_stricter_than_trait_different_trait_generics() { +// let src = r#" +// trait Foo { +// fn foo() where T: T2; +// } +// +// impl Foo for () { +// // Should be A: T2 +// fn foo() where A: T2 {} +// } +// +// trait T2 {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { +// constraint_typ, +// constraint_name, +// constraint_generics, +// .. +// }) = &errors[0].0 +// { +// assert!(matches!(constraint_typ.to_string().as_str(), "A")); +// assert!(matches!(constraint_name.as_str(), "T2")); +// assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); +// } else { +// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn impl_not_found_for_inner_impl() { +// // We want to guarantee that we get a no impl found error +// let src = r#" +// trait Serialize { +// fn serialize(self) -> [Field; N]; +// } +// +// trait ToField { +// fn to_field(self) -> Field; +// } +// +// fn process_array(array: [Field; N]) -> Field { +// array[0] +// } +// +// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { +// thing.serialize() +// } +// +// struct MyType { +// a: T, +// b: T, +// } +// +// impl Serialize<2> for MyType where T: ToField { +// fn serialize(self) -> [Field; 2] { +// [ self.a.to_field(), self.b.to_field() ] +// } +// } +// +// impl MyType { +// fn do_thing_with_serialization_with_extra_steps(self) -> Field { +// process_array(serialize_thing(self)) +// } +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// &errors[0].0, +// CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) +// )); +// } +// +// // Regression for #5388 +// #[test] +// fn comptime_let() { +// let src = r#"fn main() { +// comptime let my_var = 2; +// assert_eq(my_var, 2); +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 0); +// } +// +// #[test] +// fn overflowing_u8() { +// let src = r#" +// fn main() { +// let _: u8 = 256; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `2⁸` cannot fit into `u8` which has range `0..=255`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn underflowing_u8() { +// let src = r#" +// fn main() { +// let _: u8 = -1; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `-1` cannot fit into `u8` which has range `0..=255`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn overflowing_i8() { +// let src = r#" +// fn main() { +// let _: i8 = 128; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn underflowing_i8() { +// let src = r#" +// fn main() { +// let _: i8 = -129; +// }"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// if let CompilationError::TypeError(error) = &errors[0].0 { +// assert_eq!( +// error.to_string(), +// "The value `-129` cannot fit into `i8` which has range `-128..=127`" +// ); +// } else { +// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); +// } +// } +// +// #[test] +// fn turbofish_numeric_generic_nested_call() { +// // Check for turbofish numeric generics used with function calls +// let src = r#" +// fn foo() -> [u8; N] { +// [0; N] +// } +// +// fn bar() -> [u8; N] { +// foo::() +// } +// +// global M: u32 = 3; +// +// fn main() { +// let _ = bar::(); +// } +// "#; +// assert_no_errors(src); +// +// // Check for turbofish numeric generics used with method calls +// let src = r#" +// struct Foo { +// a: T +// } +// +// impl Foo { +// fn static_method() -> [u8; N] { +// [0; N] +// } +// +// fn impl_method(self) -> [T; N] { +// [self.a; N] +// } +// } +// +// fn bar() -> [u8; N] { +// let _ = Foo::static_method::(); +// let x: Foo = Foo { a: 0 }; +// x.impl_method::() +// } +// +// global M: u32 = 3; +// +// fn main() { +// let _ = bar::(); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn use_super() { +// let src = r#" +// fn some_func() {} +// +// mod foo { +// use super::some_func; +// +// pub fn bar() { +// some_func(); +// } +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn use_super_in_path() { +// let src = r#" +// fn some_func() {} +// +// mod foo { +// pub fn func() { +// super::some_func(); +// } +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn no_super() { +// let src = "use super::some_func;"; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( +// PathResolutionError::NoSuper(span), +// )) = &errors[0].0 +// else { +// panic!("Expected a 'no super' error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(span.start(), 4); +// assert_eq!(span.end(), 9); +// } +// +// #[test] +// fn cannot_call_unconstrained_function_outside_of_unsafe() { +// let src = r#" +// fn main() { +// foo(); +// } +// +// unconstrained fn foo() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { +// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { +// let src = r#" +// fn main() { +// let func = foo; +// // Warning should trigger here +// func(); +// inner(func); +// } +// +// fn inner(x: unconstrained fn() -> ()) { +// // Warning should trigger here +// x(); +// } +// +// unconstrained fn foo() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 2); +// +// for error in &errors { +// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { +// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); +// }; +// } +// } +// +// #[test] +// fn missing_unsafe_block_when_needing_type_annotations() { +// // This test is a regression check that even when an unsafe block is missing +// // that we still appropriately continue type checking and infer type annotations. +// let src = r#" +// fn main() { +// let z = BigNum { limbs: [2, 0, 0] }; +// assert(z.__is_zero() == false); +// } +// +// struct BigNum { +// limbs: [u64; N], +// } +// +// impl BigNum { +// unconstrained fn __is_zero_impl(self) -> bool { +// let mut result: bool = true; +// for i in 0..N { +// result = result & (self.limbs[i] == 0); +// } +// result +// } +// } +// +// trait BigNumTrait { +// fn __is_zero(self) -> bool; +// } +// +// impl BigNumTrait for BigNum { +// fn __is_zero(self) -> bool { +// self.__is_zero_impl() +// } +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { +// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn cannot_pass_unconstrained_function_to_regular_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_regular(func); +// } +// +// unconstrained fn foo() {} +// +// fn expect_regular(_func: fn() -> ()) { +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { +// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn cannot_assign_unconstrained_and_regular_fn_to_variable() { +// let src = r#" +// fn main() { +// let _func = if true { foo } else { bar }; +// } +// +// fn foo() {} +// unconstrained fn bar() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { +// panic!("Expected a context error, got {:?}", errors[0].0); +// }; +// +// if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { +// assert_eq!(expected_typ, "fn() -> ()"); +// assert_eq!(expr_typ, "unconstrained fn() -> ()"); +// } else { +// panic!("Expected a type mismatch error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn can_pass_regular_function_to_unconstrained_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_unconstrained(func); +// } +// +// fn foo() {} +// +// fn expect_unconstrained(_func: unconstrained fn() -> ()) {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn cannot_pass_unconstrained_function_to_constrained_function() { +// let src = r#" +// fn main() { +// let func = foo; +// expect_regular(func); +// } +// +// unconstrained fn foo() {} +// +// fn expect_regular(_func: fn() -> ()) {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { +// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { +// let src = r#" +// fn main() { +// let _func: unconstrained fn() -> () = foo; +// } +// +// fn foo() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { +// let src = r#" +// fn main() { +// let _ = Foo { func: foo }; +// } +// +// fn foo() {} +// +// struct Foo { +// func: unconstrained fn() -> (), +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn trait_impl_generics_count_mismatch() { +// let src = r#" +// trait Foo {} +// +// impl Foo<()> for Field {} +// +// fn main() {}"#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// item, +// expected, +// found, +// .. +// }) = &errors[0].0 +// else { +// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(item, "Foo"); +// assert_eq!(*expected, 0); +// assert_eq!(*found, 1); +// } +// +// #[test] +// fn bit_not_on_untyped_integer() { +// let src = r#" +// fn main() { +// let _: u32 = 3 & !1; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn duplicate_struct_field() { +// let src = r#" +// struct Foo { +// x: i32, +// x: i32, +// } +// +// fn main() {} +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { +// first_def, +// second_def, +// }) = &errors[0].0 +// else { +// panic!("Expected a duplicate field error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(first_def.to_string(), "x"); +// assert_eq!(second_def.to_string(), "x"); +// +// assert_eq!(first_def.span().start(), 26); +// assert_eq!(second_def.span().start(), 42); +// } +// +// #[test] +// fn trait_constraint_on_tuple_type() { +// let src = r#" +// trait Foo { +// fn foo(self, x: A) -> bool; +// } +// +// pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { +// x.foo(y) +// } +// +// fn main() {}"#; +// assert_no_errors(src); +// } +// +// #[test] +// fn turbofish_in_constructor_generics_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let _ = Foo:: { x: 1 }; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), +// )); +// } +// +// #[test] +// fn turbofish_in_constructor() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let x: Field = 0; +// let _ = Foo:: { x: x }; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatch { +// expected_typ, expr_typ, .. +// }) = &errors[0].0 +// else { +// panic!("Expected a type mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(expected_typ, "i32"); +// assert_eq!(expr_typ, "Field"); +// } +// +// #[test] +// fn turbofish_in_middle_of_variable_unsupported_yet() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// impl Foo { +// fn new(x: T) -> Self { +// Foo { x } +// } +// } +// +// fn main() { +// let _ = Foo::::new(1); +// } +// "#; +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), +// )); +// } +// +// #[test] +// fn turbofish_in_struct_pattern() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value: Field = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn turbofish_in_struct_pattern_errors_if_type_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value: Field = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 +// else { +// panic!("Expected a type mismatch error, got {:?}", errors[0].0); +// }; +// } +// +// #[test] +// fn turbofish_in_struct_pattern_generic_count_mismatch() { +// let src = r#" +// struct Foo { +// x: T +// } +// +// fn main() { +// let value = 0; +// let Foo:: { x } = Foo { x: value }; +// let _ = x; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// item, +// expected, +// found, +// .. +// }) = &errors[0].0 +// else { +// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(item, "struct Foo"); +// assert_eq!(*expected, 1); +// assert_eq!(*found, 2); +// } +// +// #[test] +// fn incorrect_generic_count_on_struct_impl() { +// let src = r#" +// struct Foo {} +// impl Foo {} +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// found, expected, .. +// }) = errors[0].0 +// else { +// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(found, 1); +// assert_eq!(expected, 0); +// } +// +// #[test] +// fn incorrect_generic_count_on_type_alias() { +// let src = r#" +// struct Foo {} +// type Bar = Foo; +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { +// found, expected, .. +// }) = errors[0].0 +// else { +// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(found, 1); +// assert_eq!(expected, 0); +// } +// +// #[test] +// fn uses_self_type_for_struct_function_call() { +// let src = r#" +// struct S { } +// +// impl S { +// fn one() -> Field { +// 1 +// } +// +// fn two() -> Field { +// Self::one() + Self::one() +// } +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn uses_self_type_inside_trait() { +// let src = r#" +// trait Foo { +// fn foo() -> Self { +// Self::bar() +// } +// +// fn bar() -> Self; +// } +// +// impl Foo for Field { +// fn bar() -> Self { +// 1 +// } +// } +// +// fn main() { +// let _: Field = Foo::foo(); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn uses_self_type_in_trait_where_clause() { +// let src = r#" +// trait Trait { +// fn trait_func() -> bool; +// } +// +// trait Foo where Self: Trait { +// fn foo(self) -> bool { +// self.trait_func() +// } +// } +// +// struct Bar { +// +// } +// +// impl Foo for Bar { +// +// } +// +// fn main() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = +// &errors[0].0 +// else { +// panic!("Expected an unresolved method call error, got {:?}", errors[0].0); +// }; +// +// assert_eq!(method_name, "trait_func"); +// } +// +// #[test] +// fn do_not_eagerly_error_on_cast_on_type_variable() { +// let src = r#" +// pub fn foo(x: T, f: fn(T) -> U) -> U { +// f(x) +// } +// +// fn main() { +// let x: u8 = 1; +// let _: Field = foo(x, |x| x as Field); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn error_on_cast_over_type_variable() { +// let src = r#" +// pub fn foo(x: T, f: fn(T) -> U) -> U { +// f(x) +// } +// +// fn main() { +// let x = "a"; +// let _: Field = foo(x, |x| x as Field); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// errors[0].0, +// CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) +// )); +// } +// +// #[test] +// fn trait_impl_for_a_type_that_implements_another_trait() { +// let src = r#" +// trait One { +// fn one(self) -> i32; +// } +// +// impl One for i32 { +// fn one(self) -> i32 { +// self +// } +// } +// +// trait Two { +// fn two(self) -> i32; +// } +// +// impl Two for T where T: One { +// fn two(self) -> i32 { +// self.one() + 1 +// } +// } +// +// pub fn use_it(t: T) -> i32 where T: Two { +// Two::two(t) +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { +// let src = r#" +// trait One { +// fn one(self) -> i32; +// } +// +// impl One for i32 { +// fn one(self) -> i32 { +// let _ = self; +// 1 +// } +// } +// +// trait Two { +// fn two(self) -> i32; +// } +// +// impl Two for T where T: One { +// fn two(self) -> i32 { +// self.one() + 1 +// } +// } +// +// impl Two for u32 { +// fn two(self) -> i32 { +// let _ = self; +// 0 +// } +// } +// +// pub fn use_it(t: u32) -> i32 { +// Two::two(t) +// } +// +// fn main() {} +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn impl_missing_associated_type() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// impl Foo for () {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) +// )); +// } +// +// #[test] +// fn as_trait_path_syntax_resolves_outside_impl() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// struct Bar {} +// +// impl Foo for Bar { +// type Assoc = i32; +// } +// +// fn main() { +// // AsTraitPath syntax is a bit silly when associated types +// // are explicitly specified +// let _: i64 = 1 as >::Assoc; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use CompilationError::TypeError; +// use TypeCheckError::TypeMismatch; +// let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { +// panic!("Expected TypeMismatch error, found {:?}", errors[0].0); +// }; +// +// assert_eq!(expected_typ, "i64".to_string()); +// assert_eq!(expr_typ, "i32".to_string()); +// } +// +// #[test] +// fn as_trait_path_syntax_no_impl() { +// let src = r#" +// trait Foo { +// type Assoc; +// } +// +// struct Bar {} +// +// impl Foo for Bar { +// type Assoc = i32; +// } +// +// fn main() { +// let _: i64 = 1 as >::Assoc; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// use CompilationError::TypeError; +// assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); +// } +// +// #[test] +// fn errors_on_unused_private_import() { +// let src = r#" +// mod foo { +// pub fn bar() {} +// pub fn baz() {} +// +// trait Foo { +// } +// } +// +// use foo::bar; +// use foo::baz; +// use foo::Foo; +// +// impl Foo for Field { +// } +// +// fn main() { +// baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "bar"); +// assert_eq!(*item_type, "import"); +// } +// +// #[test] +// fn errors_on_unused_pub_crate_import() { +// let src = r#" +// mod foo { +// pub fn bar() {} +// pub fn baz() {} +// +// trait Foo { +// } +// } +// +// pub(crate) use foo::bar; +// use foo::baz; +// use foo::Foo; +// +// impl Foo for Field { +// } +// +// fn main() { +// baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "bar"); +// assert_eq!(*item_type, "import"); +// } +// +// #[test] +// fn warns_on_use_of_private_exported_item() { +// let src = r#" +// mod foo { +// mod bar { +// pub fn baz() {} +// } +// +// use bar::baz; +// +// pub fn qux() { +// baz(); +// } +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::ResolverError(ResolverError::PathResolutionError( +// PathResolutionError::Private(..), +// )) +// )); +// } +// +// #[test] +// fn can_use_pub_use_item() { +// let src = r#" +// mod foo { +// mod bar { +// pub fn baz() {} +// } +// +// pub use bar::baz; +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// assert_no_errors(src); +// } +// +// #[test] +// fn warns_on_re_export_of_item_with_less_visibility() { +// let src = r#" +// mod foo { +// mod bar { +// pub(crate) fn baz() {} +// } +// +// pub use bar::baz; +// } +// +// fn main() { +// foo::baz(); +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// assert!(matches!( +// &errors[0].0, +// CompilationError::DefinitionError( +// DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } +// ) +// )); +// } +// +// #[test] +// fn unquoted_integer_as_integer_token() { +// let src = r#" +// trait Serialize { +// fn serialize() {} +// } +// +// #[attr] +// pub fn foobar() {} +// +// comptime fn attr(_f: FunctionDefinition) -> Quoted { +// let serialized_len = 1; +// // We are testing that when we unquote $serialized_len, it's unquoted +// // as the token `1` and not as something else that later won't be parsed correctly +// // in the context of a generic argument. +// quote { +// impl Serialize<$serialized_len> for Field { +// fn serialize() { } +// } +// } +// } +// +// fn main() {} +// "#; +// +// assert_no_errors(src); +// } +// +// #[test] +// fn errors_on_unused_function() { +// let src = r#" +// contract some_contract { +// // This function is unused, but it's a contract entrypoint +// // so it should not produce a warning +// fn foo() -> pub Field { +// 1 +// } +// } +// +// +// fn foo() { +// bar(); +// } +// +// fn bar() {} +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = +// &errors[0].0 +// else { +// panic!("Expected an unused item error"); +// }; +// +// assert_eq!(ident.to_string(), "foo"); +// assert_eq!(*item_type, "function"); +// } +// +// #[test] +// fn constrained_reference_to_unconstrained() { +// let src = r#" +// fn main(mut x: u32, y: pub u32) { +// let x_ref = &mut x; +// if x == 5 { +// unsafe { +// mut_ref_input(x_ref, y); +// } +// } +// +// assert(x == 10); +// } +// +// unconstrained fn mut_ref_input(x: &mut u32, y: u32) { +// *x = y; +// } +// "#; +// +// let errors = get_program_errors(src); +// assert_eq!(errors.len(), 1); +// +// let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = +// &errors[0].0 +// else { +// panic!("Expected an error about passing a constrained reference to unconstrained"); +// }; +// } +// +// #[test] +// fn comptime_type_in_runtime_code() { +// let source = "pub fn foo(_f: FunctionDefinition) {}"; +// let errors = get_program_errors(source); +// assert_eq!(errors.len(), 1); +// assert!(matches!( +// errors[0].0, +// CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) +// )); +// } diff --git a/compiler/noirc_frontend/src/tests/name_shadowing.rs b/compiler/noirc_frontend/src/tests/name_shadowing.rs index b0d83510039..abc3415efe9 100644 --- a/compiler/noirc_frontend/src/tests/name_shadowing.rs +++ b/compiler/noirc_frontend/src/tests/name_shadowing.rs @@ -2,418 +2,419 @@ use super::get_program_errors; use std::collections::HashSet; -#[test] -fn test_name_shadowing() { - let src = " - trait Default { - fn default() -> Self; - } - - impl Default for bool { - fn default() -> bool { - false - } - } - - impl Default for Field { - fn default() -> Field { - 0 - } - } - - impl Default for [T; N] where T: Default { - fn default() -> [T; N] { - [Default::default(); N] - } - } - - impl Default for (T, U) where T: Default, U: Default { - fn default() -> (T, U) { - (Default::default(), Default::default()) - } - } - - fn drop_var(_x: T, y: U) -> U { y } - - mod local_module { - use crate::{Default, drop_var}; - - global LOCAL_GLOBAL_N: Field = 0; - - global LOCAL_GLOBAL_M: Field = 1; - - struct LocalStruct { - field1: A, - field2: B, - field3: [A; N], - field4: ([A; N], [B; M]), - field5: &mut A, - } - - impl Default for LocalStruct where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - field1: Default::default(), - field2: Default::default(), - field3: Default::default(), - field4: Default::default(), - field5: mut_field, - } - } - } - - trait DefinedInLocalModule1 { - fn trait_fn1(self, x: A); - fn trait_fn2(self, y: B); - fn trait_fn3(&mut self, x: A, y: B); - fn trait_fn4(self, x: [A; 0], y: [B]); - fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl DefinedInLocalModule1 for LocalStruct { - fn trait_fn1(self, _x: A) { drop_var(self, ()) } - fn trait_fn2(self, _y: B) { drop_var(self, ()) } - fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); - let x: Field = 0; - assert(x == 0); - let x: Field = 1; - assert(x == 1); - [] - } - } - - mod library { - use crate::{Default, drop_var}; - - mod library2 { - use crate::{Default, drop_var}; - - global IMPORT_GLOBAL_N_2: Field = 4; - - global IMPORT_GLOBAL_M_2: Field = 5; - - // When we re-export this type from another library and then use it in - // main, we get a panic - struct ReExportMeFromAnotherLib1 { - x : Field, - } - - struct PubLibLocalStruct3 { - pub_field1: A, - pub_field2: B, - pub_field3: [A; N], - pub_field4: ([A; N], [B; M]), - pub_field5: &mut A, - } - - impl Default for PubLibLocalStruct3 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - pub_field1: Default::default(), - pub_field2: Default::default(), - pub_field3: Default::default(), - pub_field4: Default::default(), - pub_field5: mut_field, - } - } - } - - trait PubLibDefinedInLocalModule3 { - fn pub_trait_fn1(self, x: A); - fn pub_trait_fn2(self, y: B); - fn pub_trait_fn3(&mut self, x: A, y: B); - fn pub_trait_fn4(self, x: [A; 0], y: [B]); - fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { - fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); - [] - } - } - - // Re-export - use library2::ReExportMeFromAnotherLib1; - - global IMPORT_GLOBAL_N_1: Field = 2; - - global IMPORT_GLOBAL_M_1: Field = 3; - - struct LibLocalStruct1 { - lib_field1: A, - lib_field2: B, - lib_field3: [A; N], - lib_field4: ([A; N], [B; M]), - lib_field5: &mut A, - } - - impl Default for LibLocalStruct1 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - lib_field1: Default::default(), - lib_field2: Default::default(), - lib_field3: Default::default(), - lib_field4: Default::default(), - lib_field5: mut_field, - } - } - } - - trait LibDefinedInLocalModule1 { - fn lib_trait_fn1(self, x: A); - fn lib_trait_fn2(self, y: B); - fn lib_trait_fn3(&mut self, x: A, y: B); - fn lib_trait_fn4(self, x: [A; 0], y: [B]); - fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl LibDefinedInLocalModule1 for LibLocalStruct1 { - fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } - } - - pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); - [] - } - } - - mod library3 { - use crate::{Default, drop_var}; - - global IMPORT_GLOBAL_N_3: Field = 6; - - global IMPORT_GLOBAL_M_3: Field = 7; - - struct ReExportMeFromAnotherLib2 { - x : Field, - } - - struct PubCrateLibLocalStruct2 { - crate_field1: A, - crate_field2: B, - crate_field3: [A; N], - crate_field4: ([A; N], [B; M]), - crate_field5: &mut A, - } - - impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { - fn default() -> Self { - let mut mut_field = &mut Default::default(); - Self { - crate_field1: Default::default(), - crate_field2: Default::default(), - crate_field3: Default::default(), - crate_field4: Default::default(), - crate_field5: mut_field, - } - } - } - - trait PubCrateLibDefinedInLocalModule2 { - fn crate_trait_fn1(self, x: A); - fn crate_trait_fn2(self, y: B); - fn crate_trait_fn3(&mut self, x: A, y: B); - fn crate_trait_fn4(self, x: [A; 0], y: [B]); - fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; - fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { - drop_var(self, N + M) - } - } - - impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { - fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } - fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } - fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } - fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } - fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } - fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } - } - - pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { - assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); - [] - } - } - - - use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; - - use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; - - // overlapping - // use library::library2::ReExportMeFromAnotherLib1; - use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; - - use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; - - - fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { - assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); - assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); - assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); - assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); - - let x: LocalStruct = Default::default(); - assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); - assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); - - let x: LibLocalStruct1 = Default::default(); - assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); - assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); - - let x: PubLibLocalStruct3 = Default::default(); - assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); - assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); - - let x: PubCrateLibLocalStruct2 = Default::default(); - assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); - assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); - - assert(drop_var(local_fn2((0, 1), [], []), true)); - assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); - assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); - assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); - }"; - - // NOTE: these names must be "replacement-unique", i.e. - // replacing one in a discinct name should do nothing - let names_to_collapse = [ - "DefinedInLocalModule1", - "IMPORT_GLOBAL_M_1", - "IMPORT_GLOBAL_M_2", - "IMPORT_GLOBAL_M_3", - "IMPORT_GLOBAL_N_1", - "IMPORT_GLOBAL_N_2", - "IMPORT_GLOBAL_N_3", - "LOCAL_GLOBAL_M", - "LOCAL_GLOBAL_N", - "LibDefinedInLocalModule1", - "LibLocalStruct1", - "Liblocal_fn1", - "LocalStruct", - "PubCrateLibDefinedInLocalModule2", - "PubCrateLibLocalStruct2", - "PubCrateLiblocal_fn2", - "PubLibDefinedInLocalModule3", - "PubLibLocalStruct3", - "PubLiblocal_fn3", - "ReExportMeFromAnotherLib1", - "ReExportMeFromAnotherLib2", - "local_fn4", - "crate_field1", - "crate_field2", - "crate_field3", - "crate_field4", - "crate_field5", - "crate_trait_fn1", - "crate_trait_fn2", - "crate_trait_fn3", - "crate_trait_fn4", - "crate_trait_fn5", - "crate_trait_fn6", - "crate_trait_fn7", - "field1", - "field2", - "field3", - "field4", - "field5", - "lib_field1", - "lib_field2", - "lib_field3", - "lib_field4", - "lib_field5", - "lib_trait_fn1", - "lib_trait_fn2", - "lib_trait_fn3", - "lib_trait_fn4", - "lib_trait_fn5", - "lib_trait_fn6", - "lib_trait_fn7", - "pub_field1", - "pub_field2", - "pub_field3", - "pub_field4", - "pub_field5", - "pub_trait_fn1", - "pub_trait_fn2", - "pub_trait_fn3", - "pub_trait_fn4", - "pub_trait_fn5", - "pub_trait_fn6", - "pub_trait_fn7", - "trait_fn1", - "trait_fn2", - "trait_fn3", - "trait_fn4", - "trait_fn5", - "trait_fn6", - "trait_fn7", - ]; - - // TODO(https://github.com/noir-lang/noir/issues/4973): - // Name resolution panic from name shadowing test - let cases_to_skip = [ - (1, 21), - (2, 11), - (2, 21), - (3, 11), - (3, 18), - (3, 21), - (4, 21), - (5, 11), - (5, 21), - (6, 11), - (6, 18), - (6, 21), - ]; - let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); - - for (i, x) in names_to_collapse.iter().enumerate() { - for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { - if !cases_to_skip.contains(&(i, j)) { - dbg!((i, j)); - - let modified_src = src.replace(x, y); - let errors = get_program_errors(&modified_src); - assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); - } - } - } -} +// TODO re-enable +// #[test] +// fn test_name_shadowing() { +// let src = " +// trait Default { +// fn default() -> Self; +// } +// +// impl Default for bool { +// fn default() -> bool { +// false +// } +// } +// +// impl Default for Field { +// fn default() -> Field { +// 0 +// } +// } +// +// impl Default for [T; N] where T: Default { +// fn default() -> [T; N] { +// [Default::default(); N] +// } +// } +// +// impl Default for (T, U) where T: Default, U: Default { +// fn default() -> (T, U) { +// (Default::default(), Default::default()) +// } +// } +// +// fn drop_var(_x: T, y: U) -> U { y } +// +// mod local_module { +// use crate::{Default, drop_var}; +// +// global LOCAL_GLOBAL_N: Field = 0; +// +// global LOCAL_GLOBAL_M: Field = 1; +// +// struct LocalStruct { +// field1: A, +// field2: B, +// field3: [A; N], +// field4: ([A; N], [B; M]), +// field5: &mut A, +// } +// +// impl Default for LocalStruct where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// field1: Default::default(), +// field2: Default::default(), +// field3: Default::default(), +// field4: Default::default(), +// field5: mut_field, +// } +// } +// } +// +// trait DefinedInLocalModule1 { +// fn trait_fn1(self, x: A); +// fn trait_fn2(self, y: B); +// fn trait_fn3(&mut self, x: A, y: B); +// fn trait_fn4(self, x: [A; 0], y: [B]); +// fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl DefinedInLocalModule1 for LocalStruct { +// fn trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); +// let x: Field = 0; +// assert(x == 0); +// let x: Field = 1; +// assert(x == 1); +// [] +// } +// } +// +// mod library { +// use crate::{Default, drop_var}; +// +// mod library2 { +// use crate::{Default, drop_var}; +// +// global IMPORT_GLOBAL_N_2: Field = 4; +// +// global IMPORT_GLOBAL_M_2: Field = 5; +// +// // When we re-export this type from another library and then use it in +// // main, we get a panic +// struct ReExportMeFromAnotherLib1 { +// x : Field, +// } +// +// struct PubLibLocalStruct3 { +// pub_field1: A, +// pub_field2: B, +// pub_field3: [A; N], +// pub_field4: ([A; N], [B; M]), +// pub_field5: &mut A, +// } +// +// impl Default for PubLibLocalStruct3 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// pub_field1: Default::default(), +// pub_field2: Default::default(), +// pub_field3: Default::default(), +// pub_field4: Default::default(), +// pub_field5: mut_field, +// } +// } +// } +// +// trait PubLibDefinedInLocalModule3 { +// fn pub_trait_fn1(self, x: A); +// fn pub_trait_fn2(self, y: B); +// fn pub_trait_fn3(&mut self, x: A, y: B); +// fn pub_trait_fn4(self, x: [A; 0], y: [B]); +// fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { +// fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); +// [] +// } +// } +// +// // Re-export +// use library2::ReExportMeFromAnotherLib1; +// +// global IMPORT_GLOBAL_N_1: Field = 2; +// +// global IMPORT_GLOBAL_M_1: Field = 3; +// +// struct LibLocalStruct1 { +// lib_field1: A, +// lib_field2: B, +// lib_field3: [A; N], +// lib_field4: ([A; N], [B; M]), +// lib_field5: &mut A, +// } +// +// impl Default for LibLocalStruct1 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// lib_field1: Default::default(), +// lib_field2: Default::default(), +// lib_field3: Default::default(), +// lib_field4: Default::default(), +// lib_field5: mut_field, +// } +// } +// } +// +// trait LibDefinedInLocalModule1 { +// fn lib_trait_fn1(self, x: A); +// fn lib_trait_fn2(self, y: B); +// fn lib_trait_fn3(&mut self, x: A, y: B); +// fn lib_trait_fn4(self, x: [A; 0], y: [B]); +// fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl LibDefinedInLocalModule1 for LibLocalStruct1 { +// fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } +// } +// +// pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); +// [] +// } +// } +// +// mod library3 { +// use crate::{Default, drop_var}; +// +// global IMPORT_GLOBAL_N_3: Field = 6; +// +// global IMPORT_GLOBAL_M_3: Field = 7; +// +// struct ReExportMeFromAnotherLib2 { +// x : Field, +// } +// +// struct PubCrateLibLocalStruct2 { +// crate_field1: A, +// crate_field2: B, +// crate_field3: [A; N], +// crate_field4: ([A; N], [B; M]), +// crate_field5: &mut A, +// } +// +// impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { +// fn default() -> Self { +// let mut mut_field = &mut Default::default(); +// Self { +// crate_field1: Default::default(), +// crate_field2: Default::default(), +// crate_field3: Default::default(), +// crate_field4: Default::default(), +// crate_field5: mut_field, +// } +// } +// } +// +// trait PubCrateLibDefinedInLocalModule2 { +// fn crate_trait_fn1(self, x: A); +// fn crate_trait_fn2(self, y: B); +// fn crate_trait_fn3(&mut self, x: A, y: B); +// fn crate_trait_fn4(self, x: [A; 0], y: [B]); +// fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; +// fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { +// drop_var(self, N + M) +// } +// } +// +// impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { +// fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } +// fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } +// fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } +// fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } +// fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } +// fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } +// } +// +// pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { +// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); +// [] +// } +// } +// +// +// use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; +// +// use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; +// +// // overlapping +// // use library::library2::ReExportMeFromAnotherLib1; +// use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; +// +// use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; +// +// +// fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { +// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); +// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); +// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); +// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); +// +// let x: LocalStruct = Default::default(); +// assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); +// assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); +// +// let x: LibLocalStruct1 = Default::default(); +// assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); +// assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); +// +// let x: PubLibLocalStruct3 = Default::default(); +// assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); +// assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); +// +// let x: PubCrateLibLocalStruct2 = Default::default(); +// assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); +// assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); +// +// assert(drop_var(local_fn2((0, 1), [], []), true)); +// assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); +// assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); +// assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); +// }"; +// +// // NOTE: these names must be "replacement-unique", i.e. +// // replacing one in a discinct name should do nothing +// let names_to_collapse = [ +// "DefinedInLocalModule1", +// "IMPORT_GLOBAL_M_1", +// "IMPORT_GLOBAL_M_2", +// "IMPORT_GLOBAL_M_3", +// "IMPORT_GLOBAL_N_1", +// "IMPORT_GLOBAL_N_2", +// "IMPORT_GLOBAL_N_3", +// "LOCAL_GLOBAL_M", +// "LOCAL_GLOBAL_N", +// "LibDefinedInLocalModule1", +// "LibLocalStruct1", +// "Liblocal_fn1", +// "LocalStruct", +// "PubCrateLibDefinedInLocalModule2", +// "PubCrateLibLocalStruct2", +// "PubCrateLiblocal_fn2", +// "PubLibDefinedInLocalModule3", +// "PubLibLocalStruct3", +// "PubLiblocal_fn3", +// "ReExportMeFromAnotherLib1", +// "ReExportMeFromAnotherLib2", +// "local_fn4", +// "crate_field1", +// "crate_field2", +// "crate_field3", +// "crate_field4", +// "crate_field5", +// "crate_trait_fn1", +// "crate_trait_fn2", +// "crate_trait_fn3", +// "crate_trait_fn4", +// "crate_trait_fn5", +// "crate_trait_fn6", +// "crate_trait_fn7", +// "field1", +// "field2", +// "field3", +// "field4", +// "field5", +// "lib_field1", +// "lib_field2", +// "lib_field3", +// "lib_field4", +// "lib_field5", +// "lib_trait_fn1", +// "lib_trait_fn2", +// "lib_trait_fn3", +// "lib_trait_fn4", +// "lib_trait_fn5", +// "lib_trait_fn6", +// "lib_trait_fn7", +// "pub_field1", +// "pub_field2", +// "pub_field3", +// "pub_field4", +// "pub_field5", +// "pub_trait_fn1", +// "pub_trait_fn2", +// "pub_trait_fn3", +// "pub_trait_fn4", +// "pub_trait_fn5", +// "pub_trait_fn6", +// "pub_trait_fn7", +// "trait_fn1", +// "trait_fn2", +// "trait_fn3", +// "trait_fn4", +// "trait_fn5", +// "trait_fn6", +// "trait_fn7", +// ]; +// +// // TODO(https://github.com/noir-lang/noir/issues/4973): +// // Name resolution panic from name shadowing test +// let cases_to_skip = [ +// (1, 21), +// (2, 11), +// (2, 21), +// (3, 11), +// (3, 18), +// (3, 21), +// (4, 21), +// (5, 11), +// (5, 21), +// (6, 11), +// (6, 18), +// (6, 21), +// ]; +// let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); +// +// for (i, x) in names_to_collapse.iter().enumerate() { +// for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { +// if !cases_to_skip.contains(&(i, j)) { +// dbg!((i, j)); +// +// let modified_src = src.replace(x, y); +// let errors = get_program_errors(&modified_src); +// assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); +// } +// } +// } +// } From dca751c08087b5749ed56475c2779ad79c805ae2 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 15:16:38 -0400 Subject: [PATCH 27/45] wip: adding kind info for infix and constant types --- .../src/elaborator/expressions.rs | 8 +-- .../noirc_frontend/src/elaborator/types.rs | 23 +++++-- .../src/hir/comptime/hir_to_display_ast.rs | 10 +-- .../src/hir/comptime/interpreter/builtin.rs | 6 +- .../noirc_frontend/src/hir/comptime/value.rs | 4 +- compiler/noirc_frontend/src/hir_def/types.rs | 68 +++++++++++++------ .../src/hir_def/types/arithmetic.rs | 28 ++++---- .../src/monomorphization/mod.rs | 4 +- compiler/noirc_frontend/src/node_interner.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 18 +++++ 10 files changed, 112 insertions(+), 59 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index ab1d1714877..121f3aa1811 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -29,7 +29,7 @@ use crate::{ }, node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId}, token::Tokens, - QuotedType, Shared, StructType, Type, + Kind, QuotedType, Shared, StructType, Type, }; use super::{Elaborator, LambdaContext}; @@ -136,7 +136,7 @@ impl<'context> Elaborator<'context> { (Lit(int), self.polymorphic_integer_or_field()) } Literal::Str(str) | Literal::RawStr(str, _) => { - let len = Type::Constant(str.len() as u32); + let len = Type::Constant(str.len() as u32, Kind::u32()); (Lit(HirLiteral::Str(str)), Type::String(Box::new(len))) } Literal::FmtStr(str) => self.elaborate_fmt_string(str, span), @@ -178,7 +178,7 @@ impl<'context> Elaborator<'context> { elem_id }); - let length = Type::Constant(elements.len() as u32); + let length = Type::Constant(elements.len() as u32, Kind::u32()); (HirArrayLiteral::Standard(elements), first_elem_type, length) } ArrayLiteral::Repeated { repeated_element, length } => { @@ -242,7 +242,7 @@ impl<'context> Elaborator<'context> { } } - let len = Type::Constant(str.len() as u32); + let len = Type::Constant(str.len() as u32, Kind::u32()); let typ = Type::FmtString(Box::new(len), Box::new(Type::Tuple(capture_types))); (HirExpression::Literal(HirLiteral::FmtStr(str, fmt_str_idents)), typ) } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 9464c147e80..2469fd7547e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -437,8 +437,9 @@ impl<'context> Elaborator<'context> { self.interner.get_global_let_statement(id), self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), ); - panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); - Some(Type::Constant(self.eval_global_as_array_length(id, path))) + + // panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); + Some(Type::Constant(self.eval_global_as_array_length(id, path), Kind::u32())) } _ => None, } @@ -454,7 +455,7 @@ impl<'context> Elaborator<'context> { let resolved_length = self.lookup_generic_or_global_type(&path).unwrap_or_else(|| { self.push_err(ResolverError::NoSuchNumericTypeVariable { path }); - Type::Constant(0) + Type::Constant(0, Kind::u32()) }); if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { @@ -475,16 +476,24 @@ impl<'context> Elaborator<'context> { } resolved_length } - UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), + UnresolvedTypeExpression::Constant(int, _span) => Type::Constant(int, Kind::u32()), UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); let lhs = self.convert_expression_type(*lhs, lhs_span); let rhs = self.convert_expression_type(*rhs, rhs_span); match (lhs, rhs) { - (Type::Constant(lhs), Type::Constant(rhs)) => { + (Type::Constant(lhs, lhs_kind), Type::Constant(rhs, rhs_kind)) => { + if lhs_kind != rhs_kind { + self.push_err(TypeCheckError::TypeKindMismatch { + expected_kind: lhs_kind.to_string(), + expr_kind: rhs_kind.to_string(), + expr_span: span, + }); + return Type::Error; + } if let Some(result) = op.function(lhs, rhs) { - Type::Constant(result) + Type::Constant(result, lhs_kind) } else { self.push_err(ResolverError::OverflowInType { lhs, op, rhs, span }); Type::Error @@ -1706,7 +1715,7 @@ impl<'context> Elaborator<'context> { | Type::Unit | Type::Error | Type::TypeVariable(_, _) - | Type::Constant(_) + | Type::Constant(..) | Type::NamedGeneric(_, _, _) | Type::Quoted(_) | Type::Forall(_, _) => (), diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs index c404b95d4b2..88265e7383f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -358,7 +358,7 @@ impl Type { // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use // this to ignore this case since it shouldn't be needed anyway. Type::Forall(_, typ) => return typ.to_display_ast(), - Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), + Type::Constant(..) => panic!("Type::Constant where a type was expected: {self:?}"), Type::Quoted(quoted_type) => UnresolvedTypeData::Quoted(*quoted_type), Type::Error => UnresolvedTypeData::Error, Type::InfixExpr(lhs, op, rhs) => { @@ -378,7 +378,7 @@ impl Type { let span = Span::default(); match self.follow_bindings() { - Type::Constant(length) => UnresolvedTypeExpression::Constant(length, span), + Type::Constant(length, _kind) => UnresolvedTypeExpression::Constant(length, span), Type::NamedGeneric(_var, name, _kind) => { let path = Path::from_single(name.as_ref().clone(), span); UnresolvedTypeExpression::Variable(path) @@ -421,10 +421,10 @@ impl HirArrayLiteral { HirArrayLiteral::Repeated { repeated_element, length } => { let repeated_element = Box::new(repeated_element.to_display_ast(interner)); let length = match length { - Type::Constant(length) => { + Type::Constant(length, _kind) => { let literal = Literal::Integer((*length as u128).into(), false); - let kind = ExpressionKind::Literal(literal); - Box::new(Expression::new(kind, span)) + let expr_kind = ExpressionKind::Literal(literal); + Box::new(Expression::new(expr_kind, span)) } other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), }; diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 9d1349b5209..3dd32cca26d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -291,7 +291,7 @@ fn str_as_bytes( let bytes: im::Vector = string.bytes().map(Value::U8).collect(); let byte_array_type = Type::Array( - Box::new(Type::Constant(bytes.len() as u32)), + Box::new(Type::Constant(bytes.len() as u32, Kind::u32())), Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight)), ); Ok(Value::Array(bytes, byte_array_type)) @@ -745,7 +745,7 @@ fn to_le_radix( let value = get_field(value)?; let radix = get_u32(radix)?; let limb_count = if let Type::Array(length, _) = return_type { - if let Type::Constant(limb_count) = *length { + if let Type::Constant(limb_count, _kind) = *length { limb_count } else { return Err(InterpreterError::TypeAnnotationsNeededForMethodCall { location }); @@ -1163,7 +1163,7 @@ fn zeroed(return_type: Type) -> IResult { // Optimistically assume we can resolve this type later or that the value is unused Type::TypeVariable(_, _) | Type::Forall(_, _) - | Type::Constant(_) + | Type::Constant(..) | Type::InfixExpr(..) | Type::Quoted(_) | Type::Error diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 4eee59489a9..d7ef26a9f66 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -27,7 +27,7 @@ use crate::{ node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, - QuotedType, Shared, Type, TypeBindings, + Kind, QuotedType, Shared, Type, TypeBindings, }; use rustc_hash::FxHashMap as HashMap; @@ -119,7 +119,7 @@ impl Value { Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), Value::String(value) => { - let length = Type::Constant(value.len() as u32); + let length = Type::Constant(value.len() as u32, Kind::u32()); Type::String(Box::new(length)) } Value::FormatString(_, typ) => return Cow::Borrowed(typ), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 84c0f115a24..c12c311cae7 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -112,7 +112,7 @@ pub enum Type { /// A type-level integer. Included to let an Array's size type variable /// bind to an integer without special checks to bind it to a non-type. - Constant(u32), + Constant(u32, Kind), /// The type of quoted code in macros. This is always a comptime-only type Quoted(QuotedType), @@ -142,6 +142,20 @@ impl Kind { pub(crate) fn is_numeric(&self) -> bool { matches!(self, Self::Numeric { .. }) } + + // TODO before merge: look for occurrences + // None matches everything, otherwise check equality + fn matches_opt(&self, other: Option) -> bool { + other.as_ref().map(|other_kind| self == other_kind).unwrap_or(true) + } + + // TODO before merge: look for occurrences + pub(crate) fn u32() -> Self { + Self::Numeric(Box::new(Type::Integer( + Signedness::Unsigned, + IntegerBitSize::ThirtyTwo, + ))) + } } impl std::fmt::Display for Kind { @@ -673,7 +687,7 @@ impl std::fmt::Display for Type { TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_) => write!(f, "{name}"), }, - Type::Constant(x) => x.fmt(f), + Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) @@ -858,7 +872,7 @@ impl Type { | Type::Bool | Type::Unit | Type::Error - | Type::Constant(_) + | Type::Constant(_, _) | Type::Forall(_, _) | Type::Quoted(_) => {} @@ -941,7 +955,7 @@ impl Type { | Type::Integer(_, _) | Type::Bool | Type::Unit - | Type::Constant(_) + | Type::Constant(_, _) | Type::Error => true, Type::FmtString(_, _) @@ -987,7 +1001,7 @@ impl Type { | Type::Integer(_, _) | Type::Bool | Type::Unit - | Type::Constant(_) + | Type::Constant(_, _) | Type::TypeVariable(_, _) | Type::NamedGeneric(_, _, _) | Type::InfixExpr(..) @@ -1030,7 +1044,7 @@ impl Type { | Type::Integer(_, _) | Type::Bool | Type::Unit - | Type::Constant(_) + | Type::Constant(_, _) | Type::Slice(_) | Type::Function(_, _, _, _) | Type::FmtString(_, _) @@ -1116,7 +1130,9 @@ impl Type { pub(crate) fn kind(&self) -> Option { match self { Type::NamedGeneric(_, _, kind) => Some(kind.clone()), - Type::Constant(..) | Type::TypeVariable(..) => None, + Type::Constant(_, kind) => Some(kind.clone()), + Type::TypeVariable(..) => None, + // TODO: ensure kinds for InfixExpr are checked, e.g. // @@ -1161,6 +1177,14 @@ impl Type { } } + // TODO + fn infix_kind(&self, other: &Self) -> Kind { + + // TODO!!! add kind check, replace u32 default with Type::Error + self.kind().or(other.kind()).unwrap_or(Kind::u32()) + } + + /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self) -> u32 { match self { @@ -1192,7 +1216,7 @@ impl Type { | Type::Function(_, _, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::Constant(_) + | Type::Constant(_, _) | Type::Quoted(_) | Type::Slice(_) | Type::InfixExpr(..) @@ -1250,7 +1274,7 @@ impl Type { let this = self.substitute(bindings).follow_bindings(); match &this { - Type::Constant(length) if *length == target_length => { + Type::Constant(length, _kind) if *length == target_length => { bindings.insert(target_id, (var.clone(), this)); Ok(()) } @@ -1588,9 +1612,9 @@ impl Type { } } - (Constant(value), other) | (other, Constant(value)) => { + (Constant(value, kind), other) | (other, Constant(value, kind)) => { if let Some(other_value) = other.evaluate_to_u32() { - if *value == other_value { + if *value == other_value && kind.matches_opt(other.kind()) { Ok(()) } else { Err(UnificationError) @@ -1598,7 +1622,7 @@ impl Type { } else if let InfixExpr(lhs, op, rhs) = other { if let Some(inverse) = op.inverse() { // Handle cases like `4 = a + b` by trying to solve to `a = 4 - b` - let new_type = InfixExpr(Box::new(Constant(*value)), inverse, rhs.clone()); + let new_type = InfixExpr(Box::new(Constant(*value, kind.clone())), inverse, rhs.clone()); new_type.try_unify(lhs, bindings)?; Ok(()) } else { @@ -1758,7 +1782,7 @@ impl Type { match self.canonicalize() { Type::TypeVariable(_, TypeVariableKind::Constant(size)) => Some(size), Type::Array(len, _elem) => len.evaluate_to_u32(), - Type::Constant(x) => Some(x), + Type::Constant(x, _) => Some(x), Type::InfixExpr(lhs, op, rhs) => { let lhs = lhs.evaluate_to_u32()?; let rhs = rhs.evaluate_to_u32()?; @@ -2036,7 +2060,7 @@ impl Type { Type::FieldElement | Type::Integer(_, _) | Type::Bool - | Type::Constant(_) + | Type::Constant(_, _) | Type::Error | Type::Quoted(_) | Type::Unit => self.clone(), @@ -2084,7 +2108,7 @@ impl Type { Type::FieldElement | Type::Integer(_, _) | Type::Bool - | Type::Constant(_) + | Type::Constant(_, _) | Type::Error | Type::Quoted(_) | Type::Unit => false, @@ -2152,7 +2176,7 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Quoted(_) | Error => { + FieldElement | Integer(_, _) | Bool | Constant(_, _) | Unit | Quoted(_) | Error => { self.clone() } } @@ -2168,7 +2192,7 @@ impl Type { pub fn replace_named_generics_with_type_variables(&mut self) { match self { Type::FieldElement - | Type::Constant(_) + | Type::Constant(_, _) | Type::Integer(_, _) | Type::Bool | Type::Unit @@ -2322,7 +2346,7 @@ impl TypeVariableKind { match self { TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), TypeVariableKind::Integer => Some(Type::default_int_type()), - TypeVariableKind::Constant(length) => Some(Type::Constant(*length)), + TypeVariableKind::Constant(length) => Some(Type::Constant(*length, Kind::u32())), TypeVariableKind::Normal => None, } } @@ -2373,7 +2397,7 @@ impl From<&Type> for PrintableType { Type::FmtString(_, _) => unreachable!("format strings cannot be printed"), Type::Error => unreachable!(), Type::Unit => PrintableType::Unit, - Type::Constant(_) => unreachable!(), + Type::Constant(_, _) => unreachable!(), Type::Struct(def, ref args) => { let struct_type = def.borrow(); let fields = struct_type.get_fields(args); @@ -2463,7 +2487,7 @@ impl std::fmt::Debug for Type { write!(f, "({} : {}){:?}", name, typ, binding) } }, - Type::Constant(x) => x.fmt(f), + Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| format!("{:?}", var)); write!(f, "forall {}. {:?}", typevars.join(" "), typ) @@ -2568,7 +2592,7 @@ impl std::hash::Hash for Type { vars.hash(state); typ.hash(state); } - Type::Constant(value) => value.hash(state), + Type::Constant(value, _) => value.hash(state), Type::Quoted(typ) => typ.hash(state), Type::InfixExpr(lhs, op, rhs) => { lhs.hash(state); @@ -2628,7 +2652,7 @@ impl PartialEq for Type { (Forall(lhs_vars, lhs_type), Forall(rhs_vars, rhs_type)) => { lhs_vars == rhs_vars && lhs_type == rhs_type } - (Constant(lhs), Constant(rhs)) => lhs == rhs, + (Constant(lhs, lhs_kind), Constant(rhs, rhs_kind)) => lhs == rhs && lhs_kind == rhs_kind, (Quoted(lhs), Quoted(rhs)) => lhs == rhs, (InfixExpr(l_lhs, l_op, l_rhs), InfixExpr(r_lhs, r_op, r_rhs)) => { l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 44a7526c894..ae49d72d8b6 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -17,9 +17,9 @@ impl Type { Type::InfixExpr(lhs, op, rhs) => { // evaluate_to_u32 also calls canonicalize so if we just called // `self.evaluate_to_u32()` we'd get infinite recursion. - if let (Some(lhs), Some(rhs)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { - if let Some(result) = op.function(lhs, rhs) { - return Type::Constant(result); + if let (Some(lhs_u32), Some(rhs_u32)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { + if let Some(result) = op.function(lhs_u32, rhs_u32) { + return Type::Constant(result, lhs.infix_kind(&rhs)); } } @@ -64,11 +64,11 @@ impl Type { queue.push(*lhs); queue.push(*rhs); } - Type::Constant(new_constant) => { + Type::Constant(new_constant, new_constant_kind) => { if let Some(result) = op.function(constant, new_constant) { constant = result; } else { - sorted.insert(Type::Constant(new_constant)); + sorted.insert(Type::Constant(new_constant, new_constant_kind)); } } other => { @@ -85,13 +85,13 @@ impl Type { } if constant != zero_value { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant))); + typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant, lhs.infix_kind(rhs)))); } typ } else { // Every type must have been a constant - Type::Constant(constant) + Type::Constant(constant, lhs.infix_kind(rhs)) } } @@ -194,7 +194,7 @@ impl Type { op = op.inverse()?; } let result = op.function(l_const, r_const)?; - Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result)))) + Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result, lhs.infix_kind(rhs))))) } (Multiplication | Division, Multiplication | Division) => { // If l_op is a division we want to inverse the rhs operator. @@ -206,7 +206,8 @@ impl Type { None } else { let result = op.function(l_const, r_const)?; - Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result)))) + // TODO: Type::InfixExpr kinds + Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result, lhs.infix_kind(rhs))))) } } _ => None, @@ -222,8 +223,9 @@ impl Type { ) -> Result<(), UnificationError> { if let Type::InfixExpr(lhs_a, op_a, rhs_a) = self { if let Some(inverse) = op_a.inverse() { - if let Some(rhs_a) = rhs_a.evaluate_to_u32() { - let rhs_a = Box::new(Type::Constant(rhs_a)); + if let Some(rhs_a_u32) = rhs_a.evaluate_to_u32() { + // TODO: Type::InfixExpr kinds + let rhs_a = Box::new(Type::Constant(rhs_a_u32, lhs_a.infix_kind(rhs_a))); let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); let mut tmp_bindings = bindings.clone(); @@ -237,8 +239,8 @@ impl Type { if let Type::InfixExpr(lhs_b, op_b, rhs_b) = other { if let Some(inverse) = op_b.inverse() { - if let Some(rhs_b) = rhs_b.evaluate_to_u32() { - let rhs_b = Box::new(Type::Constant(rhs_b)); + if let Some(rhs_b_u32) = rhs_b.evaluate_to_u32() { + let rhs_b = Box::new(Type::Constant(rhs_b_u32, lhs_b.infix_kind(rhs_b))); let new_self = Type::InfixExpr(Box::new(self.clone()), inverse, rhs_b); let mut tmp_bindings = bindings.clone(); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index fd06a2b04a8..a90b029692c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1049,7 +1049,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::MutableReference(Box::new(element)) } - HirType::Forall(_, _) | HirType::Constant(_) | HirType::InfixExpr(..) => { + HirType::Forall(_, _) | HirType::Constant(..) | HirType::InfixExpr(..) => { unreachable!("Unexpected type {typ} found") } HirType::Error => { @@ -1073,7 +1073,7 @@ impl<'interner> Monomorphizer<'interner> { | HirType::Unit | HirType::TraitAsType(..) | HirType::Forall(_, _) - | HirType::Constant(_) + | HirType::Constant(..) | HirType::Error | HirType::Quoted(_) => Ok(()), HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index f298559e65c..065e33608ba 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -2321,7 +2321,7 @@ fn get_type_method_key(typ: &Type) -> Option { // We do not support adding methods to these types Type::TypeVariable(_, _) | Type::Forall(_, _) - | Type::Constant(_) + | Type::Constant(..) | Type::Error | Type::Struct(_, _) | Type::InfixExpr(..) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9613c840fcc..6eed9429509 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1932,6 +1932,24 @@ fn assert_no_errors(src: &str) { #[test] fn numeric_generics_type_kind_mismatch() { + // let src = r#" + // fn foo() -> u16 { + // N as u16 + // } + // + // global J: u16 = 10; + // + // fn bar() -> u16 { + // foo::() + // } + // + // global M: u16 = 3; + // + // fn main() { + // let _ = bar::(); + // } + // "#; + let src = r#" fn foo() -> u16 { N as u16 From d334fd4fa2f2fa8a81d26f23761db74cd3a97606 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 15:21:27 -0400 Subject: [PATCH 28/45] wip debugging tests --- .../noirc_frontend/src/elaborator/types.rs | 36 +- compiler/noirc_frontend/src/tests.rs | 6733 ++++++++--------- .../src/tests/name_shadowing.rs | 831 +- 3 files changed, 3790 insertions(+), 3810 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2469fd7547e..c53c988ee23 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -170,7 +170,7 @@ impl<'context> Elaborator<'context> { } // TODO cleanup - dbg!("resolve_type_inner", &resolved_type, &kind); + // dbg!("resolve_type_inner", &resolved_type, &kind); // When Type::kind() is None, it matches every Kind if resolved_type @@ -178,15 +178,15 @@ impl<'context> Elaborator<'context> { .map(|resolved_type_kind| resolved_type_kind != *kind) .unwrap_or(false) { - // TODO cleanup - dbg!( - "resolve_type_inner", - &resolved_type, - format!("{:?}", &resolved_type), - &resolved_type.kind(), - &kind, - matches!(&resolved_type, Type::Constant(..)) - ); + // // TODO cleanup + // dbg!( + // "resolve_type_inner", + // &resolved_type, + // format!("{:?}", &resolved_type), + // &resolved_type.kind(), + // &kind, + // matches!(&resolved_type, Type::Constant(..)) + // ); let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), expr_kind: resolved_type @@ -430,13 +430,13 @@ impl<'context> Elaborator<'context> { self.interner.add_global_reference(id, reference_location); // TODO cleanup - dbg!( - "lookup_generic_or_global_type", - &path, - self.eval_global_as_array_length(id, path), - self.interner.get_global_let_statement(id), - self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), - ); + // dbg!( + // "lookup_generic_or_global_type", + // &path, + // self.eval_global_as_array_length(id, path), + // self.interner.get_global_let_statement(id), + // self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), + // ); // panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); Some(Type::Constant(self.eval_global_as_array_length(id, path), Kind::u32())) @@ -461,7 +461,7 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { // TODO: cleanup - dbg!("convert_expression_type", &kind); + // dbg!("convert_expression_type", &kind); self.push_err(TypeCheckError::TypeKindMismatch { expected_kind: Kind::Numeric(Box::new(Type::Integer( Signedness::Unsigned, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 6eed9429509..248edcc8dc5 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -125,3398 +125,3379 @@ fn assert_no_errors(src: &str) { } } -// TODO re-enable -// #[test] -// fn check_trait_implemented_for_all_t() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait IsDefault { -// fn is_default(self) -> bool; -// } -// -// impl IsDefault for T where T: Default + Eq { -// fn is_default(self) -> bool { -// self.eq(T::default()) -// } -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Default for u64 { -// fn default() -> Self { -// 0 -// } -// } -// -// impl Default for Foo { -// fn default() -> Self { -// Foo { a: Default::default() } -// } -// } -// -// fn main(a: Foo) -> pub bool { -// a.is_default() -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_implementation_duplicate_method() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// impl Default for Foo { -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// y + 2 * x -// } -// // Duplicate trait methods should not compile -// fn default(x: Field, y: Field) -> Field { -// x + 2 * y -// } -// } -// -// fn main() {}"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); -// assert_eq!(first_def, "default"); -// assert_eq!(second_def, "default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// struct Foo { -// } -// -// impl Default for Foo { -// fn default() -> Field { -// 0 -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_return_type2() { -// let src = " -// 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() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, -// expr_typ, -// expr_span: _, -// }) => { -// assert_eq!(expected_typ, "Foo"); -// assert_eq!(expr_typ, "Field"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_missing_implementation() { -// let src = " -// 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() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { -// trait_name, -// method_name, -// trait_impl_span: _, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(method_name, "method2"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_not_in_scope() { -// let src = " -// 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() { -// } -// -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { -// trait_path, -// }) => { -// assert_eq!(trait_path.as_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_method_name() { -// let src = " -// trait Default { -// } -// -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// // wrong trait name method should not compile -// impl Default for Foo { -// fn does_not_exist(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() { -// }"; -// let compilation_errors = get_program_errors(src); -// assert!(!has_parser_error(&compilation_errors)); -// assert!( -// compilation_errors.len() == 1, -// "Expected 1 compilation error, got: {:?}", -// compilation_errors -// ); -// -// for (err, _file_id) in compilation_errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { -// trait_name, -// impl_method, -// }) => { -// assert_eq!(trait_name, "Default"); -// assert_eq!(impl_method, "does_not_exist"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter() { -// let src = " -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// struct Foo { -// bar: u32, -// } -// -// impl Default for Foo { -// fn default(x: u32) -> Self { -// Foo {bar: x} -// } -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "u32"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter2() { -// let src = " -// 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() { -// }"; -// -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { -// method_name, -// expected_typ, -// actual_typ, -// .. -// }) => { -// assert_eq!(method_name, "default"); -// assert_eq!(expected_typ, "Field"); -// assert_eq!(actual_typ, "Foo"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameter_type() { -// let src = " -// trait Default { -// fn default(x: Field, y: NotAType) -> Field; -// } -// -// fn main(x: Field, y: Field) { -// assert(y == x); -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// -// // This is a duplicate error in the name resolver & type checker. -// // In the elaborator there is no duplicate and only 1 error is issued -// assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Unresolved(ident), -// )) => { -// assert_eq!(ident, "NotAType"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_wrong_parameters_count() { -// let src = " -// 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() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { -// actual_num_parameters, -// expected_num_parameters, -// trait_name, -// method_name, -// .. -// }) => { -// assert_eq!(actual_num_parameters, &1_usize); -// assert_eq!(expected_num_parameters, &2_usize); -// assert_eq!(method_name, "default"); -// assert_eq!(trait_name, "Default"); -// } -// _ => { -// panic!("No other errors are expected in this test case! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_impl_for_non_type() { -// let src = " -// trait Default { -// fn default(x: Field, y: Field) -> Field; -// } -// -// impl Default for main { -// fn default(x: Field, y: Field) -> Field { -// x + y -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { -// assert_eq!(expected, "type"); -// assert_eq!(got, "function"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_impl_struct_not_trait() { -// let src = " -// struct Foo { -// bar: Field, -// array: [Field; 2], -// } -// -// struct Default { -// x: Field, -// z: Field, -// } -// -// // Default is a struct not a trait -// impl Default for Foo { -// fn default(x: Field, y: Field) -> Self { -// Self { bar: x, array: [x,y] } -// } -// } -// -// fn main() {} -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { -// not_a_trait_name, -// }) => { -// assert_eq!(not_a_trait_name.to_string(), "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_declaration() { -// let src = " -// 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] } -// } -// } -// -// -// trait Default { -// fn default(x: Field) -> Self; -// } -// -// fn main() { -// }"; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { -// typ, -// first_def, -// second_def, -// }) => { -// assert_eq!(typ, &DuplicateType::Trait); -// assert_eq!(first_def, "Default"); -// assert_eq!(second_def, "Default"); -// } -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation() { -// let src = " -// trait Default { -// } -// struct Foo { -// bar: Field, -// } -// -// impl Default for Foo { -// } -// impl Default for Foo { -// } -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn check_trait_duplicate_implementation_with_alias() { -// let src = " -// trait Default { -// } -// -// struct MyStruct { -// } -// -// type MyType = MyStruct; -// -// impl Default for MyStruct { -// } -// -// impl Default for MyType { -// } -// -// fn main() { -// } -// "; -// let errors = get_program_errors(src); -// assert!(!has_parser_error(&errors)); -// assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); -// for (err, _file_id) in errors { -// match &err { -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { -// .. -// }) => (), -// CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { -// .. -// }) => (), -// _ => { -// panic!("No other errors are expected! Found = {:?}", err); -// } -// }; -// } -// } -// -// #[test] -// fn test_impl_self_within_default_def() { -// let src = " -// trait Bar { -// fn ok(self) -> Self; -// -// fn ref_ok(self) -> Self { -// self.ok() -// } -// } -// -// impl Bar for (T, T) where T: Bar { -// fn ok(self) -> Self { -// self -// } -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_fn_parameter() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// fn test_eq(x: impl Eq) -> bool { -// x.eq(x) -// } -// -// fn main(a: Foo) -> pub bool { -// test_eq(a) -// }"; -// assert_no_errors(src); -// } -// -// #[test] -// fn check_trait_as_type_as_two_fn_parameters() { -// let src = " -// trait Eq { -// fn eq(self, other: Self) -> bool; -// } -// -// trait Test { -// fn test(self) -> bool; -// } -// -// struct Foo { -// a: u64, -// } -// -// impl Eq for Foo { -// fn eq(self, other: Foo) -> bool { self.a == other.a } -// } -// -// impl Test for u64 { -// fn test(self) -> bool { self == self } -// } -// -// fn test_eq(x: impl Eq, y: impl Test) -> bool { -// x.eq(x) == y.test() -// } -// -// fn main(a: Foo, b: u64) -> pub bool { -// test_eq(a, b) -// }"; -// assert_no_errors(src); -// } -// -// fn get_program_captures(src: &str) -> Vec> { -// let (program, context, _errors) = get_program(src); -// let interner = context.def_interner; -// let mut all_captures: Vec> = Vec::new(); -// for func in program.into_sorted().functions { -// let func_id = interner.find_function(func.item.name()).unwrap(); -// let hir_func = interner.function(&func_id); -// // Iterate over function statements and apply filtering function -// find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); -// } -// all_captures -// } -// -// fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { -// for stmt_id in stmts.iter() { -// let hir_stmt = interner.statement(stmt_id); -// let expr_id = match hir_stmt { -// HirStatement::Expression(expr_id) => expr_id, -// HirStatement::Let(let_stmt) => let_stmt.expression, -// HirStatement::Assign(assign_stmt) => assign_stmt.expression, -// HirStatement::Constrain(constr_stmt) => constr_stmt.0, -// HirStatement::Semi(semi_expr) => semi_expr, -// HirStatement::For(for_loop) => for_loop.block, -// HirStatement::Error => panic!("Invalid HirStatement!"), -// HirStatement::Break => panic!("Unexpected break"), -// HirStatement::Continue => panic!("Unexpected continue"), -// HirStatement::Comptime(_) => panic!("Unexpected comptime"), -// }; -// let expr = interner.expression(&expr_id); -// -// get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter -// } -// } -// -// fn get_lambda_captures( -// expr: HirExpression, -// interner: &NodeInterner, -// result: &mut Vec>, -// ) { -// if let HirExpression::Lambda(lambda_expr) = expr { -// let mut cur_capture = Vec::new(); -// -// for capture in lambda_expr.captures.iter() { -// cur_capture.push(interner.definition(capture.ident.id).name.clone()); -// } -// result.push(cur_capture); -// -// // Check for other captures recursively within the lambda body -// let hir_body_expr = interner.expression(&lambda_expr.body); -// if let HirExpression::Block(block_expr) = hir_body_expr { -// find_lambda_captures(block_expr.statements(), interner, result); -// } -// } -// } -// -// #[test] -// fn resolve_empty_function() { -// let src = " -// fn main() { -// -// } -// "; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_basic_function() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// #[test] -// fn resolve_unused_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(x == x); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unused variable -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { -// assert_eq!(&ident.0.contents, "y"); -// } -// _ => unreachable!("we should only have an unused var error"), -// } -// } -// -// #[test] -// fn resolve_unresolved_var() { -// let src = r#" -// fn main(x : Field) { -// let y = x + x; -// assert(y == z); -// } -// "#; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) -// match &errors[0].0 { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { -// assert_eq!(name, "z"); -// } -// _ => unimplemented!("we should only have an unresolved variable"), -// } -// } -// -// #[test] -// fn unresolved_path() { -// let src = " -// fn main(x : Field) { -// let _z = some::path::to::a::func(x); -// } -// "; -// let errors = get_program_errors(src); -// assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "some"); -// } -// _ => unimplemented!("we should only have an unresolved function"), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_literal_expr() { -// let src = r#" -// fn main(x : Field) { -// let y = 5; -// assert(y == x); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn multiple_resolution_errors() { -// let src = r#" -// fn main(x : Field) { -// let y = foo::bar(x); -// let z = y + a; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); -// -// // Errors are: -// // `a` is undeclared -// // `z` is unused -// // `foo::bar` does not exist -// for (compilation_error, _file_id) in errors { -// match compilation_error { -// CompilationError::ResolverError(err) => { -// match err { -// ResolverError::UnusedVariable { ident } => { -// assert_eq!(&ident.0.contents, "z"); -// } -// ResolverError::VariableNotDeclared { name, .. } => { -// assert_eq!(name, "a"); -// } -// ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { -// assert_eq!(name.to_string(), "foo"); -// } -// _ => unimplemented!(), -// }; -// } -// _ => unimplemented!(), -// } -// } -// } -// -// #[test] -// fn resolve_prefix_expr() { -// let src = r#" -// fn main(x : Field) { -// let _y = -x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_for_expr() { -// let src = r#" -// fn main(x : u64) { -// for i in 1..20 { -// let _z = x + i; -// }; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_call_expr() { -// let src = r#" -// fn main(x : Field) { -// let _z = foo(x); -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_shadowing() { -// let src = r#" -// fn main(x : Field) { -// let x = foo(x); -// let x = x; -// let (x, x) = (x, x); -// let _ = x; -// } -// -// fn foo(x : Field) -> Field { -// x -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_basic_closure() { -// let src = r#" -// fn main(x : Field) -> pub Field { -// let closure = |y| y + x; -// closure(x) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn resolve_simplified_closure() { -// // based on bug https://github.com/noir-lang/noir/issues/1088 -// -// let src = r#"fn do_closure(x: Field) -> Field { -// let y = x; -// let ret_capture = || { -// y -// }; -// ret_capture() -// } -// -// fn main(x: Field) { -// assert(do_closure(x) == 100); -// } -// -// "#; -// let parsed_captures = get_program_captures(src); -// let expected_captures = vec![vec!["y".to_string()]]; -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_complex_closures() { -// let src = r#" -// fn main(x: Field) -> pub Field { -// let closure_without_captures = |x: Field| -> Field { x + x }; -// let a = closure_without_captures(1); -// -// let closure_capturing_a_param = |y: Field| -> Field { y + x }; -// let b = closure_capturing_a_param(2); -// -// let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; -// let c = closure_capturing_a_local_var(3); -// -// let closure_with_transitive_captures = |y: Field| -> Field { -// let d = 5; -// let nested_closure = |z: Field| -> Field { -// let doubly_nested_closure = |w: Field| -> Field { w + x + b }; -// a + z + y + d + x + doubly_nested_closure(4) + x + y -// }; -// let res = nested_closure(5); -// res -// }; -// -// a + b + c + closure_with_transitive_captures(6) -// } -// "#; -// assert_no_errors(src); -// -// let expected_captures = vec![ -// vec![], -// vec!["x".to_string()], -// vec!["b".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string()], -// vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], -// vec!["x".to_string(), "b".to_string()], -// ]; -// -// let parsed_captures = get_program_captures(src); -// -// assert_eq!(expected_captures, parsed_captures); -// } -// -// #[test] -// fn resolve_fmt_strings() { -// let src = r#" -// fn main() { -// let string = f"this is i: {i}"; -// println(string); -// -// println(f"I want to print {0}"); -// -// let new_val = 10; -// println(f"random_string{new_val}{new_val}"); -// } -// fn println(x : T) -> T { -// x -// } -// "#; -// -// let errors = get_program_errors(src); -// assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); -// -// for (err, _file_id) in errors { -// match &err { -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { -// name, .. -// }) => { -// assert_eq!(name, "i"); -// } -// CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { -// name, -// .. -// }) => { -// assert_eq!(name, "0"); -// } -// CompilationError::TypeError(TypeCheckError::UnusedResultError { -// expr_type: _, -// expr_span, -// }) => { -// let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); -// assert!( -// a == "println(string)" -// || a == "println(f\"I want to print {0}\")" -// || a == "println(f\"random_string{new_val}{new_val}\")" -// ); -// } -// _ => unimplemented!(), -// }; -// } -// } -// -// fn check_rewrite(src: &str, expected: &str) { -// let (_program, mut context, _errors) = get_program(src); -// let main_func_id = context.def_interner.find_function("main").unwrap(); -// let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); -// assert!(format!("{}", program) == expected); -// } -// -// #[test] -// fn simple_closure_with_no_captured_variables() { -// let src = r#" -// fn main() -> pub Field { -// let x = 1; -// let closure = || x; -// closure() -// } -// "#; -// -// let expected_rewrite = r#"fn main$f0() -> Field { -// let x$0 = 1; -// let closure$3 = { -// let closure_variable$2 = { -// let env$1 = (x$l0); -// (env$l1, lambda$f1) -// }; -// closure_variable$l2 -// }; -// { -// let tmp$4 = closure$l3; -// tmp$l4.1(tmp$l4.0) -// } -// } -// fn lambda$f1(mut env$l1: (Field)) -> Field { -// env$l1.0 -// } -// "#; -// check_rewrite(src, expected_rewrite); -// } -// -// #[test] -// fn deny_cyclic_globals() { -// let src = r#" -// global A = B; -// global B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_cyclic_type_aliases() { -// let src = r#" -// type A = B; -// type B = A; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn ensure_nested_type_aliases_type_check() { -// let src = r#" -// type A = B; -// type B = u8; -// fn main() { -// let _a: A = 0 as u16; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn type_aliases_in_entry_point() { -// let src = r#" -// type Foo = u8; -// fn main(_x: Foo) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn operators_in_global_used_in_type() { -// let src = r#" -// global ONE = 1; -// global COUNT = ONE + 2; -// fn main() { -// let _array: [Field; COUNT] = [1, 2, 3]; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn break_and_continue_in_constrained_fn() { -// let src = r#" -// fn main() { -// for i in 0 .. 10 { -// if i == 2 { -// continue; -// } -// if i == 5 { -// break; -// } -// } -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } -// -// #[test] -// fn break_and_continue_outside_loop() { -// let src = r#" -// unconstrained fn main() { -// continue; -// break; -// } -// "#; -// assert_eq!(get_program_errors(src).len(), 2); -// } -// -// // Regression for #2540 -// #[test] -// fn for_loop_over_array() { -// let src = r#" -// fn hello(_array: [u1; N]) { -// for _ in 0..N {} -// } -// -// fn main() { -// let array: [u1; 2] = [0, 1]; -// hello(array); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// // Regression for #4545 -// #[test] -// fn type_aliases_in_main() { -// let src = r#" -// type Outer = [u8; N]; -// fn main(_arg: Outer<1>) {} -// "#; -// assert_eq!(get_program_errors(src).len(), 0); -// } -// -// #[test] -// fn ban_mutable_globals() { -// // Mutable globals are only allowed in a comptime context -// let src = r#" -// mut global FOO: Field = 0; -// fn main() {} -// "#; -// assert_eq!(get_program_errors(src).len(), 1); -// } -// -// #[test] -// fn deny_inline_attribute_on_unconstrained() { -// let src = r#" -// #[no_predicates] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn deny_fold_attribute_on_unconstrained() { -// let src = r#" -// #[fold] -// unconstrained pub fn foo(x: Field, y: Field) { -// assert(x != y); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) -// )); -// } -// -// #[test] -// fn specify_function_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn specify_method_types_with_turbofish() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn incorrect_turbofish_count_function_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// impl Default for u64 { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// fn generic_func() -> (T, U) where T: Default, U: Default { -// (T::default(), U::default()) -// } -// -// fn main() { -// let _ = generic_func::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn incorrect_turbofish_count_method_call() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for Field { -// fn default() -> Self { 0 } -// } -// -// // Need the above as we don't have access to the stdlib here. -// // We also need to construct a concrete value of `U` without giving away its type -// // as otherwise the unspecified type is ignored. -// -// struct Foo { -// inner: T -// } -// -// impl Foo { -// fn generic_method(_self: Self) -> U where U: Default { -// U::default() -// } -// } -// -// fn main() { -// let foo: Foo = Foo { inner: 1 }; -// let _ = foo.generic_method::(); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_function() { -// let src = r#" -// struct Foo { -// inner: u64 -// } -// -// pub fn bar() { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn struct_numeric_generic_in_struct() { -// let src = r#" -// struct Foo { -// inner: u64 -// } -// -// struct Bar { } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::UnsupportedNumericGenericType { .. } -// ), -// )); -// } -// -// #[test] -// fn bool_numeric_generic() { -// let src = r#" -// pub fn read() -> Field { -// if N { -// 0 -// } else { -// 1 -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_binary_operation_type_mismatch() { -// let src = r#" -// pub fn foo() -> bool { -// let mut check: bool = true; -// check = N; -// check -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), -// )); -// } -// -// #[test] -// fn bool_generic_as_loop_bound() { -// let src = r#" -// pub fn read() { -// let mut fields = [0; N]; -// for i in 0..N { -// fields[i] = i + 1; -// } -// assert(fields[0] == 1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), -// )); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, expr_typ, .. -// }) = &errors[1].0 -// else { -// panic!("Got an error other than a type mismatch"); -// }; -// -// assert_eq!(expected_typ, "Field"); -// assert_eq!(expr_typ, "bool"); -// } -// -// #[test] -// fn numeric_generic_in_function_signature() { -// let src = r#" -// pub fn foo(arr: [Field; N]) -> [Field; N] { arr } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_as_struct_field_type_fails() { -// let src = r#" -// struct Foo { -// a: Field, -// b: N, -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_as_array_length() { -// let src = r#" -// struct Foo { -// a: Field, -// b: [Field; N], -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_as_param_type() { -// let src = r#" -// pub fn foo(x: I) -> I { -// let _q: I = 5; -// x -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// -// // Error from the parameter type -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the let statement annotated type -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // Error from the return type -// assert!(matches!( -// errors[2].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_fails() { -// let src = r#" -// struct Foo { -// a: Field, -// b: Bar, -// } -// struct Bar { -// inner: N -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn normal_generic_used_in_nested_array_length_fail() { -// let src = r#" -// struct Foo { -// a: Field, -// b: Bar, -// } -// struct Bar { -// inner: [Field; N] -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// } -// -// #[test] -// fn numeric_generic_used_in_nested_type_pass() { -// // The order of these structs should not be changed to make sure -// // that we are accurately resolving all struct generics before struct fields -// let src = r#" -// struct NestedNumeric { -// a: Field, -// b: InnerNumeric -// } -// struct InnerNumeric { -// inner: [u64; N], -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_trait() { -// // We want to make sure that `N` in `impl Deserialize` does -// // not trigger `expected type, found numeric generic parameter N` as the trait -// // does in fact expect a numeric generic. -// let src = r#" -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// impl Deserialize for MyType { -// fn deserialize(fields: [Field; N], other: T) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: other } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N], other: T) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_in_trait_impl_with_extra_impl_generics() { -// let src = r#" -// trait Default { -// fn default() -> Self; -// } -// -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } -// -// // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. -// // `N` is used first in the trait impl generics (`Deserialize for MyType`). -// // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind -// // while `T` has a normal kind. -// impl Deserialize for MyType where T: Default { -// fn deserialize(fields: [Field; N]) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } -// } -// } -// -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_where_clause() { -// let src = r#" -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// -// pub fn read() -> T where T: Deserialize { -// let mut fields: [Field; N] = [0; N]; -// for i in 0..N { -// fields[i] = i as Field + 1; -// } -// T::deserialize(fields) -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn numeric_generic_used_in_turbofish() { -// let src = r#" -// pub fn double() -> u32 { -// // Used as an expression -// N * 2 -// } -// -// pub fn double_numeric_generics_test() { -// // Example usage of a numeric generic arguments. -// assert(double::<9>() == 18); -// assert(double::<7 + 8>() == 30); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn constant_used_with_numeric_generic() { -// let src = r#" -// struct ValueNote { -// value: Field, -// } -// -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// impl Serialize<1> for ValueNote { -// fn serialize(self) -> [Field; 1] { -// [self.value] -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn normal_generic_used_when_numeric_expected_in_where_clause() { -// let src = r#" -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// -// pub fn read() -> T where T: Deserialize { -// T::deserialize([0, 1]) -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// -// let src = r#" -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } -// -// pub fn read() -> T where T: Deserialize { -// let mut fields: [Field; N] = [0; N]; -// for i in 0..N { -// fields[i] = i as Field + 1; -// } -// T::deserialize(fields) -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 4); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// assert!(matches!( -// errors[1].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// assert!(matches!( -// errors[2].0, -// CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), -// )); -// // N -// assert!(matches!( -// errors[3].0, -// CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), -// )); -// } +#[test] +fn check_trait_implemented_for_all_t() { + let src = " + trait Default { + fn default() -> Self; + } + + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait IsDefault { + fn is_default(self) -> bool; + } + + impl IsDefault for T where T: Default + Eq { + fn is_default(self) -> bool { + self.eq(T::default()) + } + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Default for u64 { + fn default() -> Self { + 0 + } + } + + impl Default for Foo { + fn default() -> Self { + Foo { a: Default::default() } + } + } + + fn main(a: Foo) -> pub bool { + a.is_default() + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_implementation_duplicate_method() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + impl Default for Foo { + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + y + 2 * x + } + // Duplicate trait methods should not compile + fn default(x: Field, y: Field) -> Field { + x + 2 * y + } + } + + fn main() {}"; + + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); + assert_eq!(first_def, "default"); + assert_eq!(second_def, "default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_return_type() { + let src = " + trait Default { + fn default() -> Self; + } + + struct Foo { + } + + impl Default for Foo { + fn default() -> Field { + 0 + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_return_type2() { + let src = " + 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() { + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, + expr_typ, + expr_span: _, + }) => { + assert_eq!(expected_typ, "Foo"); + assert_eq!(expr_typ, "Field"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_missing_implementation() { + let src = " + 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() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { + trait_name, + method_name, + trait_impl_span: _, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(method_name, "method2"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_not_in_scope() { + let src = " + 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() { + } + + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { + trait_path, + }) => { + assert_eq!(trait_path.as_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_method_name() { + let src = " + trait Default { + } + + struct Foo { + bar: Field, + array: [Field; 2], + } + + // wrong trait name method should not compile + impl Default for Foo { + fn does_not_exist(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() { + }"; + let compilation_errors = get_program_errors(src); + assert!(!has_parser_error(&compilation_errors)); + assert!( + compilation_errors.len() == 1, + "Expected 1 compilation error, got: {:?}", + compilation_errors + ); + + for (err, _file_id) in compilation_errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { + trait_name, + impl_method, + }) => { + assert_eq!(trait_name, "Default"); + assert_eq!(impl_method, "does_not_exist"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter() { + let src = " + trait Default { + fn default(x: Field) -> Self; + } + + struct Foo { + bar: u32, + } + + impl Default for Foo { + fn default(x: u32) -> Self { + Foo {bar: x} + } + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "u32"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter2() { + let src = " + 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() { + }"; + + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name, + expected_typ, + actual_typ, + .. + }) => { + assert_eq!(method_name, "default"); + assert_eq!(expected_typ, "Field"); + assert_eq!(actual_typ, "Foo"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameter_type() { + let src = " + trait Default { + fn default(x: Field, y: NotAType) -> Field; + } + + fn main(x: Field, y: Field) { + assert(y == x); + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + + // This is a duplicate error in the name resolver & type checker. + // In the elaborator there is no duplicate and only 1 error is issued + assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Unresolved(ident), + )) => { + assert_eq!(ident, "NotAType"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_wrong_parameters_count() { + let src = " + 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() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { + actual_num_parameters, + expected_num_parameters, + trait_name, + method_name, + .. + }) => { + assert_eq!(actual_num_parameters, &1_usize); + assert_eq!(expected_num_parameters, &2_usize); + assert_eq!(method_name, "default"); + assert_eq!(trait_name, "Default"); + } + _ => { + panic!("No other errors are expected in this test case! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_impl_for_non_type() { + let src = " + trait Default { + fn default(x: Field, y: Field) -> Field; + } + + impl Default for main { + fn default(x: Field, y: Field) -> Field { + x + y + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { + assert_eq!(expected, "type"); + assert_eq!(got, "function"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_impl_struct_not_trait() { + let src = " + struct Foo { + bar: Field, + array: [Field; 2], + } + + struct Default { + x: Field, + z: Field, + } + + // Default is a struct not a trait + impl Default for Foo { + fn default(x: Field, y: Field) -> Self { + Self { bar: x, array: [x,y] } + } + } + + fn main() {} + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { + not_a_trait_name, + }) => { + assert_eq!(not_a_trait_name.to_string(), "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_declaration() { + let src = " + 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] } + } + } + + + trait Default { + fn default(x: Field) -> Self; + } + + fn main() { + }"; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { + typ, + first_def, + second_def, + }) => { + assert_eq!(typ, &DuplicateType::Trait); + assert_eq!(first_def, "Default"); + assert_eq!(second_def, "Default"); + } + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_implementation() { + let src = " + trait Default { + } + struct Foo { + bar: Field, + } + + impl Default for Foo { + } + impl Default for Foo { + } + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn check_trait_duplicate_implementation_with_alias() { + let src = " + trait Default { + } + + struct MyStruct { + } + + type MyType = MyStruct; + + impl Default for MyStruct { + } + + impl Default for MyType { + } + + fn main() { + } + "; + let errors = get_program_errors(src); + assert!(!has_parser_error(&errors)); + assert!(errors.len() == 2, "Expected 2 errors, got: {:?}", errors); + for (err, _file_id) in errors { + match &err { + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { + .. + }) => (), + CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImplNote { + .. + }) => (), + _ => { + panic!("No other errors are expected! Found = {:?}", err); + } + }; + } +} + +#[test] +fn test_impl_self_within_default_def() { + let src = " + trait Bar { + fn ok(self) -> Self; + + fn ref_ok(self) -> Self { + self.ok() + } + } + + impl Bar for (T, T) where T: Bar { + fn ok(self) -> Self { + self + } + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_fn_parameter() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + fn test_eq(x: impl Eq) -> bool { + x.eq(x) + } + + fn main(a: Foo) -> pub bool { + test_eq(a) + }"; + assert_no_errors(src); +} + +#[test] +fn check_trait_as_type_as_two_fn_parameters() { + let src = " + trait Eq { + fn eq(self, other: Self) -> bool; + } + + trait Test { + fn test(self) -> bool; + } + + struct Foo { + a: u64, + } + + impl Eq for Foo { + fn eq(self, other: Foo) -> bool { self.a == other.a } + } + + impl Test for u64 { + fn test(self) -> bool { self == self } + } + + fn test_eq(x: impl Eq, y: impl Test) -> bool { + x.eq(x) == y.test() + } + + fn main(a: Foo, b: u64) -> pub bool { + test_eq(a, b) + }"; + assert_no_errors(src); +} + +fn get_program_captures(src: &str) -> Vec> { + let (program, context, _errors) = get_program(src); + let interner = context.def_interner; + let mut all_captures: Vec> = Vec::new(); + for func in program.into_sorted().functions { + let func_id = interner.find_function(func.item.name()).unwrap(); + let hir_func = interner.function(&func_id); + // Iterate over function statements and apply filtering function + find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); + } + all_captures +} + +fn find_lambda_captures(stmts: &[StmtId], interner: &NodeInterner, result: &mut Vec>) { + for stmt_id in stmts.iter() { + let hir_stmt = interner.statement(stmt_id); + let expr_id = match hir_stmt { + HirStatement::Expression(expr_id) => expr_id, + HirStatement::Let(let_stmt) => let_stmt.expression, + HirStatement::Assign(assign_stmt) => assign_stmt.expression, + HirStatement::Constrain(constr_stmt) => constr_stmt.0, + HirStatement::Semi(semi_expr) => semi_expr, + HirStatement::For(for_loop) => for_loop.block, + HirStatement::Error => panic!("Invalid HirStatement!"), + HirStatement::Break => panic!("Unexpected break"), + HirStatement::Continue => panic!("Unexpected continue"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), + }; + let expr = interner.expression(&expr_id); + + get_lambda_captures(expr, interner, result); // TODO: dyn filter function as parameter + } +} + +fn get_lambda_captures( + expr: HirExpression, + interner: &NodeInterner, + result: &mut Vec>, +) { + if let HirExpression::Lambda(lambda_expr) = expr { + let mut cur_capture = Vec::new(); + + for capture in lambda_expr.captures.iter() { + cur_capture.push(interner.definition(capture.ident.id).name.clone()); + } + result.push(cur_capture); + + // Check for other captures recursively within the lambda body + let hir_body_expr = interner.expression(&lambda_expr.body); + if let HirExpression::Block(block_expr) = hir_body_expr { + find_lambda_captures(block_expr.statements(), interner, result); + } + } +} + +#[test] +fn resolve_empty_function() { + let src = " + fn main() { + + } + "; + assert_no_errors(src); +} +#[test] +fn resolve_basic_function() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == x); + } + "#; + assert_no_errors(src); +} +#[test] +fn resolve_unused_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(x == x); + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unused variable + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { + assert_eq!(&ident.0.contents, "y"); + } + _ => unreachable!("we should only have an unused var error"), + } +} + +#[test] +fn resolve_unresolved_var() { + let src = r#" + fn main(x : Field) { + let y = x + x; + assert(y == z); + } + "#; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) + match &errors[0].0 { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, span: _ }) => { + assert_eq!(name, "z"); + } + _ => unimplemented!("we should only have an unresolved variable"), + } +} + +#[test] +fn unresolved_path() { + let src = " + fn main(x : Field) { + let _z = some::path::to::a::func(x); + } + "; + let errors = get_program_errors(src); + assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "some"); + } + _ => unimplemented!("we should only have an unresolved function"), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_literal_expr() { + let src = r#" + fn main(x : Field) { + let y = 5; + assert(y == x); + } + "#; + assert_no_errors(src); +} + +#[test] +fn multiple_resolution_errors() { + let src = r#" + fn main(x : Field) { + let y = foo::bar(x); + let z = y + a; + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); + + // Errors are: + // `a` is undeclared + // `z` is unused + // `foo::bar` does not exist + for (compilation_error, _file_id) in errors { + match compilation_error { + CompilationError::ResolverError(err) => { + match err { + ResolverError::UnusedVariable { ident } => { + assert_eq!(&ident.0.contents, "z"); + } + ResolverError::VariableNotDeclared { name, .. } => { + assert_eq!(name, "a"); + } + ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { + assert_eq!(name.to_string(), "foo"); + } + _ => unimplemented!(), + }; + } + _ => unimplemented!(), + } + } +} + +#[test] +fn resolve_prefix_expr() { + let src = r#" + fn main(x : Field) { + let _y = -x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_for_expr() { + let src = r#" + fn main(x : u64) { + for i in 1..20 { + let _z = x + i; + }; + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_call_expr() { + let src = r#" + fn main(x : Field) { + let _z = foo(x); + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_shadowing() { + let src = r#" + fn main(x : Field) { + let x = foo(x); + let x = x; + let (x, x) = (x, x); + let _ = x; + } + + fn foo(x : Field) -> Field { + x + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_basic_closure() { + let src = r#" + fn main(x : Field) -> pub Field { + let closure = |y| y + x; + closure(x) + } + "#; + assert_no_errors(src); +} + +#[test] +fn resolve_simplified_closure() { + // based on bug https://github.com/noir-lang/noir/issues/1088 + + let src = r#"fn do_closure(x: Field) -> Field { + let y = x; + let ret_capture = || { + y + }; + ret_capture() + } + + fn main(x: Field) { + assert(do_closure(x) == 100); + } + + "#; + let parsed_captures = get_program_captures(src); + let expected_captures = vec![vec!["y".to_string()]]; + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_complex_closures() { + let src = r#" + fn main(x: Field) -> pub Field { + let closure_without_captures = |x: Field| -> Field { x + x }; + let a = closure_without_captures(1); + + let closure_capturing_a_param = |y: Field| -> Field { y + x }; + let b = closure_capturing_a_param(2); + + let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; + let c = closure_capturing_a_local_var(3); + + let closure_with_transitive_captures = |y: Field| -> Field { + let d = 5; + let nested_closure = |z: Field| -> Field { + let doubly_nested_closure = |w: Field| -> Field { w + x + b }; + a + z + y + d + x + doubly_nested_closure(4) + x + y + }; + let res = nested_closure(5); + res + }; + + a + b + c + closure_with_transitive_captures(6) + } + "#; + assert_no_errors(src); + + let expected_captures = vec![ + vec![], + vec!["x".to_string()], + vec!["b".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string()], + vec!["x".to_string(), "b".to_string(), "a".to_string(), "y".to_string(), "d".to_string()], + vec!["x".to_string(), "b".to_string()], + ]; + + let parsed_captures = get_program_captures(src); + + assert_eq!(expected_captures, parsed_captures); +} + +#[test] +fn resolve_fmt_strings() { + let src = r#" + fn main() { + let string = f"this is i: {i}"; + println(string); + + println(f"I want to print {0}"); + + let new_val = 10; + println(f"random_string{new_val}{new_val}"); + } + fn println(x : T) -> T { + x + } + "#; + + let errors = get_program_errors(src); + assert!(errors.len() == 5, "Expected 5 errors, got: {:?}", errors); + + for (err, _file_id) in errors { + match &err { + CompilationError::ResolverError(ResolverError::VariableNotDeclared { + name, .. + }) => { + assert_eq!(name, "i"); + } + CompilationError::ResolverError(ResolverError::NumericConstantInFormatString { + name, + .. + }) => { + assert_eq!(name, "0"); + } + CompilationError::TypeError(TypeCheckError::UnusedResultError { + expr_type: _, + expr_span, + }) => { + let a = src.get(expr_span.start() as usize..expr_span.end() as usize).unwrap(); + assert!( + a == "println(string)" + || a == "println(f\"I want to print {0}\")" + || a == "println(f\"random_string{new_val}{new_val}\")" + ); + } + _ => unimplemented!(), + }; + } +} + +fn check_rewrite(src: &str, expected: &str) { + let (_program, mut context, _errors) = get_program(src); + let main_func_id = context.def_interner.find_function("main").unwrap(); + let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); + assert!(format!("{}", program) == expected); +} + +#[test] +fn simple_closure_with_no_captured_variables() { + let src = r#" + fn main() -> pub Field { + let x = 1; + let closure = || x; + closure() + } + "#; + + let expected_rewrite = r#"fn main$f0() -> Field { + let x$0 = 1; + let closure$3 = { + let closure_variable$2 = { + let env$1 = (x$l0); + (env$l1, lambda$f1) + }; + closure_variable$l2 + }; + { + let tmp$4 = closure$l3; + tmp$l4.1(tmp$l4.0) + } +} +fn lambda$f1(mut env$l1: (Field)) -> Field { + env$l1.0 +} +"#; + check_rewrite(src, expected_rewrite); +} + +#[test] +fn deny_cyclic_globals() { + let src = r#" + global A = B; + global B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_cyclic_type_aliases() { + let src = r#" + type A = B; + type B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn ensure_nested_type_aliases_type_check() { + let src = r#" + type A = B; + type B = u8; + fn main() { + let _a: A = 0 as u16; + } + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn type_aliases_in_entry_point() { + let src = r#" + type Foo = u8; + fn main(_x: Foo) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn operators_in_global_used_in_type() { + let src = r#" + global ONE = 1; + global COUNT = ONE + 2; + fn main() { + let _array: [Field; COUNT] = [1, 2, 3]; + } + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn break_and_continue_in_constrained_fn() { + let src = r#" + fn main() { + for i in 0 .. 10 { + if i == 2 { + continue; + } + if i == 5 { + break; + } + } + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} + +#[test] +fn break_and_continue_outside_loop() { + let src = r#" + unconstrained fn main() { + continue; + break; + } + "#; + assert_eq!(get_program_errors(src).len(), 2); +} + +// Regression for #2540 +#[test] +fn for_loop_over_array() { + let src = r#" + fn hello(_array: [u1; N]) { + for _ in 0..N {} + } + + fn main() { + let array: [u1; 2] = [0, 1]; + hello(array); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +// Regression for #4545 +#[test] +fn type_aliases_in_main() { + let src = r#" + type Outer = [u8; N]; + fn main(_arg: Outer<1>) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); +} + +#[test] +fn ban_mutable_globals() { + // Mutable globals are only allowed in a comptime context + let src = r#" + mut global FOO: Field = 0; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); +} + +#[test] +fn deny_inline_attribute_on_unconstrained() { + let src = r#" + #[no_predicates] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn deny_fold_attribute_on_unconstrained() { + let src = r#" + #[fold] + unconstrained pub fn foo(x: Field, y: Field) { + assert(x != y); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) + )); +} + +#[test] +fn specify_function_types_with_turbofish() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn specify_method_types_with_turbofish() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn incorrect_turbofish_count_function_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn incorrect_turbofish_count_method_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn struct_numeric_generic_in_function() { + let src = r#" + struct Foo { + inner: u64 + } + + pub fn bar() { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn struct_numeric_generic_in_struct() { + let src = r#" + struct Foo { + inner: u64 + } + + struct Bar { } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::UnsupportedNumericGenericType { .. } + ), + )); +} + +#[test] +fn bool_numeric_generic() { + let src = r#" + pub fn read() -> Field { + if N { + 0 + } else { + 1 + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); +} + +#[test] +fn numeric_generic_binary_operation_type_mismatch() { + let src = r#" + pub fn foo() -> bool { + let mut check: bool = true; + check = N; + check + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), + )); +} + +#[test] +fn bool_generic_as_loop_bound() { + let src = r#" + pub fn read() { + let mut fields = [0; N]; + for i in 0..N { + fields[i] = i + 1; + } + assert(fields[0] == 1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + )); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[1].0 + else { + panic!("Got an error other than a type mismatch"); + }; + + assert_eq!(expected_typ, "Field"); + assert_eq!(expr_typ, "bool"); +} + +#[test] +fn numeric_generic_in_function_signature() { + let src = r#" + pub fn foo(arr: [Field; N]) -> [Field; N] { arr } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_as_struct_field_type_fails() { + let src = r#" + struct Foo { + a: Field, + b: N, + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_as_array_length() { + let src = r#" + struct Foo { + a: Field, + b: [Field; N], + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_as_param_type() { + let src = r#" + pub fn foo(x: I) -> I { + let _q: I = 5; + x + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + + // Error from the parameter type + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the let statement annotated type + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // Error from the return type + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_fails() { + let src = r#" + struct Foo { + a: Field, + b: Bar, + } + struct Bar { + inner: N + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn normal_generic_used_in_nested_array_length_fail() { + let src = r#" + struct Foo { + a: Field, + b: Bar, + } + struct Bar { + inner: [Field; N] + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generic_used_in_nested_type_pass() { + // The order of these structs should not be changed to make sure + // that we are accurately resolving all struct generics before struct fields + let src = r#" + struct NestedNumeric { + a: Field, + b: InnerNumeric + } + struct InnerNumeric { + inner: [u64; N], + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_trait() { + // We want to make sure that `N` in `impl Deserialize` does + // not trigger `expected type, found numeric generic parameter N` as the trait + // does in fact expect a numeric generic. + let src = r#" + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + impl Deserialize for MyType { + fn deserialize(fields: [Field; N], other: T) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: other } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N], other: T) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_in_trait_impl_with_extra_impl_generics() { + let src = r#" + trait Default { + fn default() -> Self; + } + + struct MyType { + a: Field, + b: Field, + c: Field, + d: T, + } + + // Make sure that `T` is placed before `N` as we want to test that the order of the generics is correctly maintained. + // `N` is used first in the trait impl generics (`Deserialize for MyType`). + // We want to make sure that the compiler correctly accounts for that `N` has a numeric kind + // while `T` has a normal kind. + impl Deserialize for MyType where T: Default { + fn deserialize(fields: [Field; N]) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2], d: T::default() } + } + } + + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + assert_no_errors(src); +} + +#[test] +fn numeric_generic_used_in_turbofish() { + let src = r#" + pub fn double() -> u32 { + // Used as an expression + N * 2 + } + + pub fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); + } + "#; + assert_no_errors(src); +} + +#[test] +fn constant_used_with_numeric_generic() { + let src = r#" + struct ValueNote { + value: Field, + } + + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + impl Serialize<1> for ValueNote { + fn serialize(self) -> [Field; 1] { + [self.value] + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn normal_generic_used_when_numeric_expected_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + T::deserialize([0, 1]) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + pub fn read() -> T where T: Deserialize { + let mut fields: [Field; N] = [0; N]; + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 4); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[1].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + assert!(matches!( + errors[2].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); + // N + assert!(matches!( + errors[3].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + )); +} + +#[test] +fn numeric_generics_type_kind_mismatch() { + let src = r#" + fn foo() -> u16 { + N as u16 + } + + global J: u16 = 10; + + fn bar() -> u16 { + foo::() + } + + global M: u16 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + )); +} + +#[test] +fn numeric_generics_value_kind_mismatch_u32_u64() { + let src = r#" + struct BoundedVec { + storage: [T; MaxLen], + // can't be compared to MaxLen: u32 + // can't be used to index self.storage + len: u64, + } + + impl BoundedVec { + pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { + // We do this to avoid an unused variable warning on `self` + let _ = self.len; + for _ in 0..Len { } + } + + pub fn push(&mut self, elem: T) { + assert(self.len < MaxLen, "push out of bounds"); + self.storage[self.len] = elem; + self.len += 1; + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IntegerBitWidth { + bit_width_x: IntegerBitSize::SixtyFour, + bit_width_y: IntegerBitSize::ThirtyTwo, + .. + }), + )); +} + +#[test] +fn quote_code_fragments() { + // This test ensures we can quote (and unquote/splice) code fragments + // which by themselves are not valid code. They only need to be valid + // by the time they are unquoted into the macro's call site. + let src = r#" + fn main() { + comptime { + concat!(quote { assert( }, quote { false); }); + } + } + + comptime fn concat(a: Quoted, b: Quoted) -> Quoted { + quote { $a $b } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use InterpreterError::FailingConstraint; + assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +} + +#[test] +fn impl_stricter_than_trait_no_trait_method_constraints() { + // This test ensures that the error we get from the where clause on the trait impl method + // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. + let src = r#" + trait Serialize { + // We want to make sure we trigger the error when override a trait method + // which itself has no trait constraints. + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType { + fn serialize(self) -> [Field; 2] where T: ToField { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) + )); +} + +#[test] +fn impl_stricter_than_trait_different_generics() { + let src = r#" + trait Default { } + + // Object type of the trait constraint differs + trait Foo { + fn foo_good() where T: Default; + + fn foo_bad() where T: Default; + } + + impl Foo for () { + fn foo_good() where A: Default {} + + fn foo_bad() where B: Default {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_object_generics() { + let src = r#" + trait MyTrait { } + + trait OtherTrait {} + + struct Option { + inner: T + } + + struct OtherOption { + inner: Option, + } + + trait Bar { + fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; + + fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + + fn array_good() where [T; 8]: MyTrait; + + fn array_bad() where [T; 8]: MyTrait; + + fn tuple_good() where (Option, Option): MyTrait; + + fn tuple_bad() where (Option, Option): MyTrait; + } + + impl Bar for () { + fn bar_good() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn bar_bad() + where + OtherOption>: OtherTrait, + Option: MyTrait { } + + fn array_good() where [A; 8]: MyTrait { } + + fn array_bad() where [B; 8]: MyTrait { } + + fn tuple_good() where (Option, Option): MyTrait { } + + fn tuple_bad() where (Option, Option): MyTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[1].0 + { + dbg!(&constraint_typ.to_string().as_str()); + assert!(matches!(constraint_typ.to_string().as_str(), "[B; (8: numeric u32)]")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } + + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[2].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); + assert!(matches!(constraint_name.as_str(), "MyTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait() { + let src = r#" + trait Default { } + + trait OtherDefault { } + + struct Option { + inner: T + } + + trait Bar { + fn bar() where Option: Default; + } + + impl Bar for () { + // Trait constraint differs due to the trait even though the constraint + // types are the same. + fn bar() where Option: OtherDefault {} + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "Option")); + assert!(matches!(constraint_name.as_str(), "OtherDefault")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn trait_impl_where_clause_stricter_pass() { + let src = r#" + trait MyTrait { + fn good_foo() where H: OtherTrait; + + fn bad_foo() where H: OtherTrait; + } + + trait OtherTrait {} + + struct Option { + inner: T + } + + impl MyTrait for [T] where Option: MyTrait { + fn good_foo() where B: OtherTrait { } + + fn bad_foo() where A: OtherTrait { } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "OtherTrait")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_stricter_than_trait_different_trait_generics() { + let src = r#" + trait Foo { + fn foo() where T: T2; + } + + impl Foo for () { + // Should be A: T2 + fn foo() where A: T2 {} + } + + trait T2 {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { + constraint_typ, + constraint_name, + constraint_generics, + .. + }) = &errors[0].0 + { + assert!(matches!(constraint_typ.to_string().as_str(), "A")); + assert!(matches!(constraint_name.as_str(), "T2")); + assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); + } else { + panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); + } +} + +#[test] +fn impl_not_found_for_inner_impl() { + // We want to guarantee that we get a no impl found error + let src = r#" + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + trait ToField { + fn to_field(self) -> Field; + } + + fn process_array(array: [Field; N]) -> Field { + array[0] + } + + fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { + thing.serialize() + } + + struct MyType { + a: T, + b: T, + } + + impl Serialize<2> for MyType where T: ToField { + fn serialize(self) -> [Field; 2] { + [ self.a.to_field(), self.b.to_field() ] + } + } + + impl MyType { + fn do_thing_with_serialization_with_extra_steps(self) -> Field { + process_array(serialize_thing(self)) + } + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) + )); +} + +// Regression for #5388 +#[test] +fn comptime_let() { + let src = r#"fn main() { + comptime let my_var = 2; + assert_eq(my_var, 2); + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 0); +} + +#[test] +fn overflowing_u8() { + let src = r#" + fn main() { + let _: u8 = 256; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁸` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_u8() { + let src = r#" + fn main() { + let _: u8 = -1; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-1` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn overflowing_i8() { + let src = r#" + fn main() { + let _: i8 = 128; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_i8() { + let src = r#" + fn main() { + let _: i8 = -129; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-129` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls + let src = r#" + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); + + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } + + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } + + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } + + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super() { + let src = r#" + fn some_func() {} + + mod foo { + use super::some_func; + + pub fn bar() { + some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super_in_path() { + let src = r#" + fn some_func() {} + + mod foo { + pub fn func() { + super::some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn no_super() { + let src = "use super::some_func;"; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( + PathResolutionError::NoSuper(span), + )) = &errors[0].0 + else { + panic!("Expected a 'no super' error, got {:?}", errors[0].0); + }; + + assert_eq!(span.start(), 4); + assert_eq!(span.end(), 9); +} + +#[test] +fn cannot_call_unconstrained_function_outside_of_unsafe() { + let src = r#" + fn main() { + foo(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { + let src = r#" + fn main() { + let func = foo; + // Warning should trigger here + func(); + inner(func); + } + + fn inner(x: unconstrained fn() -> ()) { + // Warning should trigger here + x(); + } + + unconstrained fn foo() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + + for error in &errors { + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; + } +} + +#[test] +fn missing_unsafe_block_when_needing_type_annotations() { + // This test is a regression check that even when an unsafe block is missing + // that we still appropriately continue type checking and infer type annotations. + let src = r#" + fn main() { + let z = BigNum { limbs: [2, 0, 0] }; + assert(z.__is_zero() == false); + } + + struct BigNum { + limbs: [u64; N], + } + + impl BigNum { + unconstrained fn __is_zero_impl(self) -> bool { + let mut result: bool = true; + for i in 0..N { + result = result & (self.limbs[i] == 0); + } + result + } + } + + trait BigNumTrait { + fn __is_zero(self) -> bool; + } + + impl BigNumTrait for BigNum { + fn __is_zero(self) -> bool { + self.__is_zero_impl() + } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { + panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_pass_unconstrained_function_to_regular_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) { + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn cannot_assign_unconstrained_and_regular_fn_to_variable() { + let src = r#" + fn main() { + let _func = if true { foo } else { bar }; + } + + fn foo() {} + unconstrained fn bar() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { + panic!("Expected a context error, got {:?}", errors[0].0); + }; + + if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { + assert_eq!(expected_typ, "fn() -> ()"); + assert_eq!(expr_typ, "unconstrained fn() -> ()"); + } else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_pass_regular_function_to_unconstrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_unconstrained(func); + } + + fn foo() {} + + fn expect_unconstrained(_func: unconstrained fn() -> ()) {} + "#; + assert_no_errors(src); +} + +#[test] +fn cannot_pass_unconstrained_function_to_constrained_function() { + let src = r#" + fn main() { + let func = foo; + expect_regular(func); + } + + unconstrained fn foo() {} + + fn expect_regular(_func: fn() -> ()) {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { + panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); + }; +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { + let src = r#" + fn main() { + let _func: unconstrained fn() -> () = foo; + } + + fn foo() {} + "#; + assert_no_errors(src); +} + +#[test] +fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { + let src = r#" + fn main() { + let _ = Foo { func: foo }; + } + + fn foo() {} + + struct Foo { + func: unconstrained fn() -> (), + } + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_generics_count_mismatch() { + let src = r#" + trait Foo {} + + impl Foo<()> for Field {} + + fn main() {}"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "Foo"); + assert_eq!(*expected, 0); + assert_eq!(*found, 1); +} + +#[test] +fn bit_not_on_untyped_integer() { + let src = r#" + fn main() { + let _: u32 = 3 & !1; + } + "#; + assert_no_errors(src); +} + +#[test] +fn duplicate_struct_field() { + let src = r#" + struct Foo { + x: i32, + x: i32, + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { + first_def, + second_def, + }) = &errors[0].0 + else { + panic!("Expected a duplicate field error, got {:?}", errors[0].0); + }; + + assert_eq!(first_def.to_string(), "x"); + assert_eq!(second_def.to_string(), "x"); + + assert_eq!(first_def.span().start(), 26); + assert_eq!(second_def.span().start(), 42); +} + +#[test] +fn trait_constraint_on_tuple_type() { + let src = r#" + trait Foo { + fn foo(self, x: A) -> bool; + } + + pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { + x.foo(y) + } + + fn main() {}"#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_constructor_generics_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let _ = Foo:: { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), + )); +} + +#[test] +fn turbofish_in_constructor() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let x: Field = 0; + let _ = Foo:: { x: x }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i32"); + assert_eq!(expr_typ, "Field"); +} + +#[test] +fn turbofish_in_middle_of_variable_unsupported_yet() { + let src = r#" + struct Foo { + x: T + } + + impl Foo { + fn new(x: T) -> Self { + Foo { x } + } + } + + fn main() { + let _ = Foo::::new(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + )); +} + +#[test] +fn turbofish_in_struct_pattern() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn turbofish_in_struct_pattern_generic_count_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "struct Foo"); + assert_eq!(*expected, 1); + assert_eq!(*found, 2); +} + +#[test] +fn incorrect_generic_count_on_struct_impl() { + let src = r#" + struct Foo {} + impl Foo {} + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn incorrect_generic_count_on_type_alias() { + let src = r#" + struct Foo {} + type Bar = Foo; + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + found, expected, .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(found, 1); + assert_eq!(expected, 0); +} + +#[test] +fn uses_self_type_for_struct_function_call() { + let src = r#" + struct S { } + + impl S { + fn one() -> Field { + 1 + } + + fn two() -> Field { + Self::one() + Self::one() + } + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_inside_trait() { + let src = r#" + trait Foo { + fn foo() -> Self { + Self::bar() + } + + fn bar() -> Self; + } + + impl Foo for Field { + fn bar() -> Self { + 1 + } + } + + fn main() { + let _: Field = Foo::foo(); + } + "#; + assert_no_errors(src); +} #[test] -fn numeric_generics_type_kind_mismatch() { - // let src = r#" - // fn foo() -> u16 { - // N as u16 - // } - // - // global J: u16 = 10; - // - // fn bar() -> u16 { - // foo::() - // } - // - // global M: u16 = 3; - // - // fn main() { - // let _ = bar::(); - // } - // "#; +fn uses_self_type_in_trait_where_clause() { + let src = r#" + trait Trait { + fn trait_func() -> bool; + } + + trait Foo where Self: Trait { + fn foo(self) -> bool { + self.trait_func() + } + } + + struct Bar { + + } + + impl Foo for Bar { + + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = + &errors[0].0 + else { + panic!("Expected an unresolved method call error, got {:?}", errors[0].0); + }; + + assert_eq!(method_name, "trait_func"); +} +#[test] +fn do_not_eagerly_error_on_cast_on_type_variable() { let src = r#" - fn foo() -> u16 { - N as u16 + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) } - global J: u16 = 10; + fn main() { + let x: u8 = 1; + let _: Field = foo(x, |x| x as Field); + } + "#; + assert_no_errors(src); +} - fn bar() -> u16 { - foo::() +#[test] +fn error_on_cast_over_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) } - global M: u16 = 3; - fn main() { - let _ = bar::(); + let x = "a"; + let _: Field = foo(x, |x| x as Field); } "#; - let errors = get_program_errors(src); - // TODO cleanup - dbg!(&errors); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); + assert!(matches!( errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + self + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + pub fn use_it(t: T) -> i32 where T: Two { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { + let src = r#" + trait One { + fn one(self) -> i32; + } + + impl One for i32 { + fn one(self) -> i32 { + let _ = self; + 1 + } + } + + trait Two { + fn two(self) -> i32; + } + + impl Two for T where T: One { + fn two(self) -> i32 { + self.one() + 1 + } + } + + impl Two for u32 { + fn two(self) -> i32 { + let _ = self; + 0 + } + } + + pub fn use_it(t: u32) -> i32 { + Two::two(t) + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn impl_missing_associated_type() { + let src = r#" + trait Foo { + type Assoc; + } + + impl Foo for () {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) + )); +} + +#[test] +fn as_trait_path_syntax_resolves_outside_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + // AsTraitPath syntax is a bit silly when associated types + // are explicitly specified + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + use TypeCheckError::TypeMismatch; + let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { + panic!("Expected TypeMismatch error, found {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i64".to_string()); + assert_eq!(expr_typ, "i32".to_string()); +} + +#[test] +fn as_trait_path_syntax_no_impl() { + let src = r#" + trait Foo { + type Assoc; + } + + struct Bar {} + + impl Foo for Bar { + type Assoc = i32; + } + + fn main() { + let _: i64 = 1 as >::Assoc; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use CompilationError::TypeError; + assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); +} + +#[test] +fn errors_on_unused_private_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + trait Foo { + } + } + + use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn errors_on_unused_pub_crate_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + trait Foo { + } + } + + pub(crate) use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn warns_on_use_of_private_exported_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + use bar::baz; + + pub fn qux() { + baz(); + } + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated + + assert!(matches!( + &errors[0].0, + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(..), + )) + )); +} + +#[test] +fn can_use_pub_use_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn warns_on_re_export_of_item_with_less_visibility() { + let src = r#" + mod foo { + mod bar { + pub(crate) fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } + ) )); } -// TODO re-enable -// #[test] -// fn numeric_generics_value_kind_mismatch_u32_u64() { -// let src = r#" -// struct BoundedVec { -// storage: [T; MaxLen], -// // can't be compared to MaxLen: u32 -// // can't be used to index self.storage -// len: u64, -// } -// -// impl BoundedVec { -// pub fn extend_from_bounded_vec(&mut self, _vec: BoundedVec) { -// // We do this to avoid an unused variable warning on `self` -// let _ = self.len; -// for _ in 0..Len { } -// } -// -// pub fn push(&mut self, elem: T) { -// assert(self.len < MaxLen, "push out of bounds"); -// self.storage[self.len] = elem; -// self.len += 1; -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::IntegerBitWidth { -// bit_width_x: IntegerBitSize::SixtyFour, -// bit_width_y: IntegerBitSize::ThirtyTwo, -// .. -// }), -// )); -// } -// -// #[test] -// fn quote_code_fragments() { -// // This test ensures we can quote (and unquote/splice) code fragments -// // which by themselves are not valid code. They only need to be valid -// // by the time they are unquoted into the macro's call site. -// let src = r#" -// fn main() { -// comptime { -// concat!(quote { assert( }, quote { false); }); -// } -// } -// -// comptime fn concat(a: Quoted, b: Quoted) -> Quoted { -// quote { $a $b } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use InterpreterError::FailingConstraint; -// assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); -// } -// -// #[test] -// fn impl_stricter_than_trait_no_trait_method_constraints() { -// // This test ensures that the error we get from the where clause on the trait impl method -// // is a `DefCollectorErrorKind::ImplIsStricterThanTrait` error. -// let src = r#" -// trait Serialize { -// // We want to make sure we trigger the error when override a trait method -// // which itself has no trait constraints. -// fn serialize(self) -> [Field; N]; -// } -// -// trait ToField { -// fn to_field(self) -> Field; -// } -// -// fn process_array(array: [Field; N]) -> Field { -// array[0] -// } -// -// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { -// thing.serialize() -// } -// -// struct MyType { -// a: T, -// b: T, -// } -// -// impl Serialize<2> for MyType { -// fn serialize(self) -> [Field; 2] where T: ToField { -// [ self.a.to_field(), self.b.to_field() ] -// } -// } -// -// impl MyType { -// fn do_thing_with_serialization_with_extra_steps(self) -> Field { -// process_array(serialize_thing(self)) -// } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// &errors[0].0, -// CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) -// )); -// } -// -// #[test] -// fn impl_stricter_than_trait_different_generics() { -// let src = r#" -// trait Default { } -// -// // Object type of the trait constraint differs -// trait Foo { -// fn foo_good() where T: Default; -// -// fn foo_bad() where T: Default; -// } -// -// impl Foo for () { -// fn foo_good() where A: Default {} -// -// fn foo_bad() where B: Default {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_object_generics() { -// let src = r#" -// trait MyTrait { } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// struct OtherOption { -// inner: Option, -// } -// -// trait Bar { -// fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; -// -// fn array_good() where [T; 8]: MyTrait; -// -// fn array_bad() where [T; 8]: MyTrait; -// -// fn tuple_good() where (Option, Option): MyTrait; -// -// fn tuple_bad() where (Option, Option): MyTrait; -// } -// -// impl Bar for () { -// fn bar_good() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn bar_bad() -// where -// OtherOption>: OtherTrait, -// Option: MyTrait { } -// -// fn array_good() where [A; 8]: MyTrait { } -// -// fn array_bad() where [B; 8]: MyTrait { } -// -// fn tuple_good() where (Option, Option): MyTrait { } -// -// fn tuple_bad() where (Option, Option): MyTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 3); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[1].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[2].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); -// assert!(matches!(constraint_name.as_str(), "MyTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait() { -// let src = r#" -// trait Default { } -// -// trait OtherDefault { } -// -// struct Option { -// inner: T -// } -// -// trait Bar { -// fn bar() where Option: Default; -// } -// -// impl Bar for () { -// // Trait constraint differs due to the trait even though the constraint -// // types are the same. -// fn bar() where Option: OtherDefault {} -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "Option")); -// assert!(matches!(constraint_name.as_str(), "OtherDefault")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn trait_impl_where_clause_stricter_pass() { -// let src = r#" -// trait MyTrait { -// fn good_foo() where H: OtherTrait; -// -// fn bad_foo() where H: OtherTrait; -// } -// -// trait OtherTrait {} -// -// struct Option { -// inner: T -// } -// -// impl MyTrait for [T] where Option: MyTrait { -// fn good_foo() where B: OtherTrait { } -// -// fn bad_foo() where A: OtherTrait { } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "OtherTrait")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_stricter_than_trait_different_trait_generics() { -// let src = r#" -// trait Foo { -// fn foo() where T: T2; -// } -// -// impl Foo for () { -// // Should be A: T2 -// fn foo() where A: T2 {} -// } -// -// trait T2 {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { -// constraint_typ, -// constraint_name, -// constraint_generics, -// .. -// }) = &errors[0].0 -// { -// assert!(matches!(constraint_typ.to_string().as_str(), "A")); -// assert!(matches!(constraint_name.as_str(), "T2")); -// assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); -// } else { -// panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn impl_not_found_for_inner_impl() { -// // We want to guarantee that we get a no impl found error -// let src = r#" -// trait Serialize { -// fn serialize(self) -> [Field; N]; -// } -// -// trait ToField { -// fn to_field(self) -> Field; -// } -// -// fn process_array(array: [Field; N]) -> Field { -// array[0] -// } -// -// fn serialize_thing(thing: A) -> [Field; N] where A: Serialize { -// thing.serialize() -// } -// -// struct MyType { -// a: T, -// b: T, -// } -// -// impl Serialize<2> for MyType where T: ToField { -// fn serialize(self) -> [Field; 2] { -// [ self.a.to_field(), self.b.to_field() ] -// } -// } -// -// impl MyType { -// fn do_thing_with_serialization_with_extra_steps(self) -> Field { -// process_array(serialize_thing(self)) -// } -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) -// )); -// } -// -// // Regression for #5388 -// #[test] -// fn comptime_let() { -// let src = r#"fn main() { -// comptime let my_var = 2; -// assert_eq(my_var, 2); -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 0); -// } -// -// #[test] -// fn overflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = 256; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁸` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_u8() { -// let src = r#" -// fn main() { -// let _: u8 = -1; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-1` cannot fit into `u8` which has range `0..=255`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn overflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = 128; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn underflowing_i8() { -// let src = r#" -// fn main() { -// let _: i8 = -129; -// }"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// if let CompilationError::TypeError(error) = &errors[0].0 { -// assert_eq!( -// error.to_string(), -// "The value `-129` cannot fit into `i8` which has range `-128..=127`" -// ); -// } else { -// panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); -// } -// } -// -// #[test] -// fn turbofish_numeric_generic_nested_call() { -// // Check for turbofish numeric generics used with function calls -// let src = r#" -// fn foo() -> [u8; N] { -// [0; N] -// } -// -// fn bar() -> [u8; N] { -// foo::() -// } -// -// global M: u32 = 3; -// -// fn main() { -// let _ = bar::(); -// } -// "#; -// assert_no_errors(src); -// -// // Check for turbofish numeric generics used with method calls -// let src = r#" -// struct Foo { -// a: T -// } -// -// impl Foo { -// fn static_method() -> [u8; N] { -// [0; N] -// } -// -// fn impl_method(self) -> [T; N] { -// [self.a; N] -// } -// } -// -// fn bar() -> [u8; N] { -// let _ = Foo::static_method::(); -// let x: Foo = Foo { a: 0 }; -// x.impl_method::() -// } -// -// global M: u32 = 3; -// -// fn main() { -// let _ = bar::(); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn use_super() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// use super::some_func; -// -// pub fn bar() { -// some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn use_super_in_path() { -// let src = r#" -// fn some_func() {} -// -// mod foo { -// pub fn func() { -// super::some_func(); -// } -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn no_super() { -// let src = "use super::some_func;"; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( -// PathResolutionError::NoSuper(span), -// )) = &errors[0].0 -// else { -// panic!("Expected a 'no super' error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(span.start(), 4); -// assert_eq!(span.end(), 9); -// } -// -// #[test] -// fn cannot_call_unconstrained_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// foo(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { -// let src = r#" -// fn main() { -// let func = foo; -// // Warning should trigger here -// func(); -// inner(func); -// } -// -// fn inner(x: unconstrained fn() -> ()) { -// // Warning should trigger here -// x(); -// } -// -// unconstrained fn foo() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); -// -// for error in &errors { -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &error.0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// } -// -// #[test] -// fn missing_unsafe_block_when_needing_type_annotations() { -// // This test is a regression check that even when an unsafe block is missing -// // that we still appropriately continue type checking and infer type annotations. -// let src = r#" -// fn main() { -// let z = BigNum { limbs: [2, 0, 0] }; -// assert(z.__is_zero() == false); -// } -// -// struct BigNum { -// limbs: [u64; N], -// } -// -// impl BigNum { -// unconstrained fn __is_zero_impl(self) -> bool { -// let mut result: bool = true; -// for i in 0..N { -// result = result & (self.limbs[i] == 0); -// } -// result -// } -// } -// -// trait BigNumTrait { -// fn __is_zero(self) -> bool; -// } -// -// impl BigNumTrait for BigNum { -// fn __is_zero(self) -> bool { -// self.__is_zero_impl() -// } -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0].0 else { -// panic!("Expected an 'unsafe' error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_pass_unconstrained_function_to_regular_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) { -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn cannot_assign_unconstrained_and_regular_fn_to_variable() { -// let src = r#" -// fn main() { -// let _func = if true { foo } else { bar }; -// } -// -// fn foo() {} -// unconstrained fn bar() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else { -// panic!("Expected a context error, got {:?}", errors[0].0); -// }; -// -// if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { -// assert_eq!(expected_typ, "fn() -> ()"); -// assert_eq!(expr_typ, "unconstrained fn() -> ()"); -// } else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_pass_regular_function_to_unconstrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_unconstrained(func); -// } -// -// fn foo() {} -// -// fn expect_unconstrained(_func: unconstrained fn() -> ()) {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn cannot_pass_unconstrained_function_to_constrained_function() { -// let src = r#" -// fn main() { -// let func = foo; -// expect_regular(func); -// } -// -// unconstrained fn foo() {} -// -// fn expect_regular(_func: fn() -> ()) {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0].0 else { -// panic!("Expected an UnsafeFn error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_explicitly_typed_var() { -// let src = r#" -// fn main() { -// let _func: unconstrained fn() -> () = foo; -// } -// -// fn foo() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn can_assign_regular_function_to_unconstrained_function_in_struct_member() { -// let src = r#" -// fn main() { -// let _ = Foo { func: foo }; -// } -// -// fn foo() {} -// -// struct Foo { -// func: unconstrained fn() -> (), -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn trait_impl_generics_count_mismatch() { -// let src = r#" -// trait Foo {} -// -// impl Foo<()> for Field {} -// -// fn main() {}"#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "Foo"); -// assert_eq!(*expected, 0); -// assert_eq!(*found, 1); -// } -// -// #[test] -// fn bit_not_on_untyped_integer() { -// let src = r#" -// fn main() { -// let _: u32 = 3 & !1; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn duplicate_struct_field() { -// let src = r#" -// struct Foo { -// x: i32, -// x: i32, -// } -// -// fn main() {} -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { -// first_def, -// second_def, -// }) = &errors[0].0 -// else { -// panic!("Expected a duplicate field error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(first_def.to_string(), "x"); -// assert_eq!(second_def.to_string(), "x"); -// -// assert_eq!(first_def.span().start(), 26); -// assert_eq!(second_def.span().start(), 42); -// } -// -// #[test] -// fn trait_constraint_on_tuple_type() { -// let src = r#" -// trait Foo { -// fn foo(self, x: A) -> bool; -// } -// -// pub fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { -// x.foo(y) -// } -// -// fn main() {}"#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_constructor_generics_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let _ = Foo:: { x: 1 }; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), -// )); -// } -// -// #[test] -// fn turbofish_in_constructor() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let x: Field = 0; -// let _ = Foo:: { x: x }; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatch { -// expected_typ, expr_typ, .. -// }) = &errors[0].0 -// else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(expected_typ, "i32"); -// assert_eq!(expr_typ, "Field"); -// } -// -// #[test] -// fn turbofish_in_middle_of_variable_unsupported_yet() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// impl Foo { -// fn new(x: T) -> Self { -// Foo { x } -// } -// } -// -// fn main() { -// let _ = Foo::::new(1); -// } -// "#; -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), -// )); -// } -// -// #[test] -// fn turbofish_in_struct_pattern() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn turbofish_in_struct_pattern_errors_if_type_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value: Field = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 -// else { -// panic!("Expected a type mismatch error, got {:?}", errors[0].0); -// }; -// } -// -// #[test] -// fn turbofish_in_struct_pattern_generic_count_mismatch() { -// let src = r#" -// struct Foo { -// x: T -// } -// -// fn main() { -// let value = 0; -// let Foo:: { x } = Foo { x: value }; -// let _ = x; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// item, -// expected, -// found, -// .. -// }) = &errors[0].0 -// else { -// panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(item, "struct Foo"); -// assert_eq!(*expected, 1); -// assert_eq!(*found, 2); -// } -// -// #[test] -// fn incorrect_generic_count_on_struct_impl() { -// let src = r#" -// struct Foo {} -// impl Foo {} -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// -// #[test] -// fn incorrect_generic_count_on_type_alias() { -// let src = r#" -// struct Foo {} -// type Bar = Foo; -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { -// found, expected, .. -// }) = errors[0].0 -// else { -// panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(found, 1); -// assert_eq!(expected, 0); -// } -// -// #[test] -// fn uses_self_type_for_struct_function_call() { -// let src = r#" -// struct S { } -// -// impl S { -// fn one() -> Field { -// 1 -// } -// -// fn two() -> Field { -// Self::one() + Self::one() -// } -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn uses_self_type_inside_trait() { -// let src = r#" -// trait Foo { -// fn foo() -> Self { -// Self::bar() -// } -// -// fn bar() -> Self; -// } -// -// impl Foo for Field { -// fn bar() -> Self { -// 1 -// } -// } -// -// fn main() { -// let _: Field = Foo::foo(); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn uses_self_type_in_trait_where_clause() { -// let src = r#" -// trait Trait { -// fn trait_func() -> bool; -// } -// -// trait Foo where Self: Trait { -// fn foo(self) -> bool { -// self.trait_func() -// } -// } -// -// struct Bar { -// -// } -// -// impl Foo for Bar { -// -// } -// -// fn main() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = -// &errors[0].0 -// else { -// panic!("Expected an unresolved method call error, got {:?}", errors[0].0); -// }; -// -// assert_eq!(method_name, "trait_func"); -// } -// -// #[test] -// fn do_not_eagerly_error_on_cast_on_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x: u8 = 1; -// let _: Field = foo(x, |x| x as Field); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn error_on_cast_over_type_variable() { -// let src = r#" -// pub fn foo(x: T, f: fn(T) -> U) -> U { -// f(x) -// } -// -// fn main() { -// let x = "a"; -// let _: Field = foo(x, |x| x as Field); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// errors[0].0, -// CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) -// )); -// } -// -// #[test] -// fn trait_impl_for_a_type_that_implements_another_trait() { -// let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// self -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 -// } -// } -// -// pub fn use_it(t: T) -> i32 where T: Two { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn trait_impl_for_a_type_that_implements_another_trait_with_another_impl_used() { -// let src = r#" -// trait One { -// fn one(self) -> i32; -// } -// -// impl One for i32 { -// fn one(self) -> i32 { -// let _ = self; -// 1 -// } -// } -// -// trait Two { -// fn two(self) -> i32; -// } -// -// impl Two for T where T: One { -// fn two(self) -> i32 { -// self.one() + 1 -// } -// } -// -// impl Two for u32 { -// fn two(self) -> i32 { -// let _ = self; -// 0 -// } -// } -// -// pub fn use_it(t: u32) -> i32 { -// Two::two(t) -// } -// -// fn main() {} -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn impl_missing_associated_type() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// impl Foo for () {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) -// )); -// } -// -// #[test] -// fn as_trait_path_syntax_resolves_outside_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// // AsTraitPath syntax is a bit silly when associated types -// // are explicitly specified -// let _: i64 = 1 as >::Assoc; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// use TypeCheckError::TypeMismatch; -// let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].0.clone() else { -// panic!("Expected TypeMismatch error, found {:?}", errors[0].0); -// }; -// -// assert_eq!(expected_typ, "i64".to_string()); -// assert_eq!(expr_typ, "i32".to_string()); -// } -// -// #[test] -// fn as_trait_path_syntax_no_impl() { -// let src = r#" -// trait Foo { -// type Assoc; -// } -// -// struct Bar {} -// -// impl Foo for Bar { -// type Assoc = i32; -// } -// -// fn main() { -// let _: i64 = 1 as >::Assoc; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// use CompilationError::TypeError; -// assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); -// } -// -// #[test] -// fn errors_on_unused_private_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// trait Foo { -// } -// } -// -// use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); -// } -// -// #[test] -// fn errors_on_unused_pub_crate_import() { -// let src = r#" -// mod foo { -// pub fn bar() {} -// pub fn baz() {} -// -// trait Foo { -// } -// } -// -// pub(crate) use foo::bar; -// use foo::baz; -// use foo::Foo; -// -// impl Foo for Field { -// } -// -// fn main() { -// baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "bar"); -// assert_eq!(*item_type, "import"); -// } -// -// #[test] -// fn warns_on_use_of_private_exported_item() { -// let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} -// } -// -// use bar::baz; -// -// pub fn qux() { -// baz(); -// } -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::ResolverError(ResolverError::PathResolutionError( -// PathResolutionError::Private(..), -// )) -// )); -// } -// -// #[test] -// fn can_use_pub_use_item() { -// let src = r#" -// mod foo { -// mod bar { -// pub fn baz() {} -// } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// assert_no_errors(src); -// } -// -// #[test] -// fn warns_on_re_export_of_item_with_less_visibility() { -// let src = r#" -// mod foo { -// mod bar { -// pub(crate) fn baz() {} -// } -// -// pub use bar::baz; -// } -// -// fn main() { -// foo::baz(); -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// assert!(matches!( -// &errors[0].0, -// CompilationError::DefinitionError( -// DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } -// ) -// )); -// } -// -// #[test] -// fn unquoted_integer_as_integer_token() { -// let src = r#" -// trait Serialize { -// fn serialize() {} -// } -// -// #[attr] -// pub fn foobar() {} -// -// comptime fn attr(_f: FunctionDefinition) -> Quoted { -// let serialized_len = 1; -// // We are testing that when we unquote $serialized_len, it's unquoted -// // as the token `1` and not as something else that later won't be parsed correctly -// // in the context of a generic argument. -// quote { -// impl Serialize<$serialized_len> for Field { -// fn serialize() { } -// } -// } -// } -// -// fn main() {} -// "#; -// -// assert_no_errors(src); -// } -// -// #[test] -// fn errors_on_unused_function() { -// let src = r#" -// contract some_contract { -// // This function is unused, but it's a contract entrypoint -// // so it should not produce a warning -// fn foo() -> pub Field { -// 1 -// } -// } -// -// -// fn foo() { -// bar(); -// } -// -// fn bar() {} -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = -// &errors[0].0 -// else { -// panic!("Expected an unused item error"); -// }; -// -// assert_eq!(ident.to_string(), "foo"); -// assert_eq!(*item_type, "function"); -// } -// -// #[test] -// fn constrained_reference_to_unconstrained() { -// let src = r#" -// fn main(mut x: u32, y: pub u32) { -// let x_ref = &mut x; -// if x == 5 { -// unsafe { -// mut_ref_input(x_ref, y); -// } -// } -// -// assert(x == 10); -// } -// -// unconstrained fn mut_ref_input(x: &mut u32, y: u32) { -// *x = y; -// } -// "#; -// -// let errors = get_program_errors(src); -// assert_eq!(errors.len(), 1); -// -// let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = -// &errors[0].0 -// else { -// panic!("Expected an error about passing a constrained reference to unconstrained"); -// }; -// } -// -// #[test] -// fn comptime_type_in_runtime_code() { -// let source = "pub fn foo(_f: FunctionDefinition) {}"; -// let errors = get_program_errors(source); -// assert_eq!(errors.len(), 1); -// assert!(matches!( -// errors[0].0, -// CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) -// )); -// } +#[test] +fn unquoted_integer_as_integer_token() { + let src = r#" + trait Serialize { + fn serialize() {} + } + + #[attr] + pub fn foobar() {} + + comptime fn attr(_f: FunctionDefinition) -> Quoted { + let serialized_len = 1; + // We are testing that when we unquote $serialized_len, it's unquoted + // as the token `1` and not as something else that later won't be parsed correctly + // in the context of a generic argument. + quote { + impl Serialize<$serialized_len> for Field { + fn serialize() { } + } + } + } + + fn main() {} + "#; + + assert_no_errors(src); +} + +#[test] +fn errors_on_unused_function() { + let src = r#" + contract some_contract { + // This function is unused, but it's a contract entrypoint + // so it should not produce a warning + fn foo() -> pub Field { + 1 + } + } + + + fn foo() { + bar(); + } + + fn bar() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "foo"); + assert_eq!(*item_type, "function"); +} + +#[test] +fn constrained_reference_to_unconstrained() { + let src = r#" + fn main(mut x: u32, y: pub u32) { + let x_ref = &mut x; + if x == 5 { + unsafe { + mut_ref_input(x_ref, y); + } + } + + assert(x == 10); + } + + unconstrained fn mut_ref_input(x: &mut u32, y: u32) { + *x = y; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = + &errors[0].0 + else { + panic!("Expected an error about passing a constrained reference to unconstrained"); + }; +} + +#[test] +fn comptime_type_in_runtime_code() { + let source = "pub fn foo(_f: FunctionDefinition) {}"; + let errors = get_program_errors(source); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) + )); +} diff --git a/compiler/noirc_frontend/src/tests/name_shadowing.rs b/compiler/noirc_frontend/src/tests/name_shadowing.rs index abc3415efe9..b0d83510039 100644 --- a/compiler/noirc_frontend/src/tests/name_shadowing.rs +++ b/compiler/noirc_frontend/src/tests/name_shadowing.rs @@ -2,419 +2,418 @@ use super::get_program_errors; use std::collections::HashSet; -// TODO re-enable -// #[test] -// fn test_name_shadowing() { -// let src = " -// trait Default { -// fn default() -> Self; -// } -// -// impl Default for bool { -// fn default() -> bool { -// false -// } -// } -// -// impl Default for Field { -// fn default() -> Field { -// 0 -// } -// } -// -// impl Default for [T; N] where T: Default { -// fn default() -> [T; N] { -// [Default::default(); N] -// } -// } -// -// impl Default for (T, U) where T: Default, U: Default { -// fn default() -> (T, U) { -// (Default::default(), Default::default()) -// } -// } -// -// fn drop_var(_x: T, y: U) -> U { y } -// -// mod local_module { -// use crate::{Default, drop_var}; -// -// global LOCAL_GLOBAL_N: Field = 0; -// -// global LOCAL_GLOBAL_M: Field = 1; -// -// struct LocalStruct { -// field1: A, -// field2: B, -// field3: [A; N], -// field4: ([A; N], [B; M]), -// field5: &mut A, -// } -// -// impl Default for LocalStruct where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// field1: Default::default(), -// field2: Default::default(), -// field3: Default::default(), -// field4: Default::default(), -// field5: mut_field, -// } -// } -// } -// -// trait DefinedInLocalModule1 { -// fn trait_fn1(self, x: A); -// fn trait_fn2(self, y: B); -// fn trait_fn3(&mut self, x: A, y: B); -// fn trait_fn4(self, x: [A; 0], y: [B]); -// fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl DefinedInLocalModule1 for LocalStruct { -// fn trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// let x: Field = 0; -// assert(x == 0); -// let x: Field = 1; -// assert(x == 1); -// [] -// } -// } -// -// mod library { -// use crate::{Default, drop_var}; -// -// mod library2 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_2: Field = 4; -// -// global IMPORT_GLOBAL_M_2: Field = 5; -// -// // When we re-export this type from another library and then use it in -// // main, we get a panic -// struct ReExportMeFromAnotherLib1 { -// x : Field, -// } -// -// struct PubLibLocalStruct3 { -// pub_field1: A, -// pub_field2: B, -// pub_field3: [A; N], -// pub_field4: ([A; N], [B; M]), -// pub_field5: &mut A, -// } -// -// impl Default for PubLibLocalStruct3 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// pub_field1: Default::default(), -// pub_field2: Default::default(), -// pub_field3: Default::default(), -// pub_field4: Default::default(), -// pub_field5: mut_field, -// } -// } -// } -// -// trait PubLibDefinedInLocalModule3 { -// fn pub_trait_fn1(self, x: A); -// fn pub_trait_fn2(self, y: B); -// fn pub_trait_fn3(&mut self, x: A, y: B); -// fn pub_trait_fn4(self, x: [A; 0], y: [B]); -// fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { -// fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// [] -// } -// } -// -// // Re-export -// use library2::ReExportMeFromAnotherLib1; -// -// global IMPORT_GLOBAL_N_1: Field = 2; -// -// global IMPORT_GLOBAL_M_1: Field = 3; -// -// struct LibLocalStruct1 { -// lib_field1: A, -// lib_field2: B, -// lib_field3: [A; N], -// lib_field4: ([A; N], [B; M]), -// lib_field5: &mut A, -// } -// -// impl Default for LibLocalStruct1 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// lib_field1: Default::default(), -// lib_field2: Default::default(), -// lib_field3: Default::default(), -// lib_field4: Default::default(), -// lib_field5: mut_field, -// } -// } -// } -// -// trait LibDefinedInLocalModule1 { -// fn lib_trait_fn1(self, x: A); -// fn lib_trait_fn2(self, y: B); -// fn lib_trait_fn3(&mut self, x: A, y: B); -// fn lib_trait_fn4(self, x: [A; 0], y: [B]); -// fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl LibDefinedInLocalModule1 for LibLocalStruct1 { -// fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } -// } -// -// pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// [] -// } -// } -// -// mod library3 { -// use crate::{Default, drop_var}; -// -// global IMPORT_GLOBAL_N_3: Field = 6; -// -// global IMPORT_GLOBAL_M_3: Field = 7; -// -// struct ReExportMeFromAnotherLib2 { -// x : Field, -// } -// -// struct PubCrateLibLocalStruct2 { -// crate_field1: A, -// crate_field2: B, -// crate_field3: [A; N], -// crate_field4: ([A; N], [B; M]), -// crate_field5: &mut A, -// } -// -// impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { -// fn default() -> Self { -// let mut mut_field = &mut Default::default(); -// Self { -// crate_field1: Default::default(), -// crate_field2: Default::default(), -// crate_field3: Default::default(), -// crate_field4: Default::default(), -// crate_field5: mut_field, -// } -// } -// } -// -// trait PubCrateLibDefinedInLocalModule2 { -// fn crate_trait_fn1(self, x: A); -// fn crate_trait_fn2(self, y: B); -// fn crate_trait_fn3(&mut self, x: A, y: B); -// fn crate_trait_fn4(self, x: [A; 0], y: [B]); -// fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; -// fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { -// drop_var(self, N + M) -// } -// } -// -// impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { -// fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } -// fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } -// fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } -// fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } -// } -// -// pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// [] -// } -// } -// -// -// use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; -// -// use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; -// -// // overlapping -// // use library::library2::ReExportMeFromAnotherLib1; -// use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; -// -// use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; -// -// -// fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { -// assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); -// assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); -// assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); -// assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); -// -// let x: LocalStruct = Default::default(); -// assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); -// -// let x: LibLocalStruct1 = Default::default(); -// assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); -// -// let x: PubLibLocalStruct3 = Default::default(); -// assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); -// -// let x: PubCrateLibLocalStruct2 = Default::default(); -// assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); -// -// assert(drop_var(local_fn2((0, 1), [], []), true)); -// assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); -// assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); -// assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); -// }"; -// -// // NOTE: these names must be "replacement-unique", i.e. -// // replacing one in a discinct name should do nothing -// let names_to_collapse = [ -// "DefinedInLocalModule1", -// "IMPORT_GLOBAL_M_1", -// "IMPORT_GLOBAL_M_2", -// "IMPORT_GLOBAL_M_3", -// "IMPORT_GLOBAL_N_1", -// "IMPORT_GLOBAL_N_2", -// "IMPORT_GLOBAL_N_3", -// "LOCAL_GLOBAL_M", -// "LOCAL_GLOBAL_N", -// "LibDefinedInLocalModule1", -// "LibLocalStruct1", -// "Liblocal_fn1", -// "LocalStruct", -// "PubCrateLibDefinedInLocalModule2", -// "PubCrateLibLocalStruct2", -// "PubCrateLiblocal_fn2", -// "PubLibDefinedInLocalModule3", -// "PubLibLocalStruct3", -// "PubLiblocal_fn3", -// "ReExportMeFromAnotherLib1", -// "ReExportMeFromAnotherLib2", -// "local_fn4", -// "crate_field1", -// "crate_field2", -// "crate_field3", -// "crate_field4", -// "crate_field5", -// "crate_trait_fn1", -// "crate_trait_fn2", -// "crate_trait_fn3", -// "crate_trait_fn4", -// "crate_trait_fn5", -// "crate_trait_fn6", -// "crate_trait_fn7", -// "field1", -// "field2", -// "field3", -// "field4", -// "field5", -// "lib_field1", -// "lib_field2", -// "lib_field3", -// "lib_field4", -// "lib_field5", -// "lib_trait_fn1", -// "lib_trait_fn2", -// "lib_trait_fn3", -// "lib_trait_fn4", -// "lib_trait_fn5", -// "lib_trait_fn6", -// "lib_trait_fn7", -// "pub_field1", -// "pub_field2", -// "pub_field3", -// "pub_field4", -// "pub_field5", -// "pub_trait_fn1", -// "pub_trait_fn2", -// "pub_trait_fn3", -// "pub_trait_fn4", -// "pub_trait_fn5", -// "pub_trait_fn6", -// "pub_trait_fn7", -// "trait_fn1", -// "trait_fn2", -// "trait_fn3", -// "trait_fn4", -// "trait_fn5", -// "trait_fn6", -// "trait_fn7", -// ]; -// -// // TODO(https://github.com/noir-lang/noir/issues/4973): -// // Name resolution panic from name shadowing test -// let cases_to_skip = [ -// (1, 21), -// (2, 11), -// (2, 21), -// (3, 11), -// (3, 18), -// (3, 21), -// (4, 21), -// (5, 11), -// (5, 21), -// (6, 11), -// (6, 18), -// (6, 21), -// ]; -// let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); -// -// for (i, x) in names_to_collapse.iter().enumerate() { -// for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { -// if !cases_to_skip.contains(&(i, j)) { -// dbg!((i, j)); -// -// let modified_src = src.replace(x, y); -// let errors = get_program_errors(&modified_src); -// assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); -// } -// } -// } -// } +#[test] +fn test_name_shadowing() { + let src = " + trait Default { + fn default() -> Self; + } + + impl Default for bool { + fn default() -> bool { + false + } + } + + impl Default for Field { + fn default() -> Field { + 0 + } + } + + impl Default for [T; N] where T: Default { + fn default() -> [T; N] { + [Default::default(); N] + } + } + + impl Default for (T, U) where T: Default, U: Default { + fn default() -> (T, U) { + (Default::default(), Default::default()) + } + } + + fn drop_var(_x: T, y: U) -> U { y } + + mod local_module { + use crate::{Default, drop_var}; + + global LOCAL_GLOBAL_N: Field = 0; + + global LOCAL_GLOBAL_M: Field = 1; + + struct LocalStruct { + field1: A, + field2: B, + field3: [A; N], + field4: ([A; N], [B; M]), + field5: &mut A, + } + + impl Default for LocalStruct where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + field1: Default::default(), + field2: Default::default(), + field3: Default::default(), + field4: Default::default(), + field5: mut_field, + } + } + } + + trait DefinedInLocalModule1 { + fn trait_fn1(self, x: A); + fn trait_fn2(self, y: B); + fn trait_fn3(&mut self, x: A, y: B); + fn trait_fn4(self, x: [A; 0], y: [B]); + fn trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl DefinedInLocalModule1 for LocalStruct { + fn trait_fn1(self, _x: A) { drop_var(self, ()) } + fn trait_fn2(self, _y: B) { drop_var(self, ()) } + fn trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn local_fn4(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + let x: Field = 0; + assert(x == 0); + let x: Field = 1; + assert(x == 1); + [] + } + } + + mod library { + use crate::{Default, drop_var}; + + mod library2 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_2: Field = 4; + + global IMPORT_GLOBAL_M_2: Field = 5; + + // When we re-export this type from another library and then use it in + // main, we get a panic + struct ReExportMeFromAnotherLib1 { + x : Field, + } + + struct PubLibLocalStruct3 { + pub_field1: A, + pub_field2: B, + pub_field3: [A; N], + pub_field4: ([A; N], [B; M]), + pub_field5: &mut A, + } + + impl Default for PubLibLocalStruct3 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + pub_field1: Default::default(), + pub_field2: Default::default(), + pub_field3: Default::default(), + pub_field4: Default::default(), + pub_field5: mut_field, + } + } + } + + trait PubLibDefinedInLocalModule3 { + fn pub_trait_fn1(self, x: A); + fn pub_trait_fn2(self, y: B); + fn pub_trait_fn3(&mut self, x: A, y: B); + fn pub_trait_fn4(self, x: [A; 0], y: [B]); + fn pub_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn pub_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubLibDefinedInLocalModule3 for PubLibLocalStruct3 { + fn pub_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn pub_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn pub_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn pub_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn pub_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn pub_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn PubLiblocal_fn3(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + [] + } + } + + // Re-export + use library2::ReExportMeFromAnotherLib1; + + global IMPORT_GLOBAL_N_1: Field = 2; + + global IMPORT_GLOBAL_M_1: Field = 3; + + struct LibLocalStruct1 { + lib_field1: A, + lib_field2: B, + lib_field3: [A; N], + lib_field4: ([A; N], [B; M]), + lib_field5: &mut A, + } + + impl Default for LibLocalStruct1 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + lib_field1: Default::default(), + lib_field2: Default::default(), + lib_field3: Default::default(), + lib_field4: Default::default(), + lib_field5: mut_field, + } + } + } + + trait LibDefinedInLocalModule1 { + fn lib_trait_fn1(self, x: A); + fn lib_trait_fn2(self, y: B); + fn lib_trait_fn3(&mut self, x: A, y: B); + fn lib_trait_fn4(self, x: [A; 0], y: [B]); + fn lib_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn lib_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl LibDefinedInLocalModule1 for LibLocalStruct1 { + fn lib_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn lib_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn lib_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn lib_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn lib_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + fn lib_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, []) } + } + + pub fn Liblocal_fn1(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + [] + } + } + + mod library3 { + use crate::{Default, drop_var}; + + global IMPORT_GLOBAL_N_3: Field = 6; + + global IMPORT_GLOBAL_M_3: Field = 7; + + struct ReExportMeFromAnotherLib2 { + x : Field, + } + + struct PubCrateLibLocalStruct2 { + crate_field1: A, + crate_field2: B, + crate_field3: [A; N], + crate_field4: ([A; N], [B; M]), + crate_field5: &mut A, + } + + impl Default for PubCrateLibLocalStruct2 where A: Default, B: Default { + fn default() -> Self { + let mut mut_field = &mut Default::default(); + Self { + crate_field1: Default::default(), + crate_field2: Default::default(), + crate_field3: Default::default(), + crate_field4: Default::default(), + crate_field5: mut_field, + } + } + } + + trait PubCrateLibDefinedInLocalModule2 { + fn crate_trait_fn1(self, x: A); + fn crate_trait_fn2(self, y: B); + fn crate_trait_fn3(&mut self, x: A, y: B); + fn crate_trait_fn4(self, x: [A; 0], y: [B]); + fn crate_trait_fn5(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn6(self, x: [A; N], y: [B; M]) -> [A; 0]; + fn crate_trait_fn7(self, _x: fn([A; 0]) -> B) -> Field { + drop_var(self, N + M) + } + } + + impl PubCrateLibDefinedInLocalModule2 for PubCrateLibLocalStruct2 { + fn crate_trait_fn1(self, _x: A) { drop_var(self, ()) } + fn crate_trait_fn2(self, _y: B) { drop_var(self, ()) } + fn crate_trait_fn3(&mut self, _x: A, _y: B) { drop_var(self, ()) } + fn crate_trait_fn4(self, _x: [A; 0], _y: [B]) { drop_var(self, ()) } + fn crate_trait_fn5(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + fn crate_trait_fn6(self, _x: [A; N], _y: [B; M]) -> [A; 0] { drop_var(self, ()); [] } + } + + pub(crate) fn PubCrateLiblocal_fn2(_x: (A, B), _y: [Field; N], _z: [Field; M]) -> [A; 0] { + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + [] + } + } + + + use crate::local_module::{local_fn4, LocalStruct, DefinedInLocalModule1, LOCAL_GLOBAL_N, LOCAL_GLOBAL_M}; + + use library::{ReExportMeFromAnotherLib1, LibLocalStruct1, LibDefinedInLocalModule1, Liblocal_fn1, IMPORT_GLOBAL_N_1, IMPORT_GLOBAL_M_1}; + + // overlapping + // use library::library2::ReExportMeFromAnotherLib1; + use crate::library::library2::{PubLibLocalStruct3, PubLibDefinedInLocalModule3, PubLiblocal_fn3, IMPORT_GLOBAL_N_2, IMPORT_GLOBAL_M_2}; + + use library3::{ReExportMeFromAnotherLib2, PubCrateLibLocalStruct2, PubCrateLibDefinedInLocalModule2, PubCrateLiblocal_fn2, IMPORT_GLOBAL_N_3, IMPORT_GLOBAL_M_3}; + + + fn main(_x: ReExportMeFromAnotherLib1, _y: ReExportMeFromAnotherLib2) { + assert(LOCAL_GLOBAL_N != LOCAL_GLOBAL_M); + assert(IMPORT_GLOBAL_N_1 != IMPORT_GLOBAL_M_1); + assert(IMPORT_GLOBAL_N_2 != IMPORT_GLOBAL_M_2); + assert(IMPORT_GLOBAL_N_3 != IMPORT_GLOBAL_M_3); + + let x: LocalStruct = Default::default(); + assert(drop_var(x.trait_fn5([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + assert(drop_var(x.trait_fn6([0; LOCAL_GLOBAL_N], [false; LOCAL_GLOBAL_M]), true)); + + let x: LibLocalStruct1 = Default::default(); + assert(drop_var(x.lib_trait_fn5([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + assert(drop_var(x.lib_trait_fn6([0; IMPORT_GLOBAL_N_1], [false; IMPORT_GLOBAL_M_1]), true)); + + let x: PubLibLocalStruct3 = Default::default(); + assert(drop_var(x.pub_trait_fn5([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + assert(drop_var(x.pub_trait_fn6([0; IMPORT_GLOBAL_N_2], [false; IMPORT_GLOBAL_M_2]), true)); + + let x: PubCrateLibLocalStruct2 = Default::default(); + assert(drop_var(x.crate_trait_fn5([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + assert(drop_var(x.crate_trait_fn6([0; IMPORT_GLOBAL_N_3], [false; IMPORT_GLOBAL_M_3]), true)); + + assert(drop_var(local_fn2((0, 1), [], []), true)); + assert(drop_var(Liblocal_fn1((0, 1), [], []), true)); + assert(drop_var(PubLiblocal_fn4((0, 1), [], []), true)); + assert(drop_var(PubCrateLiblocal_fn3((0, 1), [], []), true)); + }"; + + // NOTE: these names must be "replacement-unique", i.e. + // replacing one in a discinct name should do nothing + let names_to_collapse = [ + "DefinedInLocalModule1", + "IMPORT_GLOBAL_M_1", + "IMPORT_GLOBAL_M_2", + "IMPORT_GLOBAL_M_3", + "IMPORT_GLOBAL_N_1", + "IMPORT_GLOBAL_N_2", + "IMPORT_GLOBAL_N_3", + "LOCAL_GLOBAL_M", + "LOCAL_GLOBAL_N", + "LibDefinedInLocalModule1", + "LibLocalStruct1", + "Liblocal_fn1", + "LocalStruct", + "PubCrateLibDefinedInLocalModule2", + "PubCrateLibLocalStruct2", + "PubCrateLiblocal_fn2", + "PubLibDefinedInLocalModule3", + "PubLibLocalStruct3", + "PubLiblocal_fn3", + "ReExportMeFromAnotherLib1", + "ReExportMeFromAnotherLib2", + "local_fn4", + "crate_field1", + "crate_field2", + "crate_field3", + "crate_field4", + "crate_field5", + "crate_trait_fn1", + "crate_trait_fn2", + "crate_trait_fn3", + "crate_trait_fn4", + "crate_trait_fn5", + "crate_trait_fn6", + "crate_trait_fn7", + "field1", + "field2", + "field3", + "field4", + "field5", + "lib_field1", + "lib_field2", + "lib_field3", + "lib_field4", + "lib_field5", + "lib_trait_fn1", + "lib_trait_fn2", + "lib_trait_fn3", + "lib_trait_fn4", + "lib_trait_fn5", + "lib_trait_fn6", + "lib_trait_fn7", + "pub_field1", + "pub_field2", + "pub_field3", + "pub_field4", + "pub_field5", + "pub_trait_fn1", + "pub_trait_fn2", + "pub_trait_fn3", + "pub_trait_fn4", + "pub_trait_fn5", + "pub_trait_fn6", + "pub_trait_fn7", + "trait_fn1", + "trait_fn2", + "trait_fn3", + "trait_fn4", + "trait_fn5", + "trait_fn6", + "trait_fn7", + ]; + + // TODO(https://github.com/noir-lang/noir/issues/4973): + // Name resolution panic from name shadowing test + let cases_to_skip = [ + (1, 21), + (2, 11), + (2, 21), + (3, 11), + (3, 18), + (3, 21), + (4, 21), + (5, 11), + (5, 21), + (6, 11), + (6, 18), + (6, 21), + ]; + let cases_to_skip: HashSet<(usize, usize)> = cases_to_skip.into_iter().collect(); + + for (i, x) in names_to_collapse.iter().enumerate() { + for (j, y) in names_to_collapse.iter().enumerate().filter(|(j, _)| i < *j) { + if !cases_to_skip.contains(&(i, j)) { + dbg!((i, j)); + + let modified_src = src.replace(x, y); + let errors = get_program_errors(&modified_src); + assert!(!errors.is_empty(), "Expected errors, got: {:?}", errors); + } + } + } +} From 6a6a826df0ae7d6a4c73bee6e6d98791a7acb5fe Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 16:57:27 -0400 Subject: [PATCH 29/45] updating Type::Constant cases, debugging failing stdlib and test_programs --- aztec_macros/src/utils/hir_utils.rs | 6 ++-- compiler/noirc_driver/src/abi_gen.rs | 2 +- .../noirc_frontend/src/elaborator/types.rs | 9 +++++- compiler/noirc_frontend/src/hir_def/types.rs | 20 +++++++------ .../src/hir_def/types/arithmetic.rs | 26 +++++++++++++---- noir_stdlib/src/hash/keccak.nr | 4 +-- noir_stdlib/src/hash/mimc.nr | 2 +- .../regression_4635/src/main.nr | 2 +- .../brillig_cow_regression/src/main.nr | 20 ++++++------- .../cast_and_shift_global/src/main.nr | 6 ++-- .../fold_numeric_generic_poseidon/src/main.nr | 2 +- .../global_consts/src/foo.nr | 4 +-- .../global_consts/src/foo/bar.nr | 2 +- .../global_consts/src/main.nr | 28 +++++++++---------- .../execution_success/hashmap/src/main.nr | 2 +- .../src/main.nr | 4 +-- tooling/lsp/src/requests/completion.rs | 2 +- tooling/lsp/src/requests/hover.rs | 4 +-- .../src/trait_impl_method_stub_generator.rs | 2 +- 19 files changed, 86 insertions(+), 61 deletions(-) diff --git a/aztec_macros/src/utils/hir_utils.rs b/aztec_macros/src/utils/hir_utils.rs index 200ce3099cb..4f1ef78b474 100644 --- a/aztec_macros/src/utils/hir_utils.rs +++ b/aztec_macros/src/utils/hir_utils.rs @@ -74,7 +74,7 @@ pub fn signature_of_type(typ: &Type) -> String { Type::FieldElement => "Field".to_owned(), Type::Bool => "bool".to_owned(), Type::Array(len, typ) => { - if let Type::Constant(len) = **len { + if let Type::Constant(len, _) = **len { format!("[{};{len}]", signature_of_type(typ)) } else { unimplemented!("Cannot generate signature for array with length type {:?}", typ) @@ -90,7 +90,7 @@ pub fn signature_of_type(typ: &Type) -> String { format!("({})", fields.join(",")) } Type::String(len_typ) => { - if let Type::Constant(len) = **len_typ { + if let Type::Constant(len, _) = **len_typ { format!("str<{len}>") } else { unimplemented!( @@ -326,7 +326,7 @@ pub fn get_serialized_length( let serialized_trait_impl = serialized_trait_impl_shared.borrow(); match serialized_trait_impl.trait_generics.first().unwrap() { - Type::Constant(value) => Ok(*value), + Type::Constant(value, _) => Ok(*value), _ => Err(MacroError { primary_message: format!("{} length for {} must be a constant", trait_name, typ), secondary_message: None, diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index e2692349baa..be89b24fee5 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -99,7 +99,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { } Type::Error | Type::Unit - | Type::Constant(_) + | Type::Constant(..) | Type::InfixExpr(..) | Type::TraitAsType(..) | Type::TypeVariable(_, _) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index c53c988ee23..5c983d6a386 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -438,8 +438,15 @@ impl<'context> Elaborator<'context> { // self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), // ); + let kind = self + .interner + .get_global_let_statement(id) + .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) + .unwrap_or(Kind::u32()); + dbg!("lookup_generic_or_global_type", &kind); + // panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); - Some(Type::Constant(self.eval_global_as_array_length(id, path), Kind::u32())) + Some(Type::Constant(self.eval_global_as_array_length(id, path), kind)) } _ => None, } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 34b45debd2f..fbe2614abe1 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -151,10 +151,7 @@ impl Kind { // TODO before merge: look for occurrences pub(crate) fn u32() -> Self { - Self::Numeric(Box::new(Type::Integer( - Signedness::Unsigned, - IntegerBitSize::ThirtyTwo, - ))) + Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } } @@ -1130,12 +1127,13 @@ impl Type { } pub(crate) fn kind(&self) -> Option { + // TODO cleanup + dbg!("fn kind", &self); match self { Type::NamedGeneric(_, _, kind) => Some(kind.clone()), Type::Constant(_, kind) => Some(kind.clone()), Type::TypeVariable(..) => None, - // TODO: ensure kinds for InfixExpr are checked, e.g. // // Type::InfixExpr(lhs, _, rhs) => @@ -1181,12 +1179,10 @@ impl Type { // TODO fn infix_kind(&self, other: &Self) -> Kind { - // TODO!!! add kind check, replace u32 default with Type::Error self.kind().or(other.kind()).unwrap_or(Kind::u32()) } - /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self) -> u32 { match self { @@ -1624,7 +1620,11 @@ impl Type { } else if let InfixExpr(lhs, op, rhs) = other { if let Some(inverse) = op.inverse() { // Handle cases like `4 = a + b` by trying to solve to `a = 4 - b` - let new_type = InfixExpr(Box::new(Constant(*value, kind.clone())), inverse, rhs.clone()); + let new_type = InfixExpr( + Box::new(Constant(*value, kind.clone())), + inverse, + rhs.clone(), + ); new_type.try_unify(lhs, bindings)?; Ok(()) } else { @@ -2654,7 +2654,9 @@ impl PartialEq for Type { (Forall(lhs_vars, lhs_type), Forall(rhs_vars, rhs_type)) => { lhs_vars == rhs_vars && lhs_type == rhs_type } - (Constant(lhs, lhs_kind), Constant(rhs, rhs_kind)) => lhs == rhs && lhs_kind == rhs_kind, + (Constant(lhs, lhs_kind), Constant(rhs, rhs_kind)) => { + lhs == rhs && lhs_kind == rhs_kind + } (Quoted(lhs), Quoted(rhs)) => lhs == rhs, (InfixExpr(l_lhs, l_op, l_rhs), InfixExpr(r_lhs, r_op, r_rhs)) => { l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index db9b9a7cfc4..ae1791736dc 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -17,7 +17,9 @@ impl Type { Type::InfixExpr(lhs, op, rhs) => { // evaluate_to_u32 also calls canonicalize so if we just called // `self.evaluate_to_u32()` we'd get infinite recursion. - if let (Some(lhs_u32), Some(rhs_u32)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { + if let (Some(lhs_u32), Some(rhs_u32)) = + (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) + { if let Some(result) = op.function(lhs_u32, rhs_u32) { return Type::Constant(result, lhs.infix_kind(&rhs)); } @@ -69,7 +71,9 @@ impl Type { if let Some(result) = op.function(constant, new_constant) { constant = result; } else { - *sorted.entry(Type::Constant(new_constant, new_constant_kind)).or_default() += 1; + *sorted + .entry(Type::Constant(new_constant, new_constant_kind)) + .or_default() += 1; } } other => { @@ -93,7 +97,11 @@ impl Type { } if constant != zero_value { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant, lhs.infix_kind(rhs)))); + typ = Type::InfixExpr( + Box::new(typ), + op, + Box::new(Type::Constant(constant, lhs.infix_kind(rhs))), + ); } typ @@ -202,7 +210,11 @@ impl Type { op = op.inverse()?; } let result = op.function(l_const, r_const)?; - Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result, lhs.infix_kind(rhs))))) + Some(Type::InfixExpr( + l_type, + l_op, + Box::new(Type::Constant(result, lhs.infix_kind(rhs))), + )) } (Multiplication | Division, Multiplication | Division) => { // If l_op is a division we want to inverse the rhs operator. @@ -215,7 +227,11 @@ impl Type { } else { let result = op.function(l_const, r_const)?; // TODO: Type::InfixExpr kinds - Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result, lhs.infix_kind(rhs))))) + Some(Type::InfixExpr( + l_type, + l_op, + Box::new(Type::Constant(result, lhs.infix_kind(rhs))), + )) } } _ => None, diff --git a/noir_stdlib/src/hash/keccak.nr b/noir_stdlib/src/hash/keccak.nr index 36787c7b4af..c3321b62693 100644 --- a/noir_stdlib/src/hash/keccak.nr +++ b/noir_stdlib/src/hash/keccak.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; use crate::runtime::is_unconstrained; global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8; -global NUM_KECCAK_LANES = 25; +global NUM_KECCAK_LANES: u32 = 25; global BLOCK_SIZE = 136; //(1600 - BITS * 2) / WORD_SIZE; global WORD_SIZE = 8; @@ -77,7 +77,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 } //2. sponge_absorb - let mut state : [u64;NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; + let mut state : [u64; NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; // When in an unconstrained runtime we can take advantage of runtime loop bounds, // thus allowing us to simplify the loop body. if is_unconstrained() { diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index 6145a387f26..c5523cf2547 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -18,7 +18,7 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp: Field) -> Fi h + k } -global MIMC_BN254_ROUNDS = 91; +global MIMC_BN254_ROUNDS: u32 = 91; //generated from seed "mimc" using keccak256 global MIMC_BN254_CONSTANTS: [Field; MIMC_BN254_ROUNDS] = [ 0, diff --git a/test_programs/compile_success_empty/regression_4635/src/main.nr b/test_programs/compile_success_empty/regression_4635/src/main.nr index 6709a421470..6bccdf9e30f 100644 --- a/test_programs/compile_success_empty/regression_4635/src/main.nr +++ b/test_programs/compile_success_empty/regression_4635/src/main.nr @@ -12,7 +12,7 @@ trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -global AZTEC_ADDRESS_LENGTH = 1; +global AZTEC_ADDRESS_LENGTH: u32 = 1; struct AztecAddress { inner : Field 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 adeadfc9f20..3fce7fb2c7d 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -1,16 +1,16 @@ // Tests a performance regression found in aztec-packages with brillig cow optimization -global MAX_NOTE_HASHES_PER_TX: u64 = 64; -global MAX_NULLIFIERS_PER_TX: u64 = 64; -global MAX_L2_TO_L1_MSGS_PER_TX: u64 = 2; -global MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX: u64 = 16; -global MAX_NEW_CONTRACTS_PER_TX: u64 = 1; -global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; -global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; -global NUM_FIELDS_PER_SHA256 = 2; +global MAX_NOTE_HASHES_PER_TX: u32 = 64; +global MAX_NULLIFIERS_PER_TX: u32 = 64; +global MAX_L2_TO_L1_MSGS_PER_TX: u32 = 2; +global MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX: u32 = 16; +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_LOG_FIELDS = 4; -global TX_EFFECT_HASH_FULL_FIELDS = 165; +global TX_EFFECT_HASH_LOG_FIELDS: u32 = 4; +global TX_EFFECT_HASH_FULL_FIELDS: u32 = 165; struct PublicDataUpdateRequest { leaf_slot : Field, diff --git a/test_programs/execution_success/cast_and_shift_global/src/main.nr b/test_programs/execution_success/cast_and_shift_global/src/main.nr index 577de78465c..2c0158fc71a 100644 --- a/test_programs/execution_success/cast_and_shift_global/src/main.nr +++ b/test_programs/execution_success/cast_and_shift_global/src/main.nr @@ -1,6 +1,6 @@ -global THREE: u64 = 3; -global EIGHT: u64 = 1 << THREE as u8; -global SEVEN: u64 = EIGHT - 1; +global THREE: u32 = 3; +global EIGHT: u32 = 1 << THREE as u8; +global SEVEN: u32 = EIGHT - 1; fn main() { assert([0; EIGHT] == [0; 8]); 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 8727b2a23de..8eaea086ec0 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,6 +1,6 @@ use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; -global NUM_HASHES = 2; +global NUM_HASHES: u32 = 2; global HASH_LENGTH = 10; #[fold] diff --git a/test_programs/execution_success/global_consts/src/foo.nr b/test_programs/execution_success/global_consts/src/foo.nr index 413b9c3a74b..50e331493dc 100644 --- a/test_programs/execution_success/global_consts/src/foo.nr +++ b/test_programs/execution_success/global_consts/src/foo.nr @@ -1,7 +1,7 @@ mod bar; -global N: u64 = 5; -global MAGIC_NUMBER: u64 = 3; +global N: u32 = 5; +global MAGIC_NUMBER: u32 = 3; global TYPE_INFERRED = 42; pub fn from_foo(x: [Field; bar::N]) { diff --git a/test_programs/execution_success/global_consts/src/foo/bar.nr b/test_programs/execution_success/global_consts/src/foo/bar.nr index 5404c9cf1e3..61ac1e8e8ed 100644 --- a/test_programs/execution_success/global_consts/src/foo/bar.nr +++ b/test_programs/execution_success/global_consts/src/foo/bar.nr @@ -1,4 +1,4 @@ -global N: u64 = 5; +global N: u32 = 5; pub fn from_bar(x: Field) -> Field { x * N as Field diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr index 966be2741d6..4a2b2f9c9b3 100644 --- a/test_programs/execution_success/global_consts/src/main.nr +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -1,13 +1,13 @@ mod foo; mod baz; -global M: Field = 32; +global M: u32 = 32; global L: Field = 10; // Unused globals currently allowed -global N: u64 = 5; -global T_LEN = 2; // Type inference is allowed on globals +global N: u32 = 5; +global T_LEN: u32 = 2; // Type inference is allowed on globals // Globals can reference other globals -global DERIVED = M + L; +global DERIVED: Field = (M as Field) + L; struct Dummy { x: [Field; N], @@ -41,7 +41,7 @@ fn main( assert(test_struct.y[i] != NESTED[1][0].v); } - assert(N as Field != M); + assert(N as Field != M as Field); let expected: u32 = 42; assert(foo::TYPE_INFERRED == expected); @@ -49,7 +49,7 @@ fn main( let mut y = 5; let mut x = M; for i in 0..N * N { - let M: Field = 10; + let M: u32 = 10; x = M; y = i; @@ -62,14 +62,14 @@ fn main( arrays_neq(a, b); - let t: [Field; T_LEN] = [N as Field, M]; + let t: [Field; T_LEN] = [N as Field, M as Field]; assert(t[1] == 32); assert(15 == my_submodule::my_helper()); - let add_submodules_N = my_submodule::N + foo::bar::N as Field; + let add_submodules_N = my_submodule::N as Field + foo::bar::N as Field; assert(15 == add_submodules_N); - let add_from_bar_N = my_submodule::N + foo::bar::from_bar(1); + let add_from_bar_N = my_submodule::N as Field + foo::bar::from_bar(1); assert(15 == add_from_bar_N); // Example showing an array filled with (my_submodule::N + 2) 0's let sugared = [0; my_submodule::N + 2]; @@ -80,13 +80,13 @@ fn main( foo::from_foo(d); baz::from_baz(c); - assert(DERIVED == M + L); + assert(DERIVED == M as Field + L); assert(CALCULATED_GLOBAL == 42); } fn multiplyByM(x: Field) -> Field { - x * M + x * M as Field } fn arrays_neq(a: [Field; M], b: [Field; M]) { @@ -94,7 +94,7 @@ fn arrays_neq(a: [Field; M], b: [Field; M]) { } mod my_submodule { - global N: Field = 10; + global N: u32 = 10; global L: Field = 50; fn my_bool_or(x: u1, y: u1) { @@ -102,8 +102,8 @@ mod my_submodule { } pub fn my_helper() -> Field { - let N: Field = 15; // Like in Rust, local variables override globals - let x = N; + let N: u32 = 15; // Like in Rust, local variables override globals + let x = N as Field; x } } diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr index 56b13d6779b..e8bc486e1e2 100644 --- a/test_programs/execution_success/hashmap/src/main.nr +++ b/test_programs/execution_success/hashmap/src/main.nr @@ -15,7 +15,7 @@ struct Entry{ value: Field } -global HASHMAP_CAP = 8; +global HASHMAP_CAP: u32 = 8; global HASHMAP_LEN = 6; global FIELD_CMP = |a: Field, b: Field| a.lt(b); 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 60a7d2f8d17..d6b463dbe30 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,6 +1,6 @@ -use std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; +use std::hash::poseidon2::Poseidon2; -global NUM_HASHES = 2; +global NUM_HASHES: u32 = 2; global HASH_LENGTH = 10; #[no_predicates] diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index d8be6c72aec..628029898f3 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -588,7 +588,7 @@ impl<'a> NodeFinder<'a> { | Type::NamedGeneric(_, _, _) | Type::Function(..) | Type::Forall(_, _) - | Type::Constant(_) + | Type::Constant(..) | Type::Quoted(_) | Type::InfixExpr(_, _, _) | Type::Error => (), diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 8e9666a624b..6781deca001 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -447,7 +447,7 @@ impl<'a> TypeLinksGatherer<'a> { | Type::FmtString(_, _) | Type::Unit | Type::Forall(_, _) - | Type::Constant(_) + | Type::Constant(..) | Type::Quoted(_) | Type::Error => (), } @@ -705,7 +705,7 @@ mod hover_tests { "workspace", "two/src/lib.nr", Position { line: 51, character: 8 }, - &format!(" let x: BoundedVec\n\nGo to [SubOneStruct](file://{}#L4,12-4,24)", workspace_on_src_lib_path), + &format!(" let x: BoundedVec\n\nGo to [SubOneStruct](file://{}#L4,12-4,24)", workspace_on_src_lib_path), ) .await; } diff --git a/tooling/lsp/src/trait_impl_method_stub_generator.rs b/tooling/lsp/src/trait_impl_method_stub_generator.rs index e4c22f1790c..0bb97eb5d4d 100644 --- a/tooling/lsp/src/trait_impl_method_stub_generator.rs +++ b/tooling/lsp/src/trait_impl_method_stub_generator.rs @@ -367,7 +367,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> { self.string.push(' '); self.append_type(right); } - Type::Constant(_) + Type::Constant(..) | Type::Integer(_, _) | Type::Bool | Type::String(_) From 6c41e4875619e2e57d53434c08606aed2e0d69c6 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 17:59:49 -0400 Subject: [PATCH 30/45] cleanup, add kind checks in infix_kind instead of defaulting, check for kind error when monomorphizing --- .../noirc_frontend/src/elaborator/types.rs | 39 +----------- compiler/noirc_frontend/src/hir_def/types.rs | 60 ++++++++----------- .../src/hir_def/types/arithmetic.rs | 2 - .../src/monomorphization/errors.rs | 5 ++ .../src/monomorphization/mod.rs | 8 ++- 5 files changed, 40 insertions(+), 74 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 5c983d6a386..c800b3b2c33 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -169,24 +169,7 @@ impl<'context> Elaborator<'context> { _ => (), } - // TODO cleanup - // dbg!("resolve_type_inner", &resolved_type, &kind); - - // When Type::kind() is None, it matches every Kind - if resolved_type - .kind() - .map(|resolved_type_kind| resolved_type_kind != *kind) - .unwrap_or(false) - { - // // TODO cleanup - // dbg!( - // "resolve_type_inner", - // &resolved_type, - // format!("{:?}", &resolved_type), - // &resolved_type.kind(), - // &kind, - // matches!(&resolved_type, Type::Constant(..)) - // ); + if !kind.matches_opt(resolved_type.kind()) { let expected_typ_err = CompilationError::TypeError(TypeCheckError::TypeKindMismatch { expected_kind: kind.to_string(), expr_kind: resolved_type @@ -428,24 +411,12 @@ impl<'context> Elaborator<'context> { let reference_location = Location::new(path.span(), self.file); self.interner.add_global_reference(id, reference_location); - - // TODO cleanup - // dbg!( - // "lookup_generic_or_global_type", - // &path, - // self.eval_global_as_array_length(id, path), - // self.interner.get_global_let_statement(id), - // self.interner.get_global_let_statement(id).map(|let_statement| let_statement.r#type), - // ); - let kind = self .interner .get_global_let_statement(id) .map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type))) .unwrap_or(Kind::u32()); - dbg!("lookup_generic_or_global_type", &kind); - // panic!("TODO: Type::Constant needs to include the kind data from the global let statement (see above)"); Some(Type::Constant(self.eval_global_as_array_length(id, path), kind)) } _ => None, @@ -467,14 +438,8 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(ref _type_var, ref _name, ref kind) = resolved_length { if !kind.is_numeric() { - // TODO: cleanup - // dbg!("convert_expression_type", &kind); self.push_err(TypeCheckError::TypeKindMismatch { - expected_kind: Kind::Numeric(Box::new(Type::Integer( - Signedness::Unsigned, - IntegerBitSize::ThirtyTwo, - ))) - .to_string(), + expected_kind: Kind::u32().to_string(), expr_kind: kind.to_string(), expr_span: span, }); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index fbe2614abe1..51e36fed47f 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -139,17 +139,21 @@ pub enum Kind { } impl Kind { + pub(crate) fn is_error(&self) -> bool { + match self { + Self::Numeric(typ) => **typ == Type::Error, + _ => false, + } + } + pub(crate) fn is_numeric(&self) -> bool { matches!(self, Self::Numeric { .. }) } - // TODO before merge: look for occurrences - // None matches everything, otherwise check equality - fn matches_opt(&self, other: Option) -> bool { + pub(crate) fn matches_opt(&self, other: Option) -> bool { other.as_ref().map(|other_kind| self == other_kind).unwrap_or(true) } - // TODO before merge: look for occurrences pub(crate) fn u32() -> Self { Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))) } @@ -786,7 +790,7 @@ impl Type { Type::TypeVariable(var, type_var_kind) } - // TODO: constant_variable along with TypeVariableKind::Constant are unused + // TODO: constant_variable and TypeVariableKind::Constant are unused /// Returns a TypeVariable(_, TypeVariableKind::Constant(length)) to bind to /// a constant integer for e.g. an array length. pub fn constant_variable(length: u32, interner: &mut NodeInterner) -> Type { @@ -1127,36 +1131,11 @@ impl Type { } pub(crate) fn kind(&self) -> Option { - // TODO cleanup - dbg!("fn kind", &self); match self { Type::NamedGeneric(_, _, kind) => Some(kind.clone()), Type::Constant(_, kind) => Some(kind.clone()), Type::TypeVariable(..) => None, - - // TODO: ensure kinds for InfixExpr are checked, e.g. - // - // Type::InfixExpr(lhs, _, rhs) => - // { - // let lhs_kind_opt = lhs.kind(); - // let rhs_kind_opt = rhs.kind(); - // if let Some(lhs_kind) = lhs_kind_opt { - // if let Some(rhs_kind) = rhs_kind_opt { - // if lhs_kind == rhs_kind { - // Some(lhs_kind) - // } else { - // fail with TypeKindMismatch or - // Kind::Numeric(Box::new(Type::Error)) - // } - // } else { - // Some(lhs_kind) - // } - // } else { - // rhs_kind - // } - // } - Type::InfixExpr(..) => None, - + Type::InfixExpr(lhs, _op, rhs) => Some(lhs.infix_kind(rhs)), Type::FieldElement | Type::Array(..) | Type::Slice(..) @@ -1177,10 +1156,23 @@ impl Type { } } - // TODO + /// if both Kind's are equal to Some(_), return that Kind, + /// otherwise return a Kind error + /// if both Kind's are None, default to u32 + /// if exactly one Kind is None, return the other one fn infix_kind(&self, other: &Self) -> Kind { - // TODO!!! add kind check, replace u32 default with Type::Error - self.kind().or(other.kind()).unwrap_or(Kind::u32()) + match (self.kind(), other.kind()) { + (Some(self_kind), Some(other_kind)) => { + if self_kind == other_kind { + self_kind + } else { + Kind::Numeric(Box::new(Type::Error)) + } + } + (None, None) => Kind::u32(), + (Some(self_kind), None) => self_kind, + (None, Some(other_kind)) => other_kind, + } } /// Returns the number of field elements required to represent the type once encoded. diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index ae1791736dc..84fa616ac77 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -226,7 +226,6 @@ impl Type { None } else { let result = op.function(l_const, r_const)?; - // TODO: Type::InfixExpr kinds Some(Type::InfixExpr( l_type, l_op, @@ -248,7 +247,6 @@ impl Type { if let Type::InfixExpr(lhs_a, op_a, rhs_a) = self { if let Some(inverse) = op_a.inverse() { if let Some(rhs_a_u32) = rhs_a.evaluate_to_u32() { - // TODO: Type::InfixExpr kinds let rhs_a = Box::new(Type::Constant(rhs_a_u32, lhs_a.infix_kind(rhs_a))); let new_other = Type::InfixExpr(Box::new(other.clone()), inverse, rhs_a); diff --git a/compiler/noirc_frontend/src/monomorphization/errors.rs b/compiler/noirc_frontend/src/monomorphization/errors.rs index 7f4172017e2..7c20328e798 100644 --- a/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -5,6 +5,7 @@ use crate::{hir::comptime::InterpreterError, Type}; #[derive(Debug)] pub enum MonomorphizationError { UnknownArrayLength { length: Type, location: Location }, + UnknownConstant { location: Location }, NoDefaultType { location: Location }, InternalError { message: &'static str, location: Location }, InterpreterError(InterpreterError), @@ -16,6 +17,7 @@ impl MonomorphizationError { fn location(&self) -> Location { match self { MonomorphizationError::UnknownArrayLength { location, .. } + | MonomorphizationError::UnknownConstant { location } | MonomorphizationError::InternalError { location, .. } | MonomorphizationError::ComptimeFnInRuntimeCode { location, .. } | MonomorphizationError::ComptimeTypeInRuntimeCode { location, .. } @@ -40,6 +42,9 @@ impl MonomorphizationError { MonomorphizationError::UnknownArrayLength { length, .. } => { format!("Could not determine array length `{length}`") } + MonomorphizationError::UnknownConstant { .. } => { + "Could not resolve constant".to_string() + } MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); let secondary = "Could not determine type of generic argument".into(); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index a90b029692c..12cc3b55b1f 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1073,9 +1073,15 @@ impl<'interner> Monomorphizer<'interner> { | HirType::Unit | HirType::TraitAsType(..) | HirType::Forall(_, _) - | HirType::Constant(..) | HirType::Error | HirType::Quoted(_) => Ok(()), + HirType::Constant(_value, kind) => { + if kind.is_error() { + Err(MonomorphizationError::UnknownConstant { location }) + } else { + Ok(()) + } + } HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), HirType::Slice(element) => Self::check_type(element.as_ref(), location), From b7dce004ddf5b306e9f29ab6e134ce7033a23c97 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 18:12:35 -0400 Subject: [PATCH 31/45] add issue to remove TypeVariableKind::Constant and cleanup --- compiler/noirc_frontend/src/hir_def/types.rs | 3 ++- compiler/noirc_frontend/src/tests.rs | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 51e36fed47f..4caf013b8e5 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -790,7 +790,8 @@ impl Type { Type::TypeVariable(var, type_var_kind) } - // TODO: constant_variable and TypeVariableKind::Constant are unused + // TODO(https://github.com/noir-lang/noir/issues/6052): constant_variable and + // TODO: TypeVariableKind::Constant are unused /// Returns a TypeVariable(_, TypeVariableKind::Constant(length)) to bind to /// a constant integer for e.g. an array length. pub fn constant_variable(length: u32, interner: &mut NodeInterner) -> Type { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index a4184220257..17a7b72230a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1949,9 +1949,6 @@ fn numeric_generics_type_kind_mismatch() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, From 78478ea1ab4a5e4cca159492f485ef5fee97ddc9 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 16 Sep 2024 19:00:00 -0400 Subject: [PATCH 32/45] fix test kinds --- compiler/noirc_frontend/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 17a7b72230a..3a65234f505 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1297,8 +1297,8 @@ fn type_aliases_in_entry_point() { #[test] fn operators_in_global_used_in_type() { let src = r#" - global ONE = 1; - global COUNT = ONE + 2; + global ONE: u32 = 1; + global COUNT: u32 = ONE + 2; fn main() { let _array: [Field; COUNT] = [1, 2, 3]; } From 2bf70487915a60edb2bedd1af292078722390da9 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:18:13 -0400 Subject: [PATCH 33/45] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 21e24f268a7..fe3b3ab487b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -151,7 +151,7 @@ impl Kind { } pub(crate) fn matches_opt(&self, other: Option) -> bool { - other.as_ref().map(|other_kind| self == other_kind).unwrap_or(true) + other.as_ref().map_or(true, |other_kind| self == other_kind) } pub(crate) fn u32() -> Self { From 9fb9341186e0f0149984ae480f5449bd3fe3cca7 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:18:44 -0400 Subject: [PATCH 34/45] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index fe3b3ab487b..0c36f4e27bd 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -676,7 +676,7 @@ impl std::fmt::Display for Type { TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_) => write!(f, "{name}"), }, - Type::Constant(x, kind) => write!(f, "({}: {})", x, kind), + Type::Constant(x, _kind) => write!(f, "{x}"), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| var.id().to_string()); write!(f, "forall {}. {}", typevars.join(" "), typ) From b78376741196f76081a71f6506209beac9a3837e Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:18:57 -0400 Subject: [PATCH 35/45] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 0c36f4e27bd..907e1b9c0f2 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -773,8 +773,7 @@ impl Type { pub fn type_variable(id: TypeVariableId) -> Type { let var = TypeVariable::unbound(id); - let type_var_kind = TypeVariableKind::Normal; - Type::TypeVariable(var, type_var_kind) + Type::TypeVariable(var, TypeVariableKind::Normal) } pub fn polymorphic_integer_or_field(interner: &mut NodeInterner) -> Type { From fec1bcf73c2f2d3d369bd60a78b0ee8c7aa6591b Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:19:15 -0400 Subject: [PATCH 36/45] Update compiler/noirc_frontend/src/hir_def/types/arithmetic.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types/arithmetic.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 84fa616ac77..2b753b115a3 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -71,9 +71,8 @@ impl Type { if let Some(result) = op.function(constant, new_constant) { constant = result; } else { - *sorted - .entry(Type::Constant(new_constant, new_constant_kind)) - .or_default() += 1; + let constant = Type::Constant(new_constant, new_constant_kind); + *sorted.entry(constant).or_default() += 1; } } other => { From ee8ee18fe61cb5418e68b55103c08a83f1dfb205 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:19:23 -0400 Subject: [PATCH 37/45] Update compiler/noirc_frontend/src/tests.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index e4fead737c6..9ec26550e7c 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2166,7 +2166,6 @@ fn impl_stricter_than_trait_different_object_generics() { .. }) = &errors[1].0 { - dbg!(&constraint_typ.to_string().as_str()); assert!(matches!(constraint_typ.to_string().as_str(), "[B; (8: numeric u32)]")); assert!(matches!(constraint_name.as_str(), "MyTrait")); } else { From f5fde5d71e9bca7ba7c6ddef0604751ab5bf44f6 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:19:37 -0400 Subject: [PATCH 38/45] Update compiler/noirc_frontend/src/hir_def/types/arithmetic.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types/arithmetic.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 2b753b115a3..b8f425a2e9f 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -96,11 +96,8 @@ impl Type { } if constant != zero_value { - typ = Type::InfixExpr( - Box::new(typ), - op, - Box::new(Type::Constant(constant, lhs.infix_kind(rhs))), - ); + let constant = Type::Constant(constant, lhs.infix_kind(rhs)); + typ = Type::InfixExpr(Box::new(typ), op, Box::new(constant)); } typ From 80da93df2b5ccfe2bc1d265207475186df18594b Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:19:47 -0400 Subject: [PATCH 39/45] Update test_programs/execution_success/global_consts/src/main.nr Co-authored-by: jfecher --- test_programs/execution_success/global_consts/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr index 4a2b2f9c9b3..74e2125144c 100644 --- a/test_programs/execution_success/global_consts/src/main.nr +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -4,7 +4,7 @@ mod baz; global M: u32 = 32; global L: Field = 10; // Unused globals currently allowed global N: u32 = 5; -global T_LEN: u32 = 2; // Type inference is allowed on globals +global T_LEN: u32 = 2; // Globals can reference other globals global DERIVED: Field = (M as Field) + L; From 9159ef65b7f65641e5511332b23900bc74aab033 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:20:23 -0400 Subject: [PATCH 40/45] Update test_programs/execution_success/global_consts/src/main.nr Co-authored-by: jfecher --- test_programs/execution_success/global_consts/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr index 74e2125144c..0b382ff6b8b 100644 --- a/test_programs/execution_success/global_consts/src/main.nr +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -7,7 +7,7 @@ global N: u32 = 5; global T_LEN: u32 = 2; // Globals can reference other globals -global DERIVED: Field = (M as Field) + L; +global DERIVED: Field = M as Field + L; struct Dummy { x: [Field; N], From 18f89feae60b9fbe429109b84bf4201f47ba54da Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:20:37 -0400 Subject: [PATCH 41/45] Update tooling/lsp/src/requests/hover.rs Co-authored-by: jfecher --- tooling/lsp/src/requests/hover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 6781deca001..edc71e94b08 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -705,7 +705,7 @@ mod hover_tests { "workspace", "two/src/lib.nr", Position { line: 51, character: 8 }, - &format!(" let x: BoundedVec\n\nGo to [SubOneStruct](file://{}#L4,12-4,24)", workspace_on_src_lib_path), + &format!(" let x: BoundedVec\n\nGo to [SubOneStruct](file://{}#L4,12-4,24)", workspace_on_src_lib_path), ) .await; } From 155e896aa1f1c575ba69e0232f487b0ba10c4378 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:20:52 -0400 Subject: [PATCH 42/45] Update compiler/noirc_frontend/src/hir_def/types/arithmetic.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types/arithmetic.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index b8f425a2e9f..012a945e0e4 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -206,11 +206,8 @@ impl Type { op = op.inverse()?; } let result = op.function(l_const, r_const)?; - Some(Type::InfixExpr( - l_type, - l_op, - Box::new(Type::Constant(result, lhs.infix_kind(rhs))), - )) + let constant = Type::Constant(result, lhs.infix_kind(rhs)); + Some(Type::InfixExpr(l_type, l_op, Box::new(constant))) } (Multiplication | Division, Multiplication | Division) => { // If l_op is a division we want to inverse the rhs operator. From db7d9f605c3d34f991f7ae37f6cea05e4fce716d Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 17 Sep 2024 12:21:05 -0400 Subject: [PATCH 43/45] Update compiler/noirc_frontend/src/hir_def/types/arithmetic.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types/arithmetic.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs index 012a945e0e4..54b4c27a1f3 100644 --- a/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs +++ b/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -219,11 +219,8 @@ impl Type { None } else { let result = op.function(l_const, r_const)?; - Some(Type::InfixExpr( - l_type, - l_op, - Box::new(Type::Constant(result, lhs.infix_kind(rhs))), - )) + let constant = Box::new(Type::Constant(result, lhs.infix_kind(rhs))); + Some(Type::InfixExpr(l_type, l_op, constant)) } } _ => None, From 075af2263cb93e4e3a3fee72b3beb36952d8d94c Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 17 Sep 2024 13:00:28 -0400 Subject: [PATCH 44/45] support for bound type variables in Type::kind --- compiler/noirc_frontend/src/hir_def/types.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 907e1b9c0f2..a24ee2635be 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1110,7 +1110,10 @@ impl Type { match self { Type::NamedGeneric(_, _, kind) => Some(kind.clone()), Type::Constant(_, kind) => Some(kind.clone()), - Type::TypeVariable(..) => None, + Type::TypeVariable(var, _) => match *var.borrow() { + TypeBinding::Bound(ref typ) => typ.kind(), + TypeBinding::Unbound(_) => None, + }, Type::InfixExpr(lhs, _op, rhs) => Some(lhs.infix_kind(rhs)), Type::FieldElement | Type::Array(..) From 7db75aac4e926d9b04d561143d2a55ba81c4dc69 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 17 Sep 2024 14:45:06 -0400 Subject: [PATCH 45/45] fix displaying '(8: numeric u32)' -> '8' --- compiler/noirc_frontend/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9ec26550e7c..44e1cce5bf8 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -2166,7 +2166,7 @@ fn impl_stricter_than_trait_different_object_generics() { .. }) = &errors[1].0 { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; (8: numeric u32)]")); + assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); assert!(matches!(constraint_name.as_str(), "MyTrait")); } else { panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0].0);