From e5976916c711c11a27554745722f4909955faa3b Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 9 Aug 2023 14:47:53 +0300 Subject: [PATCH 1/3] Add Trait types into HIR and node_interner --- .../src/hir/resolution/resolver.rs | 9 +- crates/noirc_frontend/src/hir_def/types.rs | 90 ++++++++++++++++++- .../src/monomorphization/mod.rs | 1 + crates/noirc_frontend/src/node_interner.rs | 31 ++++++- 4 files changed, 125 insertions(+), 6 deletions(-) diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 945d11aae51..c201349d37c 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -26,7 +26,7 @@ use crate::graph::CrateId; use crate::hir::def_map::{ModuleDefId, ModuleId, TryFromModuleDefId, MAIN_FUNCTION}; use crate::hir_def::stmt::{HirAssignStatement, HirLValue, HirPattern}; use crate::node_interner::{ - DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, StructId, + DefinitionId, DefinitionKind, ExprId, FuncId, NodeInterner, StmtId, StructId, TraitId, }; use crate::{ hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver}, @@ -35,7 +35,7 @@ use crate::{ }; use crate::{ ArrayLiteral, ContractFunctionType, Generics, LValue, NoirStruct, NoirTypeAlias, Path, Pattern, - Shared, StructType, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, + Shared, StructType, Trait, Type, TypeAliasType, TypeBinding, TypeVariable, UnaryOp, UnresolvedGenerics, UnresolvedType, UnresolvedTypeExpression, ERROR_IDENT, }; use fm::FileId; @@ -826,6 +826,7 @@ impl<'a> Resolver<'a> { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::NotConstant + | Type::Trait(..) | Type::Forall(_, _) => (), Type::Array(length, element_type) => { @@ -1297,6 +1298,10 @@ impl<'a> Resolver<'a> { self.interner.get_struct(type_id) } + pub fn get_trait(&self, type_id: TraitId) -> Shared { + self.interner.get_trait(type_id) + } + fn lookup(&mut self, path: Path) -> Result { let span = path.span(); let id = self.resolve_path(path)?; diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index b29b01e1ed2..483f9e9bdab 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -12,7 +12,7 @@ use iter_extended::vecmap; use noirc_abi::AbiType; use noirc_errors::Span; -use crate::{node_interner::StructId, Ident, Signedness}; +use crate::{node_interner::StructId, node_interner::TraitId, Ident, Signedness}; use super::expr::{HirCallExpression, HirExpression, HirIdent}; @@ -48,6 +48,11 @@ pub enum Type { /// represents the generic arguments (if any) to this struct type. Struct(Shared, Vec), + /// A user-defined trait type. The `Shared` field here refers to + /// the shared definition for each instance of this trait type. The `Vec` + /// represents the generic arguments (if any) to this trait type. + Trait(Shared, Vec), + /// A tuple type with the given list of fields in the order they appear in source code. Tuple(Vec), @@ -124,6 +129,39 @@ pub struct StructType { pub span: Span, } +#[derive(Debug, PartialEq, Eq)] +pub enum TraitItemType { + /// A function declaration in a trait. + Function { + name: Ident, + generics: Generics, + arguments: Vec, + return_type: Type, + span: Span, + }, + + /// A constant declaration in a trait. + Constant { name: Ident, ty: Type, span: Span }, + + /// A type declaration in a trait. + Type { name: Ident, ty: Type, span: Span }, +} +/// Represents a trait type in the type system. Each instance of this +/// rust struct will be shared across all Type::Trait variants that represent +/// the same trait type. +#[derive(Debug, Eq)] +pub struct Trait { + /// A unique id representing this trait type. Used to check if two + /// struct traits are equal. + pub id: TraitId, + + pub items: Vec, + + pub name: Ident, + pub generics: Generics, + pub span: Span, +} + /// Corresponds to generic lists such as `` in the source /// program. The `TypeVariableId` portion is used to match two /// type variables to check for equality, while the `TypeVariable` is @@ -142,6 +180,36 @@ impl PartialEq for StructType { } } +impl std::hash::Hash for Trait { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl PartialEq for Trait { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Trait { + pub fn new( + id: TraitId, + name: Ident, + span: Span, + items: Vec, + generics: Generics, + ) -> Trait { + Trait { id, name, span, items, generics } + } +} + +impl std::fmt::Display for Trait { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + impl StructType { pub fn new( id: StructId, @@ -476,6 +544,7 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::NotConstant + | Type::Trait(..) | Type::Forall(_, _) => false, Type::Array(length, elem) => { @@ -553,6 +622,15 @@ impl std::fmt::Display for Type { write!(f, "{}<{}>", s.borrow(), args.join(", ")) } } + Type::Trait(s, args) => { + // TODO: Extract this into a shared helper for traits and structs + let args = vecmap(args, |arg| arg.to_string()); + if args.is_empty() { + write!(f, "{}", s.borrow()) + } else { + write!(f, "{}<{}>", s.borrow(), args.join(", ")) + } + } Type::Tuple(elements) => { let elements = vecmap(elements, ToString::to_string); write!(f, "({})", elements.join(", ")) @@ -992,6 +1070,7 @@ impl Type { let fields = vecmap(fields, |(name, typ)| (name, typ.as_abi_type())); AbiType::Struct { fields, name: struct_type.name.to_string() } } + Type::Trait(_, _) => unreachable!("traits cannot be used in the abi"), Type::Tuple(_) => todo!("as_abi_type not yet implemented for tuple types"), Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), @@ -1098,6 +1177,12 @@ impl Type { let args = vecmap(args, |arg| arg.substitute(type_bindings)); Type::Struct(fields.clone(), args) } + Type::Trait(items, args) => { + let args = vecmap(args, |arg| arg.substitute(type_bindings)); + // TODO: Don't we need to substitute any reference to the generic parameters within the + // items as well here? How does it work for structs? + Type::Trait(items.clone(), args) + } Type::Tuple(fields) => { let fields = vecmap(fields, |field| field.substitute(type_bindings)); Type::Tuple(fields) @@ -1142,6 +1227,7 @@ impl Type { len_occurs || field_occurs } Type::Struct(_, generic_args) => generic_args.iter().any(|arg| arg.occurs(target_id)), + Type::Trait(_, generic_args) => 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, _) => { match &*binding.borrow() { @@ -1210,7 +1296,7 @@ impl Type { MutableReference(element) => MutableReference(Box::new(element.follow_bindings())), // Expect that this function should only be called on instantiated types - Forall(..) => unreachable!(), + Forall(..) | Trait(..) => unreachable!(), FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Error | NotConstant => { self.clone() diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 998f3093d49..b50d46f3967 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -727,6 +727,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::NotConstant + | HirType::Trait(..) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index ad1638c3f0b..1ec389488ce 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -11,7 +11,7 @@ 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; -use crate::hir_def::types::{StructType, Type}; +use crate::hir_def::types::{StructType, Trait, Type}; use crate::hir_def::{ expr::HirExpression, function::{FuncMeta, HirFunction}, @@ -61,6 +61,15 @@ pub struct NodeInterner { // When resolving types, check against this map to see if a type alias is defined. type_aliases: Vec, + // Trait map. + // + // Each trait definition is possibly shared across multiple type nodes. + // It is also mutated through the RefCell during name resolution to append + // methods from impls to the type. + // + // TODO: We may be able to remove the Shared wrapper once traits are no longer types. + // We'd just lookup their methods as needed through the NodeInterner. + traits: HashMap>, /// 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. @@ -150,6 +159,18 @@ impl TypeAliasId { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct TraitId(pub ModuleId); + +impl TraitId { + // dummy id for error reporting + // This can be anything, as the program will ultimately fail + // after resolution + pub fn dummy_id() -> TraitId { + TraitId(ModuleId { krate: CrateId::dummy_id(), local_id: LocalModuleId::dummy_id() }) + } +} + macro_rules! into_index { ($id_type:ty) => { impl From<$id_type> for Index { @@ -262,6 +283,7 @@ impl Default for NodeInterner { id_to_type: HashMap::new(), structs: HashMap::new(), type_aliases: Vec::new(), + traits: HashMap::new(), instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), next_type_variable_id: 0, @@ -547,6 +569,10 @@ impl NodeInterner { self.structs[&id].clone() } + pub fn get_trait(&self, id: TraitId) -> Shared { + self.traits[&id].clone() + } + pub fn get_type_alias(&self, id: TypeAliasId) -> &TypeAliasType { &self.type_aliases[id.0] } @@ -683,6 +709,7 @@ fn get_type_method_key(typ: &Type) -> Option { | Type::Error | Type::NotConstant | Type::Struct(_, _) - | Type::FmtString(_, _) => None, + | Type::FmtString(_, _) + | Type::Trait(_, _) => None, } } From a7a88bcaaee7c38efb1a5d357091202153fc95b7 Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Wed, 9 Aug 2023 15:07:20 +0300 Subject: [PATCH 2/3] Add traits in module data definitions --- .../src/hir/def_map/item_scope.rs | 1 + .../src/hir/def_map/module_data.rs | 6 ++++- .../src/hir/def_map/module_def.rs | 27 +++++++++++++++++-- .../src/hir/resolution/import.rs | 4 +-- 4 files changed, 33 insertions(+), 5 deletions(-) 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 760088a3b7e..7dcc5051a0c 100644 --- a/crates/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/crates/noirc_frontend/src/hir/def_map/item_scope.rs @@ -49,6 +49,7 @@ impl ItemScope { ModuleDefId::FunctionId(_) => add_item(&mut self.values), ModuleDefId::TypeId(_) => add_item(&mut self.types), ModuleDefId::TypeAliasId(_) => add_item(&mut self.types), + ModuleDefId::TraitId(_) => 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 5382ca6ad15..1a5b1692208 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 noirc_errors::Location; use crate::{ - node_interner::{FuncId, StmtId, StructId, TypeAliasId}, + node_interner::{FuncId, StmtId, StructId, TraitId, TypeAliasId}, Ident, }; @@ -69,6 +69,10 @@ impl ModuleData { self.declare(name, id.into()) } + pub fn declare_trait(&mut self, name: Ident, id: TraitId) -> Result<(), (Ident, Ident)> { + self.declare(name, ModuleDefId::TraitId(id)) + } + 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 b64ced78772..ade0fcaf7aa 100644 --- a/crates/noirc_frontend/src/hir/def_map/module_def.rs +++ b/crates/noirc_frontend/src/hir/def_map/module_def.rs @@ -1,14 +1,15 @@ -use crate::node_interner::{FuncId, StmtId, StructId, TypeAliasId}; +use crate::node_interner::{FuncId, StmtId, StructId, TraitId, TypeAliasId}; use super::ModuleId; -/// A generic ID that references either a module, function, type, or global +/// A generic ID that references either a module, function, type, interface or global #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ModuleDefId { ModuleId(ModuleId), FunctionId(FuncId), TypeId(StructId), TypeAliasId(TypeAliasId), + TraitId(TraitId), GlobalId(StmtId), } @@ -34,6 +35,13 @@ impl ModuleDefId { } } + pub fn as_trait(&self) -> Option { + match self { + ModuleDefId::TraitId(trait_id) => Some(*trait_id), + _ => None, + } + } + pub fn as_global(&self) -> Option { match self { ModuleDefId::GlobalId(stmt_id) => Some(*stmt_id), @@ -48,6 +56,7 @@ impl ModuleDefId { ModuleDefId::FunctionId(_) => "function", ModuleDefId::TypeId(_) => "type", ModuleDefId::TypeAliasId(_) => "type alias", + ModuleDefId::TraitId(_) => "trait", ModuleDefId::ModuleId(_) => "module", ModuleDefId::GlobalId(_) => "global", } @@ -126,6 +135,20 @@ impl TryFromModuleDefId for TypeAliasId { } } +impl TryFromModuleDefId for TraitId { + fn try_from(id: ModuleDefId) -> Option { + id.as_trait() + } + + fn dummy_id() -> Self { + TraitId::dummy_id() + } + + fn description() -> String { + "trait".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 9a6ef9b1b8b..68796cf26bd 100644 --- a/crates/noirc_frontend/src/hir/resolution/import.rs +++ b/crates/noirc_frontend/src/hir/resolution/import.rs @@ -153,6 +153,7 @@ fn resolve_name_in_module( // 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::TraitId(id) => id.0, ModuleDefId::GlobalId(_) => panic!("globals cannot be in the type namespace"), }; @@ -160,15 +161,14 @@ fn resolve_name_in_module( // Check if namespace let found_ns = current_mod.find_name(segment); + if found_ns.is_none() { return Err(PathResolutionError::Unresolved(segment.clone())); } - // Check if it is a contract and we're calling from a non-contract context if current_mod.is_contract && !allow_contracts { return Err(PathResolutionError::ExternalContractUsed(segment.clone())); } - current_ns = found_ns; } From 2024f444658a228f727da006461ec111e12be4ca Mon Sep 17 00:00:00 2001 From: Yordan Madzhunkov Date: Thu, 17 Aug 2023 09:05:00 +0300 Subject: [PATCH 3/3] Remove Trait from HIR Types --- .../src/hir/resolution/resolver.rs | 1 - crates/noirc_frontend/src/hir_def/types.rs | 25 +------------------ .../src/monomorphization/mod.rs | 1 - crates/noirc_frontend/src/node_interner.rs | 3 +-- 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index c201349d37c..26c99d436cc 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -826,7 +826,6 @@ impl<'a> Resolver<'a> { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::NotConstant - | Type::Trait(..) | Type::Forall(_, _) => (), Type::Array(length, element_type) => { diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index 483f9e9bdab..bb566880fb3 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -48,11 +48,6 @@ pub enum Type { /// represents the generic arguments (if any) to this struct type. Struct(Shared, Vec), - /// A user-defined trait type. The `Shared` field here refers to - /// the shared definition for each instance of this trait type. The `Vec` - /// represents the generic arguments (if any) to this trait type. - Trait(Shared, Vec), - /// A tuple type with the given list of fields in the order they appear in source code. Tuple(Vec), @@ -544,7 +539,6 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::NotConstant - | Type::Trait(..) | Type::Forall(_, _) => false, Type::Array(length, elem) => { @@ -622,15 +616,6 @@ impl std::fmt::Display for Type { write!(f, "{}<{}>", s.borrow(), args.join(", ")) } } - Type::Trait(s, args) => { - // TODO: Extract this into a shared helper for traits and structs - let args = vecmap(args, |arg| arg.to_string()); - if args.is_empty() { - write!(f, "{}", s.borrow()) - } else { - write!(f, "{}<{}>", s.borrow(), args.join(", ")) - } - } Type::Tuple(elements) => { let elements = vecmap(elements, ToString::to_string); write!(f, "({})", elements.join(", ")) @@ -1070,7 +1055,6 @@ impl Type { let fields = vecmap(fields, |(name, typ)| (name, typ.as_abi_type())); AbiType::Struct { fields, name: struct_type.name.to_string() } } - Type::Trait(_, _) => unreachable!("traits cannot be used in the abi"), Type::Tuple(_) => todo!("as_abi_type not yet implemented for tuple types"), Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), @@ -1177,12 +1161,6 @@ impl Type { let args = vecmap(args, |arg| arg.substitute(type_bindings)); Type::Struct(fields.clone(), args) } - Type::Trait(items, args) => { - let args = vecmap(args, |arg| arg.substitute(type_bindings)); - // TODO: Don't we need to substitute any reference to the generic parameters within the - // items as well here? How does it work for structs? - Type::Trait(items.clone(), args) - } Type::Tuple(fields) => { let fields = vecmap(fields, |field| field.substitute(type_bindings)); Type::Tuple(fields) @@ -1227,7 +1205,6 @@ impl Type { len_occurs || field_occurs } Type::Struct(_, generic_args) => generic_args.iter().any(|arg| arg.occurs(target_id)), - Type::Trait(_, generic_args) => 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, _) => { match &*binding.borrow() { @@ -1296,7 +1273,7 @@ impl Type { MutableReference(element) => MutableReference(Box::new(element.follow_bindings())), // Expect that this function should only be called on instantiated types - Forall(..) | Trait(..) => unreachable!(), + Forall(..) => unreachable!(), FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Error | NotConstant => { self.clone() diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index b50d46f3967..998f3093d49 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -727,7 +727,6 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::NotConstant - | HirType::Trait(..) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 1ec389488ce..5a45cfee42b 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -709,7 +709,6 @@ fn get_type_method_key(typ: &Type) -> Option { | Type::Error | Type::NotConstant | Type::Struct(_, _) - | Type::FmtString(_, _) - | Type::Trait(_, _) => None, + | Type::FmtString(_, _) => None, } }