From b0a0bc9931e07fafba68e4e4698c0cbafe742101 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 May 2024 14:15:31 +0000 Subject: [PATCH 01/85] initial work on explicit numeric generics and initial working tests --- aztec_macros/src/transforms/storage.rs | 4 +- compiler/noirc_frontend/src/ast/expression.rs | 23 +++++++- compiler/noirc_frontend/src/ast/statement.rs | 19 ++++++- compiler/noirc_frontend/src/ast/structure.rs | 2 +- compiler/noirc_frontend/src/ast/traits.rs | 4 +- compiler/noirc_frontend/src/elaborator/mod.rs | 21 ++++--- .../noirc_frontend/src/elaborator/types.rs | 3 +- .../src/hir/resolution/resolver.rs | 32 +++++++---- .../src/parser/parser/function.rs | 16 +++++- .../numeric_generics/src/main.nr | 55 +++++++++++++++++++ .../numeric_generics/Nargo.toml | 7 +++ .../numeric_generics/src/main.nr | 11 ++++ tooling/nargo_fmt/src/utils.rs | 15 ++++- 13 files changed, 179 insertions(+), 33 deletions(-) create mode 100644 test_programs/execution_success/numeric_generics/Nargo.toml create mode 100644 test_programs/execution_success/numeric_generics/src/main.nr diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs index bd9fff3c3d3..5adc7b23709 100644 --- a/aztec_macros/src/transforms/storage.rs +++ b/aztec_macros/src/transforms/storage.rs @@ -91,7 +91,7 @@ pub fn inject_context_in_storage(module: &mut SortedModule) -> Result<(), AztecM r#struct.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) }) .unwrap(); - storage_struct.generics.push(ident("Context")); + storage_struct.generics.push(ident("Context").into()); storage_struct .fields .iter_mut() @@ -243,7 +243,7 @@ pub fn generate_storage_implementation( span: Some(Span::default()), }, type_span: Span::default(), - generics: vec![generic_context_ident], + generics: vec![generic_context_ident.into()], methods: vec![(init, Span::default())], }; diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 21131c71217..3b3a7d36a79 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -38,7 +38,28 @@ pub enum ExpressionKind { /// A Vec of unresolved names for type variables. /// For `fn foo(...)` this corresponds to vec!["A", "B"]. -pub type UnresolvedGenerics = Vec; +pub type UnresolvedGenerics = Vec; + +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum UnresolvedGeneric { + Variable(Ident), + Numeric { ident: Ident, typ: UnresolvedType }, +} + +impl Display for UnresolvedGeneric { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + UnresolvedGeneric::Variable(ident) => write!(f, "{ident}"), + UnresolvedGeneric::Numeric { ident, typ } => write!(f, "let {ident}: {typ}"), + } + } +} + +impl From for UnresolvedGeneric { + fn from(value: Ident) -> Self { + UnresolvedGeneric::Variable(value) + } +} impl ExpressionKind { pub fn into_path(self) -> Option { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 9b2c0fbfee8..a680d9d33eb 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -7,8 +7,7 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use super::{ - BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, - MethodCallExpression, UnresolvedType, + BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, MethodCallExpression, UnresolvedGeneric, UnresolvedType }; use crate::lexer::token::SpannedToken; use crate::macros_api::SecondaryAttribute; @@ -225,6 +224,22 @@ impl From for Ident { } } +impl From for Ident { + fn from(value: UnresolvedGeneric) -> Self { + match value { + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident + } + } +} + +impl From<&UnresolvedGeneric> for Ident { + fn from(value: &UnresolvedGeneric) -> Self { + match value { + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident.clone() + } + } +} + impl From for Expression { fn from(i: Ident) -> Expression { Expression { diff --git a/compiler/noirc_frontend/src/ast/structure.rs b/compiler/noirc_frontend/src/ast/structure.rs index bda6b8c0b11..bb2d89841b9 100644 --- a/compiler/noirc_frontend/src/ast/structure.rs +++ b/compiler/noirc_frontend/src/ast/structure.rs @@ -20,7 +20,7 @@ impl NoirStruct { pub fn new( name: Ident, attributes: Vec, - generics: Vec, + generics: UnresolvedGenerics, fields: Vec<(Ident, UnresolvedType)>, span: Span, ) -> NoirStruct { diff --git a/compiler/noirc_frontend/src/ast/traits.rs b/compiler/noirc_frontend/src/ast/traits.rs index 772675723b5..d069b55e970 100644 --- a/compiler/noirc_frontend/src/ast/traits.rs +++ b/compiler/noirc_frontend/src/ast/traits.rs @@ -14,7 +14,7 @@ use crate::node_interner::TraitId; #[derive(Clone, Debug)] pub struct NoirTrait { pub name: Ident, - pub generics: Vec, + pub generics: UnresolvedGenerics, pub where_clause: Vec, pub span: Span, pub items: Vec, @@ -26,7 +26,7 @@ pub struct NoirTrait { pub enum TraitItem { Function { name: Ident, - generics: Vec, + generics: UnresolvedGenerics, parameters: Vec<(Ident, UnresolvedType)>, return_type: FunctionReturnType, where_clause: Vec, diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 10ebd657494..71014b53bb1 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedTraitConstraint}, + ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, hir::{ def_collector::{ dc_crate::{ @@ -385,7 +385,8 @@ impl<'context> Elaborator<'context> { let mut new_generic_ident: Ident = format!("T{}_impl_{}", func_id, path.as_string()).into(); let mut new_generic_path = Path::from_ident(new_generic_ident.clone()); - while impl_trait_generics.contains(&new_generic_ident) + let mut new_generic = UnresolvedGeneric::from(new_generic_ident.clone()); + while impl_trait_generics.contains(&new_generic) || self.lookup_generic_or_global_type(&new_generic_path).is_some() { new_generic_ident = @@ -393,7 +394,7 @@ impl<'context> Elaborator<'context> { new_generic_path = Path::from_ident(new_generic_ident.clone()); counter += 1; } - impl_trait_generics.insert(new_generic_ident.clone()); + impl_trait_generics.insert(UnresolvedGeneric::from(new_generic_ident.clone())); let is_synthesized = true; let new_generic_type_data = @@ -411,13 +412,13 @@ impl<'context> Elaborator<'context> { }; parameter.typ.typ = new_generic_type_data; - func.def.generics.push(new_generic_ident); + func.def.generics.push(new_generic_ident.into()); func.def.where_clause.push(new_trait_constraint); } } self.add_generics(&impl_trait_generics.into_iter().collect()); } - + /// Add the given generics to scope. /// Each generic will have a fresh Shared associated with it. pub fn add_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { @@ -425,6 +426,7 @@ impl<'context> Elaborator<'context> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); + let generic = Ident::from(generic); let span = generic.0.span(); // Check for name collisions of this generic @@ -643,7 +645,10 @@ impl<'context> Elaborator<'context> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| self.find_generic(&generic.0.contents)) + .filter_map(|generic| { + let generic = Ident::from(generic); + self.find_generic(&generic.0.contents) + }) .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) .collect(); @@ -844,7 +849,7 @@ impl<'context> Elaborator<'context> { } } - fn elaborate_impls(&mut self, impls: Vec<(Vec, Span, UnresolvedFunctions)>) { + fn elaborate_impls(&mut self, impls: Vec<(UnresolvedGenerics, Span, UnresolvedFunctions)>) { for (_, _, functions) in impls { self.file = functions.file_id; self.recover_generics(|this| this.elaborate_functions(functions)); @@ -868,7 +873,7 @@ impl<'context> Elaborator<'context> { fn collect_impls( &mut self, module: LocalModuleId, - impls: &mut [(Vec, Span, UnresolvedFunctions)], + impls: &mut [(UnresolvedGenerics, Span, UnresolvedFunctions)], ) { self.local_module = module; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 059ff857df8..2933e16a003 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -7,7 +7,7 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ BinaryOpKind, IntegerBitSize, UnresolvedGenerics, UnresolvedTraitConstraint, - UnresolvedTypeExpression, + UnresolvedTypeExpression, Ident, }, hir::{ def_map::ModuleDefId, @@ -1443,6 +1443,7 @@ impl<'context> Elaborator<'context> { assert_eq!(names.len(), generics.len()); for (name, typevar) in names.iter().zip(generics) { + let name = Ident::from(name); self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index fd77312e4f2..bef47eb3dab 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -28,11 +28,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::rc::Rc; use crate::ast::{ - ArrayLiteral, BinaryOpKind, BlockExpression, Expression, ExpressionKind, ForRange, - FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, LValue, - LetStatement, Literal, NoirFunction, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, - Statement, StatementKind, TraitBound, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, - UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, + ArrayLiteral, BinaryOpKind, BlockExpression, Expression, ExpressionKind, ForRange, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, LValue, LetStatement, Literal, NoirFunction, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Statement, StatementKind, TraitBound, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT }; use crate::graph::CrateId; use crate::hir::def_map::{ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION}; @@ -125,6 +121,7 @@ pub struct Resolver<'a> { /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics /// were declared in. + // TODO: Switch this to a ResolvedGeneric struct generics: Vec<(Rc, TypeVariable, Span)>, /// When resolving lambda expressions, we need to keep track of the variables @@ -217,7 +214,8 @@ impl<'a> Resolver<'a> { let mut new_generic_ident: Ident = format!("T{}_impl_{}", func_id, path.as_string()).into(); let mut new_generic_path = Path::from_ident(new_generic_ident.clone()); - while impl_trait_generics.contains(&new_generic_ident) + let mut new_generic = UnresolvedGeneric::from(new_generic_ident.clone()); + while impl_trait_generics.contains(&new_generic) || self.lookup_generic_or_global_type(&new_generic_path).is_some() { new_generic_ident = @@ -225,7 +223,7 @@ impl<'a> Resolver<'a> { new_generic_path = Path::from_ident(new_generic_ident.clone()); counter += 1; } - impl_trait_generics.insert(new_generic_ident.clone()); + impl_trait_generics.insert(UnresolvedGeneric::from(new_generic_ident.clone())); let is_synthesized = true; let new_generic_type_data = @@ -243,7 +241,7 @@ impl<'a> Resolver<'a> { }; parameter.typ.typ = new_generic_type_data; - func.def.generics.push(new_generic_ident); + func.def.generics.push(new_generic_ident.into()); func.def.where_clause.push(new_trait_constraint); } } @@ -902,14 +900,20 @@ impl<'a> Resolver<'a> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - let span = generic.0.span(); + let ident = Ident::from(generic); + let span = ident.0.span(); + + if let UnresolvedGeneric::Numeric { ident, .. } = generic { + let definition = DefinitionKind::GenericType(typevar.clone()); + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + } // Check for name collisions of this generic - let name = Rc::new(generic.0.contents.clone()); + let name = Rc::new(ident.0.contents.clone()); if let Some((_, _, first_span)) = self.find_generic(&name) { self.errors.push(ResolverError::DuplicateDefinition { - name: generic.0.contents.clone(), + name: ident.0.contents.clone(), first_span: *first_span, second_span: span, }); @@ -928,6 +932,7 @@ impl<'a> Resolver<'a> { assert_eq!(names.len(), generics.len()); for (name, typevar) in names.iter().zip(generics) { + let name = Ident::from(name); self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); } } @@ -1088,7 +1093,10 @@ impl<'a> Resolver<'a> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| self.find_generic(&generic.0.contents)) + .filter_map(|generic| { + let generic = Ident::from(generic); + self.find_generic(&generic.0.contents) + }) .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) .collect(); diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 40180a9f9ac..7e01195c0a8 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -4,7 +4,7 @@ use super::{ parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, self_parameter, where_clause, NoirParser, }; -use crate::parser::labels::ParsingRuleLabel; +use crate::{ast::{UnresolvedGeneric, UnresolvedGenerics}, parser::labels::ParsingRuleLabel}; use crate::parser::spanned; use crate::token::{Keyword, Token}; use crate::{ @@ -80,13 +80,23 @@ fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { }) } +pub(super) fn generic() -> impl NoirParser { + keyword(Keyword::Let).or_not().ignore_then(ident()).then_ignore(just(Token::Colon).or_not()).then(parse_type().or_not()).map(|(ident, typ)| { + if let Some(typ) = typ { + UnresolvedGeneric::Numeric { ident, typ } + } else { + UnresolvedGeneric::Variable(ident) + } + }) +} + /// non_empty_ident_list: ident ',' non_empty_ident_list /// | ident /// /// generics: '<' non_empty_ident_list '>' /// | %empty -pub(super) fn generics() -> impl NoirParser> { - ident() +pub(super) fn generics() -> impl NoirParser { + generic() .separated_by(just(Token::Comma)) .allow_trailing() .at_least(1) 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 1e03a382fed..00486714f30 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -12,6 +12,14 @@ fn main() { assert(itAlsoWorks.data[1] == 2); assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); + + double_numeric_generics_test(); + + let my_type: MyType<20> = PublicStorage::read(); + assert(my_type.fields.len() == 20); + for field in my_type.fields { + assert(field == 1); + } } fn id(x: [Field; I]) -> [Field; I] { @@ -37,3 +45,50 @@ fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { s } +fn double() -> u64 { + N * 2 +} + +fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<123>() == 246); + assert(double::<7 + 8>() == 30); +} + +struct MyType { + fields: [Field; N] +} + +// impl MyType { +// } + +impl Deserialize for MyType { + fn deserialize(fields: [Field; N]) -> Self { + let mut new_fields = [0; N]; + for i in 0..N { + new_fields[i] = fields[i]; + } + MyType { fields: new_fields } + } +} + +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} + +fn storage_read() -> Field { + 1 +} + +struct PublicStorage {} + +impl PublicStorage { + fn read() -> T where T: Deserialize { + let mut fields = [0; N]; + for i in 0..N { + fields[i] = storage_read(); + } + T::deserialize(fields) + } +} diff --git a/test_programs/execution_success/numeric_generics/Nargo.toml b/test_programs/execution_success/numeric_generics/Nargo.toml new file mode 100644 index 00000000000..7993eb0f1cb --- /dev/null +++ b/test_programs/execution_success/numeric_generics/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "numeric_generics" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/numeric_generics/src/main.nr b/test_programs/execution_success/numeric_generics/src/main.nr new file mode 100644 index 00000000000..3c30bf08424 --- /dev/null +++ b/test_programs/execution_success/numeric_generics/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field, y: pub Field) { + assert(x != y); +} + +#[test] +fn test_main() { + main(1, 2); + + // Uncomment to make test fail + // main(1, 1); +} diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 2c5c3085e66..444145bc729 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use crate::items::HasItem; use crate::rewrite; use crate::visitor::{FmtVisitor, Shape}; -use noirc_frontend::ast::{Expression, Ident, Param, Visibility}; +use noirc_frontend::ast::{Expression, Ident, Param, UnresolvedGeneric, Visibility}; use noirc_frontend::hir::resolution::errors::Span; use noirc_frontend::lexer::Lexer; use noirc_frontend::token::Token; @@ -170,6 +170,19 @@ impl HasItem for Ident { } } +impl HasItem for UnresolvedGeneric { + fn span(&self) -> Span { + match self { + UnresolvedGeneric::Variable(ident) => ident.span(), + UnresolvedGeneric::Numeric { ident, typ } => ident.span().merge(typ.span.expect("Should have a span for numeric generic type")), + } + } + + fn format(self, visitor: &FmtVisitor, _shape: Shape) -> String { + visitor.slice(self.span()).into() + } +} + pub(crate) fn first_line_width(exprs: &str) -> usize { exprs.lines().next().map_or(0, |line: &str| line.chars().count()) } From 6a6081f5ba2ca41a9c0650fa285ba114c99f8cc2 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 30 May 2024 18:40:04 +0000 Subject: [PATCH 02/85] error for bad numeric generic type --- .../src/hir/resolution/errors.rs | 11 ++++++++ .../src/hir/resolution/resolver.rs | 6 ++++- compiler/noirc_frontend/src/tests.rs | 26 +++++++++++++++++++ .../numeric_generics/src/main.nr | 3 --- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index fa4ea96316a..07663c8821d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -94,6 +94,8 @@ pub enum ResolverError { NoPredicatesAttributeOnUnconstrained { ident: Ident }, #[error("#[fold] attribute is only allowed on constrained functions")] FoldAttributeOnUnconstrained { ident: Ident }, + #[error("The only supported types of numeric generics are integers, fields, and booleans")] + UnsupportedNumericGenericType { ident: Ident, typ: Type }, } impl ResolverError { @@ -386,6 +388,15 @@ impl<'a> From<&'a ResolverError> for Diagnostic { diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diag } + ResolverError::UnsupportedNumericGenericType { ident , typ } => { + let name = &ident.0.contents; + + Diagnostic::simple_error( + format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), + "Unsupported numeric generic type".to_string(), + ident.0.span(), + ) + } } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index bef47eb3dab..6e0e0fa6532 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -903,7 +903,11 @@ impl<'a> Resolver<'a> { let ident = Ident::from(generic); let span = ident.0.span(); - if let UnresolvedGeneric::Numeric { ident, .. } = generic { + if let UnresolvedGeneric::Numeric { ident, typ } = generic { + let typ = self.resolve_type(typ.clone()); + if !matches!(typ, Type::FieldElement | Type::Integer(_, _) | Type::Bool) { + self.errors.push(ResolverError::UnsupportedNumericGenericType { ident: ident.clone(), typ }) + } let definition = DefinitionKind::GenericType(typevar.clone()); self.add_variable_decl_inner(ident.clone(), false, false, false, definition); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index bd81752c046..049851d7065 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1443,3 +1443,29 @@ fn specify_method_types_with_turbofish() { let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } + +#[test] +fn unsupported_numeric_generic_type() { + let src = r#" + struct Foo { + inner: u64 + } + + fn double() -> u64 { + N.inner * 2 + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = double::(); + } + "#; + let errors = get_program_errors(src); + dbg!(errors.clone()); +} 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 00486714f30..b62f3d81725 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -60,9 +60,6 @@ struct MyType { fields: [Field; N] } -// impl MyType { -// } - impl Deserialize for MyType { fn deserialize(fields: [Field; N]) -> Self { let mut new_fields = [0; N]; From c9e84e486c56a9979285079e6d53ffa664432320 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 31 May 2024 15:39:02 +0000 Subject: [PATCH 03/85] fmt and clippy --- compiler/noirc_frontend/src/ast/mod.rs | 2 +- compiler/noirc_frontend/src/ast/statement.rs | 9 ++++--- compiler/noirc_frontend/src/elaborator/mod.rs | 4 +-- .../noirc_frontend/src/elaborator/types.rs | 4 +-- .../src/hir/resolution/resolver.rs | 23 +++++++++++----- compiler/noirc_frontend/src/parser/parser.rs | 4 +-- .../src/parser/parser/function.rs | 26 ++++++++++++------- .../src/parser/parser/primitives.rs | 4 +-- compiler/noirc_frontend/src/tests.rs | 7 +++-- tooling/nargo_fmt/src/utils.rs | 4 ++- 10 files changed, 54 insertions(+), 33 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index bf7b1cd96e9..29142a7e28b 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -138,7 +138,7 @@ pub(crate) type UnaryRhsMemberAccess = (Ident, Option<(Option>, Vec)>); /// The precursor to TypeExpression, this is the type that the parser allows -/// to be used in the length position of an array type. Only constant integers, booleans, variables, +/// to be used in the length position of an array type. Only constant integers, variables, /// and numeric binary operators are allowed here. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeExpression { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index a680d9d33eb..6143adc374b 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -7,7 +7,8 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use super::{ - BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, MethodCallExpression, UnresolvedGeneric, UnresolvedType + BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, + MethodCallExpression, UnresolvedGeneric, UnresolvedType, }; use crate::lexer::token::SpannedToken; use crate::macros_api::SecondaryAttribute; @@ -227,7 +228,7 @@ impl From for Ident { impl From for Ident { fn from(value: UnresolvedGeneric) -> Self { match value { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, } } } @@ -235,7 +236,9 @@ impl From for Ident { impl From<&UnresolvedGeneric> for Ident { fn from(value: &UnresolvedGeneric) -> Self { match value { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident.clone() + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => { + ident.clone() + } } } } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index a672e2fe398..9b15b213d4c 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, + ast::{FunctionKind, UnresolvedTraitConstraint}, hir::{ def_collector::{ dc_crate::{ @@ -398,7 +398,7 @@ impl<'context> Elaborator<'context> { generic_type } - + /// Add the given generics to scope. /// Each generic will have a fresh Shared associated with it. pub fn add_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2933e16a003..4886ef6e78c 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -6,8 +6,8 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ - BinaryOpKind, IntegerBitSize, UnresolvedGenerics, UnresolvedTraitConstraint, - UnresolvedTypeExpression, Ident, + BinaryOpKind, Ident, IntegerBitSize, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedTypeExpression, }, hir::{ def_map::ModuleDefId, diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index da384446ffa..a19cef75b4b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -28,7 +28,12 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::rc::Rc; use crate::ast::{ - ArrayLiteral, BinaryOpKind, BlockExpression, Expression, ExpressionKind, ForRange, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, LValue, LetStatement, Literal, NoirFunction, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Statement, StatementKind, TraitBound, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT + ArrayLiteral, BinaryOpKind, BlockExpression, Expression, ExpressionKind, ForRange, + FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, LValue, + LetStatement, Literal, NoirFunction, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, + Statement, StatementKind, TraitBound, UnaryOp, UnresolvedGeneric, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, + Visibility, ERROR_IDENT, }; use crate::graph::CrateId; use crate::hir::def_map::{ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION}; @@ -214,7 +219,7 @@ impl<'a> Resolver<'a> { let mut new_generic_ident: Ident = format!("T{}_impl_{}", func_id, path.as_string()).into(); let mut new_generic_path = Path::from_ident(new_generic_ident.clone()); - let mut new_generic = UnresolvedGeneric::from(new_generic_ident.clone()); + let new_generic = UnresolvedGeneric::from(new_generic_ident.clone()); while impl_trait_generics.contains(&new_generic) || self.lookup_generic_or_global_type(&new_generic_path).is_some() { @@ -906,17 +911,21 @@ impl<'a> Resolver<'a> { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - self.errors.push(ResolverError::UnsupportedNumericGenericType { ident: ident.clone(), typ: typ.clone() }) + self.errors.push(ResolverError::UnsupportedNumericGenericType { + ident: ident.clone(), + typ: typ.clone(), + }); } typevar.bind(typ.clone()); let definition = DefinitionKind::GenericType(typevar.clone()); - let hir_ident = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + let hir_ident = + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); // Push the definition type because if one is missing, when the numeric generic is used in an expression - // its definition type will be resolved to a polymorphic integer or field. - // We do not yet fully support bool generics but this will be a foot-gun once we look to add support + // its definition type will be resolved to a polymorphic integer or field. + // We do not yet fully support bool generics but this will be a foot-gun once we look to add support // and is can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); - } + } // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 842ad3a824b..890ab795e00 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -810,9 +810,7 @@ fn generic_type_args<'a>( // parser afterward. .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) .or(type_expression() - .map_with_span(|expr, span| { - UnresolvedTypeData::Expression(expr).with_span(span) - })) + .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) .separated_by(just(Token::Comma)) .allow_trailing() .at_least(1) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 7e01195c0a8..d58f3bb9de7 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -4,16 +4,19 @@ use super::{ parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, self_parameter, where_clause, NoirParser, }; -use crate::{ast::{UnresolvedGeneric, UnresolvedGenerics}, parser::labels::ParsingRuleLabel}; use crate::parser::spanned; use crate::token::{Keyword, Token}; use crate::{ ast::{ - FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, + FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, }, parser::{ParserError, ParserErrorReason}, }; +use crate::{ + ast::{UnresolvedGeneric, UnresolvedGenerics}, + parser::labels::ParsingRuleLabel, +}; use chumsky::prelude::*; @@ -81,13 +84,18 @@ fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { } pub(super) fn generic() -> impl NoirParser { - keyword(Keyword::Let).or_not().ignore_then(ident()).then_ignore(just(Token::Colon).or_not()).then(parse_type().or_not()).map(|(ident, typ)| { - if let Some(typ) = typ { - UnresolvedGeneric::Numeric { ident, typ } - } else { - UnresolvedGeneric::Variable(ident) - } - }) + keyword(Keyword::Let) + .or_not() + .ignore_then(ident()) + .then_ignore(just(Token::Colon).or_not()) + .then(parse_type().or_not()) + .map(|(ident, typ)| { + if let Some(typ) = typ { + UnresolvedGeneric::Numeric { ident, typ } + } else { + UnresolvedGeneric::Variable(ident) + } + }) } /// non_empty_ident_list: ident ',' non_empty_ident_list diff --git a/compiler/noirc_frontend/src/parser/parser/primitives.rs b/compiler/noirc_frontend/src/parser/parser/primitives.rs index e52d53b264e..9da19c0a185 100644 --- a/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -87,9 +87,7 @@ pub(super) fn turbofish<'a>( pub(super) fn variable() -> impl NoirParser { path() .then(turbofish(super::parse_type())) - .map(|(path, generics)| { - ExpressionKind::Variable(path, generics) - }) + .map(|(path, generics)| ExpressionKind::Variable(path, generics)) } pub(super) fn variable_no_turbofish() -> impl NoirParser { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 686021d9c49..993f59429db 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1517,10 +1517,13 @@ fn bool_generic_as_loop_bound() { CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), )); - let CompilationError::TypeError(TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. }) = &errors[1].0 else { + 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"); -} \ No newline at end of file +} diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 444145bc729..91c3972e18f 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -174,7 +174,9 @@ impl HasItem for UnresolvedGeneric { fn span(&self) -> Span { match self { UnresolvedGeneric::Variable(ident) => ident.span(), - UnresolvedGeneric::Numeric { ident, typ } => ident.span().merge(typ.span.expect("Should have a span for numeric generic type")), + UnresolvedGeneric::Numeric { ident, typ } => { + ident.span().merge(typ.span.expect("Should have a span for numeric generic type")) + } } } From cef55da7e74b6b71612ee976448ae16f6255ef3e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 31 May 2024 16:52:41 +0000 Subject: [PATCH 04/85] fully working explciit num generics in resolver elaborator still has some bugs --- compiler/noirc_frontend/src/elaborator/mod.rs | 51 ++++++++++++++++--- .../src/hir/resolution/errors.rs | 11 ++++ .../src/hir/resolution/resolver.rs | 17 ++++++- .../src/parser/parser/function.rs | 3 +- compiler/noirc_frontend/src/tests.rs | 9 ++++ .../numeric_generics/src/main.nr | 23 ++++++++- 6 files changed, 103 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 9b15b213d4c..8e7a02e3dfe 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedTraitConstraint}, + ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, hir::{ def_collector::{ dc_crate::{ @@ -406,15 +406,36 @@ impl<'context> Elaborator<'context> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - let generic = Ident::from(generic); - let span = generic.0.span(); + let ident = Ident::from(generic); + let span = ident.0.span(); + + // Declare numeric generics + if let UnresolvedGeneric::Numeric { ident, typ } = generic { + let typ = self.resolve_type(typ.clone()); + if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { + let unsupported_typ_err = CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { + ident: ident.clone(), + typ: typ.clone(), + }); + self.errors.push((unsupported_typ_err, self.file)); + } + typevar.bind(typ.clone()); + let definition = DefinitionKind::GenericType(typevar.clone()); + let hir_ident = + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + // Push the definition type because if one is missing, when the numeric generic is used in an expression + // its definition type will be resolved to a polymorphic integer or field. + // We do not yet fully support bool generics but this will be a foot-gun once we look to add support + // and is can lead to confusing errors. + self.interner.push_definition_type(hir_ident.id, typ); + } // Check for name collisions of this generic - let name = Rc::new(generic.0.contents.clone()); + let name = Rc::new(ident.0.contents.clone()); if let Some((_, _, first_span)) = self.find_generic(&name) { self.push_err(ResolverError::DuplicateDefinition { - name: generic.0.contents.clone(), + name: ident.0.contents.clone(), first_span: *first_span, second_span: span, }); @@ -721,9 +742,27 @@ impl<'context> Elaborator<'context> { if let Some((name, _, span)) = self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) { + 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; + } let ident = Ident::new(name.to_string(), *span); let definition = DefinitionKind::GenericType(type_variable); - self.add_variable_decl_inner(ident, false, false, false, definition); + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + self.errors.push(( + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { + ident, + }), + self.file, + )); } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 07663c8821d..73e64cb2fce 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -96,6 +96,8 @@ 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 }, } impl ResolverError { @@ -397,6 +399,15 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ident.0.span(), ) } + 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."), + format!("Numeric generic `{name}` should now be specified with `let {name}: `"), + ident.0.span(), + ) + } } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index a19cef75b4b..e2328f0c80f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -926,6 +926,7 @@ impl<'a> Resolver<'a> { // and is can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); } + // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -1167,6 +1168,7 @@ impl<'a> Resolver<'a> { !func.def.is_unconstrained } + // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this method in favor of explicit numeric generics fn declare_numeric_generics(&mut self, params: &[Type], return_type: &Type) { if self.generics.is_empty() { return; @@ -1182,9 +1184,22 @@ impl<'a> Resolver<'a> { if let Some((name, _, span)) = self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) { + 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; + } let ident = Ident::new(name.to_string(), *span); let definition = DefinitionKind::GenericType(type_variable); - self.add_variable_decl_inner(ident, false, false, false, definition); + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + self.errors.push(ResolverError::UseExplicitNumericGeneric { ident }); } } } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index d58f3bb9de7..e64364c88aa 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -8,8 +8,7 @@ use crate::parser::spanned; use crate::token::{Keyword, Token}; use crate::{ ast::{ - FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, - Visibility, + FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, }, parser::{ParserError, ParserErrorReason}, }; diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 993f59429db..42977884b27 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1527,3 +1527,12 @@ fn bool_generic_as_loop_bound() { assert_eq!(expected_typ, "Field"); assert_eq!(expr_typ, "bool"); } + +#[test] +fn numeric_generic_in_function_signature() { + let src = r#" + fn foo(arr: [Field; N]) -> [Field; N] { arr } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} 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 c728c5e0f71..c29856253ef 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -19,6 +19,12 @@ fn main() { assert(my_type.a == 1); assert(my_type.b == 2); assert(my_type.c == 3); + + let foo = baz::<10>(); + assert(foo.inner == 10); + + let new_arr = update_arr([0, 1, 2, 3]); + assert(new_arr[0] == 5); } fn id(x: [Field; I]) -> [Field; I] { @@ -45,6 +51,7 @@ fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { } fn double() -> u64 { + // Used as an expression N * 2 } @@ -67,7 +74,7 @@ impl Deserialize for MyType { } } -trait Deserialize { +trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } @@ -75,7 +82,9 @@ struct PublicStorage {} impl PublicStorage { fn read() -> T where T: Deserialize { - let mut fields = [0; N]; + // Used as a type within a function body + let mut fields: [Field; N] = [0; N]; + // Used a loop bound for i in 0..N { // TODO: Removing this cast I get an error as expected as deserialzie // expects an array of fields not u64, but the error could be improved. @@ -86,4 +95,14 @@ impl PublicStorage { } } +// Used in the signature of a function +fn update_arr(mut arr: [Field; N]) -> [Field; N] { + arr[0] = 5; + arr +} +// Used as a field of a struct +struct Foo { + inner: N, +} +fn baz() -> Foo { Foo { inner: N } } From 84f4b2c9a39062728cce50bf9a9aafa51040b6ea Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 3 Jun 2024 17:22:13 +0000 Subject: [PATCH 05/85] working numeric_generics test with elaborator --- compiler/noirc_frontend/src/elaborator/mod.rs | 173 ++++++++++++++++-- .../noirc_frontend/src/elaborator/patterns.rs | 1 + .../noirc_frontend/src/elaborator/traits.rs | 4 + .../src/hir/resolution/resolver.rs | 6 +- .../noirc_frontend/src/hir_def/function.rs | 3 + compiler/noirc_frontend/src/tests.rs | 3 + .../numeric_generics/src/main.nr | 20 +- .../numeric_generics/Nargo.toml | 7 - .../numeric_generics/src/main.nr | 11 -- 9 files changed, 192 insertions(+), 36 deletions(-) delete mode 100644 test_programs/execution_success/numeric_generics/Nargo.toml delete mode 100644 test_programs/execution_success/numeric_generics/src/main.nr diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 8e7a02e3dfe..bee195cb79a 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -100,6 +100,11 @@ pub struct Elaborator<'context> { /// were declared in. generics: Vec<(Rc, TypeVariable, Span)>, + /// The idents for each numeric generic on a function + /// These definitions are generated when creating function metas + /// and need to be brought into scope later when elaborating the function body. + generic_idents: Vec, + /// When resolving lambda expressions, we need to keep track of the variables /// that are captured. We do this in order to create the hidden environment /// parameter for the lambda function. @@ -167,6 +172,7 @@ impl<'context> Elaborator<'context> { nested_loops: 0, in_contract: false, generics: Vec::new(), + generic_idents: Vec::new(), lambda_stack: Vec::new(), self_type: None, current_item: None, @@ -199,23 +205,76 @@ impl<'context> Elaborator<'context> { for global in literal_globals { this.elaborate_global(global); } - + if !this.errors.is_empty() { + dbg!(this.errors.clone()); + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } for (alias_id, alias) in items.type_aliases { this.define_type_alias(alias_id, alias); } - + if !this.errors.is_empty() { + dbg!(this.errors.clone()); + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); + if !this.errors.is_empty() { + dbg!(this.errors.clone()); + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } this.collect_traits(items.traits); - + // if !this.errors.is_empty() { + // dbg!(this.errors.clone()); + // } + if !this.errors.is_empty() { + dbg!(this.errors.clone()); + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } + dbg!("COLLECTED TRAITS"); + dbg!(this.generic_idents.clone()); // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); - + // if !this.errors.is_empty() { + // dbg!(this.errors.clone()); + // } + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + dbg!(this.generic_idents.clone()); + } + } + } // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { this.collect_trait_impl(trait_impl); } - + // if !this.errors.is_empty() { + // dbg!(this.errors.clone()); + // } + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be // done during def collection since we need to be able to resolve the type of @@ -226,25 +285,61 @@ impl<'context> Elaborator<'context> { for ((_self_type, module), impls) in &mut items.impls { this.collect_impls(*module, impls); } - + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } + // if !this.errors.is_empty() { + // dbg!(this.errors.clone()); + // } // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. for global in non_literal_globals { this.elaborate_global(global); } + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } for functions in items.functions { this.elaborate_functions(functions); } + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } for impls in items.impls.into_values() { this.elaborate_impls(impls); } - + if !this.errors.is_empty() { + for e in this.errors.iter() { + if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + dbg!("GOT DUP DEF"); + } + } + } for trait_impl in items.trait_impls { this.elaborate_trait_impl(trait_impl); } - + // if !this.errors.is_empty() { + // dbg!(this.errors.clone()); + // for e in this.errors.iter() { + // if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { + // dbg!("GOT DUP DEF"); + // } + // } + // } let cycle_errors = this.interner.check_for_dependency_cycles(); this.errors.extend(cycle_errors); this.errors @@ -254,8 +349,12 @@ impl<'context> Elaborator<'context> { /// back to the previous length. fn recover_generics(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { let generics_count = self.generics.len(); + let generic_idents_count = self.generic_idents.len(); let ret = f(self); self.generics.truncate(generics_count); + // dbg!(self.generic_idents.clone()); + // dbg!(generic_idents_count); + self.generic_idents.truncate(generic_idents_count); ret } @@ -268,7 +367,9 @@ impl<'context> Elaborator<'context> { self.local_module = local_module; self.recover_generics(|this| this.elaborate_function(func, id)); } - + // if !self.generic_idents.is_empty() { + // dbg!(self.generic_idents.clone()); + // } self.self_type = None; self.trait_id = None; } @@ -301,8 +402,14 @@ impl<'context> Elaborator<'context> { let name = self.interner.definition_name(parameter.id).to_owned(); self.add_existing_variable_to_scope(name, parameter.clone()); } + // We should introduce the IDs for numeric generic into scope as we do with parameters + for numeric_generic in &func_meta.generic_idents { + let name = self.interner.definition_name(numeric_generic.id).to_owned(); + self.add_existing_variable_to_scope(name, numeric_generic.clone()); + } self.generics = func_meta.all_generics.clone(); + self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); self.add_trait_constraints_to_scope(&func_meta); @@ -419,21 +526,24 @@ impl<'context> Elaborator<'context> { }); self.errors.push((unsupported_typ_err, self.file)); } - typevar.bind(typ.clone()); let definition = DefinitionKind::GenericType(typevar.clone()); + // dbg!(ident.clone()); let hir_ident = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + // dbg!(hir_ident.clone()); // Push the definition type because if one is missing, when the numeric generic is used in an expression // its definition type will be resolved to a polymorphic integer or field. // We do not yet fully support bool generics but this will be a foot-gun once we look to add support // and is can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); + self.generic_idents.push(hir_ident) } // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); if let Some((_, _, first_span)) = self.find_generic(&name) { + dbg!(name.clone()); self.push_err(ResolverError::DuplicateDefinition { name: ident.0.contents.clone(), first_span: *first_span, @@ -675,6 +785,7 @@ impl<'context> Elaborator<'context> { trait_impl: self.current_trait_impl, parameters: parameters.into(), parameter_idents, + generic_idents: self.generic_idents.clone(), return_type: func.def.return_type.clone(), return_visibility: func.def.return_visibility, has_body: !func.def.body.is_empty(), @@ -745,6 +856,8 @@ impl<'context> Elaborator<'context> { let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); if value.is_some() { + let value = value.unwrap(); + // dbg!(value.ident.clone()); // 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 @@ -756,7 +869,12 @@ impl<'context> Elaborator<'context> { } 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); + // if name.to_string() == "N".to_string() { + // dbg!(ident.clone()); + // dbg!(self.generic_idents.clone()); + // } + let x = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + self.generic_idents.push(x); self.errors.push(( CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident, @@ -899,6 +1017,8 @@ impl<'context> Elaborator<'context> { self.self_type = None; self.current_trait_impl = None; self.generics.clear(); + // dbg!(self.generic_idents.clone()); + self.generic_idents.clear(); } fn collect_impls( @@ -909,15 +1029,23 @@ impl<'context> Elaborator<'context> { self.local_module = module; for (generics, span, unresolved) in impls { + self.push_scope(); + self.file = unresolved.file_id; let old_generic_count = self.generics.len(); + let old_generic_ident_count = self.generic_idents.len(); self.add_generics(generics); self.declare_methods_on_struct(false, unresolved, *span); self.generics.truncate(old_generic_count); + self.generic_idents.truncate(old_generic_ident_count); + + self.pop_scope(); } } fn collect_trait_impl(&mut self, trait_impl: &mut UnresolvedTraitImpl) { + self.push_scope(); + self.local_module = trait_impl.module_id; self.file = trait_impl.file_id; trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); @@ -933,6 +1061,7 @@ impl<'context> Elaborator<'context> { self.push_err(DefCollectorErrorKind::MutableReferenceInTraitImpl { span }); } + // TODO: this assertion will trigger an assertion rather than a nice error assert!(trait_impl.trait_id.is_some()); if let Some(trait_id) = trait_impl.trait_id { self.collect_trait_impl_methods(trait_id, trait_impl); @@ -988,6 +1117,9 @@ impl<'context> Elaborator<'context> { } self.generics.clear(); + self.generic_idents.clear(); + + self.pop_scope(); } fn get_module_mut(&mut self, module: ModuleId) -> &mut ModuleData { @@ -1176,6 +1308,8 @@ impl<'context> Elaborator<'context> { } fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { + self.push_scope(); + self.file = alias.file_id; self.local_module = alias.module_id; @@ -1184,6 +1318,9 @@ impl<'context> Elaborator<'context> { let typ = self.resolve_type(alias.type_alias_def.typ); self.interner.set_type_alias(alias_id, typ, generics); self.generics.clear(); + self.generic_idents.clear(); + + self.pop_scope(); } fn collect_struct_definitions(&mut self, structs: BTreeMap) { @@ -1194,6 +1331,8 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { + self.push_scope(); + self.file = typ.file_id; self.local_module = typ.module_id; let (generics, fields) = self.resolve_struct_fields(typ.struct_def, type_id); @@ -1202,6 +1341,7 @@ impl<'context> Elaborator<'context> { struct_def.set_fields(fields); struct_def.generics = generics; }); + self.pop_scope(); } // Check whether the struct fields have nested slices @@ -1281,19 +1421,27 @@ impl<'context> Elaborator<'context> { } for ((self_type, local_module), function_sets) in impls { + self.push_scope(); + self.local_module = *local_module; for (generics, _, function_set) in function_sets { + self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); self.self_type = Some(self_type); self.define_function_metas_for_functions(function_set); self.generics.clear(); + self.generic_idents.clear(); } + + self.pop_scope(); } for trait_impl in trait_impls { + self.push_scope(); + self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; @@ -1318,6 +1466,9 @@ impl<'context> Elaborator<'context> { trait_impl.resolved_object_type = self.self_type.take(); trait_impl.impl_id = self.current_trait_impl.take(); self.generics.clear(); + self.generic_idents.clear(); + + self.pop_scope(); } } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 12b0b7b5e64..670dfb0882e 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -264,6 +264,7 @@ impl<'context> Elaborator<'context> { if !allow_shadowing { if let Some(old_value) = old_value { + dbg!(old_value.ident.clone()); self.push_err(ResolverError::DuplicateDefinition { name: name.0.contents, first_span: old_value.ident.location.span, diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 30a6f50ad5a..957d4b157ff 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -21,6 +21,7 @@ use super::Elaborator; impl<'context> Elaborator<'context> { pub fn collect_traits(&mut self, traits: BTreeMap) { for (trait_id, unresolved_trait) in traits { + self.scopes.start_scope(); self.recover_generics(|this| { this.add_generics(&unresolved_trait.trait_def.generics); @@ -44,6 +45,7 @@ impl<'context> Elaborator<'context> { if self.crate_id.is_stdlib() { self.interner.try_add_operator_trait(trait_id); } + self.scopes.end_scope(); } } @@ -147,6 +149,7 @@ impl<'context> Elaborator<'context> { func_id: FuncId, ) { let old_generic_count = self.generics.len(); + let old_generic_ident_count = self.generic_idents.len(); self.scopes.start_function(); self.trait_bounds = where_clause.to_vec(); @@ -179,5 +182,6 @@ impl<'context> Elaborator<'context> { // Don't check the scope tree for unused variables, they can't be used in a declaration anyway. self.trait_bounds.clear(); self.generics.truncate(old_generic_count); + self.generic_idents.truncate(old_generic_ident_count); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index e2328f0c80f..7e3274557d2 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -126,7 +126,6 @@ pub struct Resolver<'a> { /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics /// were declared in. - // TODO: Switch this to a ResolvedGeneric struct generics: Vec<(Rc, TypeVariable, Span)>, /// When resolving lambda expressions, we need to keep track of the variables @@ -908,6 +907,7 @@ impl<'a> Resolver<'a> { let ident = Ident::from(generic); let span = ident.0.span(); + // Declare numeric generics if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { @@ -916,7 +916,6 @@ impl<'a> Resolver<'a> { typ: typ.clone(), }); } - typevar.bind(typ.clone()); let definition = DefinitionKind::GenericType(typevar.clone()); let hir_ident = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); @@ -926,7 +925,7 @@ impl<'a> Resolver<'a> { // and is can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); } - + // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -1138,6 +1137,7 @@ impl<'a> Resolver<'a> { all_generics: Vec::new(), is_trait_function: false, parameter_idents: Vec::new(), + generic_idents: Vec::new(), } } diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 3bd85e94dca..27834bbd949 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -99,6 +99,9 @@ pub struct FuncMeta { /// Note that this includes separate entries for each identifier in e.g. tuple patterns. pub parameter_idents: Vec, + /// The HirIdent of each numeric generic identifier + pub generic_idents: Vec, + pub return_type: FunctionReturnType, pub return_visibility: Visibility, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 42977884b27..28e4e07f3d9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1536,3 +1536,6 @@ fn numeric_generic_in_function_signature() { let errors = get_program_errors(src); assert!(errors.is_empty()); } + +// #[test] +// fn generic \ No newline at end of file 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 c29856253ef..8e5513202d0 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -1,3 +1,5 @@ +global N = 1000; + fn main() { let a = id([1, 2]); let b = id([1, 2, 3]); @@ -21,7 +23,7 @@ fn main() { assert(my_type.c == 3); let foo = baz::<10>(); - assert(foo.inner == 10); + assert(foo.inner == [1; 10]); let new_arr = update_arr([0, 1, 2, 3]); assert(new_arr[0] == 5); @@ -68,7 +70,9 @@ struct MyType { c: Field, } -impl Deserialize for MyType { +// TODO 1: getting some duplicate definition errors here +// TODO 2: Ban in the trait constraint specifying a numeric generic +impl Deserialize for MyType { fn deserialize(fields: [Field; N]) -> Self { MyType { a: fields[0], b: fields[1], c: fields[2] } } @@ -103,6 +107,14 @@ fn update_arr(mut arr: [Field; N]) -> [Field; N] { // Used as a field of a struct struct Foo { - inner: N, + inner: [u64; N], +} +fn baz() -> Foo { + Foo { inner: [1; N] } } -fn baz() -> Foo { Foo { inner: N } } + +impl Foo { + fn bar(self) -> u64 { + N * self.inner[0] + } +} \ No newline at end of file diff --git a/test_programs/execution_success/numeric_generics/Nargo.toml b/test_programs/execution_success/numeric_generics/Nargo.toml deleted file mode 100644 index 7993eb0f1cb..00000000000 --- a/test_programs/execution_success/numeric_generics/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "numeric_generics" -type = "bin" -authors = [""] -compiler_version = ">=0.30.0" - -[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/numeric_generics/src/main.nr b/test_programs/execution_success/numeric_generics/src/main.nr deleted file mode 100644 index 3c30bf08424..00000000000 --- a/test_programs/execution_success/numeric_generics/src/main.nr +++ /dev/null @@ -1,11 +0,0 @@ -fn main(x: Field, y: pub Field) { - assert(x != y); -} - -#[test] -fn test_main() { - main(1, 2); - - // Uncomment to make test fail - // main(1, 1); -} From 0e567489ec9a643ef1aac7642cb3be65c0789508 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 3 Jun 2024 17:26:55 +0000 Subject: [PATCH 06/85] little cleanup --- compiler/noirc_frontend/src/elaborator/mod.rs | 14 +++----------- .../numeric_generics/src/main.nr | 3 --- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index bee195cb79a..bee1d1a06f7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -249,9 +249,7 @@ impl<'context> Elaborator<'context> { dbg!(this.generic_idents.clone()); // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); - // if !this.errors.is_empty() { - // dbg!(this.errors.clone()); - // } + if !this.errors.is_empty() { for e in this.errors.iter() { if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { @@ -265,9 +263,7 @@ impl<'context> Elaborator<'context> { for trait_impl in &mut items.trait_impls { this.collect_trait_impl(trait_impl); } - // if !this.errors.is_empty() { - // dbg!(this.errors.clone()); - // } + if !this.errors.is_empty() { for e in this.errors.iter() { if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { @@ -292,9 +288,7 @@ impl<'context> Elaborator<'context> { } } } - // if !this.errors.is_empty() { - // dbg!(this.errors.clone()); - // } + // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. for global in non_literal_globals { @@ -352,8 +346,6 @@ impl<'context> Elaborator<'context> { let generic_idents_count = self.generic_idents.len(); let ret = f(self); self.generics.truncate(generics_count); - // dbg!(self.generic_idents.clone()); - // dbg!(generic_idents_count); self.generic_idents.truncate(generic_idents_count); ret } 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 8e5513202d0..26e26a7769d 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -90,9 +90,6 @@ impl PublicStorage { let mut fields: [Field; N] = [0; N]; // Used a loop bound for i in 0..N { - // TODO: Removing this cast I get an error as expected as deserialzie - // expects an array of fields not u64, but the error could be improved. - // Getting the following: `Expected type [Field; u64], found type [u64; u64]` fields[i] = i as Field + 1; } T::deserialize(fields) From 3de41d66335ea8384305867f9b91c2259e0d1a09 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 14:34:48 +0000 Subject: [PATCH 07/85] error for numeric generic used as type --- compiler/noirc_frontend/src/elaborator/mod.rs | 145 ++++-------------- .../noirc_frontend/src/elaborator/patterns.rs | 5 +- .../noirc_frontend/src/elaborator/traits.rs | 8 +- .../noirc_frontend/src/elaborator/types.rs | 67 ++++---- .../src/hir/def_collector/dc_crate.rs | 8 +- .../src/hir/resolution/errors.rs | 11 ++ .../src/hir/resolution/functions.rs | 11 +- .../src/hir/resolution/resolver.rs | 80 ++++++---- .../src/hir/resolution/structs.rs | 4 +- .../src/hir/resolution/traits.rs | 8 +- .../noirc_frontend/src/hir/type_check/mod.rs | 1 + .../noirc_frontend/src/hir_def/function.rs | 4 +- compiler/noirc_frontend/src/hir_def/types.rs | 8 + compiler/noirc_frontend/src/tests.rs | 20 ++- .../numeric_generics/src/main.nr | 46 +++--- 15 files changed, 205 insertions(+), 221 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index bee1d1a06f7..3ea9e5ea6c0 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,8 +4,7 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, - hir::{ + ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, hir::{ def_collector::{ dc_crate::{ filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, @@ -16,13 +15,9 @@ use crate::{ resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext}, scope::ScopeForest as GenericScopeForest, type_check::TypeCheckError, - }, - hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, - macros_api::{ + }, hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, macros_api::{ Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, SecondaryAttribute, StructId, - }, - node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, TraitId, TypeAliasId}, - Shared, Type, TypeVariable, + }, node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, TraitId, TypeAliasId}, ResolvedGeneric, Shared, Type, TypeVariable }; use crate::{ ast::{TraitBound, UnresolvedGenerics}, @@ -98,7 +93,7 @@ pub struct Elaborator<'context> { /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics /// were declared in. - generics: Vec<(Rc, TypeVariable, Span)>, + generics: Vec, /// The idents for each numeric generic on a function /// These definitions are generated when creating function metas @@ -205,72 +200,24 @@ impl<'context> Elaborator<'context> { for global in literal_globals { this.elaborate_global(global); } - if !this.errors.is_empty() { - dbg!(this.errors.clone()); - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } + for (alias_id, alias) in items.type_aliases { this.define_type_alias(alias_id, alias); } - if !this.errors.is_empty() { - dbg!(this.errors.clone()); - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } + this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - if !this.errors.is_empty() { - dbg!(this.errors.clone()); - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } + this.collect_traits(items.traits); - // if !this.errors.is_empty() { - // dbg!(this.errors.clone()); - // } - if !this.errors.is_empty() { - dbg!(this.errors.clone()); - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } - dbg!("COLLECTED TRAITS"); - dbg!(this.generic_idents.clone()); + // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - dbg!(this.generic_idents.clone()); - } - } - } // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { this.collect_trait_impl(trait_impl); } - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be // done during def collection since we need to be able to resolve the type of @@ -281,59 +228,25 @@ impl<'context> Elaborator<'context> { for ((_self_type, module), impls) in &mut items.impls { this.collect_impls(*module, impls); } - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to to ensure they are valid. for global in non_literal_globals { this.elaborate_global(global); } - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } for functions in items.functions { this.elaborate_functions(functions); } - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } for impls in items.impls.into_values() { this.elaborate_impls(impls); } - if !this.errors.is_empty() { - for e in this.errors.iter() { - if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - dbg!("GOT DUP DEF"); - } - } - } + for trait_impl in items.trait_impls { this.elaborate_trait_impl(trait_impl); } - // if !this.errors.is_empty() { - // dbg!(this.errors.clone()); - // for e in this.errors.iter() { - // if matches!(e.0, CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. })) { - // dbg!("GOT DUP DEF"); - // } - // } - // } + let cycle_errors = this.interner.check_for_dependency_cycles(); this.errors.extend(cycle_errors); this.errors @@ -392,12 +305,12 @@ impl<'context> Elaborator<'context> { // so we need to reintroduce the same IDs into scope here. for parameter in &func_meta.parameter_idents { let name = self.interner.definition_name(parameter.id).to_owned(); - self.add_existing_variable_to_scope(name, parameter.clone()); + self.add_existing_variable_to_scope(name, parameter.clone(), true); } // We should introduce the IDs for numeric generic into scope as we do with parameters for numeric_generic in &func_meta.generic_idents { let name = self.interner.definition_name(numeric_generic.id).to_owned(); - self.add_existing_variable_to_scope(name, numeric_generic.clone()); + self.add_existing_variable_to_scope(name, numeric_generic.clone(), false); } self.generics = func_meta.all_generics.clone(); @@ -508,6 +421,7 @@ impl<'context> Elaborator<'context> { let ident = Ident::from(generic); let span = ident.0.span(); + let mut is_numeric_generic = false; // Declare numeric generics if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); @@ -519,30 +433,37 @@ impl<'context> Elaborator<'context> { self.errors.push((unsupported_typ_err, self.file)); } let definition = DefinitionKind::GenericType(typevar.clone()); - // dbg!(ident.clone()); let hir_ident = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - // dbg!(hir_ident.clone()); + // Push the definition type because if one is missing, when the numeric generic is used in an expression // its definition type will be resolved to a polymorphic integer or field. // We do not yet fully support bool generics but this will be a foot-gun once we look to add support - // and is can lead to confusing errors. + // and can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); - self.generic_idents.push(hir_ident) + // Store the ident of the numeric generic to set up the function meta so that + // any numeric generics are accurately brought into scope. + self.generic_idents.push(hir_ident); + is_numeric_generic = true; } // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - if let Some((_, _, first_span)) = self.find_generic(&name) { - dbg!(name.clone()); + if let Some(generic) = self.find_generic(&name) { self.push_err(ResolverError::DuplicateDefinition { name: ident.0.contents.clone(), - first_span: *first_span, + first_span: generic.span, second_span: span, }); } else { - self.generics.push((name, typevar.clone(), span)); + let resolved_generic = ResolvedGeneric { + name, + type_var: typevar.clone(), + is_numeric_generic, + span, + }; + self.generics.push(resolved_generic); } typevar @@ -671,7 +592,7 @@ impl<'context> Elaborator<'context> { let mut trait_constraints = self.resolve_trait_constraints(&func.def.where_clause); - let mut generics = vecmap(&self.generics, |(_, typevar, _)| typevar.clone()); + let mut generics = vecmap(&self.generics, |generic| generic.type_var.clone()); let mut parameters = Vec::new(); let mut parameter_types = Vec::new(); let mut parameter_idents = Vec::new(); @@ -764,7 +685,7 @@ impl<'context> Elaborator<'context> { let generic = Ident::from(generic); self.find_generic(&generic.0.contents) }) - .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) + .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) .collect(); let meta = FuncMeta { @@ -842,8 +763,8 @@ impl<'context> Elaborator<'context> { // 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((name, _, span)) = - self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) + if let Some(ResolvedGeneric { name, span, .. }) = + self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) { let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); @@ -1085,7 +1006,7 @@ impl<'context> Elaborator<'context> { methods, }); - let generics = vecmap(&self.generics, |(_, type_variable, _)| type_variable.clone()); + let generics = vecmap(&self.generics, |generic| generic.type_var.clone()); if let Err((prev_span, prev_file)) = self.interner.add_trait_implementation( self_type.clone(), diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 670dfb0882e..8a026275ba1 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -264,7 +264,6 @@ impl<'context> Elaborator<'context> { if !allow_shadowing { if let Some(old_value) = old_value { - dbg!(old_value.ident.clone()); self.push_err(ResolverError::DuplicateDefinition { name: name.0.contents, first_span: old_value.ident.location.span, @@ -276,9 +275,9 @@ impl<'context> Elaborator<'context> { ident } - pub fn add_existing_variable_to_scope(&mut self, name: String, ident: HirIdent) { + pub fn add_existing_variable_to_scope(&mut self, name: String, ident: HirIdent, warn_if_unused: bool) { let second_span = ident.location.span; - let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused: true }; + let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; let old_value = self.scopes.get_mut_scope().add_key_value(name.clone(), resolver_meta); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 957d4b157ff..eed39b059e0 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -4,7 +4,7 @@ use iter_extended::vecmap; use noirc_errors::Location; use crate::{ - ast::{FunctionKind, TraitItem, UnresolvedGenerics, UnresolvedTraitConstraint}, + ast::{FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint}, hir::def_collector::dc_crate::UnresolvedTrait, hir_def::traits::{TraitConstant, TraitFunction, TraitType}, macros_api::{ @@ -35,7 +35,7 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = vecmap(&this.generics, |(_, generic, _)| generic.clone()); + trait_def.generics = vecmap(&this.generics, |generic| generic.type_var.clone()); }); }); @@ -89,7 +89,7 @@ impl<'context> Elaborator<'context> { Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); let name_span = the_trait.name.span(); - this.add_existing_generic("Self", name_span, self_typevar); + this.add_existing_generic(&UnresolvedGeneric::Variable(Ident::from("Self")), name_span, self_typevar); this.self_type = Some(self_type.clone()); let func_id = unresolved_trait.method_ids[&name.0.contents]; @@ -107,7 +107,7 @@ impl<'context> Elaborator<'context> { let arguments = vecmap(&func_meta.parameters.0, |(_, typ, _)| typ.clone()); let return_type = func_meta.return_type().clone(); - let generics = vecmap(&this.generics, |(_, type_var, _)| type_var.clone()); + let generics = vecmap(&this.generics, |generic| generic.type_var.clone()); let default_impl_list: Vec<_> = unresolved_trait .fns_with_default_impl diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 4886ef6e78c..68a511f3f00 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -6,31 +6,23 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ - BinaryOpKind, Ident, IntegerBitSize, UnresolvedGenerics, UnresolvedTraitConstraint, - UnresolvedTypeExpression, - }, - hir::{ - def_map::ModuleDefId, - resolution::{ + BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedTypeExpression + }, hir::{ + def_collector::dc_crate::CompilationError, def_map::ModuleDefId, resolution::{ errors::ResolverError, resolver::{verify_mutable_reference, SELF_TYPE_NAME}, - }, - type_check::{Source, TypeCheckError}, - }, - hir_def::{ + }, type_check::{Source, TypeCheckError} + }, hir_def::{ expr::{ HirBinaryOp, HirCallExpression, HirIdent, HirMemberAccess, HirMethodReference, HirPrefixExpression, }, function::FuncMeta, traits::TraitConstraint, - }, - macros_api::{ + }, macros_api::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, - }, - node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, - Generics, Type, TypeBinding, TypeVariable, TypeVariableKind, + }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, Generics, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind }; use super::Elaborator; @@ -132,8 +124,8 @@ impl<'context> Elaborator<'context> { resolved_type } - pub fn find_generic(&self, target_name: &str) -> Option<&(Rc, TypeVariable, Span)> { - self.generics.iter().find(|(name, _, _)| name.as_ref() == target_name) + pub fn find_generic(&self, target_name: &str) -> Option<&ResolvedGeneric> { + self.generics.iter().find(|generic| generic.name.as_ref() == target_name) } fn resolve_named_type( @@ -268,8 +260,13 @@ impl<'context> Elaborator<'context> { pub fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; - if let Some((name, var, _)) = self.find_generic(name) { - return Some(Type::NamedGeneric(var.clone(), name.clone())); + if let Some(generic) = self.find_generic(name) { + if generic.is_numeric_generic { + let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { ident: path.last_segment() }); + self.errors.push((expected_typ_err, self.file)); + return Some(Type::Error); + } + return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); } } @@ -1439,27 +1436,37 @@ impl<'context> Elaborator<'context> { } } - pub fn add_existing_generics(&mut self, names: &UnresolvedGenerics, generics: &Generics) { - assert_eq!(names.len(), generics.len()); + pub fn add_existing_generics(&mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics) { + assert_eq!(unresolved_generics.len(), generics.len()); - for (name, typevar) in names.iter().zip(generics) { - let name = Ident::from(name); - self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); + for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { + let name = Ident::from(unresolved_generic); + self.add_existing_generic(unresolved_generic, name.0.span(), typevar.clone()); } } - pub fn add_existing_generic(&mut self, name: &str, span: Span, typevar: TypeVariable) { + pub fn add_existing_generic(&mut self, unresolved_generic: &UnresolvedGeneric, span: Span, typevar: TypeVariable) { + let ident = Ident::from(unresolved_generic); + let name = ident.0.contents; + // Check for name collisions of this generic - let rc_name = Rc::new(name.to_owned()); + let rc_name = Rc::new(name.clone()); - if let Some((_, _, first_span)) = self.find_generic(&rc_name) { + if let Some(generic) = self.find_generic(&rc_name) { self.push_err(ResolverError::DuplicateDefinition { - name: name.to_owned(), - first_span: *first_span, + name: name, + first_span: generic.span, second_span: span, }); } else { - self.generics.push((rc_name, typevar, span)); + let is_numeric_generic = matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); + let resolved_generic = ResolvedGeneric { + name: rc_name, + type_var: typevar.clone(), + is_numeric_generic, + span, + }; + self.generics.push(resolved_generic); } } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 05147af5459..2878649172d 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -5,7 +5,7 @@ use crate::graph::CrateId; use crate::hir::comptime::{Interpreter, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; -use crate::{Type, TypeVariable}; +use crate::{ResolvedGeneric, Type, TypeVariable}; use crate::hir::resolution::import::{resolve_import, ImportDirective, PathResolution}; use crate::hir::resolution::{ @@ -125,7 +125,7 @@ pub struct UnresolvedTraitImpl { pub trait_id: Option, pub impl_id: Option, pub resolved_object_type: Option, - pub resolved_generics: Vec<(Rc, TypeVariable, Span)>, + pub resolved_generics: Vec, // The resolved generic on the trait itself. E.g. it is the `` in // `impl Foo for Bar { ... }` @@ -379,13 +379,15 @@ impl DefCollector { def_collector.items.traits, crate_id, )); + dbg!(resolved_module.errors.len()); + // Must resolve structs before we resolve globals. resolved_module.errors.extend(resolve_structs( context, def_collector.items.types, crate_id, )); - + dbg!(resolved_module.errors.len()); // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. resolved_module.errors.extend(collect_trait_impls( diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 73e64cb2fce..64aab3f67ed 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -98,6 +98,8 @@ pub enum ResolverError { UnsupportedNumericGenericType { ident: Ident, typ: Type }, #[error("Numeric generics should be explicit")] UseExplicitNumericGeneric { ident: Ident }, + #[error("expected type, found numeric generic parameter")] + NumericGenericUsedForType { ident: Ident }, } impl ResolverError { @@ -408,6 +410,15 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ident.0.span(), ) } + ResolverError::NumericGenericUsedForType { ident } => { + let name = &ident.0.contents; + + Diagnostic::simple_error( + format!("expected type, found numeric generic parameter {name}"), + String::from("not a type"), + ident.0.span(), + ) + } } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/functions.rs b/compiler/noirc_frontend/src/hir/resolution/functions.rs index e63de9b9173..5b9118d68c3 100644 --- a/compiler/noirc_frontend/src/hir/resolution/functions.rs +++ b/compiler/noirc_frontend/src/hir/resolution/functions.rs @@ -5,16 +5,13 @@ use iter_extended::vecmap; use noirc_errors::Span; use crate::{ - graph::CrateId, - hir::{ + graph::CrateId, hir::{ def_collector::dc_crate::{CompilationError, UnresolvedFunctions}, def_map::{CrateDefMap, ModuleId}, - }, - node_interner::{FuncId, NodeInterner, TraitImplId}, - Type, TypeVariable, + }, node_interner::{FuncId, NodeInterner, TraitImplId}, ResolvedGeneric, Type, TypeVariable }; -use super::{path_resolver::StandardPathResolver, resolver::Resolver}; +use super::{path_resolver::StandardPathResolver, Resolver}; #[allow(clippy::too_many_arguments)] pub(crate) fn resolve_function_set( @@ -24,7 +21,7 @@ pub(crate) fn resolve_function_set( mut unresolved_functions: UnresolvedFunctions, self_type: Option, trait_impl_id: Option, - impl_generics: Vec<(Rc, TypeVariable, Span)>, + impl_generics: Vec, errors: &mut Vec<(CompilationError, FileId)>, ) -> Vec<(FileId, FuncId)> { let file_id = unresolved_functions.file_id; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7e3274557d2..87dbc4b6452 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -43,7 +43,7 @@ use crate::node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId, StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; -use crate::{Generics, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind}; +use crate::{Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind}; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span, Spanned}; @@ -126,7 +126,7 @@ pub struct Resolver<'a> { /// unique type variables if we're resolving a struct. Empty otherwise. /// This is a Vec rather than a map to preserve the order a functions generics /// were declared in. - generics: Vec<(Rc, TypeVariable, Span)>, + generics: Vec, /// When resolving lambda expressions, we need to keep track of the variables /// that are captured. We do this in order to create the hidden environment @@ -619,8 +619,8 @@ impl<'a> Resolver<'a> { resolved_type } - fn find_generic(&self, target_name: &str) -> Option<&(Rc, TypeVariable, Span)> { - self.generics.iter().find(|(name, _, _)| name.as_ref() == target_name) + fn find_generic(&self, target_name: &str) -> Option<&ResolvedGeneric> { + self.generics.iter().find(|generic| generic.name.as_ref() == target_name) } fn resolve_named_type( @@ -631,6 +631,7 @@ impl<'a> Resolver<'a> { ) -> Type { if args.is_empty() { if let Some(typ) = self.lookup_generic_or_global_type(&path) { + dbg!(self.errors.len()); return typ; } } @@ -755,9 +756,13 @@ impl<'a> Resolver<'a> { fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; - if let Some((name, var, _)) = self.find_generic(name) { - return Some(Type::NamedGeneric(var.clone(), name.clone())); - } + if let Some(generic) = self.find_generic(name) { + if generic.is_numeric_generic { + self.errors.push(ResolverError::NumericGenericUsedForType { ident: path.last_segment() }); + return Some(Type::Error); + } + return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); + }; } // If we cannot find a local generic of the same name, try to look up a global @@ -877,14 +882,14 @@ impl<'a> Resolver<'a> { /// Return the current generics. /// Needed to keep referring to the same type variables across many /// methods in a single impl. - pub fn get_generics(&self) -> &[(Rc, TypeVariable, Span)] { + pub fn get_generics(&self) -> &[ResolvedGeneric] { &self.generics } /// Set the current generics that are in scope. /// Unlike add_generics, this function will not create any new type variables, /// opting to reuse the existing ones it is directly given. - pub fn set_generics(&mut self, generics: Vec<(Rc, TypeVariable, Span)>) { + pub fn set_generics(&mut self, generics: Vec) { self.generics = generics; } @@ -907,6 +912,7 @@ impl<'a> Resolver<'a> { let ident = Ident::from(generic); let span = ident.0.span(); + let mut is_numeric_generic = false; // Declare numeric generics if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); @@ -924,19 +930,26 @@ impl<'a> Resolver<'a> { // We do not yet fully support bool generics but this will be a foot-gun once we look to add support // and is can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); + is_numeric_generic = true; } // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - if let Some((_, _, first_span)) = self.find_generic(&name) { + if let Some(generic) = self.find_generic(&name) { self.errors.push(ResolverError::DuplicateDefinition { - name: ident.0.contents.clone(), - first_span: *first_span, + name: ident.0.contents, + first_span: generic.span, second_span: span, }); } else { - self.generics.push((name, typevar.clone(), span)); + let resolved_generic = ResolvedGeneric { + name, + type_var: typevar.clone(), + is_numeric_generic, + span, + }; + self.generics.push(resolved_generic); } typevar @@ -946,27 +959,36 @@ impl<'a> Resolver<'a> { /// Add the given existing generics to scope. /// This is useful for adding the same generics to many items. E.g. apply impl generics /// to each function in the impl or trait generics to each item in the trait. - pub fn add_existing_generics(&mut self, names: &UnresolvedGenerics, generics: &Generics) { - assert_eq!(names.len(), generics.len()); + pub fn add_existing_generics(&mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics) { + assert_eq!(unresolved_generics.len(), generics.len()); - for (name, typevar) in names.iter().zip(generics) { - let name = Ident::from(name); - self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); + for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { + let name = Ident::from(unresolved_generic); + self.add_existing_generic(&unresolved_generic, name.0.span(), typevar.clone()); } } - pub fn add_existing_generic(&mut self, name: &str, span: Span, typevar: TypeVariable) { + pub fn add_existing_generic(&mut self, unresolved_generic: &UnresolvedGeneric, span: Span, typevar: TypeVariable) { + let ident = Ident::from(unresolved_generic); + let name = ident.0.contents; // Check for name collisions of this generic - let rc_name = Rc::new(name.to_owned()); + let rc_name = Rc::new(name.clone()); - if let Some((_, _, first_span)) = self.find_generic(&rc_name) { + if let Some(generic) = self.find_generic(&rc_name) { self.errors.push(ResolverError::DuplicateDefinition { - name: name.to_owned(), - first_span: *first_span, + name: name, + first_span: generic.span, second_span: span, }); } else { - self.generics.push((rc_name, typevar, span)); + let is_numeric_generic = matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); + let resolved_generic = ResolvedGeneric { + name: rc_name, + type_var: typevar.clone(), + is_numeric_generic, + span, + }; + self.generics.push(resolved_generic); } } @@ -985,7 +1007,7 @@ impl<'a> Resolver<'a> { self.resolving_ids.insert(struct_id); let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); self.resolving_ids.remove(&struct_id); - + (generics, fields, self.errors) } @@ -1042,7 +1064,7 @@ impl<'a> Resolver<'a> { // indicate we should code generate in the same way. Thus, we unify the attributes into one flag here. let has_inline_attribute = has_no_predicates_attribute || should_fold; - let mut generics = vecmap(&self.generics, |(_, typevar, _)| typevar.clone()); + let mut generics = vecmap(&self.generics, |generic| generic.type_var.clone()); let mut parameters = vec![]; let mut parameter_types = vec![]; @@ -1115,7 +1137,7 @@ impl<'a> Resolver<'a> { let generic = Ident::from(generic); self.find_generic(&generic.0.contents) }) - .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) + .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) .collect(); FuncMeta { @@ -1181,8 +1203,8 @@ impl<'a> Resolver<'a> { // 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((name, _, span)) = - self.generics.iter().find(|(name, _, _)| name.as_ref() == &name_to_find) + if let Some(ResolvedGeneric { name, span, .. }) = + self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) { let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs index f62e5589d74..2127ebbe773 100644 --- a/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -41,7 +41,7 @@ pub(crate) fn resolve_structs( struct_def.generics = generics; }); } - + dbg!(errors.len()); // Check whether the struct fields have nested slices // We need to check after all structs are resolved to // make sure every struct's fields is accurately set. @@ -78,6 +78,6 @@ fn resolve_struct_fields( let (generics, fields, errors) = Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file_id) .resolve_struct_fields(unresolved.struct_def, type_id); - + dbg!(errors.len()); (generics, fields, errors) } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 3d355fd4447..fc586b2660c 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -4,7 +4,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; -use crate::ast::{ItemVisibility, Path, TraitItem}; +use crate::ast::{ItemVisibility, Path, TraitItem, UnresolvedGeneric, Ident}; use crate::{ graph::CrateId, hir::{ @@ -126,7 +126,7 @@ fn resolve_trait_methods( resolver.add_generics(generics); resolver.add_existing_generics(&unresolved_trait.trait_def.generics, trait_generics); - resolver.add_existing_generic("Self", name_span, self_typevar); + resolver.add_existing_generic(&UnresolvedGeneric::Variable(Ident::from("Self")), name_span, self_typevar); resolver.set_self_type(Some(self_type.clone())); let func_id = unresolved_trait.method_ids[&name.0.contents]; @@ -143,7 +143,7 @@ fn resolve_trait_methods( let arguments = vecmap(parameters, |param| resolver.resolve_type(param.1.clone())); let return_type = resolver.resolve_type(return_type.get_type().into_owned()); - let generics = vecmap(resolver.get_generics(), |(_, type_var, _)| type_var.clone()); + let generics = vecmap(resolver.get_generics(), |generic| generic.type_var.clone()); let default_impl_list: Vec<_> = unresolved_trait .fns_with_default_impl @@ -463,7 +463,7 @@ pub(crate) fn resolve_trait_impls( methods: vecmap(&impl_methods, |(_, func_id)| *func_id), }); - let impl_generics = vecmap(impl_generics, |(_, type_variable, _)| type_variable); + let impl_generics = vecmap(impl_generics, |generic| generic.type_var); if let Err((prev_span, prev_file)) = interner.add_trait_implementation( self_type.clone(), diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index de2e575b4e2..96fd2648d1b 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -553,6 +553,7 @@ pub mod test { has_inline_attribute: false, all_generics: Vec::new(), parameter_idents: Vec::new(), + generic_idents: Vec::new(), }; interner.push_fn_meta(func_meta, func_id); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 27834bbd949..3d67e92b4cb 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -8,7 +8,7 @@ use super::stmt::HirPattern; use super::traits::TraitConstraint; use crate::ast::{FunctionKind, FunctionReturnType, Visibility}; use crate::node_interner::{ExprId, NodeInterner, TraitImplId}; -use crate::{Type, TypeVariable}; +use crate::{ResolvedGeneric, Type, TypeVariable}; /// A Hir function is a block expression /// with a list of statements @@ -120,7 +120,7 @@ pub struct FuncMeta { /// from outer scopes, such as those introduced by an impl. /// This is stored when the FuncMeta is first created to later be used to set the current /// generics when the function's body is later resolved. - pub all_generics: Vec<(Rc, TypeVariable, Span)>, + pub all_generics: Vec, pub location: Location, diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index cf9aafbb308..1f0a7b80882 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -218,6 +218,14 @@ pub struct StructType { /// Corresponds to generic lists such as `` in the source program. pub type Generics = Vec; +#[derive(Clone, Debug)] +pub struct ResolvedGeneric { + pub name: Rc, + pub type_var: TypeVariable, + pub is_numeric_generic: bool, + pub span: Span, +} + impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { self.id.hash(state); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 28e4e07f3d9..478da452879 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -81,7 +81,7 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation &mut context, program.clone().into_sorted(), root_file_id, - false, + true, &[], // No macro processors )); } @@ -1491,6 +1491,7 @@ fn numeric_generic_binary_operation_type_mismatch() { } "#; let errors = get_program_errors(src); + dbg!(errors.clone()); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1537,5 +1538,18 @@ fn numeric_generic_in_function_signature() { assert!(errors.is_empty()); } -// #[test] -// fn generic \ No newline at end of file +#[test] +fn numeric_generic_as_struct_field_type() { + 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::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + )); +} \ No newline at end of file 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 26e26a7769d..97cdf5ec834 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -1,4 +1,7 @@ -global N = 1000; +// TODO: A global of the same name with the const generic does not work +// at the moment. This will work once we switch over to the "elaborator" +// in the noirc_frontend +// global N = 1000; fn main() { let a = id([1, 2]); @@ -29,18 +32,19 @@ fn main() { assert(new_arr[0] == 5); } -fn id(x: [Field; I]) -> [Field; I] { +// TODO: getting unused variable warnings +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); + assert(index as u64 < S); self.data[index] = elem; self @@ -64,14 +68,26 @@ fn double_numeric_generics_test() { assert(double::<7 + 8>() == 30); } +// Used as a field of a struct +struct Foo { + inner: [u64; N], +} +fn baz() -> Foo { + Foo { inner: [1; N] } +} +// Used in an impl +impl Foo { + fn bar(self) -> u64 { + N * self.inner[0] + } +} + struct MyType { a: Field, b: Field, c: Field, } -// TODO 1: getting some duplicate definition errors here -// TODO 2: Ban in the trait constraint specifying a numeric generic impl Deserialize for MyType { fn deserialize(fields: [Field; N]) -> Self { MyType { a: fields[0], b: fields[1], c: fields[2] } @@ -85,7 +101,7 @@ trait Deserialize { struct PublicStorage {} impl PublicStorage { - fn read() -> T where T: Deserialize { + fn read() -> T where T: Deserialize { // Used as a type within a function body let mut fields: [Field; N] = [0; N]; // Used a loop bound @@ -101,17 +117,3 @@ fn update_arr(mut arr: [Field; N]) -> [Field; N] { arr[0] = 5; arr } - -// Used as a field of a struct -struct Foo { - inner: [u64; N], -} -fn baz() -> Foo { - Foo { inner: [1; N] } -} - -impl Foo { - fn bar(self) -> u64 { - N * self.inner[0] - } -} \ No newline at end of file From 8c2860a9e094ef7f73d984d1bcc9d059143607a9 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 14:37:51 +0000 Subject: [PATCH 08/85] fmt and clippy --- compiler/noirc_frontend/src/elaborator/mod.rs | 43 +++++++++-------- .../noirc_frontend/src/elaborator/patterns.rs | 7 ++- .../noirc_frontend/src/elaborator/traits.rs | 10 +++- .../noirc_frontend/src/elaborator/types.rs | 48 ++++++++++++++----- .../src/hir/def_collector/dc_crate.rs | 3 +- .../src/hir/resolution/functions.rs | 10 ++-- .../src/hir/resolution/resolver.rs | 40 ++++++++++------ .../src/hir/resolution/traits.rs | 8 +++- .../noirc_frontend/src/hir_def/function.rs | 2 +- compiler/noirc_frontend/src/tests.rs | 2 +- 10 files changed, 111 insertions(+), 62 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 3ea9e5ea6c0..8c9f7eef5e7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,8 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, hir::{ + ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, + hir::{ def_collector::{ dc_crate::{ filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, @@ -15,9 +16,13 @@ use crate::{ resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext}, scope::ScopeForest as GenericScopeForest, type_check::TypeCheckError, - }, hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, macros_api::{ + }, + hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, + macros_api::{ Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, SecondaryAttribute, StructId, - }, node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, TraitId, TypeAliasId}, ResolvedGeneric, Shared, Type, TypeVariable + }, + node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, TraitId, TypeAliasId}, + ResolvedGeneric, Shared, Type, TypeVariable, }; use crate::{ ast::{TraitBound, UnresolvedGenerics}, @@ -96,7 +101,7 @@ pub struct Elaborator<'context> { generics: Vec, /// The idents for each numeric generic on a function - /// These definitions are generated when creating function metas + /// These definitions are generated when creating function metas /// and need to be brought into scope later when elaborating the function body. generic_idents: Vec, @@ -426,10 +431,12 @@ impl<'context> Elaborator<'context> { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { - ident: ident.clone(), - typ: typ.clone(), - }); + let unsupported_typ_err = CompilationError::ResolverError( + ResolverError::UnsupportedNumericGenericType { + ident: ident.clone(), + typ: typ.clone(), + }, + ); self.errors.push((unsupported_typ_err, self.file)); } let definition = DefinitionKind::GenericType(typevar.clone()); @@ -441,7 +448,7 @@ impl<'context> Elaborator<'context> { // We do not yet fully support bool generics but this will be a foot-gun once we look to add support // and can lead to confusing errors. self.interner.push_definition_type(hir_ident.id, typ); - // Store the ident of the numeric generic to set up the function meta so that + // Store the ident of the numeric generic to set up the function meta so that // any numeric generics are accurately brought into scope. self.generic_idents.push(hir_ident); is_numeric_generic = true; @@ -457,12 +464,8 @@ impl<'context> Elaborator<'context> { second_span: span, }); } else { - let resolved_generic = ResolvedGeneric { - name, - type_var: typevar.clone(), - is_numeric_generic, - span, - }; + let resolved_generic = + ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; self.generics.push(resolved_generic); } @@ -763,14 +766,12 @@ impl<'context> Elaborator<'context> { // 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, .. }) = - self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) + if let Some(ResolvedGeneric { name, span, .. }) = + self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) { let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); if value.is_some() { - let value = value.unwrap(); - // dbg!(value.ident.clone()); // 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 @@ -786,7 +787,8 @@ impl<'context> Elaborator<'context> { // dbg!(ident.clone()); // dbg!(self.generic_idents.clone()); // } - let x = self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + let x = + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); self.generic_idents.push(x); self.errors.push(( CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { @@ -1339,7 +1341,6 @@ impl<'context> Elaborator<'context> { self.local_module = *local_module; for (generics, _, function_set) in function_sets { - self.add_generics(generics); let self_type = self.resolve_type(self_type.clone()); function_set.self_type = Some(self_type.clone()); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 8a026275ba1..7abc7c4aa2e 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -275,7 +275,12 @@ impl<'context> Elaborator<'context> { ident } - pub fn add_existing_variable_to_scope(&mut self, name: String, ident: HirIdent, warn_if_unused: bool) { + pub fn add_existing_variable_to_scope( + &mut self, + name: String, + ident: HirIdent, + warn_if_unused: bool, + ) { let second_span = ident.location.span; let resolver_meta = ResolverMeta { num_times_used: 0, ident, warn_if_unused }; diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index eed39b059e0..7b3f4d5351d 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -4,7 +4,9 @@ use iter_extended::vecmap; use noirc_errors::Location; use crate::{ - ast::{FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint}, + ast::{ + FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, + }, hir::def_collector::dc_crate::UnresolvedTrait, hir_def::traits::{TraitConstant, TraitFunction, TraitType}, macros_api::{ @@ -89,7 +91,11 @@ impl<'context> Elaborator<'context> { Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); let name_span = the_trait.name.span(); - this.add_existing_generic(&UnresolvedGeneric::Variable(Ident::from("Self")), name_span, self_typevar); + this.add_existing_generic( + &UnresolvedGeneric::Variable(Ident::from("Self")), + name_span, + self_typevar, + ); this.self_type = Some(self_type.clone()); let func_id = unresolved_trait.method_ids[&name.0.contents]; diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 68a511f3f00..2bd582a1d4d 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -6,23 +6,32 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ - BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedTypeExpression - }, hir::{ - def_collector::dc_crate::CompilationError, def_map::ModuleDefId, resolution::{ + BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedTypeExpression, + }, + hir::{ + def_collector::dc_crate::CompilationError, + def_map::ModuleDefId, + resolution::{ errors::ResolverError, resolver::{verify_mutable_reference, SELF_TYPE_NAME}, - }, type_check::{Source, TypeCheckError} - }, hir_def::{ + }, + type_check::{Source, TypeCheckError}, + }, + hir_def::{ expr::{ HirBinaryOp, HirCallExpression, HirIdent, HirMemberAccess, HirMethodReference, HirPrefixExpression, }, function::FuncMeta, traits::TraitConstraint, - }, macros_api::{ + }, + macros_api::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, - }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, Generics, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind + }, + node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, + Generics, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind, }; use super::Elaborator; @@ -262,10 +271,13 @@ impl<'context> Elaborator<'context> { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { if generic.is_numeric_generic { - let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { ident: path.last_segment() }); + let expected_typ_err = + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { + ident: path.last_segment(), + }); self.errors.push((expected_typ_err, self.file)); return Some(Type::Error); - } + } return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); } } @@ -1436,7 +1448,11 @@ impl<'context> Elaborator<'context> { } } - pub fn add_existing_generics(&mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics) { + pub fn add_existing_generics( + &mut self, + unresolved_generics: &UnresolvedGenerics, + generics: &Generics, + ) { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { @@ -1445,7 +1461,12 @@ impl<'context> Elaborator<'context> { } } - pub fn add_existing_generic(&mut self, unresolved_generic: &UnresolvedGeneric, span: Span, typevar: TypeVariable) { + pub fn add_existing_generic( + &mut self, + unresolved_generic: &UnresolvedGeneric, + span: Span, + typevar: TypeVariable, + ) { let ident = Ident::from(unresolved_generic); let name = ident.0.contents; @@ -1454,12 +1475,13 @@ impl<'context> Elaborator<'context> { if let Some(generic) = self.find_generic(&rc_name) { self.push_err(ResolverError::DuplicateDefinition { - name: name, + name, first_span: generic.span, second_span: span, }); } else { - let is_numeric_generic = matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); + let is_numeric_generic = + matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 2878649172d..235f6f4022f 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -5,7 +5,7 @@ use crate::graph::CrateId; use crate::hir::comptime::{Interpreter, InterpreterError}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; -use crate::{ResolvedGeneric, Type, TypeVariable}; +use crate::{ResolvedGeneric, Type}; use crate::hir::resolution::import::{resolve_import, ImportDirective, PathResolution}; use crate::hir::resolution::{ @@ -33,7 +33,6 @@ use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Span}; use std::collections::{BTreeMap, HashMap}; -use std::rc::Rc; use std::vec; #[derive(Default)] diff --git a/compiler/noirc_frontend/src/hir/resolution/functions.rs b/compiler/noirc_frontend/src/hir/resolution/functions.rs index 5b9118d68c3..fe46796ed24 100644 --- a/compiler/noirc_frontend/src/hir/resolution/functions.rs +++ b/compiler/noirc_frontend/src/hir/resolution/functions.rs @@ -1,14 +1,16 @@ -use std::{collections::BTreeMap, rc::Rc}; +use std::collections::BTreeMap; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::Span; use crate::{ - graph::CrateId, hir::{ + graph::CrateId, + hir::{ def_collector::dc_crate::{CompilationError, UnresolvedFunctions}, def_map::{CrateDefMap, ModuleId}, - }, node_interner::{FuncId, NodeInterner, TraitImplId}, ResolvedGeneric, Type, TypeVariable + }, + node_interner::{FuncId, NodeInterner, TraitImplId}, + ResolvedGeneric, Type, }; use super::{path_resolver::StandardPathResolver, Resolver}; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 87dbc4b6452..99aa33f5d49 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -43,7 +43,9 @@ use crate::node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId, StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; -use crate::{Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind}; +use crate::{ + Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, +}; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span, Spanned}; @@ -758,9 +760,11 @@ impl<'a> Resolver<'a> { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { if generic.is_numeric_generic { - self.errors.push(ResolverError::NumericGenericUsedForType { ident: path.last_segment() }); + self.errors.push(ResolverError::NumericGenericUsedForType { + ident: path.last_segment(), + }); return Some(Type::Error); - } + } return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); }; } @@ -943,12 +947,8 @@ impl<'a> Resolver<'a> { second_span: span, }); } else { - let resolved_generic = ResolvedGeneric { - name, - type_var: typevar.clone(), - is_numeric_generic, - span, - }; + let resolved_generic = + ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; self.generics.push(resolved_generic); } @@ -959,16 +959,25 @@ impl<'a> Resolver<'a> { /// Add the given existing generics to scope. /// This is useful for adding the same generics to many items. E.g. apply impl generics /// to each function in the impl or trait generics to each item in the trait. - pub fn add_existing_generics(&mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics) { + pub fn add_existing_generics( + &mut self, + unresolved_generics: &UnresolvedGenerics, + generics: &Generics, + ) { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { let name = Ident::from(unresolved_generic); - self.add_existing_generic(&unresolved_generic, name.0.span(), typevar.clone()); + self.add_existing_generic(unresolved_generic, name.0.span(), typevar.clone()); } } - pub fn add_existing_generic(&mut self, unresolved_generic: &UnresolvedGeneric, span: Span, typevar: TypeVariable) { + pub fn add_existing_generic( + &mut self, + unresolved_generic: &UnresolvedGeneric, + span: Span, + typevar: TypeVariable, + ) { let ident = Ident::from(unresolved_generic); let name = ident.0.contents; // Check for name collisions of this generic @@ -976,12 +985,13 @@ impl<'a> Resolver<'a> { if let Some(generic) = self.find_generic(&rc_name) { self.errors.push(ResolverError::DuplicateDefinition { - name: name, + name, first_span: generic.span, second_span: span, }); } else { - let is_numeric_generic = matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); + let is_numeric_generic = + matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), @@ -1007,7 +1017,7 @@ impl<'a> Resolver<'a> { self.resolving_ids.insert(struct_id); let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); self.resolving_ids.remove(&struct_id); - + (generics, fields, self.errors) } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index fc586b2660c..3167958b089 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -4,7 +4,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; -use crate::ast::{ItemVisibility, Path, TraitItem, UnresolvedGeneric, Ident}; +use crate::ast::{Ident, ItemVisibility, Path, TraitItem, UnresolvedGeneric}; use crate::{ graph::CrateId, hir::{ @@ -126,7 +126,11 @@ fn resolve_trait_methods( resolver.add_generics(generics); resolver.add_existing_generics(&unresolved_trait.trait_def.generics, trait_generics); - resolver.add_existing_generic(&UnresolvedGeneric::Variable(Ident::from("Self")), name_span, self_typevar); + resolver.add_existing_generic( + &UnresolvedGeneric::Variable(Ident::from("Self")), + name_span, + self_typevar, + ); resolver.set_self_type(Some(self_type.clone())); let func_id = unresolved_trait.method_ids[&name.0.contents]; diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 3d67e92b4cb..380a21716e6 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -99,7 +99,7 @@ pub struct FuncMeta { /// Note that this includes separate entries for each identifier in e.g. tuple patterns. pub parameter_idents: Vec, - /// The HirIdent of each numeric generic identifier + /// The HirIdent of each numeric generic identifier pub generic_idents: Vec, pub return_type: FunctionReturnType, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 478da452879..d8585bf80e8 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1552,4 +1552,4 @@ fn numeric_generic_as_struct_field_type() { errors[0].0, CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); -} \ No newline at end of file +} From 11655d5ead67a1f174bf9ea476e678d33d2a2f38 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 15:36:57 +0000 Subject: [PATCH 09/85] add span method ot UnresolvedGeneric --- compiler/noirc_frontend/src/ast/expression.rs | 10 ++++++++++ compiler/noirc_frontend/src/elaborator/mod.rs | 17 +++++------------ compiler/noirc_frontend/src/elaborator/types.rs | 7 +++++-- .../src/hir/resolution/resolver.rs | 8 ++++++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 3b3a7d36a79..6eee69f5e62 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -46,6 +46,16 @@ pub enum UnresolvedGeneric { Numeric { ident: Ident, typ: UnresolvedType }, } +impl UnresolvedGeneric { + pub(crate) fn span(&self) -> Span { + match self { + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => { + ident.0.span() + } + } + } +} + impl Display for UnresolvedGeneric { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index cce35181d28..8af5ec5fd1e 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -98,7 +98,7 @@ pub struct Elaborator<'context> { /// were declared in. generics: Vec, - /// The idents for each numeric generic on a function + /// The idents for each numeric generic on a function. /// These definitions are generated when creating function metas /// and need to be brought into scope later when elaborating the function body. generic_idents: Vec, @@ -275,9 +275,7 @@ impl<'context> Elaborator<'context> { self.local_module = local_module; self.recover_generics(|this| this.elaborate_function(func, id)); } - // if !self.generic_idents.is_empty() { - // dbg!(self.generic_idents.clone()); - // } + self.self_type = None; self.trait_id = None; } @@ -715,6 +713,7 @@ 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; @@ -744,13 +743,8 @@ impl<'context> Elaborator<'context> { } let ident = Ident::new(name.to_string(), *span); let definition = DefinitionKind::GenericType(type_variable); - // if name.to_string() == "N".to_string() { - // dbg!(ident.clone()); - // dbg!(self.generic_idents.clone()); - // } - let x = - self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - self.generic_idents.push(x); + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + self.errors.push(( CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident, @@ -899,7 +893,6 @@ impl<'context> Elaborator<'context> { self.self_type = None; self.current_trait_impl = None; self.generics.clear(); - // dbg!(self.generic_idents.clone()); self.generic_idents.clear(); } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index fd3c2a82328..63f82103046 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1357,8 +1357,11 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { - let name = Ident::from(unresolved_generic); - self.add_existing_generic(unresolved_generic, name.0.span(), typevar.clone()); + self.add_existing_generic( + unresolved_generic, + unresolved_generic.span(), + typevar.clone(), + ); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index fa99e229f94..36bb9a55288 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -934,8 +934,11 @@ impl<'a> Resolver<'a> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { - let name = Ident::from(unresolved_generic); - self.add_existing_generic(unresolved_generic, name.0.span(), typevar.clone()); + self.add_existing_generic( + unresolved_generic, + unresolved_generic.span(), + typevar.clone(), + ); } } @@ -947,6 +950,7 @@ impl<'a> Resolver<'a> { ) { let ident = Ident::from(unresolved_generic); let name = ident.0.contents; + // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); From 2a098bf0c84a43e11542e47744a8f706cf2e7a53 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 15:38:07 +0000 Subject: [PATCH 10/85] remove dbgs --- compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs | 3 +-- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 1 - compiler/noirc_frontend/src/hir/resolution/structs.rs | 4 ++-- compiler/noirc_frontend/src/tests.rs | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 235f6f4022f..a8caf86eaa8 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -378,7 +378,6 @@ impl DefCollector { def_collector.items.traits, crate_id, )); - dbg!(resolved_module.errors.len()); // Must resolve structs before we resolve globals. resolved_module.errors.extend(resolve_structs( @@ -386,7 +385,7 @@ impl DefCollector { def_collector.items.types, crate_id, )); - dbg!(resolved_module.errors.len()); + // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. resolved_module.errors.extend(collect_trait_impls( diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 36bb9a55288..cbaaf9cae96 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -626,7 +626,6 @@ impl<'a> Resolver<'a> { fn resolve_named_type(&mut self, path: Path, args: Vec) -> Type { if args.is_empty() { if let Some(typ) = self.lookup_generic_or_global_type(&path) { - dbg!(self.errors.len()); return typ; } } diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs index 2127ebbe773..f62e5589d74 100644 --- a/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -41,7 +41,7 @@ pub(crate) fn resolve_structs( struct_def.generics = generics; }); } - dbg!(errors.len()); + // Check whether the struct fields have nested slices // We need to check after all structs are resolved to // make sure every struct's fields is accurately set. @@ -78,6 +78,6 @@ fn resolve_struct_fields( let (generics, fields, errors) = Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file_id) .resolve_struct_fields(unresolved.struct_def, type_id); - dbg!(errors.len()); + (generics, fields, errors) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 6207866a6d3..c24350917f9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -81,7 +81,7 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation &mut context, program.clone().into_sorted(), root_file_id, - true, + false, &[], // No macro processors )); } @@ -1492,7 +1492,6 @@ fn numeric_generic_binary_operation_type_mismatch() { } "#; let errors = get_program_errors(src); - dbg!(errors.clone()); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, From 883dfa48b589dd5693a6df8de9c8d58d17b6c5ae Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 16:45:21 +0000 Subject: [PATCH 11/85] accurate checks against numeric generic used as a type --- compiler/noirc_frontend/src/elaborator/mod.rs | 19 ++++++++- .../noirc_frontend/src/elaborator/types.rs | 27 ++++++++---- .../src/hir/resolution/errors.rs | 8 ++-- .../src/hir/resolution/resolver.rs | 41 +++++++++++++++---- compiler/noirc_frontend/src/tests.rs | 21 ++++++++++ .../numeric_generics/src/main.nr | 12 +++++- 6 files changed, 103 insertions(+), 25 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 8af5ec5fd1e..dd51604247d 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -626,6 +626,8 @@ impl<'context> Elaborator<'context> { has_inline_attribute, type_span, ); + self.check_type_is_not_numeric_generic(&typ, type_span); + let pattern = self.elaborate_pattern_and_store_ids( pattern, typ.clone(), @@ -637,7 +639,12 @@ impl<'context> Elaborator<'context> { parameter_types.push(typ); } - let return_type = Box::new(self.resolve_type(func.return_type())); + let unresolved_return_type = func.return_type(); + let return_type_span = unresolved_return_type.span; + let return_type = Box::new(self.resolve_type(unresolved_return_type)); + if let Some(return_type_span) = return_type_span { + self.check_type_is_not_numeric_generic(return_type.as_ref(), return_type_span); + } let mut typ = Type::Function(parameter_types, return_type, Box::new(Type::Unit)); @@ -1254,7 +1261,15 @@ impl<'context> Elaborator<'context> { this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); - let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, this.resolve_type(typ))); + let fields = vecmap(unresolved.fields, |(ident, typ)| { + let type_span = typ.span; + let typ = this.resolve_type(typ); + if let Some(type_span) = type_span { + this.check_type_is_not_numeric_generic(&typ, type_span); + } + (ident, typ) + }); + this.resolving_ids.remove(&struct_id); (generics, fields) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 63f82103046..363af9adb04 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -249,18 +249,14 @@ impl<'context> Elaborator<'context> { } } + /// This method is used for looking up generics used as named types + /// as well as generics used in type expressions. + /// The `resolving_type` flag makes indicates the compiler should error out + /// when a generic is found. pub fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - if generic.is_numeric_generic { - let expected_typ_err = - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { - ident: path.last_segment(), - }); - self.errors.push((expected_typ_err, self.file)); - return Some(Type::Error); - } return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); } } @@ -1395,4 +1391,19 @@ impl<'context> Elaborator<'context> { self.generics.push(resolved_generic); } } + + pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) { + if let Type::NamedGeneric(_, name) = &typ { + if let Some(generic) = self.find_generic(name.as_ref()) { + if generic.is_numeric_generic { + let expected_typ_err = + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { + name: name.to_string(), + span, + }); + self.errors.push((expected_typ_err, self.file)); + } + } + } + } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 64aab3f67ed..180fced71d4 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -99,7 +99,7 @@ pub enum ResolverError { #[error("Numeric generics should be explicit")] UseExplicitNumericGeneric { ident: Ident }, #[error("expected type, found numeric generic parameter")] - NumericGenericUsedForType { ident: Ident }, + NumericGenericUsedForType { name: String, span: Span }, } impl ResolverError { @@ -410,13 +410,11 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ident.0.span(), ) } - ResolverError::NumericGenericUsedForType { ident } => { - let name = &ident.0.contents; - + ResolverError::NumericGenericUsedForType { name, span } => { Diagnostic::simple_error( format!("expected type, found numeric generic parameter {name}"), String::from("not a type"), - ident.0.span(), + *span, ) } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index cbaaf9cae96..09bff58331f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -742,16 +742,14 @@ impl<'a> Resolver<'a> { } } + /// This method is used for looking up generics used as named types + /// as well as generics used in type expressions. + /// The `resolving_type` flag makes indicates the compiler should error out + /// when a generic is found. fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - if generic.is_numeric_generic { - self.errors.push(ResolverError::NumericGenericUsedForType { - ident: path.last_segment(), - }); - return Some(Type::Error); - } return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); }; } @@ -985,7 +983,14 @@ impl<'a> Resolver<'a> { self.current_item = Some(DependencyId::Struct(struct_id)); self.resolving_ids.insert(struct_id); - let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); + let fields = vecmap(unresolved.fields, |(ident, typ)| { + let type_span = typ.span; + let typ = self.resolve_type(typ); + if let Some(type_span) = type_span { + self.check_type_is_not_numeric_generic(&typ, type_span); + } + (ident, typ) + }); self.resolving_ids.remove(&struct_id); (generics, fields, self.errors) @@ -1055,15 +1060,23 @@ impl<'a> Resolver<'a> { position: PubPosition::Parameter, }); } + let type_span = typ.span.unwrap_or_else(|| pattern.span()); let pattern = self.resolve_pattern(pattern, DefinitionKind::Local(None)); let typ = self.resolve_type_inner(typ); + self.check_type_is_not_numeric_generic(&typ, type_span); + parameters.push((pattern, typ.clone(), visibility)); parameter_types.push(typ); } - let return_type = Box::new(self.resolve_type(func.return_type())); + let unresolved_return_type = func.return_type(); + let return_type_span = unresolved_return_type.span; + let return_type = Box::new(self.resolve_type(unresolved_return_type)); + if let Some(return_type_span) = return_type_span { + self.check_type_is_not_numeric_generic(return_type.as_ref(), return_type_span); + } self.declare_numeric_generics(¶meter_types, &return_type); @@ -2183,6 +2196,18 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); } } + + pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) { + if let Type::NamedGeneric(_, name) = &typ { + if let Some(generic) = self.find_generic(name.as_ref()) { + if generic.is_numeric_generic { + let expected_typ_err = + ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; + self.errors.push(expected_typ_err); + } + } + } + } } /// Gives an error if a user tries to create a mutable reference diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c24350917f9..fd58577c7a5 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1553,3 +1553,24 @@ fn numeric_generic_as_struct_field_type() { CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); } + +#[test] +fn numeric_generic_as_param_type() { + let src = r#" + fn foo(x: I) -> I { + x + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); + // Error for the parameter type + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + )); + // Error for the return type + assert!(matches!( + errors[1].0, + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + )); +} 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 97cdf5ec834..da94aaa7cf4 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -32,7 +32,15 @@ fn main() { assert(new_arr[0] == 5); } -// TODO: getting unused variable warnings +struct Trash { + a: Field, + b: N, +} + +fn id_two(x: I) -> I { + x +} + fn id(x: [Field; I]) -> [Field; I] { x } @@ -101,7 +109,7 @@ trait Deserialize { struct PublicStorage {} impl PublicStorage { - fn read() -> T where T: Deserialize { + fn read() -> T where T: Deserialize { // Used as a type within a function body let mut fields: [Field; N] = [0; N]; // Used a loop bound From fe28f92df74ecba7458bdbca10f38c6b5b6e6556 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 16:46:03 +0000 Subject: [PATCH 12/85] remove bad errros --- .../numeric_generics/src/main.nr | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) 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 da94aaa7cf4..7e4aaa4fd24 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -32,15 +32,6 @@ fn main() { assert(new_arr[0] == 5); } -struct Trash { - a: Field, - b: N, -} - -fn id_two(x: I) -> I { - x -} - fn id(x: [Field; I]) -> [Field; I] { x } @@ -109,7 +100,7 @@ trait Deserialize { struct PublicStorage {} impl PublicStorage { - fn read() -> T where T: Deserialize { + fn read() -> T where T: Deserialize { // Used as a type within a function body let mut fields: [Field; N] = [0; N]; // Used a loop bound From e7dcd2b0f374543f2a14aed9cfb11117443914d9 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 17:10:26 +0000 Subject: [PATCH 13/85] improve err for numeric generic used in let type annotation --- .../noirc_frontend/src/elaborator/statements.rs | 11 ++++++++++- compiler/noirc_frontend/src/elaborator/types.rs | 6 +++++- .../src/hir/resolution/resolver.rs | 17 +++++++++++++++-- compiler/noirc_frontend/src/tests.rs | 14 ++++++++++---- .../numeric_generics/src/main.nr | 2 +- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 8f2f4d3911a..f4331023ff9 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -83,7 +83,16 @@ impl<'context> Elaborator<'context> { let expr_span = let_stmt.expression.span; let (expression, expr_type) = self.elaborate_expression(let_stmt.expression); let definition = DefinitionKind::Local(Some(expression)); - let annotated_type = self.resolve_type(let_stmt.r#type); + + let annotated_type_span = let_stmt.r#type.span; + let mut annotated_type = self.resolve_type(let_stmt.r#type); + if let Some(span) = annotated_type_span { + let is_numeric_generic = self.check_type_is_not_numeric_generic(&annotated_type, span); + // Make sure that we do not get a unification error in case of a misused numeric generic + if is_numeric_generic { + annotated_type = Type::Error; + } + } // First check if the LHS is unspecified // If so, then we give it the same type as the expression diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 363af9adb04..bc8fbfb5621 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1392,10 +1392,13 @@ impl<'context> Elaborator<'context> { } } - pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) { + pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) -> bool { + let mut is_numeric_generic = false; if let Type::NamedGeneric(_, name) = &typ { if let Some(generic) = self.find_generic(name.as_ref()) { if generic.is_numeric_generic { + is_numeric_generic = true; + let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { name: name.to_string(), @@ -1405,5 +1408,6 @@ impl<'context> Elaborator<'context> { } } } + is_numeric_generic } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 09bff58331f..03e17eace63 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1346,9 +1346,18 @@ impl<'a> Resolver<'a> { StatementKind::Let(let_stmt) => { let expression = self.resolve_expression(let_stmt.expression); let definition = DefinitionKind::Local(Some(expression)); + let type_span = let_stmt.r#type.span; + let mut r#type = self.resolve_type(let_stmt.r#type); + if let Some(span) = type_span { + let is_numeric_generic = self.check_type_is_not_numeric_generic(&r#type, span); + // Make sure that we do not get a unification error in case of a misused numeric generic + if is_numeric_generic { + r#type = Type::Error; + } + } HirStatement::Let(HirLetStatement { pattern: self.resolve_pattern(let_stmt.pattern, definition), - r#type: self.resolve_type(let_stmt.r#type), + r#type, expression, attributes: let_stmt.attributes, comptime: let_stmt.comptime, @@ -2197,16 +2206,20 @@ impl<'a> Resolver<'a> { } } - pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) { + pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) -> bool { + let mut is_numeric_generic = false; if let Type::NamedGeneric(_, name) = &typ { if let Some(generic) = self.find_generic(name.as_ref()) { if generic.is_numeric_generic { + is_numeric_generic = true; + let expected_typ_err = ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; self.errors.push(expected_typ_err); } } } + is_numeric_generic } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index fd58577c7a5..80be0b524b4 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1558,19 +1558,25 @@ fn numeric_generic_as_struct_field_type() { fn numeric_generic_as_param_type() { let src = r#" fn foo(x: I) -> I { + let q: I = 5; x } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - // Error for the parameter type + assert_eq!(errors.len(), 3); + // Error from the parameter type assert!(matches!( errors[0].0, CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); - // Error for the return type + // Error from the let statement annotated type + assert!(matches!( + errors[2].0, + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + )); + // Error from the return type assert!(matches!( - errors[1].0, + errors[3].0, CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); } 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 7e4aaa4fd24..3e910414394 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -100,7 +100,7 @@ trait Deserialize { struct PublicStorage {} impl PublicStorage { - fn read() -> T where T: Deserialize { + fn read() -> T where T: Deserialize { // Used as a type within a function body let mut fields: [Field; N] = [0; N]; // Used a loop bound From 6fff4c81aacdf87c233d9530cf5f33378866c6a4 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 17:33:14 +0000 Subject: [PATCH 14/85] fix frontend/tests --- compiler/noirc_frontend/src/tests.rs | 4 +--- .../compile_success_empty/numeric_generics/src/main.nr | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 80be0b524b4..ed114077349 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1452,9 +1452,7 @@ fn struct_numeric_generic() { inner: u64 } - fn double() -> u64 { - N * 2 - } + fn bar() { } "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); 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 7e4aaa4fd24..3e910414394 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -100,7 +100,7 @@ trait Deserialize { struct PublicStorage {} impl PublicStorage { - fn read() -> T where T: Deserialize { + fn read() -> T where T: Deserialize { // Used as a type within a function body let mut fields: [Field; N] = [0; N]; // Used a loop bound From b7feb20c155cda77a73cbb3252a1f79d8f4bde1e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 17:35:01 +0000 Subject: [PATCH 15/85] use type::error for bad numeric generic type --- compiler/noirc_frontend/src/elaborator/mod.rs | 3 ++- compiler/noirc_frontend/src/hir/resolution/resolver.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index e965f71cef9..a6ae2dedbb7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -434,7 +434,7 @@ impl<'context> Elaborator<'context> { let mut is_numeric_generic = false; // Declare numeric generics if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = self.resolve_type(typ.clone()); + let mut typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { let unsupported_typ_err = CompilationError::ResolverError( ResolverError::UnsupportedNumericGenericType { @@ -443,6 +443,7 @@ impl<'context> Elaborator<'context> { }, ); self.errors.push((unsupported_typ_err, self.file)); + typ = Type::Error; } let definition = DefinitionKind::GenericType(typevar.clone()); let hir_ident = diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index e092a9fd2da..c14c532979d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -883,12 +883,13 @@ impl<'a> Resolver<'a> { let mut is_numeric_generic = false; // Declare numeric generics if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = self.resolve_type(typ.clone()); + let mut typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { self.errors.push(ResolverError::UnsupportedNumericGenericType { ident: ident.clone(), typ: typ.clone(), }); + typ = Type::Error; } let definition = DefinitionKind::GenericType(typevar.clone()); let hir_ident = From 8159a541e51fd713e30b469d89616235333b3aad Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 4 Jun 2024 17:54:23 +0000 Subject: [PATCH 16/85] use numeric generic in for_loop_over_array test --- 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 ed114077349..43bbd98d875 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1316,7 +1316,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 {} } From 21540169f9606509fbddea4a3941d47fc52cd9ea Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 5 Jun 2024 11:21:57 +0000 Subject: [PATCH 17/85] make separate numeric_generics_explicit test and ignored non elaborator tests --- compiler/noirc_frontend/src/ast/expression.rs | 7 +- .../src/hir/resolution/resolver.rs | 60 ++++----- .../numeric_generics/src/main.nr | 86 +------------ .../numeric_generics_explicit/Nargo.toml | 7 ++ .../numeric_generics_explicit/src/main.nr | 116 ++++++++++++++++++ tooling/nargo_cli/build.rs | 15 ++- tooling/nargo_fmt/src/items.rs | 2 +- tooling/nargo_fmt/src/utils.rs | 21 +++- tooling/nargo_fmt/tests/expected/fn.nr | 5 + tooling/nargo_fmt/tests/input/fn.nr | 6 + 10 files changed, 194 insertions(+), 131 deletions(-) create mode 100644 test_programs/compile_success_empty/numeric_generics_explicit/Nargo.toml create mode 100644 test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index a0a18cfcf15..cf6f98b0a00 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -54,10 +54,11 @@ pub enum UnresolvedGeneric { } impl UnresolvedGeneric { - pub(crate) fn span(&self) -> Span { + pub fn span(&self) -> Span { match self { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => { - ident.0.span() + UnresolvedGeneric::Variable(ident) => ident.0.span(), + UnresolvedGeneric::Numeric { ident, typ } => { + ident.0.span().merge(typ.span.unwrap_or_default()) } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index c14c532979d..f02a5dad1e1 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -880,28 +880,6 @@ impl<'a> Resolver<'a> { let ident = Ident::from(generic); let span = ident.0.span(); - let mut is_numeric_generic = false; - // Declare numeric generics - if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let mut typ = self.resolve_type(typ.clone()); - if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - self.errors.push(ResolverError::UnsupportedNumericGenericType { - ident: ident.clone(), - typ: typ.clone(), - }); - typ = Type::Error; - } - let definition = DefinitionKind::GenericType(typevar.clone()); - let hir_ident = - self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - // Push the definition type because if one is missing, when the numeric generic is used in an expression - // its definition type will be resolved to a polymorphic integer or field. - // We do not yet fully support bool generics but this will be a foot-gun once we look to add support - // and is can lead to confusing errors. - self.interner.push_definition_type(hir_ident.id, typ); - is_numeric_generic = true; - } - // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -912,8 +890,13 @@ impl<'a> Resolver<'a> { second_span: span, }); } else { - let resolved_generic = - ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; + let resolved_generic = ResolvedGeneric { + name, + type_var: typevar.clone(), + // We only support numeric generics in the elaborator + is_numeric_generic: false, + span, + }; self.generics.push(resolved_generic); } @@ -1200,22 +1183,25 @@ impl<'a> Resolver<'a> { if let Some(ResolvedGeneric { name, span, .. }) = self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) { - 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; - } + // 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; + // } 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); - self.errors.push(ResolverError::UseExplicitNumericGeneric { ident }); + // dbg!(self.current_trait_impl.is_none() || self.trait_id.is_none()); + // if self.current_trait_impl.is_none() || self.trait_id.is_none() { + // self.errors.push(ResolverError::UseExplicitNumericGeneric { ident }); + // } } } } 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 3e910414394..86c7bae1340 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -1,8 +1,3 @@ -// TODO: A global of the same name with the const generic does not work -// at the moment. This will work once we switch over to the "elaborator" -// in the noirc_frontend -// global N = 1000; - fn main() { let a = id([1, 2]); let b = id([1, 2, 3]); @@ -17,22 +12,9 @@ fn main() { assert(itAlsoWorks.data[1] == 2); assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); - - double_numeric_generics_test(); - - let my_type = PublicStorage::read::(); - assert(my_type.a == 1); - assert(my_type.b == 2); - assert(my_type.c == 3); - - let foo = baz::<10>(); - assert(foo.inner == [1; 10]); - - let new_arr = update_arr([0, 1, 2, 3]); - assert(new_arr[0] == 5); } -fn id(x: [Field; I]) -> [Field; I] { +fn id(x: [Field; I]) -> [Field; I] { x } @@ -40,10 +22,10 @@ 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); + assert(index as u64 < S as u64); self.data[index] = elem; self @@ -54,65 +36,3 @@ fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { s.data[0] = s.data[0] + 1; s } - -fn double() -> u64 { - // Used as an expression - N * 2 -} - -fn double_numeric_generics_test() { - // Example usage of a numeric generic arguments. - assert(double::<9>() == 18); - assert(double::<123>() == 246); - assert(double::<7 + 8>() == 30); -} - -// Used as a field of a struct -struct Foo { - inner: [u64; N], -} -fn baz() -> Foo { - Foo { inner: [1; N] } -} -// Used in an impl -impl Foo { - fn bar(self) -> u64 { - N * self.inner[0] - } -} - -struct MyType { - a: Field, - b: Field, - c: Field, -} - -impl Deserialize for MyType { - fn deserialize(fields: [Field; N]) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2] } - } -} - -trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; -} - -struct PublicStorage {} - -impl PublicStorage { - fn read() -> T where T: Deserialize { - // Used as a type within a function body - let mut fields: [Field; N] = [0; N]; - // Used a loop bound - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) - } -} - -// Used in the signature of a function -fn update_arr(mut arr: [Field; N]) -> [Field; N] { - arr[0] = 5; - arr -} diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/Nargo.toml b/test_programs/compile_success_empty/numeric_generics_explicit/Nargo.toml new file mode 100644 index 00000000000..bc3d43498db --- /dev/null +++ b/test_programs/compile_success_empty/numeric_generics_explicit/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "numeric_generics_explicit" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr new file mode 100644 index 00000000000..0b9cc689337 --- /dev/null +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -0,0 +1,116 @@ +// Regression that a global of the same name does not trigger a duplicate definition error +global N = 1000; + +fn main() { + let a = id([1, 2]); + let b = id([1, 2, 3]); + + let itWorks1 = MyStruct { data: a }; + assert(itWorks1.data[1] == 2); + let itWorks2 = MyStruct { data: b }; + assert(itWorks2.data[1] == 2); + + let c = [1, 2]; + let itAlsoWorks = MyStruct { data: c }; + assert(itAlsoWorks.data[1] == 2); + + assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); + + double_numeric_generics_test(); + + let my_type = PublicStorage::read::(); + assert(my_type.a == 1); + assert(my_type.b == 2); + assert(my_type.c == 3); + + let foo = baz::<10>(); + assert(foo.inner == [1; 10]); + + let new_arr = update_arr([0, 1, 2, 3]); + assert(new_arr[0] == 5); +} + +fn id(x: [Field; I]) -> [Field; I] { + x +} + +struct MyStruct { + data: [Field; S], +} + +impl MyStruct { + fn insert(mut self: Self, index: Field, elem: Field) -> Self { + // Regression test for numeric generics on impls + assert(index as u64 < S); + + self.data[index] = elem; + self + } +} + +fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { + s.data[0] = s.data[0] + 1; + s +} + +fn double() -> u64 { + // Used as an expression + N * 2 +} + +fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<123>() == 246); + assert(double::<7 + 8>() == 30); +} + +// Used as a field of a struct +struct Foo { + inner: [u64; N], +} +fn baz() -> Foo { + Foo { inner: [1; N] } +} +// Used in an impl +impl Foo { + fn bar(self) -> u64 { + N * self.inner[0] + } +} + +struct MyType { + a: Field, + b: Field, + c: Field, +} + +impl Deserialize for MyType { + fn deserialize(fields: [Field; N]) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2] } + } +} + +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} + +struct PublicStorage {} + +impl PublicStorage { + fn read() -> T where T: Deserialize { + // Used as a type within a function body + let mut fields: [Field; N] = [0; N]; + // Used a loop bound + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } +} + +// Used in the signature of a function +fn update_arr(mut arr: [Field; N]) -> [Field; N] { + arr[0] = 5; + arr +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 53c3966cb4c..f9cb8918027 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -58,6 +58,13 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ &"is_unconstrained", ]; +/// Some tests are explicit ignored when not working with the elaborator. +/// These should be fixed and removed from this list. +const IGNORED_NON_ELABORATOR_TESTS: [&str; 1] = [ + // Explicit numeric generics are only supported in the elaborator. + &"numeric_generics_explicit", +]; + fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -237,10 +244,16 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); + let non_elaborator_ignored = if IGNORED_NON_ELABORATOR_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; + write!( test_file, r#" -#[test] +#[test]{non_elaborator_ignored} fn compile_success_empty_{test_name}() {{ // We use a mocked backend for this test as we do not rely on the returned circuit size diff --git a/tooling/nargo_fmt/src/items.rs b/tooling/nargo_fmt/src/items.rs index 7f998f45b59..49cb9c16bef 100644 --- a/tooling/nargo_fmt/src/items.rs +++ b/tooling/nargo_fmt/src/items.rs @@ -5,7 +5,7 @@ use crate::{ visitor::{FmtVisitor, Shape}, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct Item { pub(crate) leading: String, pub(crate) value: String, diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 91c3972e18f..dbf1bf8d674 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -172,17 +172,26 @@ impl HasItem for Ident { impl HasItem for UnresolvedGeneric { fn span(&self) -> Span { - match self { - UnresolvedGeneric::Variable(ident) => ident.span(), - UnresolvedGeneric::Numeric { ident, typ } => { - ident.span().merge(typ.span.expect("Should have a span for numeric generic type")) - } - } + self.span() } fn format(self, visitor: &FmtVisitor, _shape: Shape) -> String { visitor.slice(self.span()).into() } + + fn start(&self) -> u32 { + let start_offset = match self { + UnresolvedGeneric::Variable(_) => 0, + // We offset by 4 here to account for the `let` identifier + // and the extra space before the actual generic numeric type + UnresolvedGeneric::Numeric { .. } => 4, + }; + self.span().start() - start_offset + } + + fn end(&self) -> u32 { + self.span().end() + } } pub(crate) fn first_line_width(exprs: &str) -> usize { diff --git a/tooling/nargo_fmt/tests/expected/fn.nr b/tooling/nargo_fmt/tests/expected/fn.nr index 3d231cd3f7f..fa4236fca68 100644 --- a/tooling/nargo_fmt/tests/expected/fn.nr +++ b/tooling/nargo_fmt/tests/expected/fn.nr @@ -61,3 +61,8 @@ fn main( ) {} pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} + +fn id(x: [Field; I]) -> [Field; I] {} + +fn id_two(x: [Field; I]) -> [Field; I] {} + diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index 1c6d201fa39..eae110422a0 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -44,3 +44,9 @@ fn main( ) {} pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} + +fn id(x: [Field; I]) -> [Field; I] {} + +fn id_two(x: [Field; I]) -> [Field; I] {} + From e5bfaeafa5fe3c3d7f93d76f7b546481189fbc54 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 5 Jun 2024 11:30:50 +0000 Subject: [PATCH 18/85] cleanup with no numeric generics in the old resovler --- .../noirc_frontend/src/elaborator/types.rs | 4 -- .../src/hir/resolution/resolver.rs | 64 +------------------ 2 files changed, 3 insertions(+), 65 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d4eb833f8e1..2d7df19853b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -250,10 +250,6 @@ impl<'context> Elaborator<'context> { } } - /// This method is used for looking up generics used as named types - /// as well as generics used in type expressions. - /// The `resolving_type` flag makes indicates the compiler should error out - /// when a generic is found. pub fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 84d13ab278b..70fee2d8f91 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -746,10 +746,6 @@ impl<'a> Resolver<'a> { } } - /// This method is used for looking up generics used as named types - /// as well as generics used in type expressions. - /// The `resolving_type` flag makes indicates the compiler should error out - /// when a generic is found. fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; @@ -971,14 +967,7 @@ impl<'a> Resolver<'a> { self.current_item = Some(DependencyId::Struct(struct_id)); self.resolving_ids.insert(struct_id); - let fields = vecmap(unresolved.fields, |(ident, typ)| { - let type_span = typ.span; - let typ = self.resolve_type(typ); - if let Some(type_span) = type_span { - self.check_type_is_not_numeric_generic(&typ, type_span); - } - (ident, typ) - }); + let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); self.resolving_ids.remove(&struct_id); (generics, fields, self.errors) @@ -1053,18 +1042,11 @@ impl<'a> Resolver<'a> { let pattern = self.resolve_pattern(pattern, DefinitionKind::Local(None)); let typ = self.resolve_type_inner(typ); - self.check_type_is_not_numeric_generic(&typ, type_span); - parameters.push((pattern, typ.clone(), visibility)); parameter_types.push(typ); } - let unresolved_return_type = func.return_type(); - let return_type_span = unresolved_return_type.span; - let return_type = Box::new(self.resolve_type(unresolved_return_type)); - if let Some(return_type_span) = return_type_span { - self.check_type_is_not_numeric_generic(return_type.as_ref(), return_type_span); - } + let return_type = Box::new(self.resolve_type(func.return_type())); self.declare_numeric_generics(¶meter_types, &return_type); @@ -1187,25 +1169,9 @@ impl<'a> Resolver<'a> { if let Some(ResolvedGeneric { name, span, .. }) = self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) { - // 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; - // } 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); - // dbg!(self.current_trait_impl.is_none() || self.trait_id.is_none()); - // if self.current_trait_impl.is_none() || self.trait_id.is_none() { - // self.errors.push(ResolverError::UseExplicitNumericGeneric { ident }); - // } } } } @@ -1338,17 +1304,9 @@ impl<'a> Resolver<'a> { let expression = self.resolve_expression(let_stmt.expression); let definition = DefinitionKind::Local(Some(expression)); let type_span = let_stmt.r#type.span; - let mut r#type = self.resolve_type(let_stmt.r#type); - if let Some(span) = type_span { - let is_numeric_generic = self.check_type_is_not_numeric_generic(&r#type, span); - // Make sure that we do not get a unification error in case of a misused numeric generic - if is_numeric_generic { - r#type = Type::Error; - } - } HirStatement::Let(HirLetStatement { pattern: self.resolve_pattern(let_stmt.pattern, definition), - r#type, + r#type: self.resolve_type(let_stmt.r#type), expression, attributes: let_stmt.attributes, comptime: let_stmt.comptime, @@ -2210,22 +2168,6 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); } } - - pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) -> bool { - let mut is_numeric_generic = false; - if let Type::NamedGeneric(_, name) = &typ { - if let Some(generic) = self.find_generic(name.as_ref()) { - if generic.is_numeric_generic { - is_numeric_generic = true; - - let expected_typ_err = - ResolverError::NumericGenericUsedForType { name: name.to_string(), span }; - self.errors.push(expected_typ_err); - } - } - } - is_numeric_generic - } } /// Gives an error if a user tries to create a mutable reference From e98df525ebd70420ed235983cf8819aebf3cb06e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 5 Jun 2024 11:38:53 +0000 Subject: [PATCH 19/85] cleanup --- .../src/hir/resolution/resolver.rs | 2 - compiler/noirc_frontend/src/tests.rs | 46 ++++++++++++++----- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 70fee2d8f91..aa8c337fe67 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1037,7 +1037,6 @@ impl<'a> Resolver<'a> { position: PubPosition::Parameter, }); } - let type_span = typ.span.unwrap_or_else(|| pattern.span()); let pattern = self.resolve_pattern(pattern, DefinitionKind::Local(None)); let typ = self.resolve_type_inner(typ); @@ -1303,7 +1302,6 @@ impl<'a> Resolver<'a> { StatementKind::Let(let_stmt) => { let expression = self.resolve_expression(let_stmt.expression); let definition = DefinitionKind::Local(Some(expression)); - let type_span = let_stmt.r#type.span; HirStatement::Let(HirLetStatement { pattern: self.resolve_pattern(let_stmt.pattern, definition), r#type: self.resolve_type(let_stmt.r#type), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 43bbd98d875..dd2682a1dd5 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -49,7 +49,10 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F }); } -pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { +pub(crate) fn get_program( + src: &str, + use_elaborator: bool, +) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -81,7 +84,7 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation &mut context, program.clone().into_sorted(), root_file_id, - false, + use_elaborator, &[], // No macro processors )); } @@ -89,7 +92,11 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation } pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { - get_program(src).2 + get_program(src, false).2 +} + +pub(crate) fn get_program_errors_elaborator(src: &str) -> Vec<(CompilationError, FileId)> { + get_program(src, true).2 } #[test] @@ -832,7 +839,7 @@ fn check_trait_as_type_as_two_fn_parameters() { } fn get_program_captures(src: &str) -> Vec> { - let (program, context, _errors) = get_program(src); + let (program, context, _errors) = get_program(src, false); let interner = context.def_interner; let mut all_captures: Vec> = Vec::new(); for func in program.into_sorted().functions { @@ -1194,7 +1201,7 @@ fn resolve_fmt_strings() { } fn check_rewrite(src: &str, expected: &str) { - let (_program, mut context, _errors) = get_program(src); + let (_program, mut context, _errors) = get_program(src, false); 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); @@ -1454,7 +1461,7 @@ fn struct_numeric_generic() { fn bar() { } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1473,7 +1480,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1489,7 +1496,7 @@ fn numeric_generic_binary_operation_type_mismatch() { assert(N == check); } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1508,7 +1515,7 @@ fn bool_generic_as_loop_bound() { assert(fields[0] == 1); } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 2); assert!(matches!( @@ -1532,7 +1539,7 @@ fn numeric_generic_in_function_signature() { let src = r#" fn foo(arr: [Field; N]) -> [Field; N] { arr } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert!(errors.is_empty()); } @@ -1544,7 +1551,7 @@ fn numeric_generic_as_struct_field_type() { b: N, } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1560,7 +1567,7 @@ fn numeric_generic_as_param_type() { x } "#; - let errors = get_program_errors(src); + let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 3); // Error from the parameter type assert!(matches!( @@ -1578,3 +1585,18 @@ fn numeric_generic_as_param_type() { CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); } + +#[test] +fn numeric_generic_used_in_nested_type() { + let src = r#" + struct Foo { + a: Field, + b: Bar, + } + struct Bar { + inner: N + } + "#; + let errors = get_program_errors_elaborator(src); + assert_eq!(errors.len(), 1); +} From 6dc164a0820ab14950c0c08699dfbbeadd97d1ad Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 7 Jun 2024 13:43:50 +0000 Subject: [PATCH 20/85] add TypeKind for Numeric generics and also get them working with implicit numeric generics to get the elaborator working with the stdlib --- compiler/noirc_frontend/src/elaborator/mod.rs | 150 ++++++--- .../noirc_frontend/src/elaborator/patterns.rs | 4 +- .../src/elaborator/statements.rs | 12 +- .../noirc_frontend/src/elaborator/traits.rs | 3 +- .../noirc_frontend/src/elaborator/types.rs | 294 +++++++++++++----- .../src/hir/resolution/resolver.rs | 37 ++- .../src/hir/resolution/structs.rs | 2 +- .../src/hir/resolution/traits.rs | 10 +- .../noirc_frontend/src/hir/type_check/expr.rs | 6 +- .../noirc_frontend/src/hir/type_check/mod.rs | 6 +- compiler/noirc_frontend/src/hir_def/traits.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 230 ++++++++++++-- .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/node_interner.rs | 15 +- compiler/noirc_frontend/src/tests.rs | 123 +++++++- .../numeric_generics_explicit/src/main.nr | 204 +++++++----- 16 files changed, 819 insertions(+), 281 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index a6ae2dedbb7..9f884493762 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,8 +4,7 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, - hir::{ + ast::{FunctionKind, UnresolvedGeneric, UnresolvedTraitConstraint}, hir::{ comptime::{self, Interpreter}, def_collector::{ dc_crate::{ @@ -17,15 +16,11 @@ use crate::{ resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext}, scope::ScopeForest as GenericScopeForest, type_check::{check_trait_impl_method_matches_declaration, TypeCheckError}, - }, - hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, - macros_api::{ + }, hir_def::{expr::HirIdent, function::Parameters, traits::TraitConstraint}, macros_api::{ Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, SecondaryAttribute, StructId, - }, - node_interner::{ + }, node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, TraitId, TypeAliasId, - }, - ResolvedGeneric, Shared, Type, TypeVariable, + }, ResolvedGeneric, Generics, Shared, Type, TypeKind, TypeVariable }; use crate::{ ast::{TraitBound, UnresolvedGenerics}, @@ -39,7 +34,6 @@ use crate::{ hir_def::function::{FuncMeta, HirFunction}, macros_api::{Param, Path, UnresolvedType, UnresolvedTypeData}, node_interner::TraitImplId, - Generics, }; use crate::{ hir::{ @@ -217,13 +211,17 @@ impl<'context> Elaborator<'context> { this.define_type_alias(alias_id, alias); } - this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - - this.collect_traits(items.traits); + // Must collect traits before we define function metas as we need to have resolved generics + // when we define trait impl function metas + this.collect_traits(items.traits.clone()); // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); + this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); + + this.collect_traits(items.traits); + // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { @@ -324,7 +322,9 @@ impl<'context> Elaborator<'context> { } self.generics = func_meta.all_generics.clone(); + // dbg!(self.generics.clone()); + // self.declare_numeric_generics(); self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); self.add_trait_constraints_to_scope(&func_meta); @@ -411,7 +411,7 @@ impl<'context> Elaborator<'context> { generics.push(new_generic.clone()); let name = format!("impl {trait_path}"); - let generic_type = Type::NamedGeneric(new_generic, Rc::new(name)); + let generic_type = Type::NamedGeneric(new_generic, Rc::new(name), TypeKind::Normal); let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics }; if let Some(new_constraint) = self.resolve_trait_bound(&trait_bound, generic_type.clone()) { @@ -471,11 +471,14 @@ impl<'context> Elaborator<'context> { }); } else { let resolved_generic = - ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; - self.generics.push(resolved_generic); + ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), is_numeric_generic, span }; + self.generics.push(resolved_generic.clone()); } - - typevar + let resolved_generic = + ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; + resolved_generic + // Generic { typevar, is_numeric_generic } + // typevar }) } @@ -627,7 +630,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } - _ => self.resolve_type_inner(typ), + _ => self.resolve_type_inner(typ, TypeKind::Normal), }; self.check_if_type_is_valid_for_program_input( @@ -636,7 +639,6 @@ impl<'context> Elaborator<'context> { has_inline_attribute, type_span, ); - self.check_type_is_not_numeric_generic(&typ, type_span); let pattern = self.elaborate_pattern_and_store_ids( pattern, @@ -649,12 +651,7 @@ impl<'context> Elaborator<'context> { parameter_types.push(typ); } - let unresolved_return_type = func.return_type(); - let return_type_span = unresolved_return_type.span; - let return_type = Box::new(self.resolve_type(unresolved_return_type)); - if let Some(return_type_span) = return_type_span { - self.check_type_is_not_numeric_generic(return_type.as_ref(), return_type_span); - } + let return_type = Box::new(self.resolve_type(func.return_type())); let mut typ = Type::Function(parameter_types, return_type, Box::new(Type::Unit)); @@ -735,17 +732,20 @@ impl<'context> Elaborator<'context> { if self.generics.is_empty() { return; } - + // dbg!(self.generics.clone()); + // dbg!(params.clone()); for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { + dbg!(name_to_find.clone()); // 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, .. }) = - self.generics.iter().find(|generic| generic.name.as_ref() == &name_to_find) + if let Some(ResolvedGeneric { name, span, is_numeric_generic, ..}) = + self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) { + dbg!(*is_numeric_generic); let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); if value.is_some() { @@ -758,6 +758,7 @@ impl<'context> Elaborator<'context> { // ``` continue; } + *is_numeric_generic = true; 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); @@ -849,7 +850,6 @@ impl<'context> Elaborator<'context> { self.local_module = trait_impl.module_id; self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; - trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); let self_type = trait_impl.methods.self_type.clone(); let self_type = @@ -1132,6 +1132,33 @@ impl<'context> Elaborator<'context> { // when adding checks after each struct field is resolved. let struct_ids = structs.keys().copied().collect::>(); + // First resolve each struct's generics as fields which contain other + // structs themselves may also contain generics. + // Without resolving all generics first we will not be able to distinguish + // between normal and numeric generics. + self.push_scope(); + for (type_id, typ) in structs.iter() { + self.push_scope(); + + self.file = typ.file_id; + self.local_module = typ.module_id; + + self.recover_generics(|this| { + this.current_item = Some(DependencyId::Struct(*type_id)); + + this.resolving_ids.insert(*type_id); + + let generics = this.add_generics(&typ.struct_def.generics); + this.interner.update_struct(*type_id, |struct_def| { + struct_def.generics = generics; + }); + + this.resolving_ids.remove(type_id); + }); + + self.pop_scope(); + } + // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { @@ -1139,11 +1166,27 @@ impl<'context> Elaborator<'context> { self.file = typ.file_id; self.local_module = typ.module_id; - let (generics, fields) = self.resolve_struct_fields(typ.struct_def, type_id); + let fields = self.resolve_struct_fields(typ.struct_def, type_id); + dbg!(fields.clone()); + // dbg! self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); - struct_def.generics = generics; + + dbg!(struct_def.generics.clone()); + // dbg!(struct_def.get_fields(.cl)) + let mut found_names = Vec::new(); + struct_def.find_numeric_generics_in_fields(&mut found_names); + dbg!(found_names.clone()); + for generic in struct_def.generics.iter_mut() { + for found_generic in found_names.iter() { + if found_generic == generic.name.as_str() { + generic.is_numeric_generic = true; + } + } + } + dbg!(struct_def.generics.clone()); + }); self.pop_scope(); } @@ -1172,25 +1215,20 @@ impl<'context> Elaborator<'context> { &mut self, unresolved: NoirStruct, struct_id: StructId, - ) -> (Generics, Vec<(Ident, Type)>) { + ) -> Vec<(Ident, Type)> { self.recover_generics(|this| { - let generics = this.add_generics(&unresolved.generics); + this.add_generics(&unresolved.generics); this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); let fields = vecmap(unresolved.fields, |(ident, typ)| { - let type_span = typ.span; - let typ = this.resolve_type(typ); - if let Some(type_span) = type_span { - this.check_type_is_not_numeric_generic(&typ, type_span); - } - (ident, typ) + (ident, this.resolve_type(typ)) }); this.resolving_ids.remove(&struct_id); - (generics, fields) + fields }) } @@ -1271,12 +1309,38 @@ impl<'context> Elaborator<'context> { self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; + trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); + // dbg!(trait_impl.trait_id); let unresolved_type = &trait_impl.object_type; + // dbg!(trait_impl.generics.clone()); + // dbg!(trait_impl.trait_generics.clone()); + // dbg!(trait_impl.trait_generics.len()); + // dbg!(trait_impl.generics.len()); self.add_generics(&trait_impl.generics); trait_impl.resolved_generics = self.generics.clone(); - let trait_generics = - vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())); + // We can have generics on the struct that exist on the trait impl but are not necessarily on the trait + // assert!(trait_impl.resolved_generics.len() > trait_impl.trait_generics.len()); + + // Fetch trait constraints here + let trait_generics = if let Some(trait_id) = trait_impl.trait_id { + let the_trait = self.interner.get_trait(trait_id); + let resolved_generics = the_trait.generics.clone(); + assert_eq!(resolved_generics.len(), trait_impl.trait_generics.len()); + trait_impl.trait_generics.iter().enumerate().map(|(i, generic)| { + let generic = generic.clone(); + if resolved_generics[i].is_numeric_generic { + self.resolve_numeric_type(generic) + } else { + self.resolve_type(generic) + } + }).collect() + } else { + // We still resolve as to continue type checking + vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())) + }; + + trait_impl.resolved_trait_generics = trait_generics; let self_type = self.resolve_type(unresolved_type.clone()); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 7abc7c4aa2e..b1aeef3395c 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -456,8 +456,8 @@ impl<'context> Elaborator<'context> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { // Avoid binding t = t - if !arg.occurs(param.id()) { - bindings.insert(param.id(), (param.clone(), arg.clone())); + if !arg.occurs(param.type_var.id()) { + bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.clone())); } } diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index ea6e2b48e3f..6e20e7dad31 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -85,16 +85,8 @@ impl<'context> Elaborator<'context> { let (expression, expr_type) = self.elaborate_expression(let_stmt.expression); let definition = DefinitionKind::Local(Some(expression)); - let annotated_type_span = let_stmt.r#type.span; - let mut annotated_type = self.resolve_type(let_stmt.r#type); - if let Some(span) = annotated_type_span { - let is_numeric_generic = self.check_type_is_not_numeric_generic(&annotated_type, span); - // Make sure that we do not get a unification error in case of a misused numeric generic - if is_numeric_generic { - annotated_type = Type::Error; - } - } - + let annotated_type = self.resolve_type(let_stmt.r#type); + // First check if the LHS is unspecified // If so, then we give it the same type as the expression let r#type = if annotated_type != Type::Error { diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 7b3f4d5351d..c5dbe9ff6e3 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -37,7 +37,8 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = vecmap(&this.generics, |generic| generic.type_var.clone()); + trait_def.generics = this.generics.clone(); + // trait_def.generics = vecmap(&this.generics, |generic| generic.type_var.clone()); }); }); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2d7df19853b..29c329413eb 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -8,8 +8,7 @@ use crate::{ ast::{ BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedTypeExpression, - }, - hir::{ + }, hir::{ comptime::{Interpreter, Value}, def_collector::dc_crate::CompilationError, def_map::ModuleDefId, @@ -18,21 +17,17 @@ use crate::{ resolver::{verify_mutable_reference, SELF_TYPE_NAME}, }, type_check::{Source, TypeCheckError}, - }, - hir_def::{ + }, hir_def::{ expr::{ HirBinaryOp, HirCallExpression, HirIdent, HirMemberAccess, HirMethodReference, HirPrefixExpression, }, function::{FuncMeta, Parameters}, traits::TraitConstraint, - }, - macros_api::{ + }, macros_api::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, - }, - node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, - Generics, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind, + }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, Generics, ResolvedGeneric, Type, TypeBinding, TypeKind, TypeVariable, TypeVariableId, TypeVariableKind }; use super::{lints, Elaborator}; @@ -40,53 +35,117 @@ use super::{lints, Elaborator}; impl<'context> Elaborator<'context> { /// Translates an UnresolvedType to a Type pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - let span = typ.span; - let resolved_type = self.resolve_type_inner(typ); - if resolved_type.is_nested_slice() { - self.push_err(ResolverError::NestedSlices { span: span.unwrap() }); - } - resolved_type + self.resolve_type_inner(typ, TypeKind::Normal) + } + // TODO: do not repeat the span and resolved_type is nested slice check + // probably could place it inside of `resolve_type_inner` + pub(super) fn resolve_numeric_type(&mut self, typ: UnresolvedType) -> Type { + self.resolve_type_inner(typ, TypeKind::Numeric) } /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. - pub fn resolve_type_inner(&mut self, typ: UnresolvedType) -> Type { + pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: TypeKind) -> Type { use crate::ast::UnresolvedTypeData::*; + let span = typ.span; + let resolved_type = match typ.typ { FieldElement => Type::FieldElement, Array(size, elem) => { - let elem = Box::new(self.resolve_type_inner(*elem)); - let size = self.convert_expression_type(size); + let elem = Box::new(self.resolve_type_inner(*elem, kind)); + let mut size = self.convert_expression_type(size); + if let Type::NamedGeneric(type_var, name, _) = size { + size = Type::NamedGeneric(type_var, name, TypeKind::Numeric); + } Type::Array(Box::new(size), elem) } Slice(elem) => { - let elem = Box::new(self.resolve_type_inner(*elem)); + let elem = Box::new(self.resolve_type_inner(*elem, kind)); Type::Slice(elem) } Expression(expr) => self.convert_expression_type(expr), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, String(size) => { - let resolved_size = self.convert_expression_type(size); + 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, TypeKind::Numeric); + } Type::String(Box::new(resolved_size)) } FormatString(size, fields) => { - let resolved_size = self.convert_expression_type(size); - let fields = self.resolve_type_inner(*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, TypeKind::Numeric); + } + let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } Code => Type::Code, Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, - Named(path, args, _) => self.resolve_named_type(path, args), - TraitAsType(path, args) => self.resolve_trait_as_type(path, args), - - Tuple(fields) => Type::Tuple(vecmap(fields, |field| self.resolve_type_inner(field))), + Named(path, args, _) => { + let mut named_typ = self.resolve_named_type(path, args, kind); + // dbg!(named_typ.clone()); + // if let Type::Struct(struct_typ, generics) = &mut named_typ { + // let mut found_names = Vec::new(); + // struct_typ.borrow().find_numeric_generics_in_fields(&mut found_names); + // dbg!(found_names.clone()); + // for generic in generics.iter_mut() { + // if let Type::NamedGeneric(_, name, kind) = generic { + // dbg!(kind.clone()); + // for found_generic in found_names.iter() { + // if found_generic == name.as_str() { + // *kind = TypeKind::Numeric; + // } + // } + // } + // } + // } + // let mut found_names = Vec::new(); + // match &mut named_typ { + // Type::Struct(struct_typ, generics) => { + // struct_typ.borrow().find_numeric_generics_in_fields(&mut found_names); + // for generic in generics.iter_mut() { + // if let Type::NamedGeneric(_, name, kind) = generic { + // dbg!(kind.clone()); + // for found_generic in found_names.iter() { + // if found_generic == name.as_str() { + // *kind = TypeKind::Numeric; + // } + // } + // } + // } + // } + // Type::Alias(alias_typ, generics) => { + // alias_typ.borrow().find_numeric_generics_in_type(&mut found_names); + // for generic in generics.iter_mut() { + // if let Type::NamedGeneric(_, name, kind) = generic { + // dbg!(kind.clone()); + // for found_generic in found_names.iter() { + // if found_generic == name.as_str() { + // *kind = TypeKind::Numeric; + // } + // } + // } + // } + // } + // _ => {}, + // } + + dbg!(named_typ.clone()); + named_typ + } + TraitAsType(path, args) => self.resolve_trait_as_type(path, args, kind), + + Tuple(fields) => { + Type::Tuple(vecmap(fields, |field| self.resolve_type_inner(field, kind))) + } Function(args, ret, env) => { - let args = vecmap(args, |arg| self.resolve_type_inner(arg)); - let ret = Box::new(self.resolve_type_inner(*ret)); + let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); + let ret = Box::new(self.resolve_type_inner(*ret, kind)); // expect() here is valid, because the only places we don't have a span are omitted types // e.g. a function without return type implicitly has a spanless UnresolvedType::Unit return type @@ -94,10 +153,10 @@ impl<'context> Elaborator<'context> { let env_span = env.span.expect("Unexpected missing span for closure environment type"); - let env = Box::new(self.resolve_type_inner(*env)); + let env = Box::new(self.resolve_type_inner(*env, kind)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { Type::Function(args, ret, env) } _ => { @@ -110,9 +169,9 @@ impl<'context> Elaborator<'context> { } } MutableReference(element) => { - Type::MutableReference(Box::new(self.resolve_type_inner(*element))) + Type::MutableReference(Box::new(self.resolve_type_inner(*element, kind))) } - Parenthesized(typ) => self.resolve_type_inner(*typ), + Parenthesized(typ) => self.resolve_type_inner(*typ, kind), }; if let Type::Struct(_, _) = resolved_type { @@ -124,14 +183,65 @@ impl<'context> Elaborator<'context> { ); } } + + if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { + if matches!(resolved_kind, TypeKind::Numeric) && matches!(kind, TypeKind::Normal) { + let expected_typ_err = + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { + name: name.to_string(), + span: span.expect("Type should have span"), + }); + self.errors.push((expected_typ_err, self.file)); + return Type::Error; + } + } + + if resolved_type.is_nested_slice() { + self.push_err(ResolverError::NestedSlices { + span: span.expect("Type should have span"), + }); + } + resolved_type } + /// Resolve the accurate type kind + /// TODO(https://github.com/noir-lang/noir/issues/5156): This implicitly sets the type kind and is necessary for implicit numeric generics. + /// This can be removed with the removal of implicit numeric generics. + pub fn resolve_type_kind(typ: Type) -> Type { + match typ { + Type::FieldElement => typ, + Type::Array(_, _) => todo!(), + Type::Slice(_) => todo!(), + Type::Integer(_, _) => todo!(), + Type::Bool => todo!(), + Type::String(_) => todo!(), + Type::FmtString(_, _) => todo!(), + Type::Unit => todo!(), + Type::Tuple(_) => todo!(), + Type::Struct(_, _) => todo!(), + Type::Alias(_, _) => todo!(), + Type::TypeVariable(_, _) => todo!(), + Type::TraitAsType(_, _, _) => todo!(), + Type::NamedGeneric(_, _, _) => todo!(), + Type::Function(_, _, _) => todo!(), + Type::MutableReference(_) => todo!(), + Type::Forall(_, _) => todo!(), + Type::Constant(_) => todo!(), + Type::Code => todo!(), + Type::Error => todo!(), + } + } + pub fn find_generic(&self, target_name: &str) -> Option<&ResolvedGeneric> { self.generics.iter().find(|generic| generic.name.as_ref() == target_name) } - fn resolve_named_type(&mut self, path: Path, args: Vec) -> Type { + pub fn find_generic_by_id(&self, target_var_id: TypeVariableId) -> Option<&ResolvedGeneric> { + self.generics.iter().find(|generic| generic.type_var.id() == target_var_id) + } + + fn resolve_named_type(&mut self, path: Path, args: Vec, kind: TypeKind) -> Type { if args.is_empty() { if let Some(typ) = self.lookup_generic_or_global_type(&path) { return typ; @@ -154,7 +264,12 @@ impl<'context> Elaborator<'context> { } let span = path.span(); - let mut args = vecmap(args, |arg| self.resolve_type_inner(arg)); + // dbg!(args.clone()); + // let mut args = vecmap(args, |arg| { + // self.resolve_type_inner(arg, kind) + // }); + // dbg!(args.clone()); + // dbg!(self.errors.is_empty()); if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let type_alias = type_alias.borrow(); @@ -162,6 +277,10 @@ impl<'context> Elaborator<'context> { let type_alias_string = type_alias.to_string(); let id = type_alias.id; + let mut args = vecmap(args, |arg| { + self.resolve_type_inner(arg, kind) + }); + self.verify_generics_count(expected_generic_count, &mut args, span, || { type_alias_string }); @@ -194,6 +313,7 @@ impl<'context> Elaborator<'context> { } let expected_generic_count = struct_type.borrow().generics.len(); + if !self.in_contract && self .interner @@ -205,6 +325,19 @@ impl<'context> Elaborator<'context> { span: struct_type.borrow().name.span(), }); } + // dbg!(struct_type.clone()); + // dbg!(struct_type.borrow().generics.clone()); + // let generic_var_ids = struct_type.borrow().generics.iter().map(|generic| generic.id()).collect::>(); + let mut args = vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { + // dbg!(generic.clone()); + // dbg!(arg.clone()); + if generic.is_numeric_generic { + self.resolve_numeric_type(arg) + } else { + self.resolve_type(arg) + } + }); + self.verify_generics_count(expected_generic_count, &mut args, span, || { struct_type.borrow().to_string() }); @@ -220,8 +353,8 @@ impl<'context> Elaborator<'context> { } } - fn resolve_trait_as_type(&mut self, path: Path, args: Vec) -> Type { - let args = vecmap(args, |arg| self.resolve_type_inner(arg)); + fn resolve_trait_as_type(&mut self, path: Path, args: Vec, kind: TypeKind) -> Type { + let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); if let Some(t) = self.lookup_trait_or_error(path) { Type::TraitAsType(t.id, Rc::new(t.name.to_string()), args) @@ -254,7 +387,8 @@ impl<'context> Elaborator<'context> { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); + let kind = if generic.is_numeric_generic { TypeKind::Numeric } else { TypeKind::Normal }; + return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), kind)); } } @@ -320,7 +454,7 @@ impl<'context> Elaborator<'context> { let constraint = TraitConstraint { typ: self.self_type.clone()?, - trait_generics: Type::from_generics(&the_trait.generics), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), trait_id, }; return Some((method, constraint, false)); @@ -351,7 +485,7 @@ impl<'context> Elaborator<'context> { the_trait.self_type_typevar.clone(), TypeVariableKind::Normal, ), - trait_generics: Type::from_generics(&the_trait.generics), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), trait_id, }; return Some((method, constraint, false)); @@ -1087,7 +1221,7 @@ impl<'context> Elaborator<'context> { }); None } - Type::NamedGeneric(_, _) => { + Type::NamedGeneric(_, _, _) => { let func_meta = self.interner.function_meta( &self.current_function.expect("unexpected method outside a function"), ); @@ -1095,6 +1229,7 @@ impl<'context> Elaborator<'context> { for constraint in &func_meta.trait_constraints { if *object_type == constraint.typ { if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) { + // dbg!(the_trait.name.0.contents); for (method_index, method) in the_trait.methods.iter().enumerate() { if method.name.0.contents == method_name { let trait_method = TraitMethodId { @@ -1353,6 +1488,7 @@ impl<'context> Elaborator<'context> { } } + // TODO: this method is not even used can delete pub fn add_existing_generics( &mut self, unresolved_generics: &UnresolvedGenerics, @@ -1360,11 +1496,11 @@ impl<'context> Elaborator<'context> { ) { assert_eq!(unresolved_generics.len(), generics.len()); - for (unresolved_generic, typevar) in unresolved_generics.iter().zip(generics) { + for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { self.add_existing_generic( unresolved_generic, unresolved_generic.span(), - typevar.clone(), + generic.type_var.clone(), ); } } @@ -1400,25 +1536,6 @@ impl<'context> Elaborator<'context> { } } - pub fn check_type_is_not_numeric_generic(&mut self, typ: &Type, span: Span) -> bool { - let mut is_numeric_generic = false; - if let Type::NamedGeneric(_, name) = &typ { - if let Some(generic) = self.find_generic(name.as_ref()) { - if generic.is_numeric_generic { - is_numeric_generic = true; - - let expected_typ_err = - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { - name: name.to_string(), - span, - }); - self.errors.push((expected_typ_err, self.file)); - } - } - } - is_numeric_generic - } - pub fn find_numeric_generics( parameters: &Parameters, return_type: &Type, @@ -1440,7 +1557,7 @@ impl<'context> Elaborator<'context> { | Type::Error | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Code | Type::Forall(_, _) => (), @@ -1451,7 +1568,7 @@ impl<'context> Elaborator<'context> { } Type::Array(length, element_type) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(element_type, found); @@ -1468,6 +1585,7 @@ impl<'context> Elaborator<'context> { } Type::Function(parameters, return_type, _env) => { + dbg!(parameters.clone()); for parameter in parameters { Self::find_numeric_generics_in_type(parameter, found); } @@ -1475,35 +1593,61 @@ 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) { + for (resolved_generic, generic) in struct_type.borrow().generics.iter().zip(generics) { + if resolved_generic.is_numeric_generic { + if let Type::NamedGeneric(type_variable, name, _) = generic { found.insert(name.to_string(), type_variable.clone()); } - } else { - Self::find_numeric_generics_in_type(generic, found); } } + // let fields = struct_type.borrow().get_fields(generics); + // dbg!(fields.clone()); + // let mut found_numeric_generics = Vec::new(); + // struct_type.borrow().find_numeric_generics_in_fields(&mut found_numeric_generics); + // // dbg!(found_numeric_generics.clone()); + // dbg!(generics.clone()); + // for (i, generic) in generics.iter().enumerate() { + // if let Type::NamedGeneric(type_variable, name, _) = generic { + // // dbg!(name.clone()); + // for found_generic in found_numeric_generics.iter() { + // if found_generic == name.as_str() { + // found.insert(name.to_string(), type_variable.clone()); + // } + // } + // } + // } + // dbg!(found_numeric_generics.clone()); } 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) { + for (resolved_generic, generic) in alias.borrow().generics.iter().zip(generics) { + if resolved_generic.is_numeric_generic { + if let Type::NamedGeneric(type_variable, name, _) = generic { found.insert(name.to_string(), type_variable.clone()); } - } else { - Self::find_numeric_generics_in_type(generic, found); } } + + // let mut found_numeric_generics = Vec::new(); + // alias.borrow().find_numeric_generics_in_type(&mut found_numeric_generics); + // for (i, generic) in generics.iter().enumerate() { + // if let Type::NamedGeneric(type_variable, name, _) = generic { + // for found_generic in found_numeric_generics.iter() { + // if found_generic == name.as_str() { + // found.insert(name.to_string(), type_variable.clone()); + // } + // } + // } + // } + } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } } Type::FmtString(length, fields) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(fields, found); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index aa8c337fe67..5b7384e6ef1 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -48,7 +48,7 @@ use crate::node_interner::{ StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; use crate::{ - Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, + GenericTypeVars, Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeKind, TypeVariable, TypeVariableKind }; use fm::FileId; use iter_extended::vecmap; @@ -593,7 +593,7 @@ impl<'a> Resolver<'a> { let env = Box::new(self.resolve_type_inner(*env)); match *env { - Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _) => { + Type::Unit | Type::Tuple(_) | Type::NamedGeneric(_, _, _) => { Type::Function(args, ret, env) } _ => { @@ -750,7 +750,9 @@ impl<'a> Resolver<'a> { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone())); + // We always insert a `TypeKind::Normal` as we do not support explicit numeric generics + // in the resolver + return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), TypeKind::Normal)); }; } @@ -891,7 +893,7 @@ impl<'a> Resolver<'a> { }); } else { let resolved_generic = ResolvedGeneric { - name, + name: name.clone(), type_var: typevar.clone(), // We only support numeric generics in the elaborator is_numeric_generic: false, @@ -900,7 +902,14 @@ impl<'a> Resolver<'a> { self.generics.push(resolved_generic); } - typevar + let resolved_generic = ResolvedGeneric { + name, + type_var: typevar, + // We only support numeric generics in the elaborator + is_numeric_generic: false, + span, + }; + resolved_generic }) } @@ -910,7 +919,7 @@ impl<'a> Resolver<'a> { pub fn add_existing_generics( &mut self, unresolved_generics: &UnresolvedGenerics, - generics: &Generics, + generics: &GenericTypeVars, ) { assert_eq!(unresolved_generics.len(), generics.len()); @@ -1196,7 +1205,7 @@ impl<'a> Resolver<'a> { | Type::Error | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Code | Type::Forall(_, _) => (), @@ -1207,7 +1216,7 @@ impl<'a> Resolver<'a> { } Type::Array(length, element_type) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(element_type, found); @@ -1232,7 +1241,7 @@ impl<'a> Resolver<'a> { Type::Struct(struct_type, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name) = generic { + if let Type::NamedGeneric(type_variable, name, _) = generic { if struct_type.borrow().generic_is_numeric(i) { found.insert(name.to_string(), type_variable.clone()); } @@ -1243,7 +1252,7 @@ impl<'a> Resolver<'a> { } Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { - if let Type::NamedGeneric(type_variable, name) = generic { + if let Type::NamedGeneric(type_variable, name, _) = generic { if alias.borrow().generic_is_numeric(i) { found.insert(name.to_string(), type_variable.clone()); } @@ -1254,12 +1263,12 @@ impl<'a> Resolver<'a> { } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } } Type::FmtString(length, fields) => { - if let Type::NamedGeneric(type_variable, name) = length.as_ref() { + if let Type::NamedGeneric(type_variable, name, _) = length.as_ref() { found.insert(name.to_string(), type_variable.clone()); } Self::find_numeric_generics_in_type(fields, found); @@ -1914,7 +1923,7 @@ impl<'a> Resolver<'a> { let constraint = TraitConstraint { typ: self.self_type.clone()?, - trait_generics: Type::from_generics(&the_trait.generics), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), trait_id, }; return Some((method, constraint, false)); @@ -1942,7 +1951,7 @@ impl<'a> Resolver<'a> { the_trait.self_type_typevar.clone(), TypeVariableKind::Normal, ), - trait_generics: Type::from_generics(&the_trait.generics), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), trait_id, }; return Some((method, constraint, false)); diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs index f62e5589d74..8ddabf29b28 100644 --- a/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -12,7 +12,7 @@ use crate::{ Context, }, node_interner::StructId, - Generics, Type, + Type, Generics, }; use super::{errors::ResolverError, path_resolver::StandardPathResolver, resolver::Resolver}; diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 3167958b089..78d05639f65 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -1,10 +1,12 @@ use std::collections::{BTreeMap, HashSet}; +use std::rc::Rc; use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; use crate::ast::{Ident, ItemVisibility, Path, TraitItem, UnresolvedGeneric}; +use crate::{GenericTypeVars, ResolvedGeneric}; use crate::{ graph::CrateId, hir::{ @@ -58,7 +60,11 @@ pub(crate) fn resolve_traits( context.def_interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = generics; + trait_def.generics = vecmap(generics, |type_var| { + let mut new_resolved = ResolvedGeneric::dummy(); + new_resolved.type_var = type_var; + new_resolved + }); }); // This check needs to be after the trait's methods are set since @@ -93,7 +99,7 @@ fn resolve_trait_methods( trait_id: TraitId, crate_id: CrateId, unresolved_trait: &UnresolvedTrait, - trait_generics: &Generics, + trait_generics: &GenericTypeVars, ) -> (Vec, Vec<(CompilationError, FileId)>) { let interner = &mut context.def_interner; let def_maps = &mut context.def_maps; diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 6504ead178f..90ac2b338cf 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -404,8 +404,8 @@ impl<'interner> TypeChecker<'interner> { for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { // Avoid binding t = t - if !arg.occurs(param.id()) { - bindings.insert(param.id(), (param.clone(), arg.clone())); + if !arg.occurs(param.type_var.id()) { + bindings.insert(param.type_var.id(), (param.type_var.clone(), arg.clone())); } } @@ -1025,7 +1025,7 @@ impl<'interner> TypeChecker<'interner> { }); None } - Type::NamedGeneric(_, _) => { + Type::NamedGeneric(_, _, _) => { let func_meta = self.interner.function_meta( &self.current_function.expect("unexpected method outside a function"), ); diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index f17fb6521c7..5439d4cca2c 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -22,7 +22,7 @@ use crate::{ traits::TraitConstraint, }, node_interner::{ExprId, FuncId, GlobalId, NodeInterner}, - Type, TypeBindings, + Type, TypeBindings, TypeKind, }; pub use self::errors::Source; @@ -263,7 +263,7 @@ pub(crate) fn check_trait_impl_method_matches_declaration( // Substitute each generic on the trait with the corresponding generic on the impl for (generic, arg) in trait_info.generics.iter().zip(&impl_.trait_generics) { - bindings.insert(generic.id(), (generic.clone(), arg.clone())); + bindings.insert(generic.type_var.id(), (generic.type_var.clone(), arg.clone())); } // If this is None, the trait does not have the corresponding function. @@ -284,7 +284,7 @@ pub(crate) fn check_trait_impl_method_matches_declaration( for ((_, trait_fn_generic), (name, impl_fn_generic)) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), TypeKind::Normal); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); } diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index e4959cb3dd9..aca41b7587e 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -151,7 +151,7 @@ impl TraitFunction { pub fn generics(&self) -> &[TypeVariable] { match &self.typ { Type::Function(..) => &[], - Type::Forall(generics, _) => generics, + Type::Forall(generics, _) => &generics, _ => unreachable!("Trait function does not have a function type"), } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 1f0a7b80882..bbbde972bda 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1,8 +1,5 @@ use std::{ - borrow::Cow, - cell::RefCell, - collections::{BTreeSet, HashMap}, - rc::Rc, + borrow::Cow, cell::RefCell, collections::{BTreeSet, HashMap}, path::Display, rc::Rc, }; use crate::{ @@ -10,6 +7,7 @@ use crate::{ hir::type_check::TypeCheckError, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, }; +use chumsky::combinator::OrElse; use iter_extended::vecmap; use noirc_errors::{Location, Span}; use noirc_printable_type::PrintableType; @@ -82,7 +80,7 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc), + NamedGeneric(TypeVariable, Rc, TypeKind), /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -98,7 +96,7 @@ pub enum Type { /// but it makes handling them both easier. The TypeVariableId should /// never be bound over during type checking, but during monomorphization it /// will be and thus needs the full TypeVariable link. - Forall(Generics, Box), + Forall(GenericTypeVars, Box), /// 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. @@ -145,7 +143,7 @@ impl Type { | Type::Unit | Type::TypeVariable(_, _) | Type::TraitAsType(..) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -190,6 +188,26 @@ impl Type { } } +/// Types need to be distinguished depending on the scenario in which they can be used. +/// +/// For example, the type of a struct field or a function parameter is expected to be +/// a normal type. While the type we expect in an array expression (`[; ]`) +/// is expected to be a numeric type. +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub enum TypeKind { + Normal, + Numeric, +} + +impl std::fmt::Display for TypeKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeKind::Normal => write!(f, "normal"), + TypeKind::Numeric => write!(f, "numeric"), + } + } +} + /// A list of TypeVariableIds to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. @@ -216,9 +234,16 @@ pub struct StructType { } /// Corresponds to generic lists such as `` in the source program. -pub type Generics = Vec; +/// Used mainly for resolved types which no longer need the information +/// that is checked during resolution/type checking. +pub type GenericTypeVars = Vec; + +/// Corresponds to generic lists such as `` with additional +/// information gathered during name resolution that is necessary +/// correctly resolving types. +pub type Generics = Vec; -#[derive(Clone, Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, @@ -226,6 +251,19 @@ pub struct ResolvedGeneric { pub span: Span, } +impl ResolvedGeneric { + // TODO: Remove once we move to the elaborator. + // This is only used during collection in the old resolution process. + pub fn dummy() -> Self { + ResolvedGeneric { + name: Rc::default(), + type_var: TypeVariable::unbound(TypeVariableId(0)), + is_numeric_generic: false, + span: Span::default(), + } + } +} + impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { self.id.hash(state); @@ -273,7 +311,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); (typ.substitute(&substitutions), i) @@ -289,7 +327,7 @@ impl StructType { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); vecmap(&self.fields, |(name, typ)| { @@ -302,11 +340,26 @@ impl StructType { self.fields.iter().map(|(name, _)| name.clone()).collect() } + /// Search the fields of a struct for any types with a `TypeKind::Numeric` + pub fn find_numeric_generics_in_fields(&self, found_names: &mut Vec) { + for (_, field) in self.fields.iter() { + field.find_numeric_type_vars(found_names); + } + } + + // pub fn find_num_generics_new(&self, found_names: &mut Vec, index_of_generics: usize) { + // for (_, field) in self.fields.iter() { + // field.find_numeric_type_vars(found_names) + // } + // } + /// 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 + /// a new strategy for finding implicit generics with `find_numeric_type_vars` as we now have type kinds pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - let target_id = self.generics[index_of_generic].0; + let target_id = self.generics[index_of_generic].type_var.id(); self.fields.iter().any(|(_, field)| field.contains_numeric_typevar(target_id)) } @@ -375,17 +428,21 @@ impl TypeAlias { .generics .iter() .zip(generic_args) - .map(|(old, new)| (old.id(), (old.clone(), new.clone()))) + .map(|(old, new)| (old.type_var.id(), (old.type_var.clone(), new.clone()))) .collect(); self.typ.substitute(&substitutions) } + pub fn find_numeric_generics_in_type(&self, found_names: &mut Vec) { + self.typ.find_numeric_type_vars(found_names) + } + /// 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. pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { - let target_id = self.generics[index_of_generic].0; + let target_id = self.generics[index_of_generic].type_var.id(); self.typ.contains_numeric_typevar(target_id) } } @@ -629,7 +686,7 @@ 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 { + if let Type::NamedGeneric(type_variable, _, _) = typ { match &*type_variable.borrow() { TypeBinding::Bound(_) => { unreachable!("Named generics should not be bound until monomorphization") @@ -649,7 +706,7 @@ impl Type { | Type::Error | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Forall(_, _) | Type::Code => false, @@ -693,6 +750,113 @@ impl Type { } } + 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| { + dbg!(typ.clone()); + if let Type::NamedGeneric(_, name, kind) = typ { + if let TypeKind::Numeric = kind { + found_names.push(name.to_string()); + true + } else { + false + } + } else { + false + } + }; + + match self { + Type::FieldElement + | Type::Integer(_, _) + | Type::Bool + | Type::Unit + | Type::Error + | Type::TypeVariable(_, _) + | Type::Constant(_) + | Type::NamedGeneric(_, _, _) + | Type::Forall(_, _) + | Type::Code => {}, + + Type::TraitAsType(_, _, args) => { + // args.iter().map(|generic| generic.find_numeric_type_vars(found_names)); + for arg in args.iter() { + arg.find_numeric_type_vars(found_names); + } + } + Type::Array(length, elem) => { + elem.find_numeric_type_vars(found_names); + named_generic_is_numeric(length, found_names); + } + Type::Slice(elem) => elem.find_numeric_type_vars(found_names), + Type::Tuple(fields) => { + // fields.iter().map(|field| field.find_numeric_type_vars(found_names)); + for field in fields.iter() { + field.find_numeric_type_vars(found_names); + } + } + Type::Function(parameters, return_type, env) => { + // parameters.iter().map(|parameter| parameter.find_numeric_type_vars(found_names)); + for parameter in parameters.iter() { + parameter.find_numeric_type_vars(found_names); + } + return_type.find_numeric_type_vars(found_names); + env.find_numeric_type_vars(found_names); + } + Type::Struct(struct_type, generics) => { + // generics.iter().enumerate().map(|(i, generic)| { + // if named_generic_is_numeric(generic, found_names) { + // struct_type.borrow().generic_is_numeric(i); + // } else { + // generic.find_numeric_type_vars(found_names); + // } + // }); + // for (i, generic) in generics.iter().enumerate() { + // if named_generic_is_numeric(generic, found_names) { + // struct_type.borrow().generic_is_numeric(i); + // } else { + // generic.find_numeric_type_vars(found_names); + // } + // } + // dbg!(found_names.clone()); + // struct_type.borrow().find_numeric_generics_in_fields(found_names); + + for generic in generics.iter() { + if !named_generic_is_numeric(generic, found_names) { + generic.find_numeric_type_vars(found_names); + } + } + } + Type::Alias(alias, generics) => { + alias.borrow().find_numeric_generics_in_type(found_names); + + // generics.iter().enumerate().map(|(i, generic)| { + // if named_generic_is_numeric(generic, found_names) { + // alias.borrow().generic_is_numeric(i, 0, found_names); + // } else { + // generic.find_numeric_type_vars(found_names); + // } + // }); + // for generic in generics.iter() { + // if named_generic_is_numeric(generic, found_names) { + // alias.borrow().find_numeric_generics_in_type(found_names); + // } else { + // generic.find_numeric_type_vars(found_names); + // } + // } + } + Type::MutableReference(element) => element.find_numeric_type_vars(found_names), + Type::String(length) => { + named_generic_is_numeric(length, found_names); + } + Type::FmtString(length, elements) => { + elements.find_numeric_type_vars(found_names); + named_generic_is_numeric(length, found_names); + } + } + } + + /// True if this type can be used as a parameter to `main` or a contract function. /// This is only false for unsized types like slices or slices that do not make sense /// as a program input such as named generics or mutable references. @@ -713,7 +877,7 @@ impl Type { Type::FmtString(_, _) | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) @@ -755,7 +919,7 @@ impl Type { | Type::Unit | Type::Constant(_) | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Error => true, Type::FmtString(_, _) @@ -792,7 +956,7 @@ impl Type { pub fn generic_count(&self) -> usize { match self { Type::Forall(generics, _) => generics.len(), - Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _) => { + Type::TypeVariable(type_variable, _) | Type::NamedGeneric(type_variable, _, _) => { match &*type_variable.borrow() { TypeBinding::Bound(binding) => binding.generic_count(), TypeBinding::Unbound(_) => 0, @@ -821,10 +985,10 @@ impl Type { /// Return the generics and type within this `Type::Forall`. /// Panics if `self` is not `Type::Forall` - pub fn unwrap_forall(&self) -> (Cow, &Type) { + pub fn unwrap_forall(&self) -> (Cow, &Type) { match self { Type::Forall(generics, typ) => (Cow::Borrowed(generics), typ.as_ref()), - other => (Cow::Owned(Generics::new()), other), + other => (Cow::Owned(GenericTypeVars::new()), other), } } } @@ -906,7 +1070,7 @@ impl std::fmt::Display for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name) => match &*binding.borrow() { + Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { TypeBinding::Bound(binding) => binding.fmt(f), TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), TypeBinding::Unbound(_) => write!(f, "{name}"), @@ -1146,7 +1310,7 @@ impl Type { fn get_inner_type_variable(&self) -> Option> { match self { - Type::TypeVariable(var, _) | Type::NamedGeneric(var, _) => Some(var.1.clone()), + Type::TypeVariable(var, _) | Type::NamedGeneric(var, _, _) => Some(var.1.clone()), _ => None, } } @@ -1206,7 +1370,7 @@ impl Type { }) } - (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { + (TypeVariable(var, _), other) | (other, TypeVariable(var, _)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { other.try_bind_to(var, bindings) }) @@ -1257,7 +1421,7 @@ impl Type { } } - (NamedGeneric(binding, _), other) | (other, NamedGeneric(binding, _)) + (NamedGeneric(binding, _, _), other) | (other, NamedGeneric(binding, _, _)) if !binding.borrow().is_unbound() => { if let TypeBinding::Bound(link) = &*binding.borrow() { @@ -1267,10 +1431,14 @@ impl Type { } } - (NamedGeneric(binding_a, name_a), NamedGeneric(binding_b, name_b)) => { + (NamedGeneric(binding_a, name_a, _), NamedGeneric(binding_b, name_b, _)) => { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); + + // if kind_a != kind_b { + // return Err(UnificationError) + // } if name_a == name_b { Ok(()) @@ -1594,7 +1762,7 @@ impl Type { let fields = fields.substitute_helper(type_bindings, substitute_bound_typevars); Type::FmtString(Box::new(size), Box::new(fields)) } - Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { + Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding, _) => { substitute_binding(binding) } // Do not substitute_helper fields, it can lead to infinite recursion @@ -1672,7 +1840,7 @@ impl Type { generic_args.iter().any(|arg| arg.occurs(target_id)) } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), - Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { + Type::NamedGeneric(binding, _, _) | Type::TypeVariable(binding, _) => { match &*binding.borrow() { TypeBinding::Bound(binding) => binding.occurs(target_id), TypeBinding::Unbound(id) => *id == target_id, @@ -1727,7 +1895,7 @@ impl Type { def.borrow().get_type(args).follow_bindings() } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), - TypeVariable(var, _) | NamedGeneric(var, _) => { + TypeVariable(var, _) | NamedGeneric(var, _, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { return typ.follow_bindings(); } @@ -1754,7 +1922,7 @@ impl Type { } } - pub fn from_generics(generics: &Generics) -> Vec { + pub fn from_generics(generics: &GenericTypeVars) -> Vec { vecmap(generics, |var| Type::TypeVariable(var.clone(), TypeVariableKind::Normal)) } } @@ -1951,7 +2119,7 @@ impl std::fmt::Debug for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name) => write!(f, "{}{:?}", name, binding), + Type::NamedGeneric(binding, name, kind) => write!(f, "{} {}{:?}", kind, name, binding), Type::Constant(x) => x.fmt(f), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| format!("{:?}", var)); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2e74eb87e60..af3a0779fd8 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -939,7 +939,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); } - HirType::NamedGeneric(binding, _) => { + HirType::NamedGeneric(binding, _, _) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { return Self::convert_type(binding, location); } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index e28a0a64ad0..afb2f714850 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -29,8 +29,11 @@ use crate::hir_def::{ stmt::HirStatement, }; use crate::token::{Attributes, SecondaryAttribute}; +use crate::GenericTypeVars; +use crate::Generics; +use crate::ResolvedGeneric; use crate::{ - Generics, Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, + Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -556,7 +559,7 @@ impl NodeInterner { // This lets us record how many arguments the type expects so that other types // can refer to it with generic arguments before the generic parameters themselves // are resolved. - TypeVariable::unbound(TypeVariableId(0)) + ResolvedGeneric::dummy() }), self_type_typevar_id, self_type_typevar: TypeVariable::unbound(self_type_typevar_id), @@ -586,7 +589,7 @@ impl NodeInterner { // This lets us record how many arguments the type expects so that other types // can refer to it with generic arguments before the generic parameters themselves // are resolved. - TypeVariable::unbound(TypeVariableId(0)) + ResolvedGeneric::dummy() }); let location = Location::new(typ.struct_def.span, file_id); @@ -604,7 +607,7 @@ impl NodeInterner { typ.type_alias_def.name.clone(), Location::new(typ.type_alias_def.span, typ.file_id), Type::Error, - vecmap(&typ.type_alias_def.generics, |_| TypeVariable::unbound(TypeVariableId(0))), + vecmap(&typ.type_alias_def.generics, |_| ResolvedGeneric::dummy()), ))); type_id @@ -1382,7 +1385,7 @@ impl NodeInterner { trait_id: TraitId, trait_generics: Vec, impl_id: TraitImplId, - impl_generics: Generics, + impl_generics: GenericTypeVars, trait_impl: Shared, ) -> Result<(), (Span, FileId)> { self.trait_implementations.insert(impl_id, trait_impl.clone()); @@ -1792,7 +1795,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Unit => Some(Unit), Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), - Type::NamedGeneric(_, _) => Some(Generic), + Type::NamedGeneric(_, _, _) => Some(Generic), Type::Code => Some(Code), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index dd2682a1dd5..11a55a59116 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1563,7 +1563,7 @@ fn numeric_generic_as_struct_field_type() { fn numeric_generic_as_param_type() { let src = r#" fn foo(x: I) -> I { - let q: I = 5; + let _q: I = 5; x } "#; @@ -1576,18 +1576,18 @@ fn numeric_generic_as_param_type() { )); // Error from the let statement annotated type assert!(matches!( - errors[2].0, + errors[1].0, CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); // Error from the return type assert!(matches!( - errors[3].0, + errors[2].0, CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), )); } #[test] -fn numeric_generic_used_in_nested_type() { +fn numeric_generic_used_in_nested_type_fail() { let src = r#" struct Foo { a: Field, @@ -1600,3 +1600,118 @@ fn numeric_generic_used_in_nested_type() { let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); } + +#[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], + } + "#; + let errors = get_program_errors_elaborator(src); + if !errors.is_empty() { + dbg!(errors.clone()); + } + assert!(errors.is_empty()); +} + +#[test] +fn numeric_generic_used_in_trait() { + 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; + } + "#; + let errors = get_program_errors_elaborator(src); + // 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. + assert!(errors.is_empty()); +} + +#[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 the order is correctly maintained + 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; + } + "#; + let errors = get_program_errors_elaborator(src); + if !errors.is_empty() { + dbg!(errors.clone()); + } + assert!(errors.is_empty()); +} + +#[test] +fn implicit_numeric_generics_elaborator() { + let src = r#" + struct BoundedVec { + storage: [T; MaxLen], + len: u64, + } + + impl BoundedVec { + + // Test that we have an implicit numeric generic for "Len" as well as "MaxLen" + 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_elaborator(src); + + 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); + } + } +} \ No newline at end of file diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index 0b9cc689337..89b4078ced1 100644 --- a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -2,115 +2,151 @@ global N = 1000; fn main() { - let a = id([1, 2]); - let b = id([1, 2, 3]); + // let a = id([1, 2]); + // let b = id([1, 2, 3]); - let itWorks1 = MyStruct { data: a }; - assert(itWorks1.data[1] == 2); - let itWorks2 = MyStruct { data: b }; - assert(itWorks2.data[1] == 2); + // let itWorks1 = MyStruct { data: a }; + // assert(itWorks1.data[1] == 2); + // let itWorks2 = MyStruct { data: b }; + // assert(itWorks2.data[1] == 2); - let c = [1, 2]; - let itAlsoWorks = MyStruct { data: c }; - assert(itAlsoWorks.data[1] == 2); + // let c = [1, 2]; + // let itAlsoWorks = MyStruct { data: c }; + // assert(itAlsoWorks.data[1] == 2); - assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); + // assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); - double_numeric_generics_test(); + // double_numeric_generics_test(); - let my_type = PublicStorage::read::(); - assert(my_type.a == 1); - assert(my_type.b == 2); - assert(my_type.c == 3); + // let my_type = PublicStorage::read::(); + // assert(my_type.a == 1); + // assert(my_type.b == 2); + // assert(my_type.c == 3); - let foo = baz::<10>(); - assert(foo.inner == [1; 10]); + // let foo = baz::<10>(); + // assert(foo.inner == [1; 10]); - let new_arr = update_arr([0, 1, 2, 3]); - assert(new_arr[0] == 5); + // let new_arr = update_arr([0, 1, 2, 3]); + // assert(new_arr[0] == 5); } -fn id(x: [Field; I]) -> [Field; I] { - x -} - -struct MyStruct { - data: [Field; S], -} - -impl MyStruct { - fn insert(mut self: Self, index: Field, elem: Field) -> Self { - // Regression test for numeric generics on impls - assert(index as u64 < S); - - self.data[index] = elem; - self - } -} - -fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { - s.data[0] = s.data[0] + 1; - s -} - -fn double() -> u64 { - // Used as an expression - N * 2 -} - -fn double_numeric_generics_test() { - // Example usage of a numeric generic arguments. - assert(double::<9>() == 18); - assert(double::<123>() == 246); - assert(double::<7 + 8>() == 30); -} +// fn id(x: [Field; I]) -> [Field; I] { +// x +// } + +// struct MyStruct { +// data: [Field; S], +// } + +// impl MyStruct { +// fn insert(mut self: Self, index: Field, elem: Field) -> Self { +// // Regression test for numeric generics on impls +// assert(index as u32 < S); + +// self.data[index] = elem; +// self +// } +// } + +// fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { +// s.data[0] = s.data[0] + 1; +// s +// } + +// fn double() -> u32 { +// // Used as an expression +// N * 2 +// } + +// fn double_numeric_generics_test() { +// // Example usage of a numeric generic arguments. +// assert(double::<9>() == 18); +// assert(double::<123>() == 246); +// assert(double::<7 + 8>() == 30); +// } // Used as a field of a struct -struct Foo { - inner: [u64; N], +struct Foo { + inner: [u32; N], } -fn baz() -> Foo { +fn baz() -> Foo { Foo { inner: [1; N] } } // Used in an impl -impl Foo { - fn bar(self) -> u64 { +impl Foo { + fn bar(self) -> u32 { N * self.inner[0] } } -struct MyType { +// struct MyType { +// a: Field, +// b: Field, +// c: Field, +// d: T, +// } + +// impl Deserialize for MyType { +// fn deserialize(fields: [Field; N]) -> Self { +// MyType { a: fields[0], b: fields[1], c: fields[2], d: dep::std::unsafe::zeroed() } +// } +// } + +// trait Deserialize { +// fn deserialize(fields: [Field; N]) -> Self; +// } + +// struct PublicStorage {} + +// impl PublicStorage { +// fn read() -> T where T: Deserialize { +// // Used as a type within a function body +// let mut fields: [Field; N] = [0; N]; +// // Used a loop bound +// for i in 0..N { +// fields[i] = i as Field + 1; +// } +// T::deserialize(fields) +// } +// } + +// Used in the signature of a function +// fn update_arr(mut arr: [Field; N]) -> [Field; N] { +// arr[0] = 5; +// arr +// } + + +// TODO: only works if InnerNumeric is specified before NestedNumeric +// struct InnerNumeric { +// inner: [u32; N], +// } +struct NestedNumeric { a: Field, - b: Field, - c: Field, + b: InnerNumeric } - -impl Deserialize for MyType { - fn deserialize(fields: [Field; N]) -> Self { - MyType { a: fields[0], b: fields[1], c: fields[2] } - } +struct InnerNumeric { + inner: [u32; N], } -trait Deserialize { - fn deserialize(fields: [Field; N]) -> Self; +struct BoundedVecTest { + storage: [T; MaxLen], + len: u64, } + +impl BoundedVecTest { + pub fn push(&mut self, elem: T) { + assert(self.len < MaxLen, "push out of bounds"); -struct PublicStorage {} - -impl PublicStorage { - fn read() -> T where T: Deserialize { - // Used as a type within a function body - let mut fields: [Field; N] = [0; N]; - // Used a loop bound - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) + self.storage[self.len] = elem; + self.len += 1; } } -// Used in the signature of a function -fn update_arr(mut arr: [Field; N]) -> [Field; N] { - arr[0] = 5; - arr -} +// struct Foo { +// a: Field, +// b: Bar, +// } +// struct Bar { +// inner: N +// } From 265e385cb949ccf378a3e457a6c0cdd318aa386b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 17:21:52 +0000 Subject: [PATCH 21/85] fixed remaining errors in stdlib with elaborator and got warning for implicit generics working in all cases --- .../src/elaborator/expressions.rs | 9 +- compiler/noirc_frontend/src/elaborator/mod.rs | 121 ++++++----- .../noirc_frontend/src/elaborator/patterns.rs | 7 +- .../noirc_frontend/src/elaborator/traits.rs | 20 +- .../noirc_frontend/src/elaborator/types.rs | 186 +++++----------- compiler/noirc_frontend/src/hir_def/types.rs | 52 +---- compiler/noirc_frontend/src/tests.rs | 50 ++++- .../numeric_generics/src/main.nr | 2 +- .../numeric_generics_explicit/src/main.nr | 201 ++++++++---------- 9 files changed, 289 insertions(+), 359 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index abd8781a213..de6d22a7a55 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -51,7 +51,9 @@ impl<'context> Elaborator<'context> { ExpressionKind::If(if_) => self.elaborate_if(*if_), ExpressionKind::Variable(variable, generics) => { let generics = generics.map(|option_inner| { - option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect() + option_inner.into_iter().map(|generic| { + self.resolve_type(generic) + }).collect() }); return self.elaborate_variable(variable, generics); } @@ -82,7 +84,7 @@ impl<'context> Elaborator<'context> { let mut statements = Vec::with_capacity(block.statements.len()); for (i, statement) in block.statements.into_iter().enumerate() { - let (id, stmt_type) = self.elaborate_statement(statement); + let (id, stmt_type) = self.elaborate_statement(statement.clone()); statements.push(id); if let HirStatement::Semi(expr) = self.interner.statement(&id) { @@ -98,6 +100,9 @@ impl<'context> Elaborator<'context> { if i + 1 == statements.len() { block_type = stmt_type; } + if self.errors.len() == 2 { + // dbg!(statement); + } } self.pop_scope(); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 9f884493762..840337c5074 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -211,10 +211,10 @@ impl<'context> Elaborator<'context> { this.define_type_alias(alias_id, alias); } - // Must collect traits before we define function metas as we need to have resolved generics + // Must collect trait generics before we define function metas as we need to have resolved generics // when we define trait impl function metas - this.collect_traits(items.traits.clone()); - + this.collect_trait_generics(&items.traits); + // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); @@ -322,9 +322,7 @@ impl<'context> Elaborator<'context> { } self.generics = func_meta.all_generics.clone(); - // dbg!(self.generics.clone()); - // self.declare_numeric_generics(); self.declare_numeric_generics(&func_meta.parameters, func_meta.return_type()); self.add_trait_constraints_to_scope(&func_meta); @@ -431,34 +429,8 @@ impl<'context> Elaborator<'context> { let ident = Ident::from(generic); let span = ident.0.span(); - let mut is_numeric_generic = false; - // Declare numeric generics - if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let mut typ = self.resolve_type(typ.clone()); - if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = CompilationError::ResolverError( - ResolverError::UnsupportedNumericGenericType { - ident: ident.clone(), - typ: typ.clone(), - }, - ); - self.errors.push((unsupported_typ_err, self.file)); - typ = Type::Error; - } - let definition = DefinitionKind::GenericType(typevar.clone()); - let hir_ident = - self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - - // Push the definition type because if one is missing, when the numeric generic is used in an expression - // its definition type will be resolved to a polymorphic integer or field. - // We do not yet fully support bool generics but this will be a foot-gun once we look to add support - // and can lead to confusing errors. - self.interner.push_definition_type(hir_ident.id, typ); - // Store the ident of the numeric generic to set up the function meta so that - // any numeric generics are accurately brought into scope. - self.generic_idents.push(hir_ident); - is_numeric_generic = true; - } + // Declare numeric generic if it is specified + let is_numeric_generic = self.add_numeric_generic(generic, typevar.clone()); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -474,14 +446,46 @@ impl<'context> Elaborator<'context> { ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), is_numeric_generic, span }; self.generics.push(resolved_generic.clone()); } + let resolved_generic = ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; resolved_generic - // Generic { typevar, is_numeric_generic } - // typevar }) } + /// If a numeric generic has been specified, add it to the current scope. + /// Returns `true` if a numeric generic was added, otherwise return `false` + pub(super) fn add_numeric_generic(&mut self, generic: &UnresolvedGeneric, typevar: TypeVariable) -> bool { + if let UnresolvedGeneric::Numeric { ident, typ } = generic { + let mut typ = self.resolve_type(typ.clone()); + if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { + let unsupported_typ_err = CompilationError::ResolverError( + ResolverError::UnsupportedNumericGenericType { + ident: ident.clone(), + typ: typ.clone(), + }, + ); + self.errors.push((unsupported_typ_err, self.file)); + typ = Type::Error; + } + let definition = DefinitionKind::GenericType(typevar.clone()); + let hir_ident = + self.add_variable_decl_inner(ident.clone(), false, false, false, definition); + + // Push the definition type because if one is missing, when the numeric generic is used in an expression + // its definition type will be resolved to a polymorphic integer or field. + // We do not yet fully support bool generics but this will be a foot-gun once we look to add support + // and can lead to confusing errors. + self.interner.push_definition_type(hir_ident.id, typ); + // Store the ident of the numeric generic to set up the function meta so that + // any numeric generics are accurately brought into scope. + self.generic_idents.push(hir_ident); + true + } else { + false + } + } + fn push_err(&mut self, error: impl Into) { self.errors.push((error.into(), self.file)); } @@ -539,12 +543,24 @@ impl<'context> Elaborator<'context> { } fn resolve_trait_bound(&mut self, bound: &TraitBound, typ: Type) -> Option { - let trait_generics = vecmap(&bound.trait_generics, |typ| self.resolve_type(typ.clone())); + let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; + + let resolved_generics = &the_trait.generics; + assert_eq!(resolved_generics.len(), bound.trait_generics.len()); + let trait_generics = vecmap(resolved_generics.clone().iter().zip(&bound.trait_generics), |(resolved_generic, typ)| { + let typ = typ.clone(); + if resolved_generic.is_numeric_generic { + self.resolve_numeric_type(typ) + } else { + self.resolve_type(typ) + } + }); - let span = bound.trait_path.span(); let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; + let span = bound.trait_path.span(); + let expected_generics = the_trait.generics.len(); let actual_generics = trait_generics.len(); @@ -732,10 +748,8 @@ impl<'context> Elaborator<'context> { if self.generics.is_empty() { return; } - // dbg!(self.generics.clone()); - // dbg!(params.clone()); + for (name_to_find, type_variable) in Self::find_numeric_generics(params, return_type) { - dbg!(name_to_find.clone()); // Declare any generics to let users use numeric generics in scope. // Don't issue a warning if these are unused // @@ -745,7 +759,6 @@ impl<'context> Elaborator<'context> { if let Some(ResolvedGeneric { name, span, is_numeric_generic, ..}) = self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) { - dbg!(*is_numeric_generic); let scope = self.scopes.get_mut_scope(); let value = scope.find(&name_to_find); if value.is_some() { @@ -850,6 +863,7 @@ impl<'context> Elaborator<'context> { self.local_module = trait_impl.module_id; self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; + trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); let self_type = trait_impl.methods.self_type.clone(); let self_type = @@ -1136,7 +1150,6 @@ impl<'context> Elaborator<'context> { // structs themselves may also contain generics. // Without resolving all generics first we will not be able to distinguish // between normal and numeric generics. - self.push_scope(); for (type_id, typ) in structs.iter() { self.push_scope(); @@ -1168,16 +1181,11 @@ impl<'context> Elaborator<'context> { self.local_module = typ.module_id; let fields = self.resolve_struct_fields(typ.struct_def, type_id); - dbg!(fields.clone()); - // dbg! self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); - dbg!(struct_def.generics.clone()); - // dbg!(struct_def.get_fields(.cl)) let mut found_names = Vec::new(); struct_def.find_numeric_generics_in_fields(&mut found_names); - dbg!(found_names.clone()); for generic in struct_def.generics.iter_mut() { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { @@ -1185,7 +1193,6 @@ impl<'context> Elaborator<'context> { } } } - dbg!(struct_def.generics.clone()); }); self.pop_scope(); @@ -1217,11 +1224,14 @@ impl<'context> Elaborator<'context> { struct_id: StructId, ) -> Vec<(Ident, Type)> { self.recover_generics(|this| { - this.add_generics(&unresolved.generics); this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); + + let struct_def = this.interner.get_struct(struct_id); + this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); + let fields = vecmap(unresolved.fields, |(ident, typ)| { (ident, this.resolve_type(typ)) }); @@ -1310,22 +1320,15 @@ impl<'context> Elaborator<'context> { self.local_module = trait_impl.module_id; trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); - // dbg!(trait_impl.trait_id); let unresolved_type = &trait_impl.object_type; - // dbg!(trait_impl.generics.clone()); - // dbg!(trait_impl.trait_generics.clone()); - // dbg!(trait_impl.trait_generics.len()); - // dbg!(trait_impl.generics.len()); + self.add_generics(&trait_impl.generics); trait_impl.resolved_generics = self.generics.clone(); - // We can have generics on the struct that exist on the trait impl but are not necessarily on the trait - // assert!(trait_impl.resolved_generics.len() > trait_impl.trait_generics.len()); - // Fetch trait constraints here let trait_generics = if let Some(trait_id) = trait_impl.trait_id { - let the_trait = self.interner.get_trait(trait_id); - let resolved_generics = the_trait.generics.clone(); + let trait_def = self.interner.get_trait(trait_id); + let resolved_generics = trait_def.generics.clone(); assert_eq!(resolved_generics.len(), trait_impl.trait_generics.len()); trait_impl.trait_generics.iter().enumerate().map(|(i, generic)| { let generic = generic.clone(); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index b1aeef3395c..9d121da0749 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -152,12 +152,14 @@ impl<'context> Elaborator<'context> { let actual_type = Type::Struct(struct_type.clone(), generics); let location = Location::new(span, self.file); - - self.unify(&actual_type, &expected_type, || TypeCheckError::TypeMismatchWithSource { + + self.unify(&actual_type, &expected_type, || { + TypeCheckError::TypeMismatchWithSource { expected: expected_type.clone(), actual: actual_type.clone(), span: location.span, source: Source::Assignment, + } }); let typ = struct_type.clone(); @@ -384,6 +386,7 @@ impl<'context> Elaborator<'context> { ) -> (ExprId, Type) { let span = variable.span; let expr = self.resolve_variable(variable); + let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone())); self.interner.push_expr_location(id, span, self.file); let typ = self.type_check_variable(expr, id, generics); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index c5dbe9ff6e3..06a4adc81e5 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -21,12 +21,26 @@ use crate::{ use super::Elaborator; impl<'context> Elaborator<'context> { - pub fn collect_traits(&mut self, traits: BTreeMap) { + pub fn collect_trait_generics(&mut self, traits: &BTreeMap) { for (trait_id, unresolved_trait) in traits { self.scopes.start_scope(); self.recover_generics(|this| { this.add_generics(&unresolved_trait.trait_def.generics); + this.interner.update_trait(*trait_id, |trait_def| { + trait_def.generics = this.generics.clone(); + }); + }); + } + } + + pub fn collect_traits(&mut self, traits: BTreeMap) { + for (trait_id, unresolved_trait) in traits { + self.scopes.start_scope(); + self.recover_generics(|this| { + let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); + this.add_existing_generics(&unresolved_trait.trait_def.generics, &resolved_generics); + // Resolve order // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) let _ = this.resolve_trait_types(&unresolved_trait); @@ -37,8 +51,6 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = this.generics.clone(); - // trait_def.generics = vecmap(&this.generics, |generic| generic.type_var.clone()); }); }); @@ -100,6 +112,7 @@ impl<'context> Elaborator<'context> { this.self_type = Some(self_type.clone()); let func_id = unresolved_trait.method_ids[&name.0.contents]; + this.resolve_trait_function( name, generics, @@ -157,6 +170,7 @@ impl<'context> Elaborator<'context> { ) { let old_generic_count = self.generics.len(); let old_generic_ident_count = self.generic_idents.len(); + self.scopes.start_function(); self.trait_bounds = where_clause.to_vec(); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 29c329413eb..a391e29454e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -33,12 +33,12 @@ use crate::{ use super::{lints, Elaborator}; impl<'context> Elaborator<'context> { - /// Translates an UnresolvedType to a Type + /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { self.resolve_type_inner(typ, TypeKind::Normal) } - // TODO: do not repeat the span and resolved_type is nested slice check - // probably could place it inside of `resolve_type_inner` + + /// Translate an UnresolvedType to a Type with a `TypeKind::Numeric` pub(super) fn resolve_numeric_type(&mut self, typ: UnresolvedType) -> Type { self.resolve_type_inner(typ, TypeKind::Numeric) } @@ -87,55 +87,8 @@ impl<'context> Elaborator<'context> { Unspecified => Type::Error, Error => Type::Error, Named(path, args, _) => { - let mut named_typ = self.resolve_named_type(path, args, kind); + let named_typ = self.resolve_named_type(path, args, kind); // dbg!(named_typ.clone()); - // if let Type::Struct(struct_typ, generics) = &mut named_typ { - // let mut found_names = Vec::new(); - // struct_typ.borrow().find_numeric_generics_in_fields(&mut found_names); - // dbg!(found_names.clone()); - // for generic in generics.iter_mut() { - // if let Type::NamedGeneric(_, name, kind) = generic { - // dbg!(kind.clone()); - // for found_generic in found_names.iter() { - // if found_generic == name.as_str() { - // *kind = TypeKind::Numeric; - // } - // } - // } - // } - // } - // let mut found_names = Vec::new(); - // match &mut named_typ { - // Type::Struct(struct_typ, generics) => { - // struct_typ.borrow().find_numeric_generics_in_fields(&mut found_names); - // for generic in generics.iter_mut() { - // if let Type::NamedGeneric(_, name, kind) = generic { - // dbg!(kind.clone()); - // for found_generic in found_names.iter() { - // if found_generic == name.as_str() { - // *kind = TypeKind::Numeric; - // } - // } - // } - // } - // } - // Type::Alias(alias_typ, generics) => { - // alias_typ.borrow().find_numeric_generics_in_type(&mut found_names); - // for generic in generics.iter_mut() { - // if let Type::NamedGeneric(_, name, kind) = generic { - // dbg!(kind.clone()); - // for found_generic in found_names.iter() { - // if found_generic == name.as_str() { - // *kind = TypeKind::Numeric; - // } - // } - // } - // } - // } - // _ => {}, - // } - - dbg!(named_typ.clone()); named_typ } TraitAsType(path, args) => self.resolve_trait_as_type(path, args, kind), @@ -184,6 +137,7 @@ impl<'context> Elaborator<'context> { } } + // Check that any types with a type kind match the expected type kind supplied to this function if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { if matches!(resolved_kind, TypeKind::Numeric) && matches!(kind, TypeKind::Normal) { let expected_typ_err = @@ -205,42 +159,10 @@ impl<'context> Elaborator<'context> { resolved_type } - /// Resolve the accurate type kind - /// TODO(https://github.com/noir-lang/noir/issues/5156): This implicitly sets the type kind and is necessary for implicit numeric generics. - /// This can be removed with the removal of implicit numeric generics. - pub fn resolve_type_kind(typ: Type) -> Type { - match typ { - Type::FieldElement => typ, - Type::Array(_, _) => todo!(), - Type::Slice(_) => todo!(), - Type::Integer(_, _) => todo!(), - Type::Bool => todo!(), - Type::String(_) => todo!(), - Type::FmtString(_, _) => todo!(), - Type::Unit => todo!(), - Type::Tuple(_) => todo!(), - Type::Struct(_, _) => todo!(), - Type::Alias(_, _) => todo!(), - Type::TypeVariable(_, _) => todo!(), - Type::TraitAsType(_, _, _) => todo!(), - Type::NamedGeneric(_, _, _) => todo!(), - Type::Function(_, _, _) => todo!(), - Type::MutableReference(_) => todo!(), - Type::Forall(_, _) => todo!(), - Type::Constant(_) => todo!(), - Type::Code => todo!(), - Type::Error => todo!(), - } - } - pub fn find_generic(&self, target_name: &str) -> Option<&ResolvedGeneric> { self.generics.iter().find(|generic| generic.name.as_ref() == target_name) } - pub fn find_generic_by_id(&self, target_var_id: TypeVariableId) -> Option<&ResolvedGeneric> { - self.generics.iter().find(|generic| generic.type_var.id() == target_var_id) - } - fn resolve_named_type(&mut self, path: Path, args: Vec, kind: TypeKind) -> Type { if args.is_empty() { if let Some(typ) = self.lookup_generic_or_global_type(&path) { @@ -264,12 +186,6 @@ impl<'context> Elaborator<'context> { } let span = path.span(); - // dbg!(args.clone()); - // let mut args = vecmap(args, |arg| { - // self.resolve_type_inner(arg, kind) - // }); - // dbg!(args.clone()); - // dbg!(self.errors.is_empty()); if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let type_alias = type_alias.borrow(); @@ -277,8 +193,12 @@ impl<'context> Elaborator<'context> { let type_alias_string = type_alias.to_string(); let id = type_alias.id; - let mut args = vecmap(args, |arg| { - self.resolve_type_inner(arg, kind) + let mut args = vecmap(type_alias.generics.iter().zip(args), |(generic, arg)| { + if generic.is_numeric_generic { + self.resolve_numeric_type(arg) + } else { + self.resolve_type(arg) + } }); self.verify_generics_count(expected_generic_count, &mut args, span, || { @@ -325,12 +245,8 @@ impl<'context> Elaborator<'context> { span: struct_type.borrow().name.span(), }); } - // dbg!(struct_type.clone()); - // dbg!(struct_type.borrow().generics.clone()); - // let generic_var_ids = struct_type.borrow().generics.iter().map(|generic| generic.id()).collect::>(); + let mut args = vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { - // dbg!(generic.clone()); - // dbg!(arg.clone()); if generic.is_numeric_generic { self.resolve_numeric_type(arg) } else { @@ -457,6 +373,7 @@ impl<'context> Elaborator<'context> { trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), trait_id, }; + return Some((method, constraint, false)); } } @@ -522,12 +439,21 @@ impl<'context> Elaborator<'context> { if let Some(method) = the_trait.find_method(path.segments.last().unwrap().0.contents.as_str()) { + // Make sure that we are resolving the correct type kind for the trait generics + let resolved_generics = &the_trait.generics; + assert_eq!(resolved_generics.len(), trait_bound.trait_generics.len()); + let trait_generics = vecmap(resolved_generics.clone().iter().zip(&trait_bound.trait_generics), |(resolved_generic, typ)| { + let typ = typ.clone(); + if resolved_generic.is_numeric_generic { + self.resolve_numeric_type(typ) + } else { + self.resolve_type(typ) + } + }); let constraint = TraitConstraint { trait_id, typ: self.resolve_type(typ.clone()), - trait_generics: vecmap(trait_bound.trait_generics, |typ| { - self.resolve_type(typ) - }), + trait_generics, }; return Some((method, constraint, true)); } @@ -1229,7 +1155,6 @@ impl<'context> Elaborator<'context> { for constraint in &func_meta.trait_constraints { if *object_type == constraint.typ { if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) { - // dbg!(the_trait.name.0.contents); for (method_index, method) in the_trait.methods.iter().enumerate() { if method.name.0.contents == method_name { let trait_method = TraitMethodId { @@ -1418,7 +1343,7 @@ impl<'context> Elaborator<'context> { span: func_span, source: Source::Return(meta.return_type.clone(), expr_span), }; - + if empty_function { error = error.add_context( "implicitly returns `()` as its body has no tail or `return` expression", @@ -1488,7 +1413,6 @@ impl<'context> Elaborator<'context> { } } - // TODO: this method is not even used can delete pub fn add_existing_generics( &mut self, unresolved_generics: &UnresolvedGenerics, @@ -1524,14 +1448,16 @@ impl<'context> Elaborator<'context> { second_span: span, }); } else { - let is_numeric_generic = - matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); + // Declare numeric generic if it is specified + let is_numeric_generic = self.add_numeric_generic(unresolved_generic, typevar.clone()); + let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), is_numeric_generic, span, }; + self.generics.push(resolved_generic); } } @@ -1585,7 +1511,6 @@ impl<'context> Elaborator<'context> { } Type::Function(parameters, return_type, _env) => { - dbg!(parameters.clone()); for parameter in parameters { Self::find_numeric_generics_in_type(parameter, found); } @@ -1593,52 +1518,43 @@ impl<'context> Elaborator<'context> { } Type::Struct(struct_type, generics) => { - for (resolved_generic, generic) in struct_type.borrow().generics.iter().zip(generics) { - if resolved_generic.is_numeric_generic { - if let Type::NamedGeneric(type_variable, name, _) = generic { + for (i, generic) in generics.iter().enumerate() { + if let Type::NamedGeneric(type_variable, name, _) = generic { + if struct_type.borrow().generic_is_numeric(i) { found.insert(name.to_string(), type_variable.clone()); } + } else { + Self::find_numeric_generics_in_type(generic, found); } } - // let fields = struct_type.borrow().get_fields(generics); - // dbg!(fields.clone()); - // let mut found_numeric_generics = Vec::new(); - // struct_type.borrow().find_numeric_generics_in_fields(&mut found_numeric_generics); - // // dbg!(found_numeric_generics.clone()); - // dbg!(generics.clone()); - // for (i, generic) in generics.iter().enumerate() { - // if let Type::NamedGeneric(type_variable, name, _) = generic { - // // dbg!(name.clone()); - // for found_generic in found_numeric_generics.iter() { - // if found_generic == name.as_str() { - // found.insert(name.to_string(), type_variable.clone()); - // } + // // dbg!(struct_type.borrow()); + // for (resolved_generic, generic) in struct_type.borrow().generics.iter().zip(generics) { + // if resolved_generic.is_numeric_generic { + // if let Type::NamedGeneric(type_variable, name, _) = generic { + // // We want to return the name of the generic on the type rather than the inner struct type + // // as the generic on `Type::Struct` can differ from that on `StructType` + // found.insert(name.to_string(), type_variable.clone()); // } // } // } - // dbg!(found_numeric_generics.clone()); } Type::Alias(alias, generics) => { - for (resolved_generic, generic) in alias.borrow().generics.iter().zip(generics) { - if resolved_generic.is_numeric_generic { - if let Type::NamedGeneric(type_variable, name, _) = generic { + for (i, generic) in generics.iter().enumerate() { + if let Type::NamedGeneric(type_variable, name, _) = generic { + if alias.borrow().generic_is_numeric(i) { found.insert(name.to_string(), type_variable.clone()); } + } else { + Self::find_numeric_generics_in_type(generic, found); } } - - // let mut found_numeric_generics = Vec::new(); - // alias.borrow().find_numeric_generics_in_type(&mut found_numeric_generics); - // for (i, generic) in generics.iter().enumerate() { - // if let Type::NamedGeneric(type_variable, name, _) = generic { - // for found_generic in found_numeric_generics.iter() { - // if found_generic == name.as_str() { - // found.insert(name.to_string(), type_variable.clone()); - // } + // for (resolved_generic, generic) in alias.borrow().generics.iter().zip(generics) { + // if resolved_generic.is_numeric_generic { + // if let Type::NamedGeneric(type_variable, name, _) = generic { + // found.insert(name.to_string(), type_variable.clone()); // } // } // } - } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index bbbde972bda..9d20421c1c4 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -347,12 +347,6 @@ impl StructType { } } - // pub fn find_num_generics_new(&self, found_names: &mut Vec, index_of_generics: usize) { - // for (_, field) in self.fields.iter() { - // field.find_numeric_type_vars(found_names) - // } - // } - /// 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. @@ -753,7 +747,6 @@ impl Type { 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| { - dbg!(typ.clone()); if let Type::NamedGeneric(_, name, kind) = typ { if let TypeKind::Numeric = kind { found_names.push(name.to_string()); @@ -779,7 +772,6 @@ impl Type { | Type::Code => {}, Type::TraitAsType(_, _, args) => { - // args.iter().map(|generic| generic.find_numeric_type_vars(found_names)); for arg in args.iter() { arg.find_numeric_type_vars(found_names); } @@ -790,60 +782,30 @@ impl Type { } Type::Slice(elem) => elem.find_numeric_type_vars(found_names), Type::Tuple(fields) => { - // fields.iter().map(|field| field.find_numeric_type_vars(found_names)); for field in fields.iter() { field.find_numeric_type_vars(found_names); } } Type::Function(parameters, return_type, env) => { - // parameters.iter().map(|parameter| parameter.find_numeric_type_vars(found_names)); for parameter in parameters.iter() { parameter.find_numeric_type_vars(found_names); } return_type.find_numeric_type_vars(found_names); env.find_numeric_type_vars(found_names); } - Type::Struct(struct_type, generics) => { - // generics.iter().enumerate().map(|(i, generic)| { - // if named_generic_is_numeric(generic, found_names) { - // struct_type.borrow().generic_is_numeric(i); - // } else { - // generic.find_numeric_type_vars(found_names); - // } - // }); - // for (i, generic) in generics.iter().enumerate() { - // if named_generic_is_numeric(generic, found_names) { - // struct_type.borrow().generic_is_numeric(i); - // } else { - // generic.find_numeric_type_vars(found_names); - // } - // } - // dbg!(found_names.clone()); - // struct_type.borrow().find_numeric_generics_in_fields(found_names); - + Type::Struct(_, generics) => { for generic in generics.iter() { if !named_generic_is_numeric(generic, found_names) { generic.find_numeric_type_vars(found_names); } } } - Type::Alias(alias, generics) => { - alias.borrow().find_numeric_generics_in_type(found_names); - - // generics.iter().enumerate().map(|(i, generic)| { - // if named_generic_is_numeric(generic, found_names) { - // alias.borrow().generic_is_numeric(i, 0, found_names); - // } else { - // generic.find_numeric_type_vars(found_names); - // } - // }); - // for generic in generics.iter() { - // if named_generic_is_numeric(generic, found_names) { - // alias.borrow().find_numeric_generics_in_type(found_names); - // } else { - // generic.find_numeric_type_vars(found_names); - // } - // } + Type::Alias(_, generics) => { + for generic in generics.iter() { + if !named_generic_is_numeric(generic, found_names) { + generic.find_numeric_type_vars(found_names); + } + } } Type::MutableReference(element) => element.find_numeric_type_vars(found_names), Type::String(length) => { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 11a55a59116..cdd0834900e 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1323,7 +1323,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 {} } @@ -1674,9 +1674,26 @@ fn numeric_generic_in_trait_impl_with_extra_impl_generics() { } "#; let errors = get_program_errors_elaborator(src); - if !errors.is_empty() { - dbg!(errors.clone()); + assert!(errors.is_empty()); +} + +#[test] +fn numeric_generic_used_in_where_clause() { + let src = r#" + + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + 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_elaborator(src); assert!(errors.is_empty()); } @@ -1703,7 +1720,6 @@ fn implicit_numeric_generics_elaborator() { self.len += 1; } } - "#; let errors = get_program_errors_elaborator(src); @@ -1714,4 +1730,30 @@ fn implicit_numeric_generics_elaborator() { panic!("Expected ResolverError::UseExplicitNumericGeneric but got {:?}", error); } } +} + +#[test] +fn nested_generic_elaborator() { + let src = r#" + trait Default { + fn default() -> Self; + } + + struct Option { + _is_some: bool, + _value: T, + } + + impl Option { + pub fn flatten(option: Option>) -> Option where T: Default { + if option._is_some { + option._value + } else { + Self { _is_some: false, _value: T::default() } + } + } + } + "#; + let errors = get_program_errors_elaborator(src); + assert!(errors.is_empty()); } \ No newline at end of file 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 86c7bae1340..340c18c2a1d 100644 --- a/test_programs/compile_success_empty/numeric_generics/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics/src/main.nr @@ -18,7 +18,7 @@ fn id(x: [Field; I]) -> [Field; I] { x } -struct MyStruct { +struct MyStruct { data: [Field; S], } diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index 89b4078ced1..977069eff44 100644 --- a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -2,68 +2,68 @@ global N = 1000; fn main() { - // let a = id([1, 2]); - // let b = id([1, 2, 3]); + let a = id([1, 2]); + let b = id([1, 2, 3]); - // let itWorks1 = MyStruct { data: a }; - // assert(itWorks1.data[1] == 2); - // let itWorks2 = MyStruct { data: b }; - // assert(itWorks2.data[1] == 2); + let itWorks1 = MyStruct { data: a }; + assert(itWorks1.data[1] == 2); + let itWorks2 = MyStruct { data: b }; + assert(itWorks2.data[1] == 2); - // let c = [1, 2]; - // let itAlsoWorks = MyStruct { data: c }; - // assert(itAlsoWorks.data[1] == 2); + let c = [1, 2]; + let itAlsoWorks = MyStruct { data: c }; + assert(itAlsoWorks.data[1] == 2); - // assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); + assert(foo(itWorks2).data[0] == itWorks2.data[0] + 1); - // double_numeric_generics_test(); + double_numeric_generics_test(); - // let my_type = PublicStorage::read::(); - // assert(my_type.a == 1); - // assert(my_type.b == 2); - // assert(my_type.c == 3); + let my_type = PublicStorage::read::(); + assert(my_type.a == 1); + assert(my_type.b == 2); + assert(my_type.c == 3); - // let foo = baz::<10>(); - // assert(foo.inner == [1; 10]); + let foo = baz::<10>(); + assert(foo.inner == [1; 10]); - // let new_arr = update_arr([0, 1, 2, 3]); - // assert(new_arr[0] == 5); + let new_arr = update_arr([0, 1, 2, 3]); + assert(new_arr[0] == 5); } -// fn id(x: [Field; I]) -> [Field; I] { -// x -// } +fn id(x: [Field; I]) -> [Field; I] { + x +} -// struct MyStruct { -// data: [Field; S], -// } +struct MyStruct { + data: [Field; S], +} -// impl MyStruct { -// fn insert(mut self: Self, index: Field, elem: Field) -> Self { -// // Regression test for numeric generics on impls -// assert(index as u32 < S); +impl MyStruct { + fn insert(mut self: Self, index: Field, elem: Field) -> Self { + // Regression test for numeric generics on impls + assert(index as u32 < S); -// self.data[index] = elem; -// self -// } -// } + self.data[index] = elem; + self + } +} -// fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { -// s.data[0] = s.data[0] + 1; -// s -// } +fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { + s.data[0] = s.data[0] + 1; + s +} -// fn double() -> u32 { -// // Used as an expression -// N * 2 -// } +fn double() -> u32 { + // Used as an expression + N * 2 +} -// fn double_numeric_generics_test() { -// // Example usage of a numeric generic arguments. -// assert(double::<9>() == 18); -// assert(double::<123>() == 246); -// assert(double::<7 + 8>() == 30); -// } +fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + assert(double::<123>() == 246); + assert(double::<7 + 8>() == 30); +} // Used as a field of a struct struct Foo { @@ -79,48 +79,55 @@ impl Foo { } } -// struct MyType { -// a: Field, -// b: Field, -// c: Field, -// d: T, -// } - -// impl Deserialize for MyType { -// fn deserialize(fields: [Field; N]) -> Self { -// MyType { a: fields[0], b: fields[1], c: fields[2], d: dep::std::unsafe::zeroed() } -// } -// } - -// trait Deserialize { -// fn deserialize(fields: [Field; N]) -> Self; -// } - -// struct PublicStorage {} - -// impl PublicStorage { -// fn read() -> T where T: Deserialize { -// // Used as a type within a function body -// let mut fields: [Field; N] = [0; N]; -// // Used a loop bound -// for i in 0..N { -// fields[i] = i as Field + 1; -// } -// T::deserialize(fields) -// } -// } +struct MyType { + a: Field, + b: Field, + c: Field, +} -// Used in the signature of a function -// fn update_arr(mut arr: [Field; N]) -> [Field; N] { -// arr[0] = 5; -// arr -// } +impl Deserialize for MyType { + fn deserialize(fields: [Field; N]) -> Self { + MyType { a: fields[0], b: fields[1], c: fields[2] } + } +} + +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} +fn read() -> T where T: Deserialize { + // Used as a type within a function body + let mut fields: [Field; N] = [0; N]; + // Used a loop bound + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) +} + +struct PublicStorage {} + +impl PublicStorage { + fn read() -> T where T: Deserialize { + // Used as a type within a function body + let mut fields: [Field; N] = [0; N]; + // Used a loop bound + for i in 0..N { + fields[i] = i as Field + 1; + } + T::deserialize(fields) + } +} + +// Used in the signature of a function +fn update_arr(mut arr: [Field; N]) -> [Field; N] { + arr[0] = 5; + arr +} -// TODO: only works if InnerNumeric is specified before NestedNumeric -// struct InnerNumeric { -// inner: [u32; N], -// } +// Check that we can thread numeric generics into nested structs +// and also that we can handle nested structs with numeric generics +// which are declared after the parent struct struct NestedNumeric { a: Field, b: InnerNumeric @@ -128,25 +135,3 @@ struct NestedNumeric { struct InnerNumeric { inner: [u32; N], } - -struct BoundedVecTest { - storage: [T; MaxLen], - len: u64, -} - -impl BoundedVecTest { - pub fn push(&mut self, elem: T) { - assert(self.len < MaxLen, "push out of bounds"); - - self.storage[self.len] = elem; - self.len += 1; - } -} - -// struct Foo { -// a: Field, -// b: Bar, -// } -// struct Bar { -// inner: N -// } From 03d8c4d5a0fb9298facad1c6cb372181b7ad52cf Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 17:51:11 +0000 Subject: [PATCH 22/85] clippy and fmt --- .../src/elaborator/expressions.rs | 4 +- compiler/noirc_frontend/src/elaborator/mod.rs | 86 +++++++++++-------- .../noirc_frontend/src/elaborator/patterns.rs | 6 +- .../noirc_frontend/src/elaborator/traits.rs | 7 +- .../noirc_frontend/src/elaborator/types.rs | 76 +++++++++------- .../src/hir/resolution/resolver.rs | 31 ++++--- .../src/hir/resolution/structs.rs | 2 +- .../src/hir/resolution/traits.rs | 5 +- compiler/noirc_frontend/src/hir_def/traits.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 36 +++----- compiler/noirc_frontend/src/node_interner.rs | 4 +- .../src/parser/parser/function.rs | 9 +- compiler/noirc_frontend/src/tests.rs | 6 +- 13 files changed, 142 insertions(+), 132 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index ced39487a61..66d693238fd 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -51,9 +51,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::If(if_) => self.elaborate_if(*if_), ExpressionKind::Variable(variable, generics) => { let generics = generics.map(|option_inner| { - option_inner.into_iter().map(|generic| { - self.resolve_type(generic) - }).collect() + option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect() }); return self.elaborate_variable(variable, generics); } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index e4bbe430ef9..8c48766abc7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -4,7 +4,8 @@ use std::{ }; use crate::{ - ast::{FunctionKind, UnresolvedTraitConstraint}, hir::{ + ast::{FunctionKind, UnresolvedTraitConstraint}, + hir::{ comptime::{self, Interpreter}, def_collector::{ dc_crate::{ @@ -21,7 +22,7 @@ use crate::{ expr::HirIdent, function::{FunctionBody, Parameters}, traits::TraitConstraint, - types::{ResolvedGeneric, TypeKind, Generics}, + types::{Generics, ResolvedGeneric, TypeKind}, }, macros_api::{ BlockExpression, Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, @@ -231,10 +232,10 @@ impl<'context> Elaborator<'context> { this.define_type_alias(alias_id, alias); } - // Must collect trait generics before we define function metas as we need to have resolved generics + // Must collect trait generics before we define function metas as we need to have resolved generics // when we define trait impl function metas this.collect_trait_generics(&items.traits); - + // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); @@ -472,6 +473,13 @@ impl<'context> Elaborator<'context> { // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); + let resolved_generic = ResolvedGeneric { + name: name.clone(), + type_var: typevar.clone(), + is_numeric_generic, + span, + }; + if let Some(generic) = self.find_generic(&name) { self.push_err(ResolverError::DuplicateDefinition { name: ident.0.contents.clone(), @@ -479,29 +487,28 @@ impl<'context> Elaborator<'context> { second_span: span, }); } else { - let resolved_generic = - ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), is_numeric_generic, span }; self.generics.push(resolved_generic.clone()); } - let resolved_generic = - ResolvedGeneric { name, type_var: typevar.clone(), is_numeric_generic, span }; resolved_generic }) } /// If a numeric generic has been specified, add it to the current scope. /// Returns `true` if a numeric generic was added, otherwise return `false` - pub(super) fn add_numeric_generic(&mut self, generic: &UnresolvedGeneric, typevar: TypeVariable) -> bool { + pub(super) fn add_numeric_generic( + &mut self, + generic: &UnresolvedGeneric, + typevar: TypeVariable, + ) -> bool { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let mut typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = CompilationError::ResolverError( - ResolverError::UnsupportedNumericGenericType { + let unsupported_typ_err = + CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { ident: ident.clone(), typ: typ.clone(), - }, - ); + }); self.errors.push((unsupported_typ_err, self.file)); typ = Type::Error; } @@ -575,14 +582,17 @@ impl<'context> Elaborator<'context> { let resolved_generics = &the_trait.generics; assert_eq!(resolved_generics.len(), bound.trait_generics.len()); - let trait_generics = vecmap(resolved_generics.clone().iter().zip(&bound.trait_generics), |(resolved_generic, typ)| { - let typ = typ.clone(); - if resolved_generic.is_numeric_generic { - self.resolve_numeric_type(typ) - } else { - self.resolve_type(typ) - } - }); + let trait_generics = vecmap( + resolved_generics.clone().iter().zip(&bound.trait_generics), + |(resolved_generic, typ)| { + let typ = typ.clone(); + if resolved_generic.is_numeric_generic { + self.resolve_numeric_type(typ) + } else { + self.resolve_type(typ) + } + }, + ); let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; @@ -787,7 +797,7 @@ impl<'context> Elaborator<'context> { // 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, is_numeric_generic, ..}) = + if let Some(ResolvedGeneric { name, span, is_numeric_generic, .. }) = self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) { let scope = self.scopes.get_mut_scope(); @@ -1224,7 +1234,6 @@ impl<'context> Elaborator<'context> { } } } - }); self.pop_scope(); } @@ -1255,17 +1264,14 @@ impl<'context> Elaborator<'context> { struct_id: StructId, ) -> Vec<(Ident, Type)> { self.recover_generics(|this| { - this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); let struct_def = this.interner.get_struct(struct_id); this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); - - let fields = vecmap(unresolved.fields, |(ident, typ)| { - (ident, this.resolve_type(typ)) - }); + + let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, this.resolve_type(typ))); this.resolving_ids.remove(&struct_id); @@ -1372,25 +1378,29 @@ impl<'context> Elaborator<'context> { self.add_generics(&trait_impl.generics); trait_impl.resolved_generics = self.generics.clone(); - // Fetch trait constraints here + // Fetch trait constraints here let trait_generics = if let Some(trait_id) = trait_impl.trait_id { let trait_def = self.interner.get_trait(trait_id); let resolved_generics = trait_def.generics.clone(); assert_eq!(resolved_generics.len(), trait_impl.trait_generics.len()); - trait_impl.trait_generics.iter().enumerate().map(|(i, generic)| { - let generic = generic.clone(); - if resolved_generics[i].is_numeric_generic { - self.resolve_numeric_type(generic) - } else { - self.resolve_type(generic) - } - }).collect() + trait_impl + .trait_generics + .iter() + .enumerate() + .map(|(i, generic)| { + let generic = generic.clone(); + if resolved_generics[i].is_numeric_generic { + self.resolve_numeric_type(generic) + } else { + self.resolve_type(generic) + } + }) + .collect() } else { // We still resolve as to continue type checking vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())) }; - trait_impl.resolved_trait_generics = trait_generics; let self_type = self.resolve_type(unresolved_type.clone()); diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index 7c52955da76..31a84b3370a 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -178,14 +178,12 @@ impl<'context> Elaborator<'context> { let actual_type = Type::Struct(struct_type.clone(), generics); let location = Location::new(span, self.file); - - self.unify(&actual_type, &expected_type, || { - TypeCheckError::TypeMismatchWithSource { + + self.unify(&actual_type, &expected_type, || TypeCheckError::TypeMismatchWithSource { expected: expected_type.clone(), actual: actual_type.clone(), span: location.span, source: Source::Assignment, - } }); let typ = struct_type.clone(); diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 96246f9ed39..9001fc488ac 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -39,7 +39,10 @@ impl<'context> Elaborator<'context> { self.scopes.start_scope(); self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); - this.add_existing_generics(&unresolved_trait.trait_def.generics, &resolved_generics); + this.add_existing_generics( + &unresolved_trait.trait_def.generics, + &resolved_generics, + ); // Resolve order // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) @@ -112,7 +115,7 @@ impl<'context> Elaborator<'context> { this.self_type = Some(self_type.clone()); let func_id = unresolved_trait.method_ids[&name.0.contents]; - + this.resolve_trait_function( name, generics, diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 2cc05941b2a..97598d719df 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -7,8 +7,9 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedTypeExpression, - }, hir::{ + UnresolvedTypeExpression, + }, + hir::{ comptime::{Interpreter, Value}, def_collector::dc_crate::CompilationError, def_map::ModuleDefId, @@ -17,17 +18,21 @@ use crate::{ resolver::{verify_mutable_reference, SELF_TYPE_NAME}, }, type_check::{Source, TypeCheckError}, - }, hir_def::{ + }, + hir_def::{ expr::{ HirBinaryOp, HirCallExpression, HirMemberAccess, HirMethodReference, HirPrefixExpression, }, function::{FuncMeta, Parameters}, traits::TraitConstraint, - }, macros_api::{ + }, + macros_api::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, - }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, Generics, ResolvedGeneric, Type, TypeBinding, TypeKind, TypeVariable, TypeVariableId, TypeVariableKind + }, + node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, + Generics, ResolvedGeneric, Type, TypeBinding, TypeKind, TypeVariable, TypeVariableKind, }; use super::{lints, Elaborator}; @@ -86,11 +91,7 @@ impl<'context> Elaborator<'context> { Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, - Named(path, args, _) => { - let named_typ = self.resolve_named_type(path, args, kind); - // dbg!(named_typ.clone()); - named_typ - } + Named(path, args, _) => self.resolve_named_type(path, args), TraitAsType(path, args) => self.resolve_trait_as_type(path, args, kind), Tuple(fields) => { @@ -141,10 +142,10 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { if matches!(resolved_kind, TypeKind::Numeric) && matches!(kind, TypeKind::Normal) { let expected_typ_err = - CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { - name: name.to_string(), - span: span.expect("Type should have span"), - }); + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { + name: name.to_string(), + span: span.expect("Type should have span"), + }); self.errors.push((expected_typ_err, self.file)); return Type::Error; } @@ -163,7 +164,7 @@ impl<'context> Elaborator<'context> { self.generics.iter().find(|generic| generic.name.as_ref() == target_name) } - fn resolve_named_type(&mut self, path: Path, args: Vec, kind: TypeKind) -> Type { + fn resolve_named_type(&mut self, path: Path, args: Vec) -> Type { if args.is_empty() { if let Some(typ) = self.lookup_generic_or_global_type(&path) { return typ; @@ -246,13 +247,14 @@ impl<'context> Elaborator<'context> { }); } - let mut args = vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { - if generic.is_numeric_generic { - self.resolve_numeric_type(arg) - } else { - self.resolve_type(arg) - } - }); + let mut args = + vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { + if generic.is_numeric_generic { + self.resolve_numeric_type(arg) + } else { + self.resolve_type(arg) + } + }); self.verify_generics_count(expected_generic_count, &mut args, span, || { struct_type.borrow().to_string() @@ -269,7 +271,12 @@ impl<'context> Elaborator<'context> { } } - fn resolve_trait_as_type(&mut self, path: Path, args: Vec, kind: TypeKind) -> Type { + fn resolve_trait_as_type( + &mut self, + path: Path, + args: Vec, + kind: TypeKind, + ) -> Type { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); if let Some(t) = self.lookup_trait_or_error(path) { @@ -303,8 +310,13 @@ impl<'context> Elaborator<'context> { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - let kind = if generic.is_numeric_generic { TypeKind::Numeric } else { TypeKind::Normal }; - return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), kind)); + let kind = + if generic.is_numeric_generic { TypeKind::Numeric } else { TypeKind::Normal }; + return Some(Type::NamedGeneric( + generic.type_var.clone(), + generic.name.clone(), + kind, + )); } } @@ -370,7 +382,9 @@ impl<'context> Elaborator<'context> { let constraint = TraitConstraint { typ: self.self_type.clone()?, - trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| { + generic.type_var.clone() + })), trait_id, }; @@ -402,7 +416,9 @@ impl<'context> Elaborator<'context> { the_trait.self_type_typevar.clone(), TypeVariableKind::Normal, ), - trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| { + generic.type_var.clone() + })), trait_id, }; return Some((method, constraint, false)); @@ -441,7 +457,7 @@ impl<'context> Elaborator<'context> { // let typ = typ.clone(); // if resolved_generic.is_numeric_generic { // self.resolve_numeric_type(typ) - // } else { + // } else { // self.resolve_type(typ) // } // }); @@ -1352,7 +1368,7 @@ impl<'context> Elaborator<'context> { span: func_span, source: Source::Return(meta.return_type.clone(), expr_span), }; - + if empty_function { error = error.add_context( "implicitly returns `()` as its body has no tail or `return` expression", @@ -1459,7 +1475,7 @@ impl<'context> Elaborator<'context> { } else { // Declare numeric generic if it is specified let is_numeric_generic = self.add_numeric_generic(unresolved_generic, typevar.clone()); - + let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 16d3efe4429..778ef96d27a 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -49,7 +49,8 @@ use crate::node_interner::{ StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; use crate::{ - GenericTypeVars, Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeKind, TypeVariable, TypeVariableKind + GenericTypeVars, Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeKind, + TypeVariable, TypeVariableKind, }; use fm::FileId; use iter_extended::vecmap; @@ -753,7 +754,11 @@ impl<'a> Resolver<'a> { if let Some(generic) = self.find_generic(name) { // We always insert a `TypeKind::Normal` as we do not support explicit numeric generics // in the resolver - return Some(Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), TypeKind::Normal)); + return Some(Type::NamedGeneric( + generic.type_var.clone(), + generic.name.clone(), + TypeKind::Normal, + )); }; } @@ -892,25 +897,15 @@ impl<'a> Resolver<'a> { first_span: generic.span, second_span: span, }); - } else { - let resolved_generic = ResolvedGeneric { - name: name.clone(), - type_var: typevar.clone(), - // We only support numeric generics in the elaborator - is_numeric_generic: false, - span, - }; - self.generics.push(resolved_generic); } - let resolved_generic = ResolvedGeneric { + ResolvedGeneric { name, type_var: typevar, // We only support numeric generics in the elaborator is_numeric_generic: false, span, - }; - resolved_generic + } }) } @@ -1927,7 +1922,9 @@ impl<'a> Resolver<'a> { let constraint = TraitConstraint { typ: self.self_type.clone()?, - trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| { + generic.type_var.clone() + })), trait_id, }; return Some((method, constraint, false)); @@ -1955,7 +1952,9 @@ impl<'a> Resolver<'a> { the_trait.self_type_typevar.clone(), TypeVariableKind::Normal, ), - trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| generic.type_var.clone())), + trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| { + generic.type_var.clone() + })), trait_id, }; return Some((method, constraint, false)); diff --git a/compiler/noirc_frontend/src/hir/resolution/structs.rs b/compiler/noirc_frontend/src/hir/resolution/structs.rs index 8ddabf29b28..f62e5589d74 100644 --- a/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -12,7 +12,7 @@ use crate::{ Context, }, node_interner::StructId, - Type, Generics, + Generics, Type, }; use super::{errors::ResolverError, path_resolver::StandardPathResolver, resolver::Resolver}; diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 78d05639f65..91c21535ad0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -1,12 +1,10 @@ use std::collections::{BTreeMap, HashSet}; -use std::rc::Rc; use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; use crate::ast::{Ident, ItemVisibility, Path, TraitItem, UnresolvedGeneric}; -use crate::{GenericTypeVars, ResolvedGeneric}; use crate::{ graph::CrateId, hir::{ @@ -19,8 +17,9 @@ use crate::{ }, hir_def::traits::{TraitConstant, TraitFunction, TraitImpl, TraitType}, node_interner::{FuncId, NodeInterner, TraitId}, - Generics, Shared, Type, TypeVariable, TypeVariableKind, + Shared, Type, TypeVariable, TypeVariableKind, }; +use crate::{GenericTypeVars, ResolvedGeneric}; use super::{ functions, get_module_mut, get_struct_type, diff --git a/compiler/noirc_frontend/src/hir_def/traits.rs b/compiler/noirc_frontend/src/hir_def/traits.rs index aca41b7587e..e4959cb3dd9 100644 --- a/compiler/noirc_frontend/src/hir_def/traits.rs +++ b/compiler/noirc_frontend/src/hir_def/traits.rs @@ -151,7 +151,7 @@ impl TraitFunction { pub fn generics(&self) -> &[TypeVariable] { match &self.typ { Type::Function(..) => &[], - Type::Forall(generics, _) => &generics, + Type::Forall(generics, _) => generics, _ => unreachable!("Trait function does not have a function type"), } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 948ee914c42..8b80f4b5e90 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1,5 +1,8 @@ use std::{ - borrow::Cow, cell::RefCell, collections::{BTreeSet, HashMap}, rc::Rc, + borrow::Cow, + cell::RefCell, + collections::{BTreeSet, HashMap}, + rc::Rc, }; use crate::{ @@ -248,7 +251,7 @@ pub struct ResolvedGeneric { } impl ResolvedGeneric { - // TODO: Remove once we move to the elaborator. + // TODO: Remove once we move to the elaborator. // This is only used during collection in the old resolution process. pub fn dummy() -> Self { ResolvedGeneric { @@ -424,10 +427,6 @@ impl TypeAlias { self.typ.substitute(&substitutions) } - pub fn find_numeric_generics_in_type(&self, found_names: &mut Vec) { - self.typ.find_numeric_type_vars(found_names) - } - /// 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. @@ -743,13 +742,9 @@ impl Type { 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| { - if let Type::NamedGeneric(_, name, kind) = typ { - if let TypeKind::Numeric = kind { - found_names.push(name.to_string()); - true - } else { - false - } + if let Type::NamedGeneric(_, name, TypeKind::Numeric) = typ { + found_names.push(name.to_string()); + true } else { false } @@ -765,14 +760,14 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _, _) | Type::Forall(_, _) - | Type::Code => {}, + | Type::Code => {} Type::TraitAsType(_, _, args) => { for arg in args.iter() { arg.find_numeric_type_vars(found_names); } } - Type::Array(length, elem) => { + Type::Array(length, elem) => { elem.find_numeric_type_vars(found_names); named_generic_is_numeric(length, found_names); } @@ -793,14 +788,14 @@ impl Type { for generic in generics.iter() { if !named_generic_is_numeric(generic, found_names) { generic.find_numeric_type_vars(found_names); - } + } } } Type::Alias(_, generics) => { for generic in generics.iter() { if !named_generic_is_numeric(generic, found_names) { generic.find_numeric_type_vars(found_names); - } + } } } Type::MutableReference(element) => element.find_numeric_type_vars(found_names), @@ -814,7 +809,6 @@ impl Type { } } - /// True if this type can be used as a parameter to `main` or a contract function. /// This is only false for unsized types like slices or slices that do not make sense /// as a program input such as named generics or mutable references. @@ -1328,7 +1322,7 @@ impl Type { }) } - (TypeVariable(var, _), other) | (other, TypeVariable(var, _)) => { + (TypeVariable(var, Kind::Normal), other) | (other, TypeVariable(var, Kind::Normal)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { other.try_bind_to(var, bindings) }) @@ -1393,10 +1387,6 @@ impl Type { // Bound NamedGenerics are caught by the check above assert!(binding_a.borrow().is_unbound()); assert!(binding_b.borrow().is_unbound()); - - // if kind_a != kind_b { - // return Err(UnificationError) - // } if name_a == name_b { Ok(()) diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 83835408149..2d4be169cfa 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -34,9 +34,7 @@ use crate::token::{Attributes, SecondaryAttribute}; use crate::GenericTypeVars; use crate::Generics; use crate::ResolvedGeneric; -use crate::{ - Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, -}; +use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind}; /// An arbitrary number to limit the recursion depth when searching for trait impls. /// This is needed to stop recursing for cases such as `impl Foo for T where T: Eq` diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 5c073835e59..b0cd9681509 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -4,14 +4,11 @@ use super::{ parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, self_parameter, where_clause, NoirParser, }; +use crate::ast::{ + FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, +}; use crate::parser::spanned; use crate::token::{Keyword, Token}; -use crate::{ - ast::{ - FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, - }, - parser::{ParserError, ParserErrorReason}, -}; use crate::{ ast::{UnresolvedGeneric, UnresolvedGenerics}, parser::labels::ParsingRuleLabel, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index cdd0834900e..7980a70dd9a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1724,7 +1724,9 @@ fn implicit_numeric_generics_elaborator() { let errors = get_program_errors_elaborator(src); for error in errors.iter() { - if let CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident }) = &errors[0].0 { + 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); @@ -1756,4 +1758,4 @@ fn nested_generic_elaborator() { "#; let errors = get_program_errors_elaborator(src); assert!(errors.is_empty()); -} \ No newline at end of file +} From 316cc470e3c0f114384649cd5318a3e421cefe31 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 18:01:35 +0000 Subject: [PATCH 23/85] cleanup and fix resolver add-generics from merge --- .../src/elaborator/expressions.rs | 5 +-- compiler/noirc_frontend/src/elaborator/mod.rs | 6 ++-- .../noirc_frontend/src/elaborator/types.rs | 33 ------------------- .../src/hir/resolution/resolver.rs | 17 ++++++---- compiler/noirc_frontend/src/tests.rs | 4 +++ 5 files changed, 18 insertions(+), 47 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index 66d693238fd..a922f552c4b 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -82,7 +82,7 @@ impl<'context> Elaborator<'context> { let mut statements = Vec::with_capacity(block.statements.len()); for (i, statement) in block.statements.into_iter().enumerate() { - let (id, stmt_type) = self.elaborate_statement(statement.clone()); + let (id, stmt_type) = self.elaborate_statement(statement); statements.push(id); if let HirStatement::Semi(expr) = self.interner.statement(&id) { @@ -98,9 +98,6 @@ impl<'context> Elaborator<'context> { if i + 1 == statements.len() { block_type = stmt_type; } - if self.errors.len() == 2 { - // dbg!(statement); - } } self.pop_scope(); diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 8c48766abc7..dd2bc6000fe 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -345,7 +345,7 @@ impl<'context> Elaborator<'context> { let name = self.interner.definition_name(parameter.id).to_owned(); self.add_existing_variable_to_scope(name, parameter.clone(), true); } - // We should introduce the IDs for numeric generic into scope as we do with parameters + // We should introduce the IDs for numeric generics into scope as we do with parameters for numeric_generic in &func_meta.generic_idents { let name = self.interner.definition_name(numeric_generic.id).to_owned(); self.add_existing_variable_to_scope(name, numeric_generic.clone(), false); @@ -1187,8 +1187,8 @@ impl<'context> Elaborator<'context> { // when adding checks after each struct field is resolved. let struct_ids = structs.keys().copied().collect::>(); - // First resolve each struct's generics as fields which contain other - // structs themselves may also contain generics. + // First resolve each struct's generics as fields with nested structs + // may themselves also contain generics. // Without resolving all generics first we will not be able to distinguish // between normal and numeric generics. for (type_id, typ) in structs.iter() { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 97598d719df..74f0a1553f1 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -450,22 +450,6 @@ impl<'context> Elaborator<'context> { if let Some(method) = the_trait.find_method(path.segments.last().unwrap().0.contents.as_str()) { - // // Make sure that we are resolving the correct type kind for the trait generics - // let resolved_generics = &the_trait.generics; - // assert_eq!(resolved_generics.len(), trait_bound.trait_generics.len()); - // let trait_generics = vecmap(resolved_generics.clone().iter().zip(&constraint.trait_generics), |(resolved_generic, typ)| { - // let typ = typ.clone(); - // if resolved_generic.is_numeric_generic { - // self.resolve_numeric_type(typ) - // } else { - // self.resolve_type(typ) - // } - // }); - // let constraint = TraitConstraint { - // trait_id: constraint.trait_id, - // typ: self.resolve_type(constraint.typ), - // trait_generics, - // }; return Some((method, constraint, true)); } } @@ -1552,16 +1536,6 @@ impl<'context> Elaborator<'context> { Self::find_numeric_generics_in_type(generic, found); } } - // // dbg!(struct_type.borrow()); - // for (resolved_generic, generic) in struct_type.borrow().generics.iter().zip(generics) { - // if resolved_generic.is_numeric_generic { - // if let Type::NamedGeneric(type_variable, name, _) = generic { - // // We want to return the name of the generic on the type rather than the inner struct type - // // as the generic on `Type::Struct` can differ from that on `StructType` - // found.insert(name.to_string(), type_variable.clone()); - // } - // } - // } } Type::Alias(alias, generics) => { for (i, generic) in generics.iter().enumerate() { @@ -1573,13 +1547,6 @@ impl<'context> Elaborator<'context> { Self::find_numeric_generics_in_type(generic, found); } } - // for (resolved_generic, generic) in alias.borrow().generics.iter().zip(generics) { - // if resolved_generic.is_numeric_generic { - // if let Type::NamedGeneric(type_variable, name, _) = generic { - // found.insert(name.to_string(), type_variable.clone()); - // } - // } - // } } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 778ef96d27a..30cf6485589 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -891,21 +891,24 @@ impl<'a> Resolver<'a> { // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); + let resolved_generic = ResolvedGeneric { + name: name.clone(), + type_var: typevar, + // We only support numeric generics in the elaborator + is_numeric_generic: false, + span, + }; if let Some(generic) = self.find_generic(&name) { self.errors.push(ResolverError::DuplicateDefinition { name: ident.0.contents, first_span: generic.span, second_span: span, }); + } else { + self.generics.push(resolved_generic.clone()); } - ResolvedGeneric { - name, - type_var: typevar, - // We only support numeric generics in the elaborator - is_numeric_generic: false, - span, - } + resolved_generic }) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7980a70dd9a..e5f389c21d5 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1599,6 +1599,10 @@ fn numeric_generic_used_in_nested_type_fail() { "#; let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { .. }), + )); } #[test] From 45cec2d63bc3f99498ab503c0b6f09eb97af06bf Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 18:07:30 +0000 Subject: [PATCH 24/85] a little more comments and cleanup --- compiler/noirc_frontend/src/elaborator/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index dd2bc6000fe..b4c8e8694cc 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1225,6 +1225,8 @@ 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() { From 5f9536bfb3abf0ce9664e1fc7c61a0975b0f2dda Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 18:12:47 +0000 Subject: [PATCH 25/85] use push and pop scope --- compiler/noirc_frontend/src/elaborator/traits.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 9001fc488ac..972f9225905 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -23,7 +23,7 @@ use super::Elaborator; impl<'context> Elaborator<'context> { pub fn collect_trait_generics(&mut self, traits: &BTreeMap) { for (trait_id, unresolved_trait) in traits { - self.scopes.start_scope(); + self.push_scope(); self.recover_generics(|this| { this.add_generics(&unresolved_trait.trait_def.generics); @@ -31,12 +31,13 @@ impl<'context> Elaborator<'context> { trait_def.generics = this.generics.clone(); }); }); + self.pop_scope(); } } pub fn collect_traits(&mut self, traits: BTreeMap) { for (trait_id, unresolved_trait) in traits { - self.scopes.start_scope(); + self.push_scope(); self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); this.add_existing_generics( @@ -63,7 +64,7 @@ impl<'context> Elaborator<'context> { if self.crate_id.is_stdlib() { self.interner.try_add_operator_trait(trait_id); } - self.scopes.end_scope(); + self.pop_scope(); } } From 6f387cb3f95637d1902e2c7e58761d53c3a01f92 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 20:42:29 +0000 Subject: [PATCH 26/85] fix panic in numeric_generic_binary_operation_type_mismatch --- compiler/noirc_frontend/src/tests.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index e5f389c21d5..433aedda1ff 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1491,10 +1491,11 @@ fn bool_numeric_generic() { #[test] fn numeric_generic_binary_operation_type_mismatch() { let src = r#" - fn double() { - let check: bool = true; - assert(N == check); - } + fn foo() -> bool { + let mut check: bool = true; + check = N; + check + } "#; let errors = get_program_errors_elaborator(src); assert_eq!(errors.len(), 1); @@ -1619,9 +1620,6 @@ fn numeric_generic_used_in_nested_type_pass() { } "#; let errors = get_program_errors_elaborator(src); - if !errors.is_empty() { - dbg!(errors.clone()); - } assert!(errors.is_empty()); } From 3165bc74858dc2d5681fe0b3f7376c11f6a3450e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 11 Jun 2024 21:22:19 +0000 Subject: [PATCH 27/85] use actual typ even if incorrect for accurate type checking when adding generics --- compiler/noirc_frontend/src/elaborator/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index b4c8e8694cc..8ffd5cdacee 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -502,7 +502,7 @@ impl<'context> Elaborator<'context> { typevar: TypeVariable, ) -> bool { if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let mut typ = self.resolve_type(typ.clone()); + let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { let unsupported_typ_err = CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { @@ -510,7 +510,6 @@ impl<'context> Elaborator<'context> { typ: typ.clone(), }); self.errors.push((unsupported_typ_err, self.file)); - typ = Type::Error; } let definition = DefinitionKind::GenericType(typevar.clone()); let hir_ident = From c880850b09b0f01e242a11a4aee533f3c6b032b7 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 12 Jun 2024 15:53:00 +0000 Subject: [PATCH 28/85] update comment --- 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 8b80f4b5e90..2ce0f4fea03 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -349,8 +349,7 @@ 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 - /// a new strategy for finding implicit generics with `find_numeric_type_vars` as we now have type kinds + /// 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)) From 89f51f31869da8fe69f587563a4a1f3ff1f24671 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 12 Jun 2024 15:53:26 +0000 Subject: [PATCH 29/85] additional comment --- compiler/noirc_frontend/src/hir_def/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 2ce0f4fea03..c4d35069261 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -738,6 +738,7 @@ impl Type { } } + /// TODO(https://github.com/noir-lang/noir/issues/5156): Remove with explicit 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| { From fe97064f7ecdb591cc5519f6aa98a64a0e0c02fd Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 13 Jun 2024 21:15:10 +0000 Subject: [PATCH 30/85] more robust numeric generic parsing --- compiler/noirc_frontend/src/parser/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 8 ++++- .../src/parser/parser/function.rs | 31 ++++++++++++------- .../src/parser/parser/structs.rs | 4 ++- compiler/noirc_frontend/src/tests.rs | 15 +++++++++ 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 9e60383afee..460842aed5a 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -197,7 +197,7 @@ fn parameter_name_recovery() -> impl NoirParser { } fn top_level_statement_recovery() -> impl NoirParser { - none_of([Token::Semicolon, Token::RightBrace, Token::EOF]) + none_of([Token::RightBrace, Token::EOF]) .repeated() .ignore_then(one_of([Token::Semicolon])) .map(|_| TopLevelStatement::Error) diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index cabc788e07d..26acd0dae27 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -824,7 +824,13 @@ fn array_type<'a>( ) -> impl NoirParser + 'a { just(Token::LeftBracket) .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression())) + .then( + just(Token::Semicolon).ignore_then( + ident() + .map(|ident| UnresolvedTypeExpression::Variable(Path::from_ident(ident))) + .or(type_expression()), + ), + ) .then_ignore(just(Token::RightBracket)) .map_with_span(|(element_type, size), span| { UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index b0cd9681509..a70337db97b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -79,19 +79,20 @@ fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { }) } -pub(super) fn generic() -> impl NoirParser { +pub(super) fn numeric_generic() -> impl NoirParser { keyword(Keyword::Let) - .or_not() .ignore_then(ident()) - .then_ignore(just(Token::Colon).or_not()) - .then(parse_type().or_not()) - .map(|(ident, typ)| { - if let Some(typ) = typ { - UnresolvedGeneric::Numeric { ident, typ } - } else { - UnresolvedGeneric::Variable(ident) - } - }) + .then_ignore(just(Token::Colon)) + .then(parse_type()) + .map(|(ident, typ)| UnresolvedGeneric::Numeric { ident, typ }) +} + +pub(super) fn generic_variable() -> impl NoirParser { + ident().map(UnresolvedGeneric::Variable) +} + +pub(super) fn generic() -> impl NoirParser { + numeric_generic().or(generic_variable()) } /// non_empty_ident_list: ident ',' non_empty_ident_list @@ -103,7 +104,7 @@ pub(super) fn generics() -> impl NoirParser { generic() .separated_by(just(Token::Comma)) .allow_trailing() - .at_least(1) + // .at_least(1) .delimited_by(just(Token::Less), just(Token::Greater)) .or_not() .map(|opt| opt.unwrap_or_default()) @@ -211,6 +212,7 @@ mod test { // fn func_name(x: impl Eq) {} with error Expected an end of input but found end of input // "fn func_name(x: impl Eq) {}", "fn func_name(x: impl Eq, y : T) where T: SomeTrait + Eq {}", + "fn func_name(x: [Field; N]) {}", ], ); @@ -227,6 +229,11 @@ mod test { // A leading plus is not allowed. "fn func_name(f: Field, y : T) where T: + SomeTrait {}", "fn func_name(f: Field, y : T) where T: TraitX + {}", + // Test ill-formed numeric generics + "fn func_name(y: T) {}", + "fn func_name(y: T) {}", + "fn func_name(y: T) {}", + "fn func_name(y: T) {}", ], ); } diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 7da956bdfea..e95fb6b3230 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -28,12 +28,14 @@ pub(super) fn struct_definition() -> impl NoirParser { .or(just(Semicolon).to(Vec::new())); attributes() + .or_not() .then_ignore(keyword(Struct)) .then(ident()) .then(function::generics()) .then(fields) .validate(|(((raw_attributes, name), generics), fields), span, emit| { - let attributes = validate_secondary_attributes(raw_attributes, span, emit); + let attributes = + validate_secondary_attributes(raw_attributes.unwrap_or_default(), span, emit); TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) }) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 433aedda1ff..f73aafe446e 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1761,3 +1761,18 @@ fn nested_generic_elaborator() { let errors = get_program_errors_elaborator(src); assert!(errors.is_empty()); } + +#[test] +fn single_parser_error_bad_numeric_generic() { + let src = r#" + fn id(x: [Field; I]) -> [Field; I] { + x + } + "#; + let errors = get_program_errors_elaborator(src); + assert!(has_parser_error(&errors)); + // We want to make sure that each ill-formed numeric generic (e.g. `` or ``) + // returns a single parser error. + // We have this test to make sure we are not displaying deceiving errors to the user. + assert_eq!(errors.len(), 1); +} From 65aae292e2d6d82c2541db71e4617be81515320e Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 13 Jun 2024 21:33:22 +0000 Subject: [PATCH 31/85] update comments with issue referencing elaborator --- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- tooling/nargo_cli/build.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index c4d35069261..ef9bee5a032 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -251,7 +251,7 @@ pub struct ResolvedGeneric { } impl ResolvedGeneric { - // TODO: Remove once we move to the elaborator. + // TODO(https://github.com/noir-lang/noir/issues/5231): Remove once we move to the elaborator. // This is only used during collection in the old resolution process. pub fn dummy() -> Self { ResolvedGeneric { diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index ec1abe6cd67..cc5a157dadb 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -58,7 +58,7 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ &"is_unconstrained", ]; -/// Some tests are explicit ignored when not working with the elaborator. +/// TODO(https://github.com/noir-lang/noir/issues/5231): Some tests are explicitly ignored when not working with the elaborator. /// These should be fixed and removed from this list. const IGNORED_NON_ELABORATOR_TESTS: [&str; 1] = [ // Explicit numeric generics are only supported in the elaborator. From e5b12d5a2ad27e605f0690b9c7b074bd48f60251 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 12:38:10 -0400 Subject: [PATCH 32/85] Update compiler/noirc_frontend/src/hir_def/types.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir_def/types.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index ef9bee5a032..d798ac2f680 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -187,11 +187,12 @@ impl Type { } } -/// Types need to be distinguished depending on the scenario in which they can be used. +/// A Kind is the type of a Type. These are used since only certain kinds of types are allowed in +/// certain positions. /// /// For example, the type of a struct field or a function parameter is expected to be -/// a normal type. While the type we expect in an array expression (`[; ]`) -/// is expected to be a numeric type. +/// a type of kind * (reprsented here as `Normal`). Types used in positions where a number +/// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum TypeKind { Normal, From 7b31c30c6152815bcf1b70c83065f522dee34d83 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 18:57:26 +0000 Subject: [PATCH 33/85] switch TypeKind -> Kind and Kind rather than bool in ResolvedGeneric --- compiler/noirc_frontend/src/ast/expression.rs | 8 ++++ compiler/noirc_frontend/src/elaborator/mod.rs | 34 +++++--------- .../noirc_frontend/src/elaborator/types.rs | 44 +++++++------------ .../src/hir/resolution/resolver.rs | 10 ++--- .../noirc_frontend/src/hir/type_check/mod.rs | 4 +- compiler/noirc_frontend/src/hir_def/types.rs | 16 +++---- 6 files changed, 48 insertions(+), 68 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 56d784ae045..4f726ffa7b7 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -8,6 +8,7 @@ use crate::ast::{ use crate::macros_api::StructId; use crate::node_interner::ExprId; use crate::token::{Attributes, Token}; +use crate::Kind; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; @@ -62,6 +63,13 @@ impl UnresolvedGeneric { } } } + + pub fn kind(&self) -> Kind { + match self { + UnresolvedGeneric::Variable(_) => Kind::Normal, + UnresolvedGeneric::Numeric { .. } => Kind::Numeric, + } + } } impl Display for UnresolvedGeneric { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 219a4095c76..74de32b025b 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -22,7 +22,7 @@ use crate::{ expr::HirIdent, function::{FunctionBody, Parameters}, traits::TraitConstraint, - types::{Generics, ResolvedGeneric, TypeKind}, + types::{Generics, Kind, ResolvedGeneric}, }, macros_api::{ BlockExpression, Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, @@ -447,7 +447,7 @@ impl<'context> Elaborator<'context> { generics.push(new_generic.clone()); let name = format!("impl {trait_path}"); - let generic_type = Type::NamedGeneric(new_generic, Rc::new(name), TypeKind::Normal); + let generic_type = Type::NamedGeneric(new_generic, Rc::new(name), Kind::Normal); let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics }; if let Some(new_constraint) = self.resolve_trait_bound(&trait_bound, generic_type.clone()) { @@ -468,7 +468,7 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - let is_numeric_generic = self.add_numeric_generic(generic, typevar.clone()); + let is_numeric_generic = self.try_add_numeric_generic(generic, typevar.clone()); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -476,7 +476,7 @@ impl<'context> Elaborator<'context> { let resolved_generic = ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), - is_numeric_generic, + kind: if is_numeric_generic { Kind::Numeric } else { Kind::Normal }, span, }; @@ -496,7 +496,7 @@ impl<'context> Elaborator<'context> { /// If a numeric generic has been specified, add it to the current scope. /// Returns `true` if a numeric generic was added, otherwise return `false` - pub(super) fn add_numeric_generic( + pub(super) fn try_add_numeric_generic( &mut self, generic: &UnresolvedGeneric, typevar: TypeVariable, @@ -583,14 +583,7 @@ impl<'context> Elaborator<'context> { assert_eq!(resolved_generics.len(), bound.trait_generics.len()); let trait_generics = vecmap( resolved_generics.clone().iter().zip(&bound.trait_generics), - |(resolved_generic, typ)| { - let typ = typ.clone(); - if resolved_generic.is_numeric_generic { - self.resolve_numeric_type(typ) - } else { - self.resolve_type(typ) - } - }, + |(resolved_generic, typ)| self.resolve_type_inner(typ.clone(), resolved_generic.kind), ); let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; @@ -682,7 +675,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } - _ => self.resolve_type_inner(typ, TypeKind::Normal), + _ => self.resolve_type_inner(typ, Kind::Normal), }; self.check_if_type_is_valid_for_program_input( @@ -798,7 +791,7 @@ impl<'context> Elaborator<'context> { // 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, is_numeric_generic, .. }) = + if let Some(ResolvedGeneric { name, span, kind, .. }) = self.generics.iter_mut().find(|generic| generic.name.as_ref() == &name_to_find) { let scope = self.scopes.get_mut_scope(); @@ -813,7 +806,7 @@ impl<'context> Elaborator<'context> { // ``` continue; } - *is_numeric_generic = true; + *kind = Kind::Numeric; 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); @@ -1251,7 +1244,7 @@ impl<'context> Elaborator<'context> { for generic in struct_def.generics.iter_mut() { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { - generic.is_numeric_generic = true; + generic.kind = Kind::Numeric; } } } @@ -1414,12 +1407,7 @@ impl<'context> Elaborator<'context> { .iter() .enumerate() .map(|(i, generic)| { - let generic = generic.clone(); - if resolved_generics[i].is_numeric_generic { - self.resolve_numeric_type(generic) - } else { - self.resolve_type(generic) - } + self.resolve_type_inner(generic.clone(), resolved_generics[i].kind) }) .collect() } else { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 74f0a1553f1..3f7ca03b537 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -32,7 +32,7 @@ use crate::{ UnaryOp, UnresolvedType, UnresolvedTypeData, }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, - Generics, ResolvedGeneric, Type, TypeBinding, TypeKind, TypeVariable, TypeVariableKind, + Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind, }; use super::{lints, Elaborator}; @@ -40,17 +40,17 @@ use super::{lints, Elaborator}; impl<'context> Elaborator<'context> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, TypeKind::Normal) + self.resolve_type_inner(typ, Kind::Normal) } /// Translate an UnresolvedType to a Type with a `TypeKind::Numeric` pub(super) fn resolve_numeric_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, TypeKind::Numeric) + self.resolve_type_inner(typ, Kind::Numeric) } /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. - pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: TypeKind) -> Type { + pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: Kind) -> Type { use crate::ast::UnresolvedTypeData::*; let span = typ.span; @@ -61,7 +61,7 @@ impl<'context> Elaborator<'context> { let elem = Box::new(self.resolve_type_inner(*elem, kind)); let mut size = self.convert_expression_type(size); if let Type::NamedGeneric(type_var, name, _) = size { - size = Type::NamedGeneric(type_var, name, TypeKind::Numeric); + size = Type::NamedGeneric(type_var, name, Kind::Numeric); } Type::Array(Box::new(size), elem) } @@ -75,14 +75,14 @@ impl<'context> Elaborator<'context> { String(size) => { 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, TypeKind::Numeric); + resolved_size = Type::NamedGeneric(type_var, name, Kind::Numeric); } 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, TypeKind::Numeric); + resolved_size = Type::NamedGeneric(type_var, name, Kind::Numeric); } let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) @@ -140,7 +140,7 @@ impl<'context> Elaborator<'context> { // Check that any types with a type kind match the expected type kind supplied to this function if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - if matches!(resolved_kind, TypeKind::Numeric) && matches!(kind, TypeKind::Normal) { + if matches!(resolved_kind, Kind::Numeric) && matches!(kind, Kind::Normal) { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { name: name.to_string(), @@ -195,11 +195,7 @@ impl<'context> Elaborator<'context> { let id = type_alias.id; let mut args = vecmap(type_alias.generics.iter().zip(args), |(generic, arg)| { - if generic.is_numeric_generic { - self.resolve_numeric_type(arg) - } else { - self.resolve_type(arg) - } + self.resolve_type_inner(arg, generic.kind) }); self.verify_generics_count(expected_generic_count, &mut args, span, || { @@ -249,11 +245,7 @@ impl<'context> Elaborator<'context> { let mut args = vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { - if generic.is_numeric_generic { - self.resolve_numeric_type(arg) - } else { - self.resolve_type(arg) - } + self.resolve_type_inner(arg, generic.kind) }); self.verify_generics_count(expected_generic_count, &mut args, span, || { @@ -271,12 +263,7 @@ impl<'context> Elaborator<'context> { } } - fn resolve_trait_as_type( - &mut self, - path: Path, - args: Vec, - kind: TypeKind, - ) -> Type { + fn resolve_trait_as_type(&mut self, path: Path, args: Vec, kind: Kind) -> Type { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); if let Some(t) = self.lookup_trait_or_error(path) { @@ -310,12 +297,10 @@ impl<'context> Elaborator<'context> { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - let kind = - if generic.is_numeric_generic { TypeKind::Numeric } else { TypeKind::Normal }; return Some(Type::NamedGeneric( generic.type_var.clone(), generic.name.clone(), - kind, + generic.kind, )); } } @@ -1458,12 +1443,13 @@ impl<'context> Elaborator<'context> { }); } else { // Declare numeric generic if it is specified - let is_numeric_generic = self.add_numeric_generic(unresolved_generic, typevar.clone()); + let is_numeric_generic = + self.try_add_numeric_generic(unresolved_generic, typevar.clone()); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), - is_numeric_generic, + kind: if is_numeric_generic { Kind::Numeric } else { Kind::Normal }, span, }; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 42dbf81a60e..5c30823b8f6 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -49,7 +49,7 @@ use crate::node_interner::{ StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; use crate::{ - GenericTypeVars, Generics, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeKind, + GenericTypeVars, Generics, Kind, ResolvedGeneric, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, }; use fm::FileId; @@ -757,7 +757,7 @@ impl<'a> Resolver<'a> { return Some(Type::NamedGeneric( generic.type_var.clone(), generic.name.clone(), - TypeKind::Normal, + Kind::Normal, )); }; } @@ -895,7 +895,7 @@ impl<'a> Resolver<'a> { name: name.clone(), type_var: typevar, // We only support numeric generics in the elaborator - is_numeric_generic: false, + kind: Kind::Normal, span, }; if let Some(generic) = self.find_generic(&name) { @@ -950,12 +950,10 @@ impl<'a> Resolver<'a> { second_span: span, }); } else { - let is_numeric_generic = - matches!(unresolved_generic, UnresolvedGeneric::Numeric { .. }); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), - is_numeric_generic, + kind: unresolved_generic.kind(), span, }; self.generics.push(resolved_generic); diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index c455b7c56f9..ec9ad60b895 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -22,7 +22,7 @@ use crate::{ traits::TraitConstraint, }, node_interner::{ExprId, FuncId, GlobalId, NodeInterner}, - Type, TypeBindings, TypeKind, + Kind, Type, TypeBindings, }; pub use self::errors::Source; @@ -284,7 +284,7 @@ pub(crate) fn check_trait_impl_method_matches_declaration( for ((_, trait_fn_generic), (name, impl_fn_generic)) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { - let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), TypeKind::Normal); + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), Kind::Normal); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index d798ac2f680..22c2d43334d 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -82,7 +82,7 @@ pub enum Type { /// NamedGenerics are the 'T' or 'U' in a user-defined generic function /// like `fn foo(...) {}`. Unlike TypeVariables, they cannot be bound over. - NamedGeneric(TypeVariable, Rc, TypeKind), + NamedGeneric(TypeVariable, Rc, Kind), /// A functions with arguments, a return type and environment. /// the environment should be `Unit` by default, @@ -194,16 +194,16 @@ impl Type { /// a type of kind * (reprsented here as `Normal`). Types used in positions where a number /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] -pub enum TypeKind { +pub enum Kind { Normal, Numeric, } -impl std::fmt::Display for TypeKind { +impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TypeKind::Normal => write!(f, "normal"), - TypeKind::Numeric => write!(f, "numeric"), + Kind::Normal => write!(f, "normal"), + Kind::Numeric => write!(f, "numeric"), } } } @@ -247,7 +247,7 @@ pub type Generics = Vec; pub struct ResolvedGeneric { pub name: Rc, pub type_var: TypeVariable, - pub is_numeric_generic: bool, + pub kind: Kind, pub span: Span, } @@ -258,7 +258,7 @@ impl ResolvedGeneric { ResolvedGeneric { name: Rc::default(), type_var: TypeVariable::unbound(TypeVariableId(0)), - is_numeric_generic: false, + kind: Kind::Normal, span: Span::default(), } } @@ -743,7 +743,7 @@ impl Type { 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| { - if let Type::NamedGeneric(_, name, TypeKind::Numeric) = typ { + if let Type::NamedGeneric(_, name, Kind::Numeric) = typ { found_names.push(name.to_string()); true } else { From 63302d0241689ded3740c72f3187e082c9a871c0 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:12:56 +0000 Subject: [PATCH 34/85] remove resolve_numeric_type as it is unused in favor of passing in the kind manually --- compiler/noirc_frontend/src/elaborator/mod.rs | 10 +++------- compiler/noirc_frontend/src/elaborator/types.rs | 10 ++-------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 74de32b025b..21070c17cd4 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -468,7 +468,7 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - let is_numeric_generic = self.try_add_numeric_generic(generic, typevar.clone()); + self.try_add_numeric_generic(generic, typevar.clone()); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -476,7 +476,7 @@ impl<'context> Elaborator<'context> { let resolved_generic = ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), - kind: if is_numeric_generic { Kind::Numeric } else { Kind::Normal }, + kind: generic.kind(), span, }; @@ -495,12 +495,11 @@ impl<'context> Elaborator<'context> { } /// If a numeric generic has been specified, add it to the current scope. - /// Returns `true` if a numeric generic was added, otherwise return `false` pub(super) fn try_add_numeric_generic( &mut self, generic: &UnresolvedGeneric, typevar: TypeVariable, - ) -> bool { + ) { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { @@ -523,9 +522,6 @@ impl<'context> Elaborator<'context> { // Store the ident of the numeric generic to set up the function meta so that // any numeric generics are accurately brought into scope. self.generic_idents.push(hir_ident); - true - } else { - false } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 3f7ca03b537..723717de6db 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -43,11 +43,6 @@ impl<'context> Elaborator<'context> { self.resolve_type_inner(typ, Kind::Normal) } - /// Translate an UnresolvedType to a Type with a `TypeKind::Numeric` - pub(super) fn resolve_numeric_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, Kind::Numeric) - } - /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: Kind) -> Type { @@ -1443,13 +1438,12 @@ impl<'context> Elaborator<'context> { }); } else { // Declare numeric generic if it is specified - let is_numeric_generic = - self.try_add_numeric_generic(unresolved_generic, typevar.clone()); + self.try_add_numeric_generic(unresolved_generic, typevar.clone()); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), - kind: if is_numeric_generic { Kind::Numeric } else { Kind::Normal }, + kind: unresolved_generic.kind(), span, }; From 1554149402ba969ad9b2a01948a2738c7e8bdc98 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 15:16:29 -0400 Subject: [PATCH 35/85] Update compiler/noirc_frontend/src/parser/parser/function.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index a70337db97b..16238c36180 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -92,7 +92,7 @@ pub(super) fn generic_variable() -> impl NoirParser { } pub(super) fn generic() -> impl NoirParser { - numeric_generic().or(generic_variable()) + generic_variable().or(numeric_generic()) } /// non_empty_ident_list: ident ',' non_empty_ident_list From 8c5fd98b994b5283ec87935ecc8b333ba796153f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 15:16:38 -0400 Subject: [PATCH 36/85] Update tooling/nargo_cli/build.rs Co-authored-by: jfecher --- tooling/nargo_cli/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index cc5a157dadb..702a9e87de2 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -62,7 +62,7 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// These should be fixed and removed from this list. const IGNORED_NON_ELABORATOR_TESTS: [&str; 1] = [ // Explicit numeric generics are only supported in the elaborator. - &"numeric_generics_explicit", + "numeric_generics_explicit", ]; fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { From aa53a0562806d82237cf1724bb3d530893af62af Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 15:16:53 -0400 Subject: [PATCH 37/85] Update tooling/nargo_fmt/tests/input/fn.nr Co-authored-by: jfecher --- tooling/nargo_fmt/tests/input/fn.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index eae110422a0..2e024f4f993 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -45,8 +45,8 @@ fn main( pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} -fn id(x: [Field; I]) -> [Field; I] {} +fn id < T , let I : Field > ( x : [ Field ; I ] ) -> [Field; I ] { } fn id_two(x: [Field; I]) -> [Field; I] {} +let I : Field >(x: [Field ; I]) -> [ Field; I] {} From 09bcfe0542012eaec3496eff568089311ac5d5f8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:21:56 +0000 Subject: [PATCH 38/85] shorten numeric_generics_explicit test --- .../numeric_generics_explicit/src/main.nr | 37 ++++--------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index 977069eff44..1896430bdb0 100644 --- a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -30,14 +30,17 @@ fn main() { assert(new_arr[0] == 5); } +// Used in the signature of a function fn id(x: [Field; I]) -> [Field; I] { x } +// Used as a field of a struct struct MyStruct { data: [Field; S], } +// Used in an impl impl MyStruct { fn insert(mut self: Self, index: Field, elem: Field) -> Self { // Regression test for numeric generics on impls @@ -53,6 +56,10 @@ fn foo(mut s: MyStruct<2+1>) -> MyStruct<10/2-2> { s } +fn baz() -> MyStruct { + MyStruct { data: [1; N] } +} + fn double() -> u32 { // Used as an expression N * 2 @@ -65,20 +72,6 @@ fn double_numeric_generics_test() { assert(double::<7 + 8>() == 30); } -// Used as a field of a struct -struct Foo { - inner: [u32; N], -} -fn baz() -> Foo { - Foo { inner: [1; N] } -} -// Used in an impl -impl Foo { - fn bar(self) -> u32 { - N * self.inner[0] - } -} - struct MyType { a: Field, b: Field, @@ -95,16 +88,6 @@ trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -fn read() -> T where T: Deserialize { - // Used as a type within a function body - let mut fields: [Field; N] = [0; N]; - // Used a loop bound - for i in 0..N { - fields[i] = i as Field + 1; - } - T::deserialize(fields) -} - struct PublicStorage {} impl PublicStorage { @@ -119,12 +102,6 @@ impl PublicStorage { } } -// Used in the signature of a function -fn update_arr(mut arr: [Field; N]) -> [Field; N] { - arr[0] = 5; - arr -} - // Check that we can thread numeric generics into nested structs // and also that we can handle nested structs with numeric generics // which are declared after the parent struct From 3db59b87176ee98c58613e886d9705790fc2f50a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:22:46 +0000 Subject: [PATCH 39/85] correctness of nuric generics test --- .../numeric_generics_explicit/src/main.nr | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr index 1896430bdb0..7c4f7761ff6 100644 --- a/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr +++ b/test_programs/compile_success_empty/numeric_generics_explicit/src/main.nr @@ -24,10 +24,7 @@ fn main() { assert(my_type.c == 3); let foo = baz::<10>(); - assert(foo.inner == [1; 10]); - - let new_arr = update_arr([0, 1, 2, 3]); - assert(new_arr[0] == 5); + assert(foo.data == [1; 10]); } // Used in the signature of a function From 6fa665eeec067a6d935ad2e02e2cabdd5181bcea Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:34:07 +0000 Subject: [PATCH 40/85] remove at_least(1) for generics --- compiler/noirc_frontend/src/parser/parser/function.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 16238c36180..3afa1cc799e 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -104,7 +104,6 @@ pub(super) fn generics() -> impl NoirParser { generic() .separated_by(just(Token::Comma)) .allow_trailing() - // .at_least(1) .delimited_by(just(Token::Less), just(Token::Greater)) .or_not() .map(|opt| opt.unwrap_or_default()) From 759b7eca0a7bf103ebe2231d37da226f0fcce31b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 15:34:35 -0400 Subject: [PATCH 41/85] Update compiler/noirc_frontend/src/parser/parser/structs.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/parser/parser/structs.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index e95fb6b3230..0ada4ca5632 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -28,7 +28,6 @@ pub(super) fn struct_definition() -> impl NoirParser { .or(just(Semicolon).to(Vec::new())); attributes() - .or_not() .then_ignore(keyword(Struct)) .then(ident()) .then(function::generics()) From eff59604a63b5f89e995f09aad72b01f7c042804 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:38:52 +0000 Subject: [PATCH 42/85] remove unnecessary unwrap_or_default --- compiler/noirc_frontend/src/parser/parser/structs.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 0ada4ca5632..7da956bdfea 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -33,8 +33,7 @@ pub(super) fn struct_definition() -> impl NoirParser { .then(function::generics()) .then(fields) .validate(|(((raw_attributes, name), generics), fields), span, emit| { - let attributes = - validate_secondary_attributes(raw_attributes.unwrap_or_default(), span, emit); + let attributes = validate_secondary_attributes(raw_attributes, span, emit); TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) }) } From 752e7f0ef8ff6c0291ad5e0ea707621303afa212 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:40:33 +0000 Subject: [PATCH 43/85] rename generic_variable -> generic_type in parser --- compiler/noirc_frontend/src/parser/parser/function.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 3afa1cc799e..3e686ee4c85 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -87,12 +87,12 @@ pub(super) fn numeric_generic() -> impl NoirParser { .map(|(ident, typ)| UnresolvedGeneric::Numeric { ident, typ }) } -pub(super) fn generic_variable() -> impl NoirParser { +pub(super) fn generic_type() -> impl NoirParser { ident().map(UnresolvedGeneric::Variable) } pub(super) fn generic() -> impl NoirParser { - generic_variable().or(numeric_generic()) + generic_type().or(numeric_generic()) } /// non_empty_ident_list: ident ',' non_empty_ident_list From c0fb7ef109b636340ce50a3992d34d9ac7db6056 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 14 Jun 2024 19:55:08 +0000 Subject: [PATCH 44/85] still destructure named generic in resolve_type_inner --- compiler/noirc_frontend/src/elaborator/types.rs | 2 +- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 723717de6db..6bfd97bf148 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -135,7 +135,7 @@ impl<'context> Elaborator<'context> { // Check that any types with a type kind match the expected type kind supplied to this function if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - if matches!(resolved_kind, Kind::Numeric) && matches!(kind, Kind::Normal) { + if *resolved_kind != kind { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { name: name.to_string(), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 22c2d43334d..c7aaebbd2c8 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -191,7 +191,7 @@ impl Type { /// certain positions. /// /// For example, the type of a struct field or a function parameter is expected to be -/// a type of kind * (reprsented here as `Normal`). Types used in positions where a number +/// a type of kind * (represented here as `Normal`). Types used in positions where a number /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] pub enum Kind { From 123256b722c07c9423a66c709112b6cee89ca78b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Sun, 16 Jun 2024 17:46:58 -0400 Subject: [PATCH 45/85] Update compiler/noirc_frontend/src/elaborator/mod.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/elaborator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 21070c17cd4..688675704e1 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1195,7 +1195,7 @@ impl<'context> Elaborator<'context> { // when adding checks after each struct field is resolved. let struct_ids = structs.keys().copied().collect::>(); - // First resolve each struct's generics as fields with nested structs + // First resolve each struct's generics because fields with nested structs // may themselves also contain generics. // Without resolving all generics first we will not be able to distinguish // between normal and numeric generics. From a021370ec5d28bc77efcb62c87302a410daf5296 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Jun 2024 14:52:19 +0000 Subject: [PATCH 46/85] ident and into_ident instead of from --- compiler/noirc_frontend/src/ast/expression.rs | 6 ++++++ compiler/noirc_frontend/src/ast/statement.rs | 20 +------------------ compiler/noirc_frontend/src/elaborator/mod.rs | 8 +++----- .../noirc_frontend/src/elaborator/types.rs | 11 +++++----- .../src/hir/resolution/resolver.rs | 14 +++++-------- 5 files changed, 21 insertions(+), 38 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 4f726ffa7b7..ed6e404365a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -70,6 +70,12 @@ impl UnresolvedGeneric { UnresolvedGeneric::Numeric { .. } => Kind::Numeric, } } + + pub(crate) fn ident(&self) -> &Ident { + match self { + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, + } + } } impl Display for UnresolvedGeneric { diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index b40be6e1f3c..57a62324064 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -8,7 +8,7 @@ use noirc_errors::{Span, Spanned}; use super::{ BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, - MethodCallExpression, UnresolvedGeneric, UnresolvedType, + MethodCallExpression, UnresolvedType, }; use crate::lexer::token::SpannedToken; use crate::macros_api::SecondaryAttribute; @@ -225,24 +225,6 @@ impl From for Ident { } } -impl From for Ident { - fn from(value: UnresolvedGeneric) -> Self { - match value { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, - } - } -} - -impl From<&UnresolvedGeneric> for Ident { - fn from(value: &UnresolvedGeneric) -> Self { - match value { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => { - ident.clone() - } - } - } -} - impl From for Expression { fn from(i: Ident) -> Expression { Expression { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 688675704e1..7572fefb031 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -464,7 +464,7 @@ impl<'context> Elaborator<'context> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - let ident = Ident::from(generic); + let ident = generic.ident(); let span = ident.0.span(); // Declare numeric generic if it is specified @@ -705,10 +705,7 @@ impl<'context> Elaborator<'context> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| { - let generic = Ident::from(generic); - self.find_generic(&generic.0.contents) - }) + .filter_map(|generic| self.find_generic(&generic.ident().0.contents)) .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) .collect(); @@ -1245,6 +1242,7 @@ impl<'context> Elaborator<'context> { } } }); + self.pop_scope(); } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6bfd97bf148..de92f7a189b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -6,7 +6,7 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{ - BinaryOpKind, Ident, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, + BinaryOpKind, IntegerBitSize, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTypeExpression, }, hir::{ @@ -135,7 +135,9 @@ impl<'context> Elaborator<'context> { // Check that any types with a type kind match the expected type kind supplied to this function if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - if *resolved_kind != kind { + // TODO: make this check more general with `*resolved_kind != kind` + // implicit numeric generics kind of messes up the check + if matches!(resolved_kind, Kind::Numeric) && matches!(kind, Kind::Normal) { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { name: name.to_string(), @@ -1424,15 +1426,14 @@ impl<'context> Elaborator<'context> { span: Span, typevar: TypeVariable, ) { - let ident = Ident::from(unresolved_generic); - let name = ident.0.contents; + let name = &unresolved_generic.ident().0.contents; // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); if let Some(generic) = self.find_generic(&rc_name) { self.push_err(ResolverError::DuplicateDefinition { - name, + name: name.clone(), first_span: generic.span, second_span: span, }); diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 5c30823b8f6..504bf89d357 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -885,7 +885,7 @@ impl<'a> Resolver<'a> { // Map the generic to a fresh type variable let id = self.interner.next_type_variable_id(); let typevar = TypeVariable::unbound(id); - let ident = Ident::from(generic); + let ident = generic.ident(); let span = ident.0.span(); // Check for name collisions of this generic @@ -900,7 +900,7 @@ impl<'a> Resolver<'a> { }; if let Some(generic) = self.find_generic(&name) { self.errors.push(ResolverError::DuplicateDefinition { - name: ident.0.contents, + name: ident.0.contents.clone(), first_span: generic.span, second_span: span, }); @@ -937,15 +937,14 @@ impl<'a> Resolver<'a> { span: Span, typevar: TypeVariable, ) { - let ident = Ident::from(unresolved_generic); - let name = ident.0.contents; + let name = &unresolved_generic.ident().0.contents; // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); if let Some(generic) = self.find_generic(&rc_name) { self.errors.push(ResolverError::DuplicateDefinition { - name, + name: name.clone(), first_span: generic.span, second_span: span, }); @@ -1101,10 +1100,7 @@ impl<'a> Resolver<'a> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| { - let generic = Ident::from(generic); - self.find_generic(&generic.0.contents) - }) + .filter_map(|generic| self.find_generic(&generic.ident().0.contents)) .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) .collect(); From 1d0561204121f1a0a949213cf9196add85fe1e07 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Jun 2024 14:55:11 +0000 Subject: [PATCH 47/85] break in inner loop for finding implicit num generics in struct --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 7572fefb031..e5e1a3928f2 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1238,6 +1238,7 @@ impl<'context> Elaborator<'context> { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { generic.kind = Kind::Numeric; + break; } } } From 43cb4d0fecb6987f871c61e5cdb3fe515d59e7ce Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Jun 2024 17:06:31 +0000 Subject: [PATCH 48/85] remove generic_idents and switch collection of generics into dc_mod instead of in the elaborator --- compiler/noirc_frontend/src/elaborator/mod.rs | 120 ++++-------------- .../noirc_frontend/src/elaborator/traits.rs | 18 --- .../noirc_frontend/src/elaborator/types.rs | 9 +- .../src/hir/def_collector/dc_mod.rs | 47 ++++++- .../src/hir/resolution/resolver.rs | 1 - .../noirc_frontend/src/hir/type_check/mod.rs | 1 - .../noirc_frontend/src/hir_def/function.rs | 3 - compiler/noirc_frontend/src/tests.rs | 4 +- 8 files changed, 79 insertions(+), 124 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index e5e1a3928f2..64926175b9f 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -106,11 +106,6 @@ pub struct Elaborator<'context> { /// were declared in. generics: Vec, - /// The idents for each numeric generic on a function. - /// These definitions are generated when creating function metas - /// and need to be brought into scope later when elaborating the function body. - generic_idents: Vec, - /// When resolving lambda expressions, we need to keep track of the variables /// that are captured. We do this in order to create the hidden environment /// parameter for the lambda function. @@ -189,7 +184,6 @@ impl<'context> Elaborator<'context> { nested_loops: 0, in_contract: false, generics: Vec::new(), - generic_idents: Vec::new(), lambda_stack: Vec::new(), self_type: None, current_item: None, @@ -232,10 +226,6 @@ impl<'context> Elaborator<'context> { this.define_type_alias(alias_id, alias); } - // Must collect trait generics before we define function metas as we need to have resolved generics - // when we define trait impl function metas - this.collect_trait_generics(&items.traits); - // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); @@ -284,10 +274,8 @@ impl<'context> Elaborator<'context> { /// back to the previous length. fn recover_generics(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { let generics_count = self.generics.len(); - let generic_idents_count = self.generic_idents.len(); let ret = f(self); self.generics.truncate(generics_count); - self.generic_idents.truncate(generic_idents_count); ret } @@ -337,17 +325,22 @@ impl<'context> Elaborator<'context> { self.in_unconstrained_fn = true; } + // Introduce all numeric generics into scope + for generic in &func_meta.all_generics { + if let Kind::Numeric = generic.kind { + let definition = DefinitionKind::GenericType(generic.type_var.clone()); + let ident = Ident::new(generic.name.to_string(), generic.span); + self.add_variable_decl_inner(ident, false, false, false, definition); + // self.interner.push_definition_type(hir_ident.id, typ); + } + } + // The DefinitionIds for each parameter were already created in define_function_meta // so we need to reintroduce the same IDs into scope here. for parameter in &func_meta.parameter_idents { let name = self.interner.definition_name(parameter.id).to_owned(); self.add_existing_variable_to_scope(name, parameter.clone(), true); } - // We should introduce the IDs for numeric generics into scope as we do with parameters - for numeric_generic in &func_meta.generic_idents { - let name = self.interner.definition_name(numeric_generic.id).to_owned(); - self.add_existing_variable_to_scope(name, numeric_generic.clone(), false); - } self.generics = func_meta.all_generics.clone(); @@ -468,7 +461,22 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - self.try_add_numeric_generic(generic, typevar.clone()); + // self.try_add_numeric_generic(generic); + let typ = if let UnresolvedGeneric::Numeric { ident, typ } = generic { + let typ = self.resolve_type(typ.clone()); + if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { + let unsupported_typ_err = CompilationError::ResolverError( + ResolverError::UnsupportedNumericGenericType { + ident: ident.clone(), + typ: typ.clone(), + }, + ); + self.errors.push((unsupported_typ_err, self.file)); + } + typ + } else { + Type::Error + }; // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -495,11 +503,7 @@ impl<'context> Elaborator<'context> { } /// If a numeric generic has been specified, add it to the current scope. - pub(super) fn try_add_numeric_generic( - &mut self, - generic: &UnresolvedGeneric, - typevar: TypeVariable, - ) { + pub(super) fn try_add_numeric_generic(&mut self, generic: &UnresolvedGeneric) { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { @@ -510,18 +514,6 @@ impl<'context> Elaborator<'context> { }); self.errors.push((unsupported_typ_err, self.file)); } - let definition = DefinitionKind::GenericType(typevar.clone()); - let hir_ident = - self.add_variable_decl_inner(ident.clone(), false, false, false, definition); - - // Push the definition type because if one is missing, when the numeric generic is used in an expression - // its definition type will be resolved to a polymorphic integer or field. - // We do not yet fully support bool generics but this will be a foot-gun once we look to add support - // and can lead to confusing errors. - self.interner.push_definition_type(hir_ident.id, typ); - // Store the ident of the numeric generic to set up the function meta so that - // any numeric generics are accurately brought into scope. - self.generic_idents.push(hir_ident); } } @@ -722,7 +714,6 @@ impl<'context> Elaborator<'context> { trait_impl: self.current_trait_impl, parameters: parameters.into(), parameter_idents, - generic_idents: self.generic_idents.clone(), return_type: func.def.return_type.clone(), return_visibility: func.def.return_visibility, has_body: !func.def.body.is_empty(), @@ -860,7 +851,6 @@ impl<'context> Elaborator<'context> { self.self_type = None; self.current_trait_impl = None; self.generics.clear(); - self.generic_idents.clear(); } fn collect_impls( @@ -871,23 +861,15 @@ impl<'context> Elaborator<'context> { self.local_module = module; for (generics, span, unresolved) in impls { - self.push_scope(); - self.file = unresolved.file_id; let old_generic_count = self.generics.len(); - let old_generic_ident_count = self.generic_idents.len(); self.add_generics(generics); self.declare_methods_on_struct(false, unresolved, *span); self.generics.truncate(old_generic_count); - self.generic_idents.truncate(old_generic_ident_count); - - self.pop_scope(); } } fn collect_trait_impl(&mut self, trait_impl: &mut UnresolvedTraitImpl) { - self.push_scope(); - self.local_module = trait_impl.module_id; self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; @@ -959,12 +941,9 @@ impl<'context> Elaborator<'context> { } self.generics.clear(); - self.generic_idents.clear(); self.current_trait_impl = None; self.self_type = None; - - self.pop_scope(); } fn get_module_mut( @@ -1172,8 +1151,6 @@ impl<'context> Elaborator<'context> { } fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { - self.push_scope(); - self.file = alias.file_id; self.local_module = alias.module_id; @@ -1182,9 +1159,6 @@ impl<'context> Elaborator<'context> { let typ = self.resolve_type(alias.type_alias_def.typ); self.interner.set_type_alias(alias_id, typ, generics); self.generics.clear(); - self.generic_idents.clear(); - - self.pop_scope(); } fn collect_struct_definitions(&mut self, structs: BTreeMap) { @@ -1192,37 +1166,9 @@ impl<'context> Elaborator<'context> { // when adding checks after each struct field is resolved. let struct_ids = structs.keys().copied().collect::>(); - // First resolve each struct's generics because fields with nested structs - // may themselves also contain generics. - // Without resolving all generics first we will not be able to distinguish - // between normal and numeric generics. - for (type_id, typ) in structs.iter() { - self.push_scope(); - - self.file = typ.file_id; - self.local_module = typ.module_id; - - self.recover_generics(|this| { - this.current_item = Some(DependencyId::Struct(*type_id)); - - this.resolving_ids.insert(*type_id); - - let generics = this.add_generics(&typ.struct_def.generics); - this.interner.update_struct(*type_id, |struct_def| { - struct_def.generics = generics; - }); - - this.resolving_ids.remove(type_id); - }); - - self.pop_scope(); - } - // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. for (type_id, typ) in structs { - self.push_scope(); - self.file = typ.file_id; self.local_module = typ.module_id; @@ -1243,8 +1189,6 @@ impl<'context> Elaborator<'context> { } } }); - - self.pop_scope(); } // Check whether the struct fields have nested slices @@ -1361,8 +1305,6 @@ impl<'context> Elaborator<'context> { } for ((self_type, local_module), function_sets) in impls { - self.push_scope(); - self.local_module = *local_module; for (generics, _, function_set) in function_sets { @@ -1374,15 +1316,10 @@ impl<'context> Elaborator<'context> { self.define_function_metas_for_functions(function_set); self.self_type = None; self.generics.clear(); - self.generic_idents.clear(); } - - self.pop_scope(); } for trait_impl in trait_impls { - self.push_scope(); - self.file = trait_impl.file_id; self.local_module = trait_impl.module_id; @@ -1424,9 +1361,6 @@ impl<'context> Elaborator<'context> { trait_impl.resolved_object_type = self.self_type.take(); trait_impl.impl_id = self.current_trait_impl.take(); self.generics.clear(); - self.generic_idents.clear(); - - self.pop_scope(); } } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 972f9225905..f21011bf6e8 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -21,23 +21,8 @@ use crate::{ use super::Elaborator; impl<'context> Elaborator<'context> { - pub fn collect_trait_generics(&mut self, traits: &BTreeMap) { - for (trait_id, unresolved_trait) in traits { - self.push_scope(); - self.recover_generics(|this| { - this.add_generics(&unresolved_trait.trait_def.generics); - - this.interner.update_trait(*trait_id, |trait_def| { - trait_def.generics = this.generics.clone(); - }); - }); - self.pop_scope(); - } - } - pub fn collect_traits(&mut self, traits: BTreeMap) { for (trait_id, unresolved_trait) in traits { - self.push_scope(); self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); this.add_existing_generics( @@ -64,7 +49,6 @@ impl<'context> Elaborator<'context> { if self.crate_id.is_stdlib() { self.interner.try_add_operator_trait(trait_id); } - self.pop_scope(); } } @@ -173,7 +157,6 @@ impl<'context> Elaborator<'context> { func_id: FuncId, ) { let old_generic_count = self.generics.len(); - let old_generic_ident_count = self.generic_idents.len(); self.scopes.start_function(); @@ -204,6 +187,5 @@ impl<'context> Elaborator<'context> { let _ = self.scopes.end_function(); // Don't check the scope tree for unused variables, they can't be used in a declaration anyway. self.generics.truncate(old_generic_count); - self.generic_idents.truncate(old_generic_ident_count); } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index de92f7a189b..b4052c117bb 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1412,11 +1412,8 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { - self.add_existing_generic( - unresolved_generic, - unresolved_generic.span(), - generic.type_var.clone(), - ); + let type_var = generic.type_var.clone(); + self.add_existing_generic(unresolved_generic, unresolved_generic.span(), type_var); } } @@ -1439,7 +1436,7 @@ impl<'context> Elaborator<'context> { }); } else { // Declare numeric generic if it is specified - self.try_add_numeric_generic(unresolved_generic, typevar.clone()); + self.try_add_numeric_generic(unresolved_generic); let resolved_generic = ResolvedGeneric { name: rc_name, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 5c196324b7d..a62340b8f48 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,7 +1,9 @@ +use std::rc::Rc; use std::{collections::HashMap, path::Path, vec}; use acvm::{AcirField, FieldElement}; use fm::{FileId, FileManager, FILE_EXTENSION}; +use iter_extended::vecmap; use noirc_errors::Location; use num_bigint::BigUint; use num_traits::Num; @@ -9,14 +11,16 @@ use num_traits::Num; use crate::ast::{ FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItem, TraitItem, - TypeImpl, + TypeImpl, UnresolvedGenerics, }; +use crate::Generics; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, macros_api::MacroProcessor, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, + ResolvedGeneric, TypeVariable, }; use super::{ @@ -332,12 +336,47 @@ impl<'a> ModCollector<'a> { definition_errors.push((error.into(), self.file_id)); } + let resolved_generics = + Self::resolve_generics(context, &unresolved.struct_def.generics); + + context.def_interner.update_struct(id, |struct_def| { + struct_def.generics = resolved_generics; + }); + // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); + + // let resolved_generics = unresolved.struct_def.generics.iter().map(|generic| { + // let resolved_generic = ResolvedGeneric { + // name: generic.ident().0.contents.clone(), + // type_var: typevar.clone(), + // kind: generic.kind(), + // span, + // }; + // resolved_generic + // }); } definition_errors } + /// Resolve generics during definition collection + /// Generics need to be resolved before elaboration to distinguish + /// between normal and numeric generics. + fn resolve_generics(context: &mut Context, generics: &UnresolvedGenerics) -> Generics { + vecmap(generics, |generic| { + // Map the generic to a fresh type variable + let id = context.def_interner.next_type_variable_id(); + let type_var = TypeVariable::unbound(id); + let ident = generic.ident(); + let span = ident.0.span(); + + // Check for name collisions of this generic + let name = Rc::new(ident.0.contents.clone()); + + ResolvedGeneric { name, type_var, kind: generic.kind(), span } + }) + } + /// Collect any type aliases definitions declared within the ast. /// Returns a vector of errors if any type aliases were already defined. fn collect_type_aliases( @@ -526,6 +565,12 @@ impl<'a> ModCollector<'a> { fns_with_default_impl: unresolved_functions, }; context.def_interner.push_empty_trait(trait_id, &unresolved); + + let resolved_generics = Self::resolve_generics(context, &unresolved.trait_def.generics); + context.def_interner.update_trait(trait_id, |trait_def| { + trait_def.generics = resolved_generics; + }); + self.def_collector.items.traits.insert(trait_id, unresolved); } errors diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 504bf89d357..729a7e9b943 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1123,7 +1123,6 @@ impl<'a> Resolver<'a> { all_generics: Vec::new(), is_trait_function: false, parameter_idents: Vec::new(), - generic_idents: Vec::new(), function_body: FunctionBody::Resolved, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index ec9ad60b895..b2d15911147 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -560,7 +560,6 @@ pub mod test { has_inline_attribute: false, all_generics: Vec::new(), parameter_idents: Vec::new(), - generic_idents: Vec::new(), function_body: FunctionBody::Resolved, }; interner.push_fn_meta(func_meta, func_id); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 163f6a657d2..d11d3132212 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -100,9 +100,6 @@ pub struct FuncMeta { /// Note that this includes separate entries for each identifier in e.g. tuple patterns. pub parameter_idents: Vec, - /// The HirIdent of each numeric generic identifier - pub generic_idents: Vec, - pub return_type: FunctionReturnType, pub return_visibility: Visibility, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index f73aafe446e..ca5e9d1e760 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1481,6 +1481,7 @@ fn bool_numeric_generic() { } "#; let errors = get_program_errors_elaborator(src); + dbg!(errors.clone()); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1517,7 +1518,8 @@ fn bool_generic_as_loop_bound() { } "#; let errors = get_program_errors_elaborator(src); - assert_eq!(errors.len(), 2); + dbg!(errors.clone()); + assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, From 0879ffa223b5d815101215d34f262dde52b783cb Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Jun 2024 18:41:19 +0000 Subject: [PATCH 49/85] clippy --- compiler/noirc_frontend/src/elaborator/mod.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index fcf33a62949..2a50a2049cb 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -462,22 +462,7 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - // self.try_add_numeric_generic(generic); - let typ = if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = self.resolve_type(typ.clone()); - if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { - let unsupported_typ_err = CompilationError::ResolverError( - ResolverError::UnsupportedNumericGenericType { - ident: ident.clone(), - typ: typ.clone(), - }, - ); - self.errors.push((unsupported_typ_err, self.file)); - } - typ - } else { - Type::Error - }; + self.try_add_numeric_generic(generic); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); From 57e5bd5584109e92a4b11793ec6dcd3e30487c60 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 17 Jun 2024 21:40:23 +0000 Subject: [PATCH 50/85] get definition typ accurate and add a type to the numeric kind --- compiler/noirc_frontend/src/ast/expression.rs | 4 +- compiler/noirc_frontend/src/elaborator/mod.rs | 34 ++++++------ .../noirc_frontend/src/elaborator/types.rs | 52 +++++++++++-------- compiler/noirc_frontend/src/hir_def/types.rs | 9 ++-- compiler/noirc_frontend/src/tests.rs | 4 +- 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 3a47576cf04..2838a57a28a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -8,7 +8,7 @@ use crate::ast::{ use crate::macros_api::StructId; use crate::node_interner::ExprId; use crate::token::{Attributes, Token}; -use crate::Kind; +use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; @@ -76,7 +76,7 @@ impl UnresolvedGeneric { pub fn kind(&self) -> Kind { match self { UnresolvedGeneric::Variable(_) => Kind::Normal, - UnresolvedGeneric::Numeric { .. } => Kind::Numeric, + UnresolvedGeneric::Numeric { .. } => Kind::Numeric { typ: Box::new(Type::Error) }, } } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 2a50a2049cb..05f9b09948b 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -328,11 +328,12 @@ impl<'context> Elaborator<'context> { // Introduce all numeric generics into scope for generic in &func_meta.all_generics { - if let Kind::Numeric = generic.kind { + if let Kind::Numeric { typ } = &generic.kind { let definition = DefinitionKind::GenericType(generic.type_var.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); - self.add_variable_decl_inner(ident, false, false, false, definition); - // self.interner.push_definition_type(hir_ident.id, typ); + let hir_ident = + self.add_variable_decl_inner(ident, false, false, false, definition); + self.interner.push_definition_type(hir_ident.id, *typ.clone()); } } @@ -462,17 +463,13 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - self.try_add_numeric_generic(generic); + let kind = self.try_add_numeric_generic(generic); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - let resolved_generic = ResolvedGeneric { - name: name.clone(), - type_var: typevar.clone(), - kind: generic.kind(), - span, - }; + let resolved_generic = + ResolvedGeneric { name: name.clone(), type_var: typevar.clone(), kind, span }; if let Some(generic) = self.find_generic(&name) { self.push_err(ResolverError::DuplicateDefinition { @@ -489,7 +486,7 @@ impl<'context> Elaborator<'context> { } /// If a numeric generic has been specified, add it to the current scope. - pub(super) fn try_add_numeric_generic(&mut self, generic: &UnresolvedGeneric) { + pub(super) fn try_add_numeric_generic(&mut self, generic: &UnresolvedGeneric) -> Kind { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { @@ -500,6 +497,9 @@ impl<'context> Elaborator<'context> { }); self.errors.push((unsupported_typ_err, self.file)); } + Kind::Numeric { typ: Box::new(typ) } + } else { + Kind::Normal } } @@ -557,7 +557,7 @@ impl<'context> Elaborator<'context> { assert_eq!(resolved_generics.len(), bound.trait_generics.len()); let trait_generics = vecmap( resolved_generics.clone().iter().zip(&bound.trait_generics), - |(resolved_generic, typ)| self.resolve_type_inner(typ.clone(), resolved_generic.kind), + |(resolved_generic, typ)| self.resolve_type_inner(typ.clone(), &resolved_generic.kind), ); let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; @@ -649,7 +649,7 @@ impl<'context> Elaborator<'context> { UnresolvedTypeData::TraitAsType(path, args) => { self.desugar_impl_trait_arg(path, args, &mut generics, &mut trait_constraints) } - _ => self.resolve_type_inner(typ, Kind::Normal), + _ => self.resolve_type_inner(typ, &Kind::Normal), }; self.check_if_type_is_valid_for_program_input( @@ -776,7 +776,8 @@ impl<'context> Elaborator<'context> { // ``` continue; } - *kind = Kind::Numeric; + // TODO: check if we need this + *kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; 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); @@ -1169,7 +1170,8 @@ impl<'context> Elaborator<'context> { for generic in struct_def.generics.iter_mut() { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { - generic.kind = Kind::Numeric; + // TODO: might need the actual type here + generic.kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; break; } } @@ -1325,7 +1327,7 @@ impl<'context> Elaborator<'context> { .iter() .enumerate() .map(|(i, generic)| { - self.resolve_type_inner(generic.clone(), resolved_generics[i].kind) + self.resolve_type_inner(generic.clone(), &resolved_generics[i].kind) }) .collect() } else { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 72f5328b4a9..b6dae684e2f 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -40,12 +40,12 @@ use super::{lints, Elaborator}; impl<'context> Elaborator<'context> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, Kind::Normal) + self.resolve_type_inner(typ, &Kind::Normal) } /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. - pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: Kind) -> Type { + pub fn resolve_type_inner(&mut self, typ: UnresolvedType, kind: &Kind) -> Type { use crate::ast::UnresolvedTypeData::*; let span = typ.span; @@ -56,7 +56,11 @@ impl<'context> Elaborator<'context> { let elem = Box::new(self.resolve_type_inner(*elem, kind)); let mut size = self.convert_expression_type(size); if let Type::NamedGeneric(type_var, name, _) = size { - size = Type::NamedGeneric(type_var, name, Kind::Numeric); + size = Type::NamedGeneric( + type_var, + name, + Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + ); } Type::Array(Box::new(size), elem) } @@ -70,14 +74,22 @@ impl<'context> Elaborator<'context> { String(size) => { 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); + resolved_size = Type::NamedGeneric( + type_var, + name, + Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + ); } 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); + resolved_size = Type::NamedGeneric( + type_var, + name, + Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + ); } let fields = self.resolve_type_inner(*fields, kind); Type::FmtString(Box::new(resolved_size), Box::new(fields)) @@ -137,7 +149,7 @@ impl<'context> Elaborator<'context> { if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { // TODO: make this check more general with `*resolved_kind != kind` // implicit numeric generics kind of messes up the check - if matches!(resolved_kind, Kind::Numeric) && matches!(kind, Kind::Normal) { + if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { name: name.to_string(), @@ -192,7 +204,7 @@ impl<'context> Elaborator<'context> { let id = type_alias.id; let mut args = vecmap(type_alias.generics.iter().zip(args), |(generic, arg)| { - self.resolve_type_inner(arg, generic.kind) + self.resolve_type_inner(arg, &generic.kind) }); self.verify_generics_count(expected_generic_count, &mut args, span, || { @@ -242,7 +254,7 @@ impl<'context> Elaborator<'context> { let mut args = vecmap(struct_type.borrow().generics.iter().zip(args), |(generic, arg)| { - self.resolve_type_inner(arg, generic.kind) + self.resolve_type_inner(arg, &generic.kind) }); self.verify_generics_count(expected_generic_count, &mut args, span, || { @@ -260,7 +272,12 @@ impl<'context> Elaborator<'context> { } } - fn resolve_trait_as_type(&mut self, path: Path, args: Vec, kind: Kind) -> Type { + fn resolve_trait_as_type( + &mut self, + path: Path, + args: Vec, + kind: &Kind, + ) -> Type { let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); if let Some(t) = self.lookup_trait_or_error(path) { @@ -294,11 +311,8 @@ impl<'context> Elaborator<'context> { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; if let Some(generic) = self.find_generic(name) { - return Some(Type::NamedGeneric( - generic.type_var.clone(), - generic.name.clone(), - generic.kind, - )); + let generic = generic.clone(); + return Some(Type::NamedGeneric(generic.type_var, generic.name, generic.kind)); } } @@ -1436,14 +1450,10 @@ impl<'context> Elaborator<'context> { }); } else { // Declare numeric generic if it is specified - self.try_add_numeric_generic(unresolved_generic); + let kind = self.try_add_numeric_generic(unresolved_generic); - let resolved_generic = ResolvedGeneric { - name: rc_name, - type_var: typevar.clone(), - kind: unresolved_generic.kind(), - span, - }; + let resolved_generic = + ResolvedGeneric { name: rc_name, type_var: typevar.clone(), kind, span }; self.generics.push(resolved_generic); } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 835fb89851a..2fad8ec93ef 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -193,17 +193,18 @@ impl Type { /// For example, the type of a struct field or a function parameter is expected to be /// a type of kind * (represented here as `Normal`). Types used in positions where a number /// is expected (such as in an array length position) are expected to be of kind `Kind::Numeric`. -#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +#[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum Kind { Normal, - Numeric, + Numeric { typ: Box }, } impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Kind::Normal => write!(f, "normal"), - Kind::Numeric => write!(f, "numeric"), + // TODO(numeric generics): Use the typ of the numeric in the kind + Kind::Numeric { .. } => write!(f, "numeric"), } } } @@ -747,7 +748,7 @@ impl Type { 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| { - if let Type::NamedGeneric(_, name, Kind::Numeric) = typ { + if let Type::NamedGeneric(_, name, Kind::Numeric { .. }) = typ { found_names.push(name.to_string()); true } else { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 2862b11a475..50c76f89857 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1481,7 +1481,6 @@ fn bool_numeric_generic() { } "#; let errors = get_program_errors_elaborator(src); - dbg!(errors.clone()); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1518,8 +1517,7 @@ fn bool_generic_as_loop_bound() { } "#; let errors = get_program_errors_elaborator(src); - dbg!(errors.clone()); - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 2); assert!(matches!( errors[0].0, From 4a1f8992f5296bc38505fba8e98d9fd3a4264c97 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 19 Jun 2024 20:13:01 +0000 Subject: [PATCH 51/85] fix up frontend tests --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 - compiler/noirc_frontend/src/elaborator/types.rs | 2 +- .../noirc_frontend/src/hir/def_collector/dc_mod.rs | 10 ---------- compiler/noirc_frontend/src/tests.rs | 14 ++++++++++---- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 35725728279..f514a1ac6cc 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -860,7 +860,6 @@ impl<'context> Elaborator<'context> { self.local_module = trait_impl.module_id; self.file = trait_impl.file_id; self.current_trait_impl = trait_impl.impl_id; - trait_impl.trait_id = self.resolve_trait_by_path(trait_impl.trait_path.clone()); let self_type = trait_impl.methods.self_type.clone(); let self_type = diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b6dae684e2f..eb58f8f238b 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1438,7 +1438,7 @@ impl<'context> Elaborator<'context> { typevar: TypeVariable, ) { let name = &unresolved_generic.ident().0.contents; - + dbg!(name.clone()); // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index a62340b8f48..e6accd3272b 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -345,16 +345,6 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - - // let resolved_generics = unresolved.struct_def.generics.iter().map(|generic| { - // let resolved_generic = ResolvedGeneric { - // name: generic.ident().0.contents.clone(), - // type_var: typevar.clone(), - // kind: generic.kind(), - // span, - // }; - // resolved_generic - // }); } definition_errors } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 50c76f89857..8e1bdc06365 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -51,7 +51,7 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec<(CompilationError, F pub(crate) fn get_program( src: &str, - use_elaborator: bool, + use_legacy: bool, ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); @@ -84,7 +84,7 @@ pub(crate) fn get_program( &mut context, program.clone().into_sorted(), root_file_id, - use_elaborator, + use_legacy, &[], // No macro processors )); } @@ -96,7 +96,7 @@ pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { } pub(crate) fn get_program_errors_elaborator(src: &str) -> Vec<(CompilationError, FileId)> { - get_program(src, true).2 + get_program(src, false).2 } #[test] @@ -1332,7 +1332,13 @@ fn for_loop_over_array() { hello(array); } "#; - assert_eq!(get_program_errors(src).len(), 0); + let errors = get_program_errors(src); + assert_eq!(get_program_errors(src).len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { .. }) + )); } // Regression for #4545 From 31e79dfacd6144b31693992c4c04fffe5d944e76 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 15:55:58 +0000 Subject: [PATCH 52/85] update fmt to handle more complex generics --- tooling/nargo_fmt/src/items.rs | 5 +++-- tooling/nargo_fmt/src/utils.rs | 23 ++++++++++++----------- tooling/nargo_fmt/tests/input/fn.nr | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tooling/nargo_fmt/src/items.rs b/tooling/nargo_fmt/src/items.rs index 49cb9c16bef..54e6ff3b254 100644 --- a/tooling/nargo_fmt/src/items.rs +++ b/tooling/nargo_fmt/src/items.rs @@ -74,7 +74,8 @@ impl<'me, T> Items<'me, T> { let mut different_line = false; let leading = self.visitor.slice(start..end); - let leading_trimmed = leading.trim(); + // Trim any possible whitespace before and after a comma separator + let leading_trimmed = leading.trim().trim_start_matches(',').trim(); let starts_with_block_comment = leading_trimmed.starts_with("/*"); let ends_with_block_comment = leading_trimmed.ends_with("*/"); @@ -96,7 +97,7 @@ impl<'me, T> Items<'me, T> { pub(crate) fn trailing(&mut self, start: u32, end: u32, is_last: bool) -> String { let slice = self.visitor.slice(start..end); let comment_end = find_comment_end(slice, is_last); - let trailing = slice[..comment_end].trim_matches(',').trim(); + let trailing = slice[..comment_end].trim_start_matches(',').trim(); self.last_position = start + (comment_end as u32); trailing.to_string() } diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index dbf1bf8d674..d56865928b8 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -80,6 +80,7 @@ pub(crate) fn find_comment_end(slice: &str, is_last: bool) -> usize { std::cmp::max(find_comment_end(slice) + block, separator_index + 1) } (_, Some(newline)) if newline > separator_index => newline + 1, + (None, None) => 0, _ => slice.len(), } } else if let Some(newline_index) = newline_index { @@ -176,17 +177,17 @@ impl HasItem for UnresolvedGeneric { } fn format(self, visitor: &FmtVisitor, _shape: Shape) -> String { - visitor.slice(self.span()).into() - } - - fn start(&self) -> u32 { - let start_offset = match self { - UnresolvedGeneric::Variable(_) => 0, - // We offset by 4 here to account for the `let` identifier - // and the extra space before the actual generic numeric type - UnresolvedGeneric::Numeric { .. } => 4, - }; - self.span().start() - start_offset + match self { + UnresolvedGeneric::Variable(_) => visitor.slice(self.span()).into(), + UnresolvedGeneric::Numeric { ident, typ } => { + let mut result = "".to_owned(); + result.push_str(&ident.0.contents); + result.push_str(": "); + let typ = rewrite::typ(visitor, _shape, typ); + result.push_str(&typ); + result + } + } } fn end(&self) -> u32 { diff --git a/tooling/nargo_fmt/tests/input/fn.nr b/tooling/nargo_fmt/tests/input/fn.nr index 2e024f4f993..496afd8d7a4 100644 --- a/tooling/nargo_fmt/tests/input/fn.nr +++ b/tooling/nargo_fmt/tests/input/fn.nr @@ -45,7 +45,7 @@ fn main( pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} -fn id < T , let I : Field > ( x : [ Field ; I ] ) -> [Field; I ] { } +fn id< T , let I : Field > ( x : [ Field ; I ] ) -> [Field; I ] { } fn id_two(x: [Field ; I]) -> [ Field; I] {} From 7ddae3e00e6ab1ca3a7372b0528841e807071f6d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 16:21:20 +0000 Subject: [PATCH 53/85] ignore stdlib warnings for now until I update the stdlib --- compiler/noirc_driver/tests/stdlib_warnings.rs | 1 + compiler/noirc_frontend/src/elaborator/mod.rs | 8 +++++--- compiler/noirc_frontend/src/elaborator/types.rs | 4 ++-- tooling/nargo_cli/tests/stdlib-tests.rs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_driver/tests/stdlib_warnings.rs b/compiler/noirc_driver/tests/stdlib_warnings.rs index 327c8daad06..84f46793caf 100644 --- a/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -3,6 +3,7 @@ use std::path::Path; use noirc_driver::{file_manager_with_stdlib, prepare_crate, ErrorsAndWarnings}; use noirc_frontend::hir::{def_map::parse_file, Context}; +#[ignore = "Temporarily ignoring the test until the stdlib is updated to use explicit numeric generics"] #[test] fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> { // We use a minimal source file so that if stdlib produces warnings then we can expect these warnings to _always_ diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 77bccc75fbf..4453a38cf66 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -473,7 +473,7 @@ impl<'context> Elaborator<'context> { let span = ident.0.span(); // Declare numeric generic if it is specified - let kind = self.try_add_numeric_generic(generic); + let kind = self.resolve_generic_kind(generic); // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); @@ -495,8 +495,10 @@ impl<'context> Elaborator<'context> { }) } - /// If a numeric generic has been specified, add it to the current scope. - pub(super) fn try_add_numeric_generic(&mut self, generic: &UnresolvedGeneric) -> Kind { + /// Return the kind of an unresolved generic. + /// If a numeric generic has been specified, resolve the annotated type to make + /// sure only primitive numeric types are being used. + pub(super) fn resolve_generic_kind(&mut self, generic: &UnresolvedGeneric) -> Kind { if let UnresolvedGeneric::Numeric { ident, typ } = generic { let typ = self.resolve_type(typ.clone()); if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b6b673e1af9..ebda9973bf5 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1440,7 +1440,7 @@ impl<'context> Elaborator<'context> { typevar: TypeVariable, ) { let name = &unresolved_generic.ident().0.contents; - dbg!(name.clone()); + // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); @@ -1452,7 +1452,7 @@ impl<'context> Elaborator<'context> { }); } else { // Declare numeric generic if it is specified - let kind = self.try_add_numeric_generic(unresolved_generic); + let kind = self.resolve_generic_kind(unresolved_generic); let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), kind, span }; diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs index 5badd76605a..1c35f41c36f 100644 --- a/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -33,7 +33,7 @@ fn run_stdlib_tests() { let (mut context, dummy_crate_id) = prepare_package(&file_manager, &parsed_files, &dummy_package); - let result = check_crate(&mut context, dummy_crate_id, true, false, false); + let result = check_crate(&mut context, dummy_crate_id, false, false, false); report_errors(result, &context.file_manager, true, false) .expect("Error encountered while compiling standard library"); From c58b7a693080db46381e27697162c0ffd285b718 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 16:28:40 +0000 Subject: [PATCH 54/85] fix NamedGeneric signature in is_valid_for_unconstrained_boundary --- 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 94308b43267..e6c7cd9b6a6 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -928,7 +928,7 @@ impl Type { | Type::Constant(_) | Type::Slice(_) | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _) + | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _) | Type::FmtString(_, _) | Type::Error => true, From 4a7a9946e46d1e5735893a00abad959be3f32d44 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 17:49:45 +0000 Subject: [PATCH 55/85] fixup trailing in fmt item and build for nargo_cli --- tooling/nargo_cli/build.rs | 4 ++-- tooling/nargo_fmt/src/items.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index f2da161267d..47fa585a593 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -61,8 +61,8 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_NEW_FEATURE_TESTS: [&str; 3] = - ["macros", "wildcard_type", "type_definition_annotation"]; +const IGNORED_NEW_FEATURE_TESTS: [&str; 4] = + ["macros", "wildcard_type", "type_definition_annotation", "numeric_generics_explicit"]; fn read_test_cases( test_data_dir: &Path, diff --git a/tooling/nargo_fmt/src/items.rs b/tooling/nargo_fmt/src/items.rs index 54e6ff3b254..b7ec3542751 100644 --- a/tooling/nargo_fmt/src/items.rs +++ b/tooling/nargo_fmt/src/items.rs @@ -97,7 +97,7 @@ impl<'me, T> Items<'me, T> { pub(crate) fn trailing(&mut self, start: u32, end: u32, is_last: bool) -> String { let slice = self.visitor.slice(start..end); let comment_end = find_comment_end(slice, is_last); - let trailing = slice[..comment_end].trim_start_matches(',').trim(); + let trailing = slice[..comment_end].trim_matches(',').trim(); self.last_position = start + (comment_end as u32); trailing.to_string() } From 36be839cddc9bec7b30a7cd7f1f4b72076bbe98a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 18:01:41 +0000 Subject: [PATCH 56/85] expand comment in numeric_generic_in_trait_impl_with_extra_impl_generics test --- compiler/noirc_frontend/src/tests.rs | 78 +++++++--------------------- 1 file changed, 18 insertions(+), 60 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 8e1bdc06365..82e4bb8aebd 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -95,10 +95,6 @@ pub(crate) fn get_program_errors(src: &str) -> Vec<(CompilationError, FileId)> { get_program(src, false).2 } -pub(crate) fn get_program_errors_elaborator(src: &str) -> Vec<(CompilationError, FileId)> { - get_program(src, false).2 -} - #[test] fn check_trait_implemented_for_all_t() { let src = " @@ -1467,7 +1463,7 @@ fn struct_numeric_generic() { fn bar() { } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1486,7 +1482,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1503,7 +1499,7 @@ fn numeric_generic_binary_operation_type_mismatch() { check } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1522,7 +1518,7 @@ fn bool_generic_as_loop_bound() { assert(fields[0] == 1); } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 2); assert!(matches!( @@ -1546,7 +1542,7 @@ fn numeric_generic_in_function_signature() { let src = r#" fn foo(arr: [Field; N]) -> [Field; N] { arr } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert!(errors.is_empty()); } @@ -1558,7 +1554,7 @@ fn numeric_generic_as_struct_field_type() { b: N, } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1574,7 +1570,7 @@ fn numeric_generic_as_param_type() { x } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 3); // Error from the parameter type assert!(matches!( @@ -1604,7 +1600,7 @@ fn numeric_generic_used_in_nested_type_fail() { inner: N } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, @@ -1625,7 +1621,7 @@ fn numeric_generic_used_in_nested_type_pass() { inner: [u64; N], } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert!(errors.is_empty()); } @@ -1649,7 +1645,7 @@ fn numeric_generic_used_in_trait() { fn deserialize(fields: [Field; N], other: T) -> Self; } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); // 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. @@ -1670,7 +1666,10 @@ fn numeric_generic_in_trait_impl_with_extra_impl_generics() { d: T, } - // Make sure that `T` is placed before `N` as we want to test the order is correctly maintained + // 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() } @@ -1681,7 +1680,7 @@ fn numeric_generic_in_trait_impl_with_extra_impl_generics() { fn deserialize(fields: [Field; N]) -> Self; } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert!(errors.is_empty()); } @@ -1701,7 +1700,7 @@ fn numeric_generic_used_in_where_clause() { T::deserialize(fields) } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); assert!(errors.is_empty()); } @@ -1729,7 +1728,7 @@ fn implicit_numeric_generics_elaborator() { } } "#; - let errors = get_program_errors_elaborator(src); + let errors = get_program_errors(src); for error in errors.iter() { if let CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident }) = @@ -1740,45 +1739,4 @@ fn implicit_numeric_generics_elaborator() { panic!("Expected ResolverError::UseExplicitNumericGeneric but got {:?}", error); } } -} - -#[test] -fn nested_generic_elaborator() { - let src = r#" - trait Default { - fn default() -> Self; - } - - struct Option { - _is_some: bool, - _value: T, - } - - impl Option { - pub fn flatten(option: Option>) -> Option where T: Default { - if option._is_some { - option._value - } else { - Self { _is_some: false, _value: T::default() } - } - } - } - "#; - let errors = get_program_errors_elaborator(src); - assert!(errors.is_empty()); -} - -#[test] -fn single_parser_error_bad_numeric_generic() { - let src = r#" - fn id(x: [Field; I]) -> [Field; I] { - x - } - "#; - let errors = get_program_errors_elaborator(src); - assert!(has_parser_error(&errors)); - // We want to make sure that each ill-formed numeric generic (e.g. `` or ``) - // returns a single parser error. - // We have this test to make sure we are not displaying deceiving errors to the user. - assert_eq!(errors.len(), 1); -} +} \ No newline at end of file From 5a290d9191621b0583925520db970e504e074253 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 18:56:27 +0000 Subject: [PATCH 57/85] use resolve_generics inside of HirContext --- compiler/noirc_frontend/src/ast/expression.rs | 2 + compiler/noirc_frontend/src/elaborator/mod.rs | 14 +++- .../src/hir/def_collector/dc_mod.rs | 47 ++--------- compiler/noirc_frontend/src/hir/mod.rs | 23 +++++- .../src/hir/resolution/traits.rs | 19 ++--- compiler/noirc_frontend/src/node_interner.rs | 23 ++---- compiler/noirc_frontend/src/tests.rs | 81 ++++++++++++++++++- 7 files changed, 135 insertions(+), 74 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 2838a57a28a..e3d77ce872f 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -76,6 +76,8 @@ impl UnresolvedGeneric { pub fn kind(&self) -> Kind { match self { UnresolvedGeneric::Variable(_) => Kind::Normal, + // The inner numeric type of the kind cannot be resolved from an `UnresolvedGeneric` + // on its own. It is expected that the inner type is updated during elaboration. UnresolvedGeneric::Numeric { .. } => Kind::Numeric { typ: Box::new(Type::Error) }, } } diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 7446806720b..d8bc5ea89a7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -472,7 +472,7 @@ impl<'context> Elaborator<'context> { let ident = generic.ident(); let span = ident.0.span(); - // Declare numeric generic if it is specified + // Resolve the generic's kind let kind = self.resolve_generic_kind(generic); // Check for name collisions of this generic @@ -787,8 +787,7 @@ impl<'context> Elaborator<'context> { // ``` continue; } - // TODO: check if we need this - *kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; + *kind = Kind::Numeric { typ: 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); @@ -1185,6 +1184,15 @@ impl<'context> Elaborator<'context> { if found_generic == generic.name.as_str() { // TODO: might need the actual type here generic.kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; + + let ident = Ident::new(generic.name.to_string(), generic.span); + self.errors.push(( + CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { + ident, + }), + self.file, + )); + break; } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index e6accd3272b..6763c7d4b79 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,9 +1,7 @@ -use std::rc::Rc; use std::{collections::HashMap, path::Path, vec}; use acvm::{AcirField, FieldElement}; use fm::{FileId, FileManager, FILE_EXTENSION}; -use iter_extended::vecmap; use noirc_errors::Location; use num_bigint::BigUint; use num_traits::Num; @@ -11,16 +9,14 @@ use num_traits::Num; use crate::ast::{ FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItem, TraitItem, - TypeImpl, UnresolvedGenerics, + TypeImpl, }; -use crate::Generics; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, macros_api::MacroProcessor, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, - ResolvedGeneric, TypeVariable, }; use super::{ @@ -312,10 +308,12 @@ impl<'a> ModCollector<'a> { struct_def: struct_definition, }; + let resolved_generics = context.resolve_generics(&unresolved.struct_def.generics); + // Create the corresponding module for the struct namespace let id = match self.push_child_module(&name, self.file_id, false, false) { Ok(local_id) => { - context.def_interner.new_struct(&unresolved, krate, local_id, self.file_id) + context.def_interner.new_struct(&unresolved, resolved_generics, krate, local_id, self.file_id) } Err(error) => { definition_errors.push((error.into(), self.file_id)); @@ -336,37 +334,12 @@ impl<'a> ModCollector<'a> { definition_errors.push((error.into(), self.file_id)); } - let resolved_generics = - Self::resolve_generics(context, &unresolved.struct_def.generics); - - context.def_interner.update_struct(id, |struct_def| { - struct_def.generics = resolved_generics; - }); - // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); } definition_errors } - /// Resolve generics during definition collection - /// Generics need to be resolved before elaboration to distinguish - /// between normal and numeric generics. - fn resolve_generics(context: &mut Context, generics: &UnresolvedGenerics) -> Generics { - vecmap(generics, |generic| { - // Map the generic to a fresh type variable - let id = context.def_interner.next_type_variable_id(); - let type_var = TypeVariable::unbound(id); - let ident = generic.ident(); - let span = ident.0.span(); - - // Check for name collisions of this generic - let name = Rc::new(ident.0.contents.clone()); - - ResolvedGeneric { name, type_var, kind: generic.kind(), span } - }) - } - /// Collect any type aliases definitions declared within the ast. /// Returns a vector of errors if any type aliases were already defined. fn collect_type_aliases( @@ -384,8 +357,8 @@ impl<'a> ModCollector<'a> { module_id: self.module_id, type_alias_def: type_alias, }; - - let type_alias_id = context.def_interner.push_type_alias(&unresolved); + let resolved_generics = context.resolve_generics(&unresolved.type_alias_def.generics); + let type_alias_id = context.def_interner.push_type_alias(&unresolved, resolved_generics); // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] @@ -545,6 +518,7 @@ impl<'a> ModCollector<'a> { } } + let resolved_generics = context.resolve_generics(&trait_definition.generics); // And store the TraitId -> TraitType mapping somewhere it is reachable let unresolved = UnresolvedTrait { file_id: self.file_id, @@ -554,12 +528,7 @@ impl<'a> ModCollector<'a> { method_ids, fns_with_default_impl: unresolved_functions, }; - context.def_interner.push_empty_trait(trait_id, &unresolved); - - let resolved_generics = Self::resolve_generics(context, &unresolved.trait_def.generics); - context.def_interner.update_trait(trait_id, |trait_def| { - trait_def.generics = resolved_generics; - }); + context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); self.def_collector.items.traits.insert(trait_id, unresolved); } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 55dc22d6c5d..b624a87e23d 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -5,17 +5,20 @@ pub mod resolution; pub mod scope; pub mod type_check; +use crate::ast::UnresolvedGenerics; use crate::debug::DebugInstrumenter; use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::ParserError; -use crate::ParsedModule; +use crate::{Generics, ParsedModule, ResolvedGeneric, TypeVariable}; use def_map::{Contract, CrateDefMap}; use fm::FileManager; +use iter_extended::vecmap; use noirc_errors::Location; use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; +use std::rc::Rc; use self::def_map::TestFunction; @@ -256,4 +259,22 @@ impl Context<'_, '_> { pub fn module(&self, module_id: def_map::ModuleId) -> &def_map::ModuleData { module_id.module(&self.def_maps) } + + /// Generics need to be resolved before elaboration to distinguish + /// between normal and numeric generics. + /// This method is expected to be used during definition collection. + pub(crate) fn resolve_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { + vecmap(generics, |generic| { + // Map the generic to a fresh type variable + let id = self.def_interner.next_type_variable_id(); + let type_var = TypeVariable::unbound(id); + let ident = generic.ident(); + let span = ident.0.span(); + + // Check for name collisions of this generic + let name = Rc::new(ident.0.contents.clone()); + + ResolvedGeneric { name, type_var, kind: generic.kind(), span } + }) + } } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 804a0522e81..fb1d60e1578 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -17,9 +17,9 @@ use crate::{ }, hir_def::traits::{TraitConstant, TraitFunction, TraitImpl, TraitType}, node_interner::{FuncId, NodeInterner, TraitId}, - Shared, Type, TypeVariable, TypeVariableKind, + Shared, Type, TypeVariableKind, + GenericTypeVars }; -use crate::{GenericTypeVars, ResolvedGeneric}; use super::{ functions, get_module_mut, get_struct_type, @@ -37,14 +37,13 @@ pub(crate) fn resolve_traits( crate_id: CrateId, ) -> Vec<(CompilationError, FileId)> { for (trait_id, unresolved_trait) in &traits { - context.def_interner.push_empty_trait(*trait_id, unresolved_trait); + context.def_interner.push_empty_trait(*trait_id, unresolved_trait, vec![]); } let mut all_errors = Vec::new(); for (trait_id, unresolved_trait) in traits { - let generics = vecmap(&unresolved_trait.trait_def.generics, |_| { - TypeVariable::unbound(context.def_interner.next_type_variable_id()) - }); + let generics = context.resolve_generics(&unresolved_trait.trait_def.generics); + let generic_type_vars = generics.iter().map(|generic| generic.type_var.clone()).collect(); // Resolve order // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) @@ -53,17 +52,13 @@ pub(crate) fn resolve_traits( let _ = resolve_trait_constants(context, crate_id, &unresolved_trait); // 3. Trait Methods let (methods, errors) = - resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait, &generics); + resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait, &generic_type_vars); all_errors.extend(errors); context.def_interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = vecmap(generics, |type_var| { - let mut new_resolved = ResolvedGeneric::dummy(); - new_resolved.type_var = type_var; - new_resolved - }); + trait_def.generics = generics; }); // This check needs to be after the trait's methods are set since diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 4b8e2db2684..08eaabb1182 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -34,7 +34,6 @@ use crate::hir_def::{ use crate::token::{Attributes, SecondaryAttribute}; use crate::GenericTypeVars; use crate::Generics; -use crate::ResolvedGeneric; use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind}; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -548,7 +547,7 @@ impl NodeInterner { self.definition_to_type.insert(definition_id, typ); } - pub fn push_empty_trait(&mut self, type_id: TraitId, unresolved_trait: &UnresolvedTrait) { + pub fn push_empty_trait(&mut self, type_id: TraitId, unresolved_trait: &UnresolvedTrait, generics: Generics) { let self_type_typevar_id = self.next_type_variable_id(); let new_trait = Trait { @@ -556,13 +555,7 @@ impl NodeInterner { name: unresolved_trait.trait_def.name.clone(), crate_id: unresolved_trait.crate_id, location: Location::new(unresolved_trait.trait_def.span, unresolved_trait.file_id), - generics: vecmap(&unresolved_trait.trait_def.generics, |_| { - // Temporary type variable ids before the trait is resolved to its actual ids. - // This lets us record how many arguments the type expects so that other types - // can refer to it with generic arguments before the generic parameters themselves - // are resolved. - ResolvedGeneric::dummy() - }), + generics, self_type_typevar_id, self_type_typevar: TypeVariable::unbound(self_type_typevar_id), methods: Vec::new(), @@ -577,6 +570,7 @@ impl NodeInterner { pub fn new_struct( &mut self, typ: &UnresolvedStruct, + generics: Generics, krate: CrateId, local_id: LocalModuleId, file_id: FileId, @@ -586,13 +580,6 @@ impl NodeInterner { // Fields will be filled in later let no_fields = Vec::new(); - let generics = vecmap(&typ.struct_def.generics, |_| { - // Temporary type variable ids before the struct is resolved to its actual ids. - // This lets us record how many arguments the type expects so that other types - // can refer to it with generic arguments before the generic parameters themselves - // are resolved. - ResolvedGeneric::dummy() - }); let location = Location::new(typ.struct_def.span, file_id); let new_struct = StructType::new(struct_id, name, location, no_fields, generics); @@ -601,7 +588,7 @@ impl NodeInterner { struct_id } - pub fn push_type_alias(&mut self, typ: &UnresolvedTypeAlias) -> TypeAliasId { + pub fn push_type_alias(&mut self, typ: &UnresolvedTypeAlias, generics: Generics) -> TypeAliasId { let type_id = TypeAliasId(self.type_aliases.len()); self.type_aliases.push(Shared::new(TypeAlias::new( @@ -609,7 +596,7 @@ impl NodeInterner { typ.type_alias_def.name.clone(), Location::new(typ.type_alias_def.span, typ.file_id), Type::Error, - vecmap(&typ.type_alias_def.generics, |_| ResolvedGeneric::dummy()), + generics, ))); type_id diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 82e4bb8aebd..b05b4037623 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1562,6 +1562,24 @@ fn numeric_generic_as_struct_field_type() { )); } +#[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); + // 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 { .. }), + )); +} + #[test] fn numeric_generic_as_param_type() { let src = r#" @@ -1608,6 +1626,27 @@ fn numeric_generic_used_in_nested_type_fail() { )); } +#[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); + // 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 { .. }), + )); +} + #[test] fn numeric_generic_used_in_nested_type_pass() { // The order of these structs should not be changed to make sure @@ -1687,7 +1726,6 @@ fn numeric_generic_in_trait_impl_with_extra_impl_generics() { #[test] fn numeric_generic_used_in_where_clause() { let src = r#" - trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } @@ -1704,6 +1742,46 @@ fn numeric_generic_used_in_where_clause() { assert!(errors.is_empty()); } +#[test] +fn normal_generic_used_when_numeric_expected_in_where_clause() { + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + 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::TypeMismatch { .. }), + )); + + let src = r#" + trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; + } + + 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(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + )); +} + +// 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#" @@ -1729,6 +1807,7 @@ fn implicit_numeric_generics_elaborator() { } "#; let errors = get_program_errors(src); + assert_eq!(errors.len(), 3); for error in errors.iter() { if let CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident }) = From ebc38cb936c619720640c97121a325a99f006517 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 18:59:25 +0000 Subject: [PATCH 58/85] fmt fix in resolve_trait-bound --- compiler/noirc_frontend/src/elaborator/mod.rs | 20 +++++++++---------- .../src/hir/def_collector/dc_mod.rs | 13 ++++++++---- .../src/hir/resolution/traits.rs | 12 +++++++---- compiler/noirc_frontend/src/node_interner.rs | 13 ++++++++++-- compiler/noirc_frontend/src/tests.rs | 8 ++++---- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index d8bc5ea89a7..9a6f4f3af39 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -565,12 +565,12 @@ impl<'context> Elaborator<'context> { fn resolve_trait_bound(&mut self, bound: &TraitBound, typ: Type) -> Option { let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; - let resolved_generics = &the_trait.generics; + let resolved_generics = &the_trait.generics.clone(); assert_eq!(resolved_generics.len(), bound.trait_generics.len()); - let trait_generics = vecmap( - resolved_generics.clone().iter().zip(&bound.trait_generics), - |(resolved_generic, typ)| self.resolve_type_inner(typ.clone(), &resolved_generic.kind), - ); + let generics_with_types = resolved_generics.iter().zip(&bound.trait_generics); + let trait_generics = vecmap(generics_with_types, |(generic, typ)| { + self.resolve_type_inner(typ.clone(), &generic.kind) + }); let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let trait_id = the_trait.id; @@ -1184,12 +1184,12 @@ impl<'context> Elaborator<'context> { if found_generic == generic.name.as_str() { // TODO: might need the actual type here generic.kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; - - let ident = Ident::new(generic.name.to_string(), generic.span); + + let ident = Ident::new(generic.name.to_string(), generic.span); self.errors.push(( - CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { - ident, - }), + CompilationError::ResolverError( + ResolverError::UseExplicitNumericGeneric { ident }, + ), self.file, )); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 6763c7d4b79..7a4a3c37098 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -312,9 +312,13 @@ impl<'a> ModCollector<'a> { // Create the corresponding module for the struct namespace let id = match self.push_child_module(&name, self.file_id, false, false) { - Ok(local_id) => { - context.def_interner.new_struct(&unresolved, resolved_generics, krate, local_id, self.file_id) - } + Ok(local_id) => context.def_interner.new_struct( + &unresolved, + resolved_generics, + krate, + local_id, + self.file_id, + ), Err(error) => { definition_errors.push((error.into(), self.file_id)); continue; @@ -358,7 +362,8 @@ impl<'a> ModCollector<'a> { type_alias_def: type_alias, }; let resolved_generics = context.resolve_generics(&unresolved.type_alias_def.generics); - let type_alias_id = context.def_interner.push_type_alias(&unresolved, resolved_generics); + let type_alias_id = + context.def_interner.push_type_alias(&unresolved, resolved_generics); // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index fb1d60e1578..5bed10d6856 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -17,8 +17,7 @@ use crate::{ }, hir_def::traits::{TraitConstant, TraitFunction, TraitImpl, TraitType}, node_interner::{FuncId, NodeInterner, TraitId}, - Shared, Type, TypeVariableKind, - GenericTypeVars + GenericTypeVars, Shared, Type, TypeVariableKind, }; use super::{ @@ -51,8 +50,13 @@ pub(crate) fn resolve_traits( // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) let _ = resolve_trait_constants(context, crate_id, &unresolved_trait); // 3. Trait Methods - let (methods, errors) = - resolve_trait_methods(context, trait_id, crate_id, &unresolved_trait, &generic_type_vars); + let (methods, errors) = resolve_trait_methods( + context, + trait_id, + crate_id, + &unresolved_trait, + &generic_type_vars, + ); all_errors.extend(errors); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 08eaabb1182..b757397582c 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -547,7 +547,12 @@ impl NodeInterner { self.definition_to_type.insert(definition_id, typ); } - pub fn push_empty_trait(&mut self, type_id: TraitId, unresolved_trait: &UnresolvedTrait, generics: Generics) { + pub fn push_empty_trait( + &mut self, + type_id: TraitId, + unresolved_trait: &UnresolvedTrait, + generics: Generics, + ) { let self_type_typevar_id = self.next_type_variable_id(); let new_trait = Trait { @@ -588,7 +593,11 @@ impl NodeInterner { struct_id } - pub fn push_type_alias(&mut self, typ: &UnresolvedTypeAlias, generics: Generics) -> TypeAliasId { + pub fn push_type_alias( + &mut self, + typ: &UnresolvedTypeAlias, + generics: Generics, + ) -> TypeAliasId { let type_id = TypeAliasId(self.type_aliases.len()); self.type_aliases.push(Shared::new(TypeAlias::new( diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index b05b4037623..f76e796f443 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1572,7 +1572,7 @@ 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 + // 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, @@ -1639,7 +1639,7 @@ fn normal_generic_used_in_nested_array_length_fail() { "#; 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 + // 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, @@ -1777,7 +1777,7 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), + CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), )); } @@ -1818,4 +1818,4 @@ fn implicit_numeric_generics_elaborator() { panic!("Expected ResolverError::UseExplicitNumericGeneric but got {:?}", error); } } -} \ No newline at end of file +} From 3ef133e217636567680c589e6a3a34fb231b8966 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 19:04:58 +0000 Subject: [PATCH 59/85] mvoe nested slice check --- compiler/noirc_frontend/src/elaborator/types.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ebda9973bf5..8799e3c9dc0 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -40,7 +40,14 @@ use super::{lints, Elaborator}; impl<'context> Elaborator<'context> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { - self.resolve_type_inner(typ, &Kind::Normal) + let span = typ.span; + let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); + if resolved_type.is_nested_slice() { + self.push_err(ResolverError::NestedSlices { + span: span.expect("Type should have span"), + }); + } + resolved_type } /// Translates an UnresolvedType into a Type and appends any @@ -55,6 +62,7 @@ impl<'context> Elaborator<'context> { Array(size, elem) => { 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 { size = Type::NamedGeneric( type_var, @@ -73,6 +81,7 @@ impl<'context> Elaborator<'context> { Bool => Type::Bool, 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 { resolved_size = Type::NamedGeneric( type_var, @@ -160,12 +169,6 @@ impl<'context> Elaborator<'context> { } } - if resolved_type.is_nested_slice() { - self.push_err(ResolverError::NestedSlices { - span: span.expect("Type should have span"), - }); - } - resolved_type } From 818667e9a13fbc06f1c9747293f0c9f9f285b385 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 19:06:31 +0000 Subject: [PATCH 60/85] remove ResolvedGeneric::dummy --- compiler/noirc_frontend/src/hir_def/types.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e6c7cd9b6a6..4fce5fa5662 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -260,19 +260,6 @@ pub struct ResolvedGeneric { pub span: Span, } -impl ResolvedGeneric { - // TODO(https://github.com/noir-lang/noir/issues/5231): Remove once we move to the elaborator. - // This is only used during collection in the old resolution process. - pub fn dummy() -> Self { - ResolvedGeneric { - name: Rc::default(), - type_var: TypeVariable::unbound(TypeVariableId(0)), - kind: Kind::Normal, - span: Span::default(), - } - } -} - impl std::hash::Hash for StructType { fn hash(&self, state: &mut H) { self.id.hash(state); From 4001dfd57a8c6900839d5c50465d95b6559f4d1d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 19:08:37 +0000 Subject: [PATCH 61/85] update GenericTypeVars comment --- compiler/noirc_frontend/src/hir_def/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 4fce5fa5662..e6413919ae9 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -243,8 +243,8 @@ pub struct StructType { } /// Corresponds to generic lists such as `` in the source program. -/// Used mainly for resolved types which no longer need the information -/// that is checked during resolution/type checking. +/// Used mainly for resolved types which no longer need information such +/// as names or kinds. pub type GenericTypeVars = Vec; /// Corresponds to generic lists such as `` with additional From 154782d5f49768af06dc1bec51d6015ba9402f33 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 19:26:46 +0000 Subject: [PATCH 62/85] check generic args for resolve_trait_as_type --- .../noirc_frontend/src/elaborator/types.rs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 8799e3c9dc0..ee11ed39b4e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -108,7 +108,7 @@ impl<'context> Elaborator<'context> { Unspecified => Type::Error, Error => Type::Error, Named(path, args, _) => self.resolve_named_type(path, args), - TraitAsType(path, args) => self.resolve_trait_as_type(path, args, kind), + TraitAsType(path, args) => self.resolve_trait_as_type(path, args), Tuple(fields) => { Type::Tuple(vecmap(fields, |field| self.resolve_type_inner(field, kind))) @@ -277,16 +277,20 @@ impl<'context> Elaborator<'context> { } } - fn resolve_trait_as_type( - &mut self, - path: Path, - args: Vec, - kind: &Kind, - ) -> Type { - let args = vecmap(args, |arg| self.resolve_type_inner(arg, kind)); - - if let Some(t) = self.lookup_trait_or_error(path) { - Type::TraitAsType(t.id, Rc::new(t.name.to_string()), args) + fn resolve_trait_as_type(&mut self, path: Path, args: Vec) -> Type { + // Fetch information needed from the trait as the closure for resolving all the `args` + // requires exclusive access to `self` + let trait_as_type_info = self + .lookup_trait_or_error(path) + .map(|t| (t.id, Rc::new(t.name.to_string()), t.generics.clone())); + + if let Some((id, name, resolved_generics)) = trait_as_type_info { + assert_eq!(resolved_generics.len(), args.len()); + let generics_with_types = resolved_generics.iter().zip(args); + let args = vecmap(generics_with_types, |(generic, typ)| { + self.resolve_type_inner(typ, &generic.kind) + }); + Type::TraitAsType(id, Rc::new(name.to_string()), args) } else { Type::Error } From e3cf451723f24e5b4695811ac2f45f58685b31e9 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 20:07:08 +0000 Subject: [PATCH 63/85] last cleanup in frontend tests and setting of implicit numeric struct fields --- compiler/noirc_frontend/src/elaborator/mod.rs | 33 ++++++++++--------- .../noirc_frontend/src/elaborator/traits.rs | 3 +- .../noirc_frontend/src/elaborator/types.rs | 30 +++++++++-------- compiler/noirc_frontend/src/hir_def/types.rs | 9 ++++- compiler/noirc_frontend/src/tests.rs | 21 +++++------- tooling/nargo_fmt/src/items.rs | 2 +- tooling/nargo_fmt/src/utils.rs | 4 --- 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 9a6f4f3af39..abf6f712645 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1171,9 +1171,10 @@ impl<'context> Elaborator<'context> { let attributes = std::mem::take(&mut typ.struct_def.attributes); let span = typ.struct_def.span; - let fields = self.resolve_struct_fields(typ.struct_def, type_id); + let (fields, generics) = self.resolve_struct_fields(typ.struct_def, type_id); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); + struct_def.generics = generics; // 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. @@ -1182,17 +1183,18 @@ impl<'context> Elaborator<'context> { for generic in struct_def.generics.iter_mut() { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { - // TODO: might need the actual type here - generic.kind = Kind::Numeric { typ: Box::new(Type::FieldElement) }; - - let ident = Ident::new(generic.name.to_string(), generic.span); - self.errors.push(( - CompilationError::ResolverError( - ResolverError::UseExplicitNumericGeneric { ident }, - ), - self.file, - )); - + dbg!(generic.kind.clone()); + 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 { typ: Box::new(Type::default_int_type()) }; + } break; } } @@ -1259,20 +1261,21 @@ impl<'context> Elaborator<'context> { &mut self, unresolved: NoirStruct, struct_id: StructId, - ) -> Vec<(Ident, Type)> { + ) -> (Vec<(Ident, Type)>, Generics) { self.recover_generics(|this| { this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); let struct_def = this.interner.get_struct(struct_id); - this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); + let generics = + this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, this.resolve_type(typ))); this.resolving_ids.remove(&struct_id); - fields + (fields, generics) }) } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index f21011bf6e8..e09a586851f 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -25,7 +25,7 @@ impl<'context> Elaborator<'context> { for (trait_id, unresolved_trait) in traits { self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); - this.add_existing_generics( + let generics = this.add_existing_generics( &unresolved_trait.trait_def.generics, &resolved_generics, ); @@ -40,6 +40,7 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); + trait_def.generics = generics; }); }); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index ee11ed39b4e..6fb7b935bca 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -156,8 +156,8 @@ impl<'context> Elaborator<'context> { // Check that any types with a type kind match the expected type kind supplied to this function if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - // TODO: make this check more general with `*resolved_kind != kind` - // implicit numeric generics kind of messes up the check + // TODO(https://github.com/noir-lang/noir/issues/5156): make this check more general with `*resolved_kind != kind` + // as implicit numeric generics still existing makes this check more challenging to enforce if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { @@ -1431,13 +1431,13 @@ impl<'context> Elaborator<'context> { &mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics, - ) { + ) -> Generics { assert_eq!(unresolved_generics.len(), generics.len()); - for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { + vecmap(unresolved_generics.iter().zip(generics), |(unresolved_generic, generic)| { let type_var = generic.type_var.clone(); - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), type_var); - } + self.add_existing_generic(unresolved_generic, unresolved_generic.span(), type_var) + }) } pub fn add_existing_generic( @@ -1445,12 +1445,18 @@ impl<'context> Elaborator<'context> { unresolved_generic: &UnresolvedGeneric, span: Span, typevar: TypeVariable, - ) { + ) -> ResolvedGeneric { let name = &unresolved_generic.ident().0.contents; // Check for name collisions of this generic let rc_name = Rc::new(name.clone()); + // Resolved the generic's kind + let kind = self.resolve_generic_kind(unresolved_generic); + + let resolved_generic = + ResolvedGeneric { name: rc_name.clone(), type_var: typevar.clone(), kind, span }; + if let Some(generic) = self.find_generic(&rc_name) { self.push_err(ResolverError::DuplicateDefinition { name: name.clone(), @@ -1458,14 +1464,10 @@ impl<'context> Elaborator<'context> { second_span: span, }); } else { - // Declare numeric generic if it is specified - let kind = self.resolve_generic_kind(unresolved_generic); - - let resolved_generic = - ResolvedGeneric { name: rc_name, type_var: typevar.clone(), kind, span }; - - self.generics.push(resolved_generic); + self.generics.push(resolved_generic.clone()); } + + resolved_generic } pub fn find_numeric_generics( diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e6413919ae9..2e580295d85 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -2123,7 +2123,14 @@ impl std::fmt::Debug for Type { } Type::Unit => write!(f, "()"), Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, kind) => write!(f, "{} {}{:?}", kind, name, binding), + Type::NamedGeneric(binding, name, kind) => match kind { + Kind::Normal => { + write!(f, "{} -> {:?}", name, binding) + } + Kind::Numeric { typ } => { + write!(f, "({} : {}) -> {:?}", name, typ, binding) + } + }, Type::Constant(x) => x.fmt(f), Type::Forall(typevars, typ) => { let typevars = vecmap(typevars, |var| format!("{:?}", var)); diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index f76e796f443..7229f4e0384 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1638,13 +1638,8 @@ fn normal_generic_used_in_nested_array_length_fail() { } "#; 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 { .. }), - )); + // 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); } #[test] @@ -1652,11 +1647,11 @@ 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 { + struct NestedNumeric { a: Field, b: InnerNumeric } - struct InnerNumeric { + struct InnerNumeric { inner: [u64; N], } "#; @@ -1680,7 +1675,7 @@ fn numeric_generic_used_in_trait() { } } - trait Deserialize { + trait Deserialize { fn deserialize(fields: [Field; N], other: T) -> Self; } "#; @@ -1709,13 +1704,13 @@ fn numeric_generic_in_trait_impl_with_extra_impl_generics() { // `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 { + 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 { + trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } "#; @@ -1807,7 +1802,7 @@ fn implicit_numeric_generics_elaborator() { } "#; let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); + assert_eq!(errors.len(), 4); for error in errors.iter() { if let CompilationError::ResolverError(ResolverError::UseExplicitNumericGeneric { ident }) = diff --git a/tooling/nargo_fmt/src/items.rs b/tooling/nargo_fmt/src/items.rs index b7ec3542751..80b641fd830 100644 --- a/tooling/nargo_fmt/src/items.rs +++ b/tooling/nargo_fmt/src/items.rs @@ -5,7 +5,7 @@ use crate::{ visitor::{FmtVisitor, Shape}, }; -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct Item { pub(crate) leading: String, pub(crate) value: String, diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index d56865928b8..020f411ae2f 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -189,10 +189,6 @@ impl HasItem for UnresolvedGeneric { } } } - - fn end(&self) -> u32 { - self.span().end() - } } pub(crate) fn first_line_width(exprs: &str) -> usize { From 5ebd02d0814baeee05d50734f46692af8815589c Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 17:18:11 -0400 Subject: [PATCH 64/85] Update compiler/noirc_frontend/src/elaborator/mod.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index abf6f712645..f66942f0590 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1183,7 +1183,6 @@ impl<'context> Elaborator<'context> { for generic in struct_def.generics.iter_mut() { for found_generic in found_names.iter() { if found_generic == generic.name.as_str() { - dbg!(generic.kind.clone()); if matches!(generic.kind, Kind::Normal) { let ident = Ident::new(generic.name.to_string(), generic.span); self.errors.push(( From d6ccab1f76b9e6b7df4ebd6ddf9b07c162468a7f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 20 Jun 2024 21:40:50 +0000 Subject: [PATCH 65/85] make resolve_numeric_kind_type inside of UnresolvedGeneric kind() --- compiler/noirc_frontend/src/ast/expression.rs | 17 ++++++++++++++--- compiler/noirc_frontend/src/elaborator/mod.rs | 9 ++++----- compiler/noirc_frontend/src/elaborator/types.rs | 6 +++--- compiler/noirc_frontend/src/hir_def/types.rs | 7 +++---- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index e3d77ce872f..bdd40297d13 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -76,9 +76,9 @@ impl UnresolvedGeneric { pub fn kind(&self) -> Kind { match self { UnresolvedGeneric::Variable(_) => Kind::Normal, - // The inner numeric type of the kind cannot be resolved from an `UnresolvedGeneric` - // on its own. It is expected that the inner type is updated during elaboration. - UnresolvedGeneric::Numeric { .. } => Kind::Numeric { typ: Box::new(Type::Error) }, + UnresolvedGeneric::Numeric { typ, .. } => { + Kind::Numeric(Box::new(Self::resolve_numeric_kind_type(typ.clone()))) + } } } @@ -87,6 +87,17 @@ impl UnresolvedGeneric { UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, } } + + fn resolve_numeric_kind_type(typ: UnresolvedType) -> Type { + use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; + + match typ.typ { + FieldElement => Type::FieldElement, + Integer(sign, bits) => Type::Integer(sign, bits), + // Only fields and integers are supported for numeric kinds + _ => Type::Error, + } + } } impl Display for UnresolvedGeneric { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index f66942f0590..cfedfca6a7b 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -338,7 +338,7 @@ impl<'context> Elaborator<'context> { // Introduce all numeric generics into scope for generic in &func_meta.all_generics { - if let Kind::Numeric { typ } = &generic.kind { + if let Kind::Numeric(typ) = &generic.kind { let definition = DefinitionKind::GenericType(generic.type_var.clone()); let ident = Ident::new(generic.name.to_string(), generic.span); let hir_ident = @@ -509,7 +509,7 @@ impl<'context> Elaborator<'context> { }); self.errors.push((unsupported_typ_err, self.file)); } - Kind::Numeric { typ: Box::new(typ) } + Kind::Numeric(Box::new(typ)) } else { Kind::Normal } @@ -787,7 +787,7 @@ impl<'context> Elaborator<'context> { // ``` continue; } - *kind = Kind::Numeric { typ: Box::new(Type::default_int_type()) }; + *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); @@ -1191,8 +1191,7 @@ impl<'context> Elaborator<'context> { ), self.file, )); - generic.kind = - Kind::Numeric { typ: Box::new(Type::default_int_type()) }; + generic.kind = Kind::Numeric(Box::new(Type::default_int_type())); } break; } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 6fb7b935bca..224c8d8f939 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -67,7 +67,7 @@ impl<'context> Elaborator<'context> { size = Type::NamedGeneric( type_var, name, - Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + Kind::Numeric(Box::new(Type::default_int_type())), ); } Type::Array(Box::new(size), elem) @@ -86,7 +86,7 @@ impl<'context> Elaborator<'context> { resolved_size = Type::NamedGeneric( type_var, name, - Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + Kind::Numeric(Box::new(Type::default_int_type())), ); } Type::String(Box::new(resolved_size)) @@ -97,7 +97,7 @@ impl<'context> Elaborator<'context> { resolved_size = Type::NamedGeneric( type_var, name, - Kind::Numeric { typ: Box::new(Type::default_int_type()) }, + Kind::Numeric(Box::new(Type::default_int_type())), ); } let fields = self.resolve_type_inner(*fields, kind); diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 2e580295d85..f9e9a0d561d 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -196,15 +196,14 @@ impl Type { #[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum Kind { Normal, - Numeric { typ: Box }, + Numeric(Box), } impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Kind::Normal => write!(f, "normal"), - // TODO(numeric generics): Use the typ of the numeric in the kind - Kind::Numeric { .. } => write!(f, "numeric"), + Kind::Numeric(typ) => write!(f, "numeric {}", typ), } } } @@ -2127,7 +2126,7 @@ impl std::fmt::Debug for Type { Kind::Normal => { write!(f, "{} -> {:?}", name, binding) } - Kind::Numeric { typ } => { + Kind::Numeric(typ) => { write!(f, "({} : {}) -> {:?}", name, typ, binding) } }, From 25d640a3b32b978ca2216f4878921de10a534a1b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 14:33:59 +0000 Subject: [PATCH 66/85] return result on kind() method of UnresolvedGeneric --- compiler/noirc_frontend/src/ast/expression.rs | 15 ++--- .../noirc_frontend/src/elaborator/traits.rs | 11 +++- .../noirc_frontend/src/elaborator/types.rs | 18 ++---- .../src/hir/def_collector/dc_mod.rs | 58 ++++++++++++++++++- .../src/hir/def_collector/errors.rs | 13 ++++- compiler/noirc_frontend/src/hir/mod.rs | 21 +++++-- .../src/hir/resolution/resolver.rs | 4 +- .../src/hir/resolution/traits.rs | 19 +++++- compiler/noirc_frontend/src/tests.rs | 6 +- 9 files changed, 129 insertions(+), 36 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index bdd40297d13..89dd9b44644 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -73,11 +73,12 @@ impl UnresolvedGeneric { } } - pub fn kind(&self) -> Kind { + pub fn kind(&self) -> Result { match self { - UnresolvedGeneric::Variable(_) => Kind::Normal, + UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), UnresolvedGeneric::Numeric { typ, .. } => { - Kind::Numeric(Box::new(Self::resolve_numeric_kind_type(typ.clone()))) + let typ = Self::resolve_numeric_kind_type(typ.clone())?; + Ok(Kind::Numeric(Box::new(typ))) } } } @@ -88,14 +89,14 @@ impl UnresolvedGeneric { } } - fn resolve_numeric_kind_type(typ: UnresolvedType) -> Type { + fn resolve_numeric_kind_type(typ: UnresolvedType) -> Result { use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; match typ.typ { - FieldElement => Type::FieldElement, - Integer(sign, bits) => Type::Integer(sign, bits), + FieldElement => Ok(Type::FieldElement), + Integer(sign, bits) => Ok(Type::Integer(sign, bits)), // Only fields and integers are supported for numeric kinds - _ => Type::Error, + _ => Err(typ.typ), } } } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index e09a586851f..9c4512e98f3 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, rc::Rc}; use iter_extended::vecmap; use noirc_errors::Location; @@ -15,7 +15,7 @@ use crate::{ }, node_interner::{FuncId, TraitId}, token::Attributes, - Type, TypeVariableKind, + Kind, ResolvedGeneric, Type, TypeVariableKind, }; use super::Elaborator; @@ -96,7 +96,12 @@ impl<'context> Elaborator<'context> { this.add_existing_generic( &UnresolvedGeneric::Variable(Ident::from("Self")), name_span, - self_typevar, + &ResolvedGeneric { + name: Rc::new("Self".to_owned()), + type_var: self_typevar, + span: name_span, + kind: Kind::Normal, + }, ); this.self_type = Some(self_type.clone()); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 224c8d8f939..b7f334b5a7c 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1435,8 +1435,7 @@ impl<'context> Elaborator<'context> { assert_eq!(unresolved_generics.len(), generics.len()); vecmap(unresolved_generics.iter().zip(generics), |(unresolved_generic, generic)| { - let type_var = generic.type_var.clone(); - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), type_var) + self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic) }) } @@ -1444,20 +1443,11 @@ impl<'context> Elaborator<'context> { &mut self, unresolved_generic: &UnresolvedGeneric, span: Span, - typevar: TypeVariable, + resolved_generic: &ResolvedGeneric, ) -> ResolvedGeneric { let name = &unresolved_generic.ident().0.contents; - // Check for name collisions of this generic - let rc_name = Rc::new(name.clone()); - - // Resolved the generic's kind - let kind = self.resolve_generic_kind(unresolved_generic); - - let resolved_generic = - ResolvedGeneric { name: rc_name.clone(), type_var: typevar.clone(), kind, span }; - - if let Some(generic) = self.find_generic(&rc_name) { + if let Some(generic) = self.find_generic(name) { self.push_err(ResolverError::DuplicateDefinition { name: name.clone(), first_span: generic.span, @@ -1467,7 +1457,7 @@ impl<'context> Elaborator<'context> { self.generics.push(resolved_generic.clone()); } - resolved_generic + resolved_generic.clone() } pub fn find_numeric_generics( diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 7a4a3c37098..5bf493d32f6 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -308,7 +308,24 @@ impl<'a> ModCollector<'a> { struct_def: struct_definition, }; - let resolved_generics = context.resolve_generics(&unresolved.struct_def.generics); + let generic_results = context.resolve_generics(&unresolved.struct_def.generics); + dbg!(generic_results.clone()); + let mut resolved_generics = Vec::new(); + for generic_result in generic_results.into_iter() { + match generic_result { + Ok(resolved_generic) => resolved_generics.push(resolved_generic), + Err((bad_generic, bad_type)) => { + let err = DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), + typ: bad_type.clone(), + }; + definition_errors.push((err.into(), self.file_id)); + // We still push the ill-formed generic as to continue definition collection and elaboration + // without triggering any other compiler errors. + resolved_generics.push(bad_generic); + } + } + } // Create the corresponding module for the struct namespace let id = match self.push_child_module(&name, self.file_id, false, false) { @@ -361,7 +378,25 @@ impl<'a> ModCollector<'a> { module_id: self.module_id, type_alias_def: type_alias, }; - let resolved_generics = context.resolve_generics(&unresolved.type_alias_def.generics); + + let generic_results = context.resolve_generics(&unresolved.type_alias_def.generics); + let mut resolved_generics = Vec::new(); + for generic_result in generic_results.into_iter() { + match generic_result { + Ok(resolved_generic) => resolved_generics.push(resolved_generic), + Err((bad_generic, bad_type)) => { + let err = DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), + typ: bad_type.clone(), + }; + errors.push((err.into(), self.file_id)); + // We still push the ill-formed generic as to continue definition collection and elaboration + // without triggering any other compiler errors. + resolved_generics.push(bad_generic); + } + } + } + let type_alias_id = context.def_interner.push_type_alias(&unresolved, resolved_generics); @@ -523,7 +558,24 @@ impl<'a> ModCollector<'a> { } } - let resolved_generics = context.resolve_generics(&trait_definition.generics); + let generic_results = context.resolve_generics(&trait_definition.generics); + let mut resolved_generics = Vec::new(); + for generic_result in generic_results.into_iter() { + match generic_result { + Ok(resolved_generic) => resolved_generics.push(resolved_generic), + Err((bad_generic, bad_type)) => { + let err = DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), + typ: bad_type.clone(), + }; + errors.push((err.into(), self.file_id)); + // We still push the ill-formed generic as to continue definition collection and elaboration + // without triggering any other compiler errors. + resolved_generics.push(bad_generic); + } + } + } + // And store the TraitId -> TraitType mapping somewhere it is reachable let unresolved = UnresolvedTrait { file_id: self.file_id, diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index edeb463e10d..7a99ab60adb 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -1,4 +1,4 @@ -use crate::ast::{Ident, Path}; +use crate::ast::{Ident, Path, UnresolvedTypeData}; use crate::hir::resolution::import::PathResolutionError; use noirc_errors::CustomDiagnostic as Diagnostic; @@ -66,6 +66,8 @@ pub enum DefCollectorErrorKind { TraitImplOrphaned { span: Span }, #[error("macro error : {0:?}")] MacroError(MacroError), + #[error("The only supported types of numeric generics are integers, fields, and booleans")] + UnsupportedNumericGenericType { ident: Ident, typ: UnresolvedTypeData }, } /// An error struct that macro processors can return. @@ -228,6 +230,15 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { DefCollectorErrorKind::MacroError(macro_error) => { Diagnostic::simple_error(macro_error.primary_message.clone(), macro_error.secondary_message.clone().unwrap_or_default(), macro_error.span.unwrap_or_default()) }, + DefCollectorErrorKind::UnsupportedNumericGenericType { ident, typ } => { + let name = &ident.0.contents; + + Diagnostic::simple_error( + format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), + "Unsupported numeric generic type".to_string(), + ident.0.span(), + ) + } } } } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index b624a87e23d..7591af10870 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -5,13 +5,13 @@ pub mod resolution; pub mod scope; pub mod type_check; -use crate::ast::UnresolvedGenerics; +use crate::ast::{UnresolvedGenerics, UnresolvedTypeData}; use crate::debug::DebugInstrumenter; use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::ParserError; -use crate::{Generics, ParsedModule, ResolvedGeneric, TypeVariable}; +use crate::{Kind, ParsedModule, ResolvedGeneric, Type, TypeVariable}; use def_map::{Contract, CrateDefMap}; use fm::FileManager; use iter_extended::vecmap; @@ -263,7 +263,12 @@ impl Context<'_, '_> { /// Generics need to be resolved before elaboration to distinguish /// between normal and numeric generics. /// This method is expected to be used during definition collection. - pub(crate) fn resolve_generics(&mut self, generics: &UnresolvedGenerics) -> Generics { + /// Each result is returned in a list rather than returned as a single result as to allow + /// definition collection to provide an error for each ill-formed numeric generic. + pub(crate) fn resolve_generics( + &mut self, + generics: &UnresolvedGenerics, + ) -> Vec> { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = self.def_interner.next_type_variable_id(); @@ -274,7 +279,15 @@ impl Context<'_, '_> { // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - ResolvedGeneric { name, type_var, kind: generic.kind(), span } + let mut resolved_generic = ResolvedGeneric { + name, + type_var, + kind: Kind::Numeric(Box::new(Type::Error)), + span, + }; + let kind = generic.kind().map_err(|typ| (resolved_generic.clone(), typ))?; + resolved_generic.kind = kind; + Ok(resolved_generic) }) } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7b45a4adcf2..c190b0b222c 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -953,7 +953,9 @@ impl<'a> Resolver<'a> { let resolved_generic = ResolvedGeneric { name: rc_name, type_var: typevar.clone(), - kind: unresolved_generic.kind(), + kind: unresolved_generic + .kind() + .expect("ICE: Deprecated code should only support normal kinds"), span, }; self.generics.push(resolved_generic); diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 5bed10d6856..ebe03dc1353 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -41,7 +41,24 @@ pub(crate) fn resolve_traits( let mut all_errors = Vec::new(); for (trait_id, unresolved_trait) in traits { - let generics = context.resolve_generics(&unresolved_trait.trait_def.generics); + let generic_results = context.resolve_generics(&unresolved_trait.trait_def.generics); + let mut generics = Vec::new(); + for generic_result in generic_results.into_iter() { + match generic_result { + Ok(resolved_generic) => generics.push(resolved_generic), + Err((bad_generic, bad_type)) => { + let err = DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), + typ: bad_type.clone(), + }; + let file = context.def_maps[&crate_id].file_id(unresolved_trait.module_id); + all_errors.push((err.into(), file)); + // We still push the ill-formed generic as to continue definition collection and elaboration + // without triggering any other compiler errors. + generics.push(bad_generic); + } + } + } let generic_type_vars = generics.iter().map(|generic| generic.type_var.clone()).collect(); // Resolve order diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7229f4e0384..ad18d185aba 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1461,13 +1461,15 @@ fn struct_numeric_generic() { inner: u64 } - fn bar() { } + struct Bar { } "#; let errors = get_program_errors(src); assert_eq!(errors.len(), 1); assert!(matches!( errors[0].0, - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), + CompilationError::DefinitionError( + DefCollectorErrorKind::UnsupportedNumericGenericType { .. } + ), )); } From c0a375cedb23b1620f7843f3be8946b1aa599568 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 14:41:04 +0000 Subject: [PATCH 67/85] don't return generics from add_existing_generics now that they are resolved during def collection --- compiler/noirc_frontend/src/elaborator/mod.rs | 11 +++++------ compiler/noirc_frontend/src/elaborator/traits.rs | 4 ++-- compiler/noirc_frontend/src/elaborator/types.rs | 12 +++++------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index cfedfca6a7b..b5f7cd09a46 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1171,10 +1171,10 @@ impl<'context> Elaborator<'context> { let attributes = std::mem::take(&mut typ.struct_def.attributes); let span = typ.struct_def.span; - let (fields, generics) = self.resolve_struct_fields(typ.struct_def, type_id); + let fields = self.resolve_struct_fields(typ.struct_def, type_id); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); - struct_def.generics = generics; + // struct_def.generics = generics; // 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. @@ -1259,21 +1259,20 @@ impl<'context> Elaborator<'context> { &mut self, unresolved: NoirStruct, struct_id: StructId, - ) -> (Vec<(Ident, Type)>, Generics) { + ) -> Vec<(Ident, Type)> { self.recover_generics(|this| { this.current_item = Some(DependencyId::Struct(struct_id)); this.resolving_ids.insert(struct_id); let struct_def = this.interner.get_struct(struct_id); - let generics = - this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); + this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, this.resolve_type(typ))); this.resolving_ids.remove(&struct_id); - (fields, generics) + fields }) } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index 9c4512e98f3..cb97e0d0d61 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -25,7 +25,7 @@ impl<'context> Elaborator<'context> { for (trait_id, unresolved_trait) in traits { self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); - let generics = this.add_existing_generics( + this.add_existing_generics( &unresolved_trait.trait_def.generics, &resolved_generics, ); @@ -40,7 +40,7 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - trait_def.generics = generics; + // trait_def.generics = generics; }); }); diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b7f334b5a7c..fd8bf506a7a 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -1431,12 +1431,12 @@ impl<'context> Elaborator<'context> { &mut self, unresolved_generics: &UnresolvedGenerics, generics: &Generics, - ) -> Generics { + ) { assert_eq!(unresolved_generics.len(), generics.len()); - vecmap(unresolved_generics.iter().zip(generics), |(unresolved_generic, generic)| { - self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic) - }) + for (unresolved_generic, generic) in unresolved_generics.iter().zip(generics) { + self.add_existing_generic(unresolved_generic, unresolved_generic.span(), generic); + } } pub fn add_existing_generic( @@ -1444,7 +1444,7 @@ impl<'context> Elaborator<'context> { unresolved_generic: &UnresolvedGeneric, span: Span, resolved_generic: &ResolvedGeneric, - ) -> ResolvedGeneric { + ) { let name = &unresolved_generic.ident().0.contents; if let Some(generic) = self.find_generic(name) { @@ -1456,8 +1456,6 @@ impl<'context> Elaborator<'context> { } else { self.generics.push(resolved_generic.clone()); } - - resolved_generic.clone() } pub fn find_numeric_generics( From 05befe500f5b3b4d697ff0eaed6be4367246d5b3 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 14:46:24 +0000 Subject: [PATCH 68/85] missed dbg --- compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 5bf493d32f6..a79d3918094 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -309,7 +309,6 @@ impl<'a> ModCollector<'a> { }; let generic_results = context.resolve_generics(&unresolved.struct_def.generics); - dbg!(generic_results.clone()); let mut resolved_generics = Vec::new(); for generic_result in generic_results.into_iter() { match generic_result { From 13fad2312e01d2387a4d4b32553b8af5395337c4 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 13:03:52 -0400 Subject: [PATCH 69/85] Update compiler/noirc_frontend/src/hir/def_collector/errors.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/hir/def_collector/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index 7a99ab60adb..af2264c3843 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -234,7 +234,7 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { let name = &ident.0.contents; Diagnostic::simple_error( - format!("{name} has a type of {typ}. The only supported types of numeric generics are integers, fields, and booleans."), + format!("{name} has a type of {typ}. The only supported types of numeric generics are integers and fields"), "Unsupported numeric generic type".to_string(), ident.0.span(), ) From 9da3de6bfb766ce20d5a1bb5d0438fce6c5b8de8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 13:04:02 -0400 Subject: [PATCH 70/85] Update compiler/noirc_frontend/src/elaborator/traits.rs Co-authored-by: jfecher --- compiler/noirc_frontend/src/elaborator/traits.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs index cb97e0d0d61..77ac8e476f8 100644 --- a/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -40,7 +40,6 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); - // trait_def.generics = generics; }); }); From 98fd19784d707e54cc304ea70e280c0feeff2854 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 17:04:50 +0000 Subject: [PATCH 71/85] remove cmmented out struct generics update --- compiler/noirc_frontend/src/elaborator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index b5f7cd09a46..4d2c47ed690 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1174,7 +1174,6 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_struct_fields(typ.struct_def, type_id); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); - // struct_def.generics = generics; // 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. From a6ec927eb29826b8c68660cb815f46a81bbeea8d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 17:10:37 +0000 Subject: [PATCH 72/85] add back struct_numeric_generic_in_function test --- compiler/noirc_frontend/src/tests.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index ad18d185aba..8aa60d166e5 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1455,7 +1455,24 @@ fn specify_method_types_with_turbofish() { } #[test] -fn struct_numeric_generic() { +fn struct_numeric_generic_in_function() { + let src = r#" + struct Foo { + inner: u64 + } + + 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 From f87bc32a42c27195fd18026d37ff7384d9457355 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 17:29:46 +0000 Subject: [PATCH 73/85] fix bad return type from context.resolve_generics --- compiler/noirc_frontend/src/ast/expression.rs | 24 ++++--- .../src/hir/def_collector/dc_mod.rs | 63 ++++--------------- compiler/noirc_frontend/src/hir/mod.rs | 28 ++++----- .../src/hir/resolution/traits.rs | 24 ++----- 4 files changed, 47 insertions(+), 92 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 89dd9b44644..3aaf23d93e6 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -5,6 +5,7 @@ use crate::ast::{ Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; +use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; use crate::node_interner::ExprId; use crate::token::{Attributes, Token}; @@ -73,22 +74,21 @@ impl UnresolvedGeneric { } } - pub fn kind(&self) -> Result { + pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), - UnresolvedGeneric::Numeric { typ, .. } => { - let typ = Self::resolve_numeric_kind_type(typ.clone())?; + UnresolvedGeneric::Numeric { ident, typ } => { + let typ = Self::resolve_numeric_kind_type(typ.clone()).map_err(|typ| { + DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: ident.clone(), + typ, + } + })?; Ok(Kind::Numeric(Box::new(typ))) } } } - pub(crate) fn ident(&self) -> &Ident { - match self { - UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, - } - } - fn resolve_numeric_kind_type(typ: UnresolvedType) -> Result { use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; @@ -99,6 +99,12 @@ impl UnresolvedGeneric { _ => Err(typ.typ), } } + + pub(crate) fn ident(&self) -> &Ident { + match self { + UnresolvedGeneric::Variable(ident) | UnresolvedGeneric::Numeric { ident, .. } => ident, + } + } } impl Display for UnresolvedGeneric { diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index a79d3918094..0a58642fdd2 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -308,23 +308,11 @@ impl<'a> ModCollector<'a> { struct_def: struct_definition, }; - let generic_results = context.resolve_generics(&unresolved.struct_def.generics); - let mut resolved_generics = Vec::new(); - for generic_result in generic_results.into_iter() { - match generic_result { - Ok(resolved_generic) => resolved_generics.push(resolved_generic), - Err((bad_generic, bad_type)) => { - let err = DefCollectorErrorKind::UnsupportedNumericGenericType { - ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), - typ: bad_type.clone(), - }; - definition_errors.push((err.into(), self.file_id)); - // We still push the ill-formed generic as to continue definition collection and elaboration - // without triggering any other compiler errors. - resolved_generics.push(bad_generic); - } - } - } + let resolved_generics = context.resolve_generics( + &unresolved.struct_def.generics, + &mut definition_errors, + self.file_id, + ); // Create the corresponding module for the struct namespace let id = match self.push_child_module(&name, self.file_id, false, false) { @@ -378,23 +366,11 @@ impl<'a> ModCollector<'a> { type_alias_def: type_alias, }; - let generic_results = context.resolve_generics(&unresolved.type_alias_def.generics); - let mut resolved_generics = Vec::new(); - for generic_result in generic_results.into_iter() { - match generic_result { - Ok(resolved_generic) => resolved_generics.push(resolved_generic), - Err((bad_generic, bad_type)) => { - let err = DefCollectorErrorKind::UnsupportedNumericGenericType { - ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), - typ: bad_type.clone(), - }; - errors.push((err.into(), self.file_id)); - // We still push the ill-formed generic as to continue definition collection and elaboration - // without triggering any other compiler errors. - resolved_generics.push(bad_generic); - } - } - } + let resolved_generics = context.resolve_generics( + &unresolved.type_alias_def.generics, + &mut errors, + self.file_id, + ); let type_alias_id = context.def_interner.push_type_alias(&unresolved, resolved_generics); @@ -557,23 +533,8 @@ impl<'a> ModCollector<'a> { } } - let generic_results = context.resolve_generics(&trait_definition.generics); - let mut resolved_generics = Vec::new(); - for generic_result in generic_results.into_iter() { - match generic_result { - Ok(resolved_generic) => resolved_generics.push(resolved_generic), - Err((bad_generic, bad_type)) => { - let err = DefCollectorErrorKind::UnsupportedNumericGenericType { - ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), - typ: bad_type.clone(), - }; - errors.push((err.into(), self.file_id)); - // We still push the ill-formed generic as to continue definition collection and elaboration - // without triggering any other compiler errors. - resolved_generics.push(bad_generic); - } - } - } + let resolved_generics = + context.resolve_generics(&trait_definition.generics, &mut errors, self.file_id); // And store the TraitId -> TraitType mapping somewhere it is reachable let unresolved = UnresolvedTrait { diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 7591af10870..71fdc6b30d2 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -5,15 +5,16 @@ pub mod resolution; pub mod scope; pub mod type_check; -use crate::ast::{UnresolvedGenerics, UnresolvedTypeData}; +use crate::ast::UnresolvedGenerics; use crate::debug::DebugInstrumenter; use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use crate::parser::ParserError; -use crate::{Kind, ParsedModule, ResolvedGeneric, Type, TypeVariable}; +use crate::{Generics, Kind, ParsedModule, ResolvedGeneric, Type, TypeVariable}; +use def_collector::dc_crate::CompilationError; use def_map::{Contract, CrateDefMap}; -use fm::FileManager; +use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_errors::Location; use std::borrow::Cow; @@ -83,7 +84,7 @@ impl Context<'_, '_> { } } - pub fn parsed_file_results(&self, file_id: fm::FileId) -> (ParsedModule, Vec) { + pub fn parsed_file_results(&self, file_id: FileId) -> (ParsedModule, Vec) { self.parsed_files.get(&file_id).expect("noir file wasn't parsed").clone() } @@ -268,7 +269,9 @@ impl Context<'_, '_> { pub(crate) fn resolve_generics( &mut self, generics: &UnresolvedGenerics, - ) -> Vec> { + errors: &mut Vec<(CompilationError, FileId)>, + file_id: FileId, + ) -> Generics { vecmap(generics, |generic| { // Map the generic to a fresh type variable let id = self.def_interner.next_type_variable_id(); @@ -279,15 +282,12 @@ impl Context<'_, '_> { // Check for name collisions of this generic let name = Rc::new(ident.0.contents.clone()); - let mut resolved_generic = ResolvedGeneric { - name, - type_var, - kind: Kind::Numeric(Box::new(Type::Error)), - span, - }; - let kind = generic.kind().map_err(|typ| (resolved_generic.clone(), typ))?; - resolved_generic.kind = kind; - Ok(resolved_generic) + let kind = generic.kind().unwrap_or_else(|err| { + errors.push((err.into(), file_id)); + Kind::Numeric(Box::new(Type::Error)) + }); + + ResolvedGeneric { name, type_var, kind, span } }) } } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index ebe03dc1353..a6b732439b0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -41,24 +41,12 @@ pub(crate) fn resolve_traits( let mut all_errors = Vec::new(); for (trait_id, unresolved_trait) in traits { - let generic_results = context.resolve_generics(&unresolved_trait.trait_def.generics); - let mut generics = Vec::new(); - for generic_result in generic_results.into_iter() { - match generic_result { - Ok(resolved_generic) => generics.push(resolved_generic), - Err((bad_generic, bad_type)) => { - let err = DefCollectorErrorKind::UnsupportedNumericGenericType { - ident: Ident::new(bad_generic.name.to_string(), bad_generic.span), - typ: bad_type.clone(), - }; - let file = context.def_maps[&crate_id].file_id(unresolved_trait.module_id); - all_errors.push((err.into(), file)); - // We still push the ill-formed generic as to continue definition collection and elaboration - // without triggering any other compiler errors. - generics.push(bad_generic); - } - } - } + let file_id = context.def_maps[&crate_id].file_id(unresolved_trait.module_id); + let generics = context.resolve_generics( + &unresolved_trait.trait_def.generics, + &mut all_errors, + file_id, + ); let generic_type_vars = generics.iter().map(|generic| generic.type_var.clone()).collect(); // Resolve order From 8ba4cec286eb5e0b95dbf6c80b82c1196a866025 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 17:42:54 +0000 Subject: [PATCH 74/85] return DefCollectorErrorKind directly in resolve_numeric_kind_type --- compiler/noirc_frontend/src/ast/expression.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 3aaf23d93e6..fd5ad53c10a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -77,26 +77,27 @@ impl UnresolvedGeneric { pub fn kind(&self) -> Result { match self { UnresolvedGeneric::Variable(_) => Ok(Kind::Normal), - UnresolvedGeneric::Numeric { ident, typ } => { - let typ = Self::resolve_numeric_kind_type(typ.clone()).map_err(|typ| { - DefCollectorErrorKind::UnsupportedNumericGenericType { - ident: ident.clone(), - typ, - } - })?; + UnresolvedGeneric::Numeric { typ, .. } => { + let typ = self.resolve_numeric_kind_type(typ)?; Ok(Kind::Numeric(Box::new(typ))) } } } - fn resolve_numeric_kind_type(typ: UnresolvedType) -> Result { + fn resolve_numeric_kind_type( + &self, + typ: &UnresolvedType, + ) -> Result { use crate::ast::UnresolvedTypeData::{FieldElement, Integer}; match typ.typ { FieldElement => Ok(Type::FieldElement), Integer(sign, bits) => Ok(Type::Integer(sign, bits)), // Only fields and integers are supported for numeric kinds - _ => Err(typ.typ), + _ => Err(DefCollectorErrorKind::UnsupportedNumericGenericType { + ident: self.ident().clone(), + typ: typ.typ.clone(), + }), } } From 2f7315a884de8c4a4b6a3b4b67fe1eab9eaf82f7 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 19:33:33 +0000 Subject: [PATCH 75/85] cleanup kind method and error for kinds --- .../noirc_frontend/src/elaborator/types.rs | 12 ++++++-- .../src/hir/type_check/errors.rs | 9 ++++++ compiler/noirc_frontend/src/hir_def/types.rs | 28 +++++++++++++++++++ compiler/noirc_frontend/src/tests.rs | 2 +- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index fd8bf506a7a..f4bce4250b1 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -155,9 +155,9 @@ 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 if let Type::NamedGeneric(_, name, resolved_kind) = &resolved_type { - // TODO(https://github.com/noir-lang/noir/issues/5156): make this check more general with `*resolved_kind != kind` - // as implicit numeric generics still existing makes this check more challenging to enforce if matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { let expected_typ_err = CompilationError::ResolverError(ResolverError::NumericGenericUsedForType { @@ -167,6 +167,14 @@ impl<'context> Elaborator<'context> { self.errors.push((expected_typ_err, self.file)); return Type::Error; } + } else 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; } resolved_type diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index bd32ba2fce5..b6f10bb7671 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -40,6 +40,8 @@ pub enum TypeCheckError { TypeMismatch { expected_typ: String, expr_typ: String, expr_span: Span }, #[error("Expected type {expected} is not the same as {actual}")] TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, + #[error("Expected type {expected_kind:?} is not the same as {expr_kind:?}")] + TypeKindMismatch { expected_kind: String, expr_kind: String, expr_span: Span }, #[error("Expected {expected:?} found {found:?}")] ArityMisMatch { expected: usize, found: usize, span: Span }, #[error("Return type in a function cannot be public")] @@ -178,6 +180,13 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { *expr_span, ) } + TypeCheckError::TypeKindMismatch { expected_kind, expr_kind, expr_span } => { + Diagnostic::simple_error( + format!("Expected kind {expected_kind}, found kind {expr_kind}"), + String::new(), + *expr_span, + ) + } TypeCheckError::TraitMethodParameterTypeMismatch { method_name, expected_typ, actual_typ, parameter_index, parameter_span } => { Diagnostic::simple_error( format!("Parameter #{parameter_index} of method `{method_name}` must be of type {expected_typ}, not {actual_typ}"), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index f9e9a0d561d..d3edcd4ec95 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -985,6 +985,34 @@ impl Type { other => (Cow::Owned(GenericTypeVars::new()), other), } } + + 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, + } + } } impl std::fmt::Display for Type { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 8aa60d166e5..55e22060690 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1341,7 +1341,7 @@ fn for_loop_over_array() { #[test] fn type_aliases_in_main() { let src = r#" - type Outer = [u8; N]; + type Outer = [u8; N]; fn main(_arg: Outer<1>) {} "#; assert_eq!(get_program_errors(src).len(), 0); From 5dcd517ace034f3c3895c6b37f90e3a2483afddc Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 20:19:06 +0000 Subject: [PATCH 76/85] fix resolution of constant in turbofish: --- compiler/noirc_frontend/src/ast/mod.rs | 4 ++++ .../src/elaborator/expressions.rs | 17 +++++++++++++++-- compiler/noirc_frontend/src/tests.rs | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index c9946ca533e..ddd675cef30 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -259,6 +259,10 @@ impl UnresolvedType { pub fn unspecified() -> UnresolvedType { UnresolvedType { typ: UnresolvedTypeData::Unspecified, span: None } } + + pub(crate) fn is_type_expression(&self) -> bool { + matches!(&self.typ, UnresolvedTypeData::Expression(_)) + } } impl UnresolvedTypeData { diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index d6ad752b67a..999a986ac7b 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -28,7 +28,7 @@ use crate::{ MethodCallExpression, PrefixExpression, }, node_interner::{DefinitionKind, ExprId, FuncId}, - QuotedType, Shared, StructType, Type, + Kind, QuotedType, Shared, StructType, Type, }; use super::Elaborator; @@ -51,7 +51,20 @@ impl<'context> Elaborator<'context> { ExpressionKind::If(if_) => self.elaborate_if(*if_), ExpressionKind::Variable(variable, generics) => { let generics = generics.map(|option_inner| { - option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect() + option_inner + .into_iter() + .map(|generic| { + // All type expressions should resolve to a `Type::Constant` + if generic.is_type_expression() { + self.resolve_type_inner( + generic, + &Kind::Numeric(Box::new(Type::default_int_type())), + ) + } else { + self.resolve_type(generic) + } + }) + .collect() }); return self.elaborate_variable(variable, generics); } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 55e22060690..9582d9fef37 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1756,6 +1756,23 @@ fn numeric_generic_used_in_where_clause() { assert!(errors.is_empty()); } +#[test] +fn numeric_generic_used_in_turbofish() { + let src = r#" + fn double() -> u32 { + // Used as an expression + N * 2 + } + + fn double_numeric_generics_test() { + // Example usage of a numeric generic arguments. + assert(double::<9>() == 18); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} + #[test] fn normal_generic_used_when_numeric_expected_in_where_clause() { let src = r#" From d3f36e7f962c6604755cea1ddaf1a8c3a7c5820f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 21:00:16 +0000 Subject: [PATCH 77/85] fixup some tests --- compiler/noirc_frontend/src/elaborator/mod.rs | 7 ++++++- compiler/noirc_frontend/src/tests.rs | 20 +++++++++++++++++++ .../regression_4635/src/main.nr | 2 +- .../trait_generics/src/main.nr | 2 +- .../regression_4088/src/main.nr | 4 ++-- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 4d2c47ed690..7b3cc59e341 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -500,7 +500,12 @@ impl<'context> Elaborator<'context> { /// sure only primitive numeric types are being used. pub(super) fn resolve_generic_kind(&mut self, generic: &UnresolvedGeneric) -> Kind { if let UnresolvedGeneric::Numeric { ident, typ } = generic { - let typ = self.resolve_type(typ.clone()); + let typ = typ.clone(); + let typ = if typ.is_type_expression() { + self.resolve_type_inner(typ, &Kind::Numeric(Box::new(Type::default_int_type()))) + } else { + self.resolve_type(typ.clone()) + }; if !matches!(typ, Type::FieldElement | Type::Integer(_, _)) { let unsupported_typ_err = CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9582d9fef37..894cc2fdf35 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1767,6 +1767,26 @@ fn numeric_generic_used_in_turbofish() { fn double_numeric_generics_test() { // Example usage of a numeric generic arguments. assert(double::<9>() == 18); + assert(double::<7 + 8>() == 30); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} + +#[test] +fn constant_used_with_implicit_generic() { + let src = r#" + struct ValueNote {} + + trait Serialize { + fn serialize(self) -> [Field; N]; + } + + impl Serialize<1> for ValueNote { + fn serialize(self) -> [Field; 1] { + [self.value] + } } "#; let errors = get_program_errors(src); 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 23918e30785..350b60ba3f7 100644 --- a/test_programs/compile_success_empty/regression_4635/src/main.nr +++ b/test_programs/compile_success_empty/regression_4635/src/main.nr @@ -8,7 +8,7 @@ impl FromField for Field { } } -trait Deserialize { +trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } 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 30b2e79d579..56ce7e8970c 100644 --- a/test_programs/compile_success_empty/trait_generics/src/main.nr +++ b/test_programs/compile_success_empty/trait_generics/src/main.nr @@ -29,7 +29,7 @@ impl MyInto for Field { /// Serialize example -trait Serializable { +trait Serializable { fn serialize(self) -> [Field; N]; } diff --git a/test_programs/execution_success/regression_4088/src/main.nr b/test_programs/execution_success/regression_4088/src/main.nr index 9e4d7892fc3..12a7afca68c 100644 --- a/test_programs/execution_success/regression_4088/src/main.nr +++ b/test_programs/execution_success/regression_4088/src/main.nr @@ -1,4 +1,4 @@ -trait Serialize { +trait Serialize { fn serialize(self) -> [Field; N]; } @@ -12,7 +12,7 @@ impl Serialize<1> for ValueNote { } } -fn check(serialized_note: [Field; N]) { +fn check(serialized_note: [Field; N]) { assert(serialized_note[0] == 0); } From 1e5effad7e2479b5771ba34501f5eae10a4d9b50 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 21:03:08 +0000 Subject: [PATCH 78/85] use explicit num generics in regression_4124 --- test_programs/execution_success/regression_4124/src/main.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_programs/execution_success/regression_4124/src/main.nr b/test_programs/execution_success/regression_4124/src/main.nr index 2b0e65a0b6c..6caea017798 100644 --- a/test_programs/execution_success/regression_4124/src/main.nr +++ b/test_programs/execution_success/regression_4124/src/main.nr @@ -1,6 +1,6 @@ use std::option::Option; -trait MyDeserialize { +trait MyDeserialize { fn deserialize(fields: [Field; N]) -> Self; } @@ -10,7 +10,7 @@ impl MyDeserialize<1> for Field { } } -pub fn storage_read() -> [Field; N] { +pub fn storage_read() -> [Field; N] { std::unsafe::zeroed() } From a46ba679c982f79aec9607ed92dd2181dbdbd0cb Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 21:08:52 +0000 Subject: [PATCH 79/85] comment out resolved_type.kind() != kind check --- .../noirc_frontend/src/elaborator/types.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index f4bce4250b1..06f7b0f9b6c 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -157,6 +157,16 @@ 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 matches!(resolved_kind, Kind::Numeric { .. }) && matches!(kind, Kind::Normal) { let expected_typ_err = @@ -167,14 +177,6 @@ impl<'context> Elaborator<'context> { self.errors.push((expected_typ_err, self.file)); return Type::Error; } - } else 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; } resolved_type From 9bc16a4530a2a35254a44e9505be5b7c0bf81dad Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 21:15:45 +0000 Subject: [PATCH 80/85] clippy --- compiler/noirc_frontend/src/hir_def/types.rs | 56 ++++++++++---------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index d3edcd4ec95..7523cf69228 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -986,33 +986,35 @@ impl Type { } } - 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(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, + // } + // } } impl std::fmt::Display for Type { From 4ee9bc410906e2a76c5eca29809945054f9749a8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 21 Jun 2024 21:42:52 +0000 Subject: [PATCH 81/85] fixup test --- compiler/noirc_frontend/src/tests.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 894cc2fdf35..a73ed068d12 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1775,9 +1775,11 @@ fn numeric_generic_used_in_turbofish() { } #[test] -fn constant_used_with_implicit_generic() { +fn constant_used_with_numeric_generic() { let src = r#" - struct ValueNote {} + struct ValueNote { + value: Field, + } trait Serialize { fn serialize(self) -> [Field; N]; From af7e2f1fe8eb33fcf7fdf7d0dfd8df5835a2a7b8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 24 Jun 2024 20:12:14 +0000 Subject: [PATCH 82/85] merge conflicts err --- .../noirc_frontend/src/hir/comptime/interpreter/builtin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index cccc9c6d545..48f05f287d8 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -86,7 +86,7 @@ fn type_def_as_type( if i != 0 { tokens.push(SpannedToken::new(Token::Comma, span)); } - tokens.push(make_token(generic.borrow().to_string())); + tokens.push(make_token(generic.type_var.borrow().to_string())); } Ok(Value::Code(Rc::new(Tokens(tokens)))) @@ -112,7 +112,7 @@ fn type_def_generics( .generics .iter() .map(|generic| { - let name = SpannedToken::new(Token::Str(generic.borrow().to_string()), span); + let name = SpannedToken::new(Token::Str(generic.type_var.borrow().to_string()), span); Value::Code(Rc::new(Tokens(vec![name]))) }) .collect(); From dfc6345bcdb2bb075f16e1a8ad567cde6e17d502 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 24 Jun 2024 20:26:56 +0000 Subject: [PATCH 83/85] fixup bad merge --- compiler/noirc_frontend/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 314fa57bde1..9251eb3db6b 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1874,6 +1874,7 @@ fn implicit_numeric_generics_elaborator() { } } +#[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 From 18cde4bdd3414ac78821d8cc4ab91862b4df22f5 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 24 Jun 2024 20:48:43 +0000 Subject: [PATCH 84/85] check type variable bindings and named generics in find_numeric_type_vars --- compiler/noirc_frontend/src/hir_def/types.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 261d0d0d400..bff42857d02 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -767,12 +767,18 @@ impl Type { | Type::Bool | Type::Unit | Type::Error - | Type::TypeVariable(_, _) | Type::Constant(_) - | Type::NamedGeneric(_, _, _) | Type::Forall(_, _) | Type::Quoted(_) => {} + Type::TypeVariable(type_var, _) => if let TypeBinding::Bound(typ) = &*type_var.borrow() { + named_generic_is_numeric(typ, found_names); + }, + + Type::NamedGeneric(_, _, _) => { + named_generic_is_numeric(self, found_names); + } + Type::TraitAsType(_, _, args) => { for arg in args.iter() { arg.find_numeric_type_vars(found_names); From f360c7dd4cb69f1a049eb78a3419d6c9b255af44 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 24 Jun 2024 20:57:14 +0000 Subject: [PATCH 85/85] Cargo fmt --- compiler/noirc_frontend/src/hir_def/types.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index bff42857d02..8733f4b2de1 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -771,9 +771,11 @@ impl Type { | Type::Forall(_, _) | Type::Quoted(_) => {} - Type::TypeVariable(type_var, _) => if let TypeBinding::Bound(typ) = &*type_var.borrow() { - named_generic_is_numeric(typ, found_names); - }, + Type::TypeVariable(type_var, _) => { + if let TypeBinding::Bound(typ) = &*type_var.borrow() { + named_generic_is_numeric(typ, found_names); + } + } Type::NamedGeneric(_, _, _) => { named_generic_is_numeric(self, found_names);