diff --git a/crates/nargo_cli/tests/test_data/type_aliases/Nargo.toml b/crates/nargo_cli/tests/test_data/type_aliases/Nargo.toml new file mode 100644 index 00000000000..a797cb0bbe2 --- /dev/null +++ b/crates/nargo_cli/tests/test_data/type_aliases/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "type_aliases" +authors = [""] +compiler_version = "0.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/test_data/type_aliases/Prover.toml b/crates/nargo_cli/tests/test_data/type_aliases/Prover.toml new file mode 100644 index 00000000000..771df41899d --- /dev/null +++ b/crates/nargo_cli/tests/test_data/type_aliases/Prover.toml @@ -0,0 +1 @@ +x = [2, 3] diff --git a/crates/nargo_cli/tests/test_data/type_aliases/src/main.nr b/crates/nargo_cli/tests/test_data/type_aliases/src/main.nr new file mode 100644 index 00000000000..6cfafc91b7d --- /dev/null +++ b/crates/nargo_cli/tests/test_data/type_aliases/src/main.nr @@ -0,0 +1,31 @@ +use dep::std; + +type Foo = [T; 2]; + +type Bar = Field; + +type Three = Two; +type Two = One; +type One = (A, B); + +struct MyStruct { + foo: Bar, +} + +fn main(x : [Field; 2]) { + let a: Foo = [1, 2]; + assert(a[0] != x[0]); + + let b: Bar = 2; + assert(x[0] == b); + + let c: u8 = 1; + let d: u32 = 2; + let e: Three = (c, d); + assert(e.0 == 1); + + let s = MyStruct { + foo: 10 + }; + assert(s.foo == 10); +} diff --git a/crates/noirc_frontend/src/ast/mod.rs b/crates/noirc_frontend/src/ast/mod.rs index b52c3e685d3..6aa373c66a9 100644 --- a/crates/noirc_frontend/src/ast/mod.rs +++ b/crates/noirc_frontend/src/ast/mod.rs @@ -9,6 +9,7 @@ mod function; mod statement; mod structure; mod traits; +mod type_alias; pub use expression::*; pub use function::*; @@ -17,6 +18,7 @@ use noirc_errors::Span; pub use statement::*; pub use structure::*; pub use traits::*; +pub use type_alias::*; use crate::{ parser::{ParserError, ParserErrorReason}, diff --git a/crates/noirc_frontend/src/ast/type_alias.rs b/crates/noirc_frontend/src/ast/type_alias.rs new file mode 100644 index 00000000000..76a1e5a7e30 --- /dev/null +++ b/crates/noirc_frontend/src/ast/type_alias.rs @@ -0,0 +1,31 @@ +use crate::{Ident, UnresolvedGenerics, UnresolvedType}; +use iter_extended::vecmap; +use noirc_errors::Span; +use std::fmt::Display; + +/// Ast node for type aliases +#[derive(Clone, Debug)] +pub struct NoirTypeAlias { + pub name: Ident, + pub generics: UnresolvedGenerics, + pub typ: UnresolvedType, + pub span: Span, +} + +impl NoirTypeAlias { + pub fn new( + name: Ident, + generics: UnresolvedGenerics, + typ: UnresolvedType, + span: Span, + ) -> NoirTypeAlias { + NoirTypeAlias { name, generics, typ, span } + } +} + +impl Display for NoirTypeAlias { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let generics = vecmap(&self.generics, |generic| generic.to_string()); + write!(f, "type {}<{}> = {}", self.name, generics.join(", "), self.typ) + } +} diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs index 3f30a4990e4..e974961a405 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -10,10 +10,10 @@ use crate::hir::resolution::{ }; use crate::hir::type_check::{type_check_func, TypeChecker}; use crate::hir::Context; -use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId}; +use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TypeAliasId}; use crate::{ - ExpressionKind, Generics, Ident, LetStatement, NoirFunction, NoirStruct, ParsedModule, Shared, - Type, TypeBinding, UnresolvedGenerics, UnresolvedType, + ExpressionKind, Generics, Ident, LetStatement, NoirFunction, NoirStruct, NoirTypeAlias, + ParsedModule, Shared, Type, TypeBinding, UnresolvedGenerics, UnresolvedType, }; use fm::FileId; use iter_extended::vecmap; @@ -40,6 +40,13 @@ pub struct UnresolvedStruct { pub struct_def: NoirStruct, } +#[derive(Clone)] +pub struct UnresolvedTypeAlias { + pub file_id: FileId, + pub module_id: LocalModuleId, + pub type_alias_def: NoirTypeAlias, +} + #[derive(Clone)] pub struct UnresolvedGlobal { pub file_id: FileId, @@ -54,6 +61,7 @@ pub struct DefCollector { pub(crate) collected_imports: Vec, pub(crate) collected_functions: Vec, pub(crate) collected_types: HashMap, + pub(crate) collected_type_aliases: HashMap, pub(crate) collected_globals: Vec, pub(crate) collected_impls: ImplMap, } @@ -71,6 +79,7 @@ impl DefCollector { collected_imports: vec![], collected_functions: vec![], collected_types: HashMap::new(), + collected_type_aliases: HashMap::new(), collected_impls: HashMap::new(), collected_globals: vec![], } @@ -157,6 +166,8 @@ impl DefCollector { let mut file_global_ids = resolve_globals(context, integer_globals, crate_id, errors); + resolve_type_aliases(context, def_collector.collected_type_aliases, crate_id, errors); + // Must resolve structs before we resolve globals. resolve_structs(context, def_collector.collected_types, crate_id, errors); @@ -358,6 +369,27 @@ fn resolve_struct_fields( (generics, fields) } +fn resolve_type_aliases( + context: &mut Context, + type_aliases: HashMap, + crate_id: CrateId, + all_errors: &mut Vec, +) { + for (type_id, unresolved_typ) in type_aliases { + let path_resolver = StandardPathResolver::new(ModuleId { + local_id: unresolved_typ.module_id, + krate: crate_id, + }); + let file = unresolved_typ.file_id; + let (typ, generics, errors) = + Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file) + .resolve_type_aliases(unresolved_typ.type_alias_def); + extend_errors(all_errors, file, errors); + + context.def_interner.set_type_alias(type_id, typ, generics); + } +} + fn resolve_impls( interner: &mut NodeInterner, crate_id: CrateId, diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs index 2e478b6c040..37c017ecb96 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -3,11 +3,12 @@ use noirc_errors::FileDiagnostic; use crate::{ graph::CrateId, hir::def_collector::dc_crate::UnresolvedStruct, node_interner::StructId, - parser::SubModule, Ident, LetStatement, NoirFunction, NoirStruct, ParsedModule, TypeImpl, + parser::SubModule, Ident, LetStatement, NoirFunction, NoirStruct, NoirTypeAlias, ParsedModule, + TypeImpl, }; use super::{ - dc_crate::{DefCollector, UnresolvedFunctions, UnresolvedGlobal}, + dc_crate::{DefCollector, UnresolvedFunctions, UnresolvedGlobal, UnresolvedTypeAlias}, errors::DefCollectorErrorKind, }; use crate::hir::def_map::{parse_file, LocalModuleId, ModuleData, ModuleId, ModuleOrigin}; @@ -55,6 +56,8 @@ pub fn collect_defs( collector.collect_structs(ast.types, crate_id, errors); + collector.collect_type_aliases(context, ast.type_aliases, errors); + collector.collect_functions(context, ast.functions, errors); collector.collect_impls(context, ast.impls); @@ -183,6 +186,39 @@ impl<'a> ModCollector<'a> { } } + /// 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( + &mut self, + context: &mut Context, + type_aliases: Vec, + errors: &mut Vec, + ) { + for type_alias in type_aliases { + let name = type_alias.name.clone(); + + // And store the TypeId -> TypeAlias mapping somewhere it is reachable + let unresolved = UnresolvedTypeAlias { + file_id: self.file_id, + module_id: self.module_id, + type_alias_def: type_alias, + }; + + let type_alias_id = context.def_interner.push_type_alias(&unresolved); + + // 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] + .declare_type_alias(name, type_alias_id); + + if let Err((first_def, second_def)) = result { + let err = DefCollectorErrorKind::DuplicateFunction { first_def, second_def }; + errors.push(err.into_file_diagnostic(self.file_id)); + } + + self.def_collector.collected_type_aliases.insert(type_alias_id, unresolved); + } + } + fn collect_submodules( &mut self, context: &mut Context, diff --git a/crates/noirc_frontend/src/hir/def_map/item_scope.rs b/crates/noirc_frontend/src/hir/def_map/item_scope.rs index 52201f7ade3..760088a3b7e 100644 --- a/crates/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/crates/noirc_frontend/src/hir/def_map/item_scope.rs @@ -48,6 +48,7 @@ impl ItemScope { ModuleDefId::ModuleId(_) => add_item(&mut self.types), ModuleDefId::FunctionId(_) => add_item(&mut self.values), ModuleDefId::TypeId(_) => add_item(&mut self.types), + ModuleDefId::TypeAliasId(_) => add_item(&mut self.types), ModuleDefId::GlobalId(_) => add_item(&mut self.values), } } diff --git a/crates/noirc_frontend/src/hir/def_map/module_data.rs b/crates/noirc_frontend/src/hir/def_map/module_data.rs index 20906885ad9..5b93d04fea7 100644 --- a/crates/noirc_frontend/src/hir/def_map/module_data.rs +++ b/crates/noirc_frontend/src/hir/def_map/module_data.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use fm::FileId; use crate::{ - node_interner::{FuncId, StmtId, StructId}, + node_interner::{FuncId, StmtId, StructId, TypeAliasId}, Ident, }; @@ -65,6 +65,14 @@ impl ModuleData { self.declare(name, ModuleDefId::TypeId(id)) } + pub fn declare_type_alias( + &mut self, + name: Ident, + id: TypeAliasId, + ) -> Result<(), (Ident, Ident)> { + self.declare(name, id.into()) + } + pub fn declare_child_module( &mut self, name: Ident, diff --git a/crates/noirc_frontend/src/hir/def_map/module_def.rs b/crates/noirc_frontend/src/hir/def_map/module_def.rs index 399ee15700c..b64ced78772 100644 --- a/crates/noirc_frontend/src/hir/def_map/module_def.rs +++ b/crates/noirc_frontend/src/hir/def_map/module_def.rs @@ -1,4 +1,4 @@ -use crate::node_interner::{FuncId, StmtId, StructId}; +use crate::node_interner::{FuncId, StmtId, StructId, TypeAliasId}; use super::ModuleId; @@ -8,6 +8,7 @@ pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), TypeId(StructId), + TypeAliasId(TypeAliasId), GlobalId(StmtId), } @@ -26,6 +27,13 @@ impl ModuleDefId { } } + pub fn as_type_alias(&self) -> Option { + match self { + ModuleDefId::TypeAliasId(type_alias_id) => Some(*type_alias_id), + _ => None, + } + } + pub fn as_global(&self) -> Option { match self { ModuleDefId::GlobalId(stmt_id) => Some(*stmt_id), @@ -39,6 +47,7 @@ impl ModuleDefId { match self { ModuleDefId::FunctionId(_) => "function", ModuleDefId::TypeId(_) => "type", + ModuleDefId::TypeAliasId(_) => "type alias", ModuleDefId::ModuleId(_) => "module", ModuleDefId::GlobalId(_) => "global", } @@ -57,6 +66,12 @@ impl From for ModuleDefId { } } +impl From for ModuleDefId { + fn from(fid: TypeAliasId) -> Self { + ModuleDefId::TypeAliasId(fid) + } +} + impl From for ModuleDefId { fn from(stmt_id: StmtId) -> Self { ModuleDefId::GlobalId(stmt_id) @@ -97,6 +112,20 @@ impl TryFromModuleDefId for StructId { } } +impl TryFromModuleDefId for TypeAliasId { + fn try_from(id: ModuleDefId) -> Option { + id.as_type_alias() + } + + fn dummy_id() -> Self { + TypeAliasId::dummy_id() + } + + fn description() -> String { + "type alias".to_string() + } +} + impl TryFromModuleDefId for StmtId { fn try_from(id: ModuleDefId) -> Option { id.as_global() diff --git a/crates/noirc_frontend/src/hir/resolution/import.rs b/crates/noirc_frontend/src/hir/resolution/import.rs index 0bc7e065adb..9a6ef9b1b8b 100644 --- a/crates/noirc_frontend/src/hir/resolution/import.rs +++ b/crates/noirc_frontend/src/hir/resolution/import.rs @@ -152,6 +152,7 @@ fn resolve_name_in_module( ModuleDefId::FunctionId(_) => panic!("functions cannot be in the type namespace"), // TODO: If impls are ever implemented, types can be used in a path ModuleDefId::TypeId(id) => id.0, + ModuleDefId::TypeAliasId(_) => panic!("type aliases cannot be used in type namespace"), ModuleDefId::GlobalId(_) => panic!("globals cannot be in the type namespace"), }; diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index fe19cb633e4..8b4f97dbd8e 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -34,9 +34,9 @@ use crate::{ Statement, }; use crate::{ - ArrayLiteral, ContractFunctionType, Generics, LValue, NoirStruct, Path, Pattern, Shared, - StructType, Type, TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedType, - UnresolvedTypeExpression, ERROR_IDENT, + ArrayLiteral, ContractFunctionType, Generics, LValue, NoirStruct, NoirTypeAlias, Path, Pattern, + Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeExpression, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -403,22 +403,27 @@ impl<'a> Resolver<'a> { } let span = path.span(); + let mut args = vecmap(args, |arg| self.resolve_type_inner(arg, new_variables)); + + if let Some(type_alias_type) = self.lookup_type_alias(path.clone()) { + let expected_generic_count = type_alias_type.generics.len(); + let type_alias_string = type_alias_type.to_string(); + let id = type_alias_type.id; + + self.verify_generics_count(expected_generic_count, &mut args, span, || { + type_alias_string + }); + + return self.interner.get_type_alias(id).get_type(&args); + } + match self.lookup_struct_or_error(path) { Some(struct_type) => { - let mut args = vecmap(args, |arg| self.resolve_type_inner(arg, new_variables)); let expected_generic_count = struct_type.borrow().generics.len(); - if args.len() != expected_generic_count { - self.push_err(ResolverError::IncorrectGenericCount { - span, - struct_type: struct_type.borrow().to_string(), - actual: args.len(), - expected: expected_generic_count, - }); - - // Fix the generic count so we can continue typechecking - args.resize_with(expected_generic_count, || Type::Error); - } + self.verify_generics_count(expected_generic_count, &mut args, span, || { + struct_type.borrow().to_string() + }); Type::Struct(struct_type, args) } @@ -426,6 +431,26 @@ impl<'a> Resolver<'a> { } } + fn verify_generics_count( + &mut self, + expected_count: usize, + args: &mut Vec, + span: Span, + type_name: impl FnOnce() -> String, + ) { + if args.len() != expected_count { + self.errors.push(ResolverError::IncorrectGenericCount { + span, + struct_type: type_name(), + actual: args.len(), + expected: expected_count, + }); + + // Fix the generic count so we can continue typechecking + args.resize_with(expected_count, || Type::Error); + } + } + fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { let name = &path.last_segment().0.contents; @@ -517,6 +542,17 @@ impl<'a> Resolver<'a> { self.resolve_type_inner(typ, &mut vec![]) } + pub fn resolve_type_aliases( + mut self, + unresolved: NoirTypeAlias, + ) -> (Type, Generics, Vec) { + let generics = self.add_generics(&unresolved.generics); + self.resolve_local_globals(); + let typ = self.resolve_type(unresolved.typ); + + (typ, generics, self.errors) + } + pub fn take_errors(self) -> Vec { self.errors } @@ -1253,6 +1289,10 @@ impl<'a> Resolver<'a> { } } + fn lookup_type_alias(&mut self, path: Path) -> Option<&TypeAliasType> { + self.lookup(path).ok().map(|id| self.interner.get_type_alias(id)) + } + fn resolve_path(&mut self, path: Path) -> Result { self.path_resolver.resolve(self.def_maps, path).map_err(ResolverError::PathResolutionError) } diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index 4b4318f79d6..df4c2f6c229 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ hir::type_check::TypeCheckError, - node_interner::{ExprId, NodeInterner}, + node_interner::{ExprId, NodeInterner, TypeAliasId}, }; use iter_extended::vecmap; use noirc_abi::AbiType; @@ -226,6 +226,72 @@ impl std::fmt::Display for StructType { } } +/// Wrap around an unsolved type +#[derive(Debug, Clone, Eq)] +pub struct TypeAliasType { + pub name: Ident, + pub id: TypeAliasId, + pub typ: Type, + pub generics: Generics, + pub span: Span, +} + +impl std::hash::Hash for TypeAliasType { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl PartialEq for TypeAliasType { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl std::fmt::Display for TypeAliasType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name)?; + + if !self.generics.is_empty() { + let generics = vecmap(&self.generics, |(_, binding)| binding.borrow().to_string()); + write!(f, "{}", generics.join(", "))?; + } + + Ok(()) + } +} + +impl TypeAliasType { + pub fn new( + id: TypeAliasId, + name: Ident, + span: Span, + typ: Type, + generics: Generics, + ) -> TypeAliasType { + TypeAliasType { id, typ, name, span, generics } + } + + pub fn set_type_and_generics(&mut self, new_typ: Type, new_generics: Generics) { + assert_eq!(self.typ, Type::Error); + self.typ = new_typ; + self.generics = new_generics; + } + + pub fn get_type(&self, generic_args: &[Type]) -> Type { + assert_eq!(self.generics.len(), generic_args.len()); + + let substitutions = self + .generics + .iter() + .zip(generic_args) + .map(|((old_id, old_var), new)| (*old_id, (old_var.clone(), new.clone()))) + .collect(); + + self.typ.substitute(&substitutions) + } +} + /// A shared, mutable reference to some T. /// Wrapper is required for Hash impl of RefCell. #[derive(Debug, Eq, PartialOrd, Ord)] diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 062e9daf2d6..f5fea5c1ea7 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -7,7 +7,7 @@ use noirc_errors::{Location, Span, Spanned}; use crate::ast::Ident; use crate::graph::CrateId; -use crate::hir::def_collector::dc_crate::UnresolvedStruct; +use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::StorageSlot; use crate::hir_def::stmt::HirLetStatement; @@ -17,7 +17,10 @@ use crate::hir_def::{ function::{FuncMeta, HirFunction}, stmt::HirStatement, }; -use crate::{Shared, TypeBinding, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind}; +use crate::{ + Generics, Shared, TypeAliasType, TypeBinding, TypeBindings, TypeVariable, TypeVariableId, + TypeVariableKind, +}; /// The node interner is the central storage location of all nodes in Noir's Hir (the /// various node types can be found in hir_def). The interner is also used to collect @@ -52,6 +55,12 @@ pub struct NodeInterner { // methods from impls to the type. structs: HashMap>, + // Type Aliases map. + // + // Map type aliases to the actual type. + // When resolving types, check against this map to see if a type alias is defined. + type_aliases: Vec, + /// Map from ExprId (referring to a Function/Method call) to its corresponding TypeBindings, /// filled out during type checking from instantiated variables. Used during monomorphization /// to map call site types back onto function parameter types, and undo this binding as needed. @@ -132,6 +141,15 @@ impl StructId { } } +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +pub struct TypeAliasId(pub usize); + +impl TypeAliasId { + pub fn dummy_id() -> TypeAliasId { + TypeAliasId(std::usize::MAX) + } +} + macro_rules! into_index { ($id_type:ty) => { impl From<$id_type> for Index { @@ -243,6 +261,7 @@ impl Default for NodeInterner { definitions: vec![], id_to_type: HashMap::new(), structs: HashMap::new(), + type_aliases: Vec::new(), instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: 0, @@ -305,11 +324,33 @@ impl NodeInterner { ); } + pub fn push_type_alias(&mut self, typ: &UnresolvedTypeAlias) -> TypeAliasId { + let type_id = TypeAliasId(self.type_aliases.len()); + + self.type_aliases.push(TypeAliasType::new( + type_id, + typ.type_alias_def.name.clone(), + typ.type_alias_def.span, + Type::Error, + vecmap(&typ.type_alias_def.generics, |_| { + let id = TypeVariableId(0); + (id, Shared::new(TypeBinding::Unbound(id))) + }), + )); + + type_id + } + pub fn update_struct(&mut self, type_id: StructId, f: impl FnOnce(&mut StructType)) { let mut value = self.structs.get_mut(&type_id).unwrap().borrow_mut(); f(&mut value); } + pub fn set_type_alias(&mut self, type_id: TypeAliasId, typ: Type, generics: Generics) { + let type_alias_type = &mut self.type_aliases[type_id.0]; + type_alias_type.set_type_and_generics(typ, generics); + } + /// Returns the interned statement corresponding to `stmt_id` pub fn update_statement(&mut self, stmt_id: &StmtId, f: impl FnOnce(&mut HirStatement)) { let def = @@ -506,6 +547,10 @@ impl NodeInterner { self.structs[&id].clone() } + pub fn get_type_alias(&self, id: TypeAliasId) -> &TypeAliasType { + &self.type_aliases[id.0] + } + pub fn get_global(&self, stmt_id: &StmtId) -> Option { self.globals.get(stmt_id).cloned() } diff --git a/crates/noirc_frontend/src/parser/mod.rs b/crates/noirc_frontend/src/parser/mod.rs index 9cf9f1e9869..ad519836b39 100644 --- a/crates/noirc_frontend/src/parser/mod.rs +++ b/crates/noirc_frontend/src/parser/mod.rs @@ -17,8 +17,8 @@ use crate::token::{Keyword, Token}; use crate::{ast::ImportStatement, Expression, NoirStruct}; use crate::{ BlockExpression, ExpressionKind, ForExpression, Ident, IndexExpression, LetStatement, - MethodCallExpression, NoirFunction, NoirTrait, Path, PathKind, Pattern, Recoverable, Statement, - TraitImpl, TypeImpl, UnresolvedType, UseTree, + MethodCallExpression, NoirFunction, NoirTrait, NoirTypeAlias, Path, PathKind, Pattern, + Recoverable, Statement, TraitImpl, TypeImpl, UnresolvedType, UseTree, }; use acvm::FieldElement; @@ -43,6 +43,7 @@ pub(crate) enum TopLevelStatement { Trait(NoirTrait), TraitImpl(TraitImpl), Impl(TypeImpl), + TypeAlias(NoirTypeAlias), SubModule(SubModule), Global(LetStatement), Error, @@ -225,6 +226,7 @@ pub struct ParsedModule { pub traits: Vec, pub trait_impls: Vec, pub impls: Vec, + pub type_aliases: Vec, pub globals: Vec, /// Module declarations like `mod foo;` @@ -264,6 +266,10 @@ impl ParsedModule { self.impls.push(r#impl); } + fn push_type_alias(&mut self, type_alias: NoirTypeAlias) { + self.type_aliases.push(type_alias); + } + fn push_import(&mut self, import_stmt: UseTree) { self.imports.extend(import_stmt.desugar(None)); } @@ -463,6 +469,7 @@ impl std::fmt::Display for TopLevelStatement { TopLevelStatement::TraitImpl(i) => i.fmt(f), TopLevelStatement::Struct(s) => s.fmt(f), TopLevelStatement::Impl(i) => i.fmt(f), + TopLevelStatement::TypeAlias(t) => t.fmt(f), TopLevelStatement::SubModule(s) => s.fmt(f), TopLevelStatement::Global(c) => c.fmt(f), TopLevelStatement::Error => write!(f, "error"), @@ -496,6 +503,10 @@ impl std::fmt::Display for ParsedModule { write!(f, "{impl_}")?; } + for type_alias in &self.type_aliases { + write!(f, "{type_alias}")?; + } + for submodule in &self.submodules { write!(f, "{submodule}")?; } diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index 65446e5d6c6..6445205eae6 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -36,8 +36,8 @@ use crate::token::{Attribute, Keyword, Token, TokenKind}; use crate::{ BinaryOp, BinaryOpKind, BlockExpression, CompTime, ConstrainStatement, FunctionDefinition, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, - NoirTrait, Path, PathKind, Pattern, Recoverable, TraitConstraint, TraitImpl, TraitImplItem, - TraitItem, TypeImpl, UnaryOp, UnresolvedTypeExpression, UseTree, UseTreeKind, + NoirTrait, NoirTypeAlias, Path, PathKind, Pattern, Recoverable, TraitConstraint, TraitImpl, + TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTypeExpression, UseTree, UseTreeKind, }; use chumsky::prelude::*; @@ -82,6 +82,7 @@ fn module() -> impl NoirParser { TopLevelStatement::Trait(t) => program.push_trait(t), TopLevelStatement::TraitImpl(t) => program.push_trait_impl(t), TopLevelStatement::Impl(i) => program.push_impl(i), + TopLevelStatement::TypeAlias(t) => program.push_type_alias(t), TopLevelStatement::SubModule(s) => program.push_submodule(s), TopLevelStatement::Global(c) => program.push_global(c), TopLevelStatement::Error => (), @@ -108,6 +109,7 @@ fn top_level_statement( trait_definition(), trait_implementation(), implementation(), + type_alias_definition().then_ignore(force(just(Token::Semicolon))), submodule(module_parser.clone()), contract(module_parser), module_declaration().then_ignore(force(just(Token::Semicolon))), @@ -236,6 +238,19 @@ fn struct_definition() -> impl NoirParser { ) } +fn type_alias_definition() -> impl NoirParser { + use self::Keyword::Type; + + let p = ignore_then_commit(keyword(Type), ident()); + let p = then_commit(p, generics()); + let p = then_commit_ignore(p, just(Token::Assign)); + let p = then_commit(p, parse_type()); + + p.map_with_span(|((name, generics), typ), span| { + TopLevelStatement::TypeAlias(NoirTypeAlias { name, generics, typ, span }) + }) +} + fn lambda_return_type() -> impl NoirParser { just(Token::Arrow) .ignore_then(parse_type()) @@ -1917,6 +1932,15 @@ mod test { parse_all_failing(struct_definition(), failing); } + #[test] + fn parse_type_aliases() { + let cases = vec!["type foo = u8", "type bar = String", "type baz = Vec"]; + parse_all(type_alias_definition(), cases); + + let failing = vec!["type = u8", "type foo", "type foo = 1"]; + parse_all_failing(type_alias_definition(), failing); + } + #[test] fn parse_member_access() { let cases = vec!["a.b", "a + b.c", "foo.bar as i32"];