Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add trait definition representation in DefCollector and HIR #2338

Merged
merged 3 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/noirc_frontend/src/hir/def_map/item_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/noirc_frontend/src/hir/def_map/module_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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,
Expand Down
27 changes: 25 additions & 2 deletions crates/noirc_frontend/src/hir/def_map/module_def.rs
Original file line number Diff line number Diff line change
@@ -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),
}

Expand All @@ -34,6 +35,13 @@ impl ModuleDefId {
}
}

pub fn as_trait(&self) -> Option<TraitId> {
match self {
ModuleDefId::TraitId(trait_id) => Some(*trait_id),
_ => None,
}
}

pub fn as_global(&self) -> Option<StmtId> {
match self {
ModuleDefId::GlobalId(stmt_id) => Some(*stmt_id),
Expand All @@ -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",
}
Expand Down Expand Up @@ -126,6 +135,20 @@ impl TryFromModuleDefId for TypeAliasId {
}
}

impl TryFromModuleDefId for TraitId {
fn try_from(id: ModuleDefId) -> Option<Self> {
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<Self> {
id.as_global()
Expand Down
4 changes: 2 additions & 2 deletions crates/noirc_frontend/src/hir/resolution/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,22 +153,22 @@ 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"),
};

current_mod = &def_maps[&new_module_id.krate].modules[new_module_id.local_id.0];

// 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;
}

Expand Down
9 changes: 7 additions & 2 deletions crates/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
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},
Expand All @@ -35,7 +35,7 @@
};
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;
Expand Down Expand Up @@ -826,6 +826,7 @@
| Type::Constant(_)
| Type::NamedGeneric(_, _)
| Type::NotConstant
| Type::Trait(..)
| Type::Forall(_, _) => (),

Type::Array(length, element_type) => {
Expand Down Expand Up @@ -1297,6 +1298,10 @@
self.interner.get_struct(type_id)
}

pub fn get_trait(&self, type_id: TraitId) -> Shared<Trait> {
self.interner.get_trait(type_id)
}

fn lookup<T: TryFromModuleDefId>(&mut self, path: Path) -> Result<T, ResolverError> {
let span = path.span();
let id = self.resolve_path(path)?;
Expand Down Expand Up @@ -1931,7 +1936,7 @@
println(f"I want to print {0}");

let new_val = 10;
println(f"randomstring{new_val}{new_val}");

Check warning on line 1939 in crates/noirc_frontend/src/hir/resolution/resolver.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (randomstring)
}
fn println<T>(x : T) -> T {
x
Expand Down
90 changes: 88 additions & 2 deletions crates/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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};

Expand Down Expand Up @@ -48,6 +48,11 @@
/// represents the generic arguments (if any) to this struct type.
Struct(Shared<StructType>, Vec<Type>),

/// A user-defined trait type. The `Shared<TraitType>` field here refers to
/// the shared definition for each instance of this trait type. The `Vec<Type>`
/// represents the generic arguments (if any) to this trait type.
Trait(Shared<Trait>, Vec<Type>),

yordanmadzhunkov marked this conversation as resolved.
Show resolved Hide resolved
/// A tuple type with the given list of fields in the order they appear in source code.
Tuple(Vec<Type>),

Expand Down Expand Up @@ -124,6 +129,39 @@
pub span: Span,
}

#[derive(Debug, PartialEq, Eq)]
pub enum TraitItemType {
/// A function declaration in a trait.
Function {
name: Ident,
generics: Generics,
arguments: Vec<Type>,
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<TraitItemType>,

pub name: Ident,
pub generics: Generics,
pub span: Span,
}

/// Corresponds to generic lists such as `<T, U>` in the source
/// program. The `TypeVariableId` portion is used to match two
/// type variables to check for equality, while the `TypeVariable` is
Expand All @@ -142,6 +180,36 @@
}
}

impl std::hash::Hash for Trait {
fn hash<H: std::hash::Hasher>(&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<TraitItemType>,
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,
Expand Down Expand Up @@ -476,6 +544,7 @@
| Type::Constant(_)
| Type::NamedGeneric(_, _)
| Type::NotConstant
| Type::Trait(..)
| Type::Forall(_, _) => false,

Type::Array(length, elem) => {
Expand Down Expand Up @@ -553,6 +622,15 @@
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(", "))
Expand All @@ -560,7 +638,7 @@
Type::Bool => write!(f, "bool"),
Type::String(len) => write!(f, "str<{len}>"),
Type::FmtString(len, elements) => {
write!(f, "fmtstr<{len}, {elements}>")

Check warning on line 641 in crates/noirc_frontend/src/hir_def/types.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (fmtstr)
}
Type::Unit => write!(f, "()"),
Type::Error => write!(f, "error"),
Expand Down Expand Up @@ -992,6 +1070,7 @@
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!(),
Expand Down Expand Up @@ -1098,6 +1177,12 @@
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)
Expand Down Expand Up @@ -1142,6 +1227,7 @@
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() {
Expand Down Expand Up @@ -1210,7 +1296,7 @@
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()
Expand Down
1 change: 1 addition & 0 deletions crates/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@
HirType::Forall(_, _)
| HirType::Constant(_)
| HirType::NotConstant
| HirType::Trait(..)
| HirType::Error => {
unreachable!("Unexpected type {} found", typ)
}
Expand Down Expand Up @@ -1046,7 +1047,7 @@
expr: node_interner::ExprId,
) -> (ast::Expression, ast::Expression) {
// returns (<closure setup>, <closure variable>)
// which can be used directly in callsites or transformed

Check warning on line 1050 in crates/noirc_frontend/src/monomorphization/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (callsites)
// directly to a single `Expression`
// for other cases by `lambda` which is called by `expr`
//
Expand Down
31 changes: 29 additions & 2 deletions crates/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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<TypeAliasType>,

// 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.
jfecher marked this conversation as resolved.
Show resolved Hide resolved
traits: HashMap<TraitId, Shared<Trait>>,
/// 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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -547,6 +569,10 @@ impl NodeInterner {
self.structs[&id].clone()
}

pub fn get_trait(&self, id: TraitId) -> Shared<Trait> {
self.traits[&id].clone()
}

pub fn get_type_alias(&self, id: TypeAliasId) -> &TypeAliasType {
&self.type_aliases[id.0]
}
Expand Down Expand Up @@ -683,6 +709,7 @@ fn get_type_method_key(typ: &Type) -> Option<TypeMethodKey> {
| Type::Error
| Type::NotConstant
| Type::Struct(_, _)
| Type::FmtString(_, _) => None,
| Type::FmtString(_, _)
| Type::Trait(_, _) => None,
}
}