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 prelude.nr #3693

Merged
merged 15 commits into from Dec 12, 2023
68 changes: 62 additions & 6 deletions compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::hir::resolution::errors::ResolverError;
use crate::hir::resolution::import::{resolve_imports, ImportDirective};
use crate::hir::resolution::resolver::Resolver;
use crate::hir::resolution::{
collect_impls, collect_trait_impls, resolve_free_functions, resolve_globals, resolve_impls,
resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits,
collect_impls, collect_trait_impls, path_resolver, resolve_free_functions, resolve_globals,
resolve_impls, resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits,
resolve_type_aliases,
};
use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker};
Expand All @@ -19,8 +19,9 @@ use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, Type

use crate::parser::{ParserError, SortedModule};
use crate::{
ExpressionKind, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias,
Path, Type, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType,
ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait,
NoirTypeAlias, Path, PathKind, Type, UnresolvedGenerics, UnresolvedTraitConstraint,
UnresolvedType,
};
use fm::FileId;
use iter_extended::vecmap;
Expand Down Expand Up @@ -243,9 +244,20 @@ impl DefCollector {
context,
));

let submodules = vecmap(def_collector.def_map.modules().iter(), |(index, _)| index);
// Add the current crate to the collection of DefMaps
context.def_maps.insert(crate_id, def_collector.def_map);

inject_prelude(crate_id, context, crate_root, &mut def_collector.collected_imports);
for submodule in submodules {
inject_prelude(
crate_id,
context,
LocalModuleId(submodule),
&mut def_collector.collected_imports,
);
}

// Resolve unresolved imports collected from the crate
let (resolved, unresolved_imports) =
resolve_imports(crate_id, def_collector.collected_imports, &context.def_maps);
Expand All @@ -264,8 +276,11 @@ impl DefCollector {
for resolved_import in resolved {
let name = resolved_import.name;
for ns in resolved_import.resolved_namespace.iter_defs() {
let result = current_def_map.modules[resolved_import.module_scope.0]
.import(name.clone(), ns);
let result = current_def_map.modules[resolved_import.module_scope.0].import(
name.clone(),
ns,
resolved_import.is_prelude,
);

if let Err((first_def, second_def)) = result {
let err = DefCollectorErrorKind::Duplicate {
Expand Down Expand Up @@ -358,6 +373,47 @@ impl DefCollector {
}
}

fn inject_prelude(
crate_id: CrateId,
context: &Context,
crate_root: LocalModuleId,
collected_imports: &mut Vec<ImportDirective>,
) {
let segments: Vec<_> = "std::prelude"
.split("::")
.map(|segment| crate::Ident::new(segment.into(), Span::default()))
.collect();

let path =
Path { segments: segments.clone(), kind: crate::PathKind::Dep, span: Span::default() };

if !crate_id.is_stdlib() {
if let Ok(module_def) = path_resolver::resolve_path(
&context.def_maps,
ModuleId { krate: crate_id, local_id: crate_root },
path,
) {
let module_id = module_def.as_module().expect("std::prelude is module");
This conversation was marked as resolved.
Show resolved Hide resolved
let prelude = context.module(module_id).scope().names();

for path in prelude {
let mut segments = segments.clone();
segments.push(Ident::new(path.to_string(), Span::default()));

collected_imports.insert(
0,
ImportDirective {
module_id: crate_root,
path: Path { segments, kind: PathKind::Dep, span: Span::default() },
alias: None,
is_prelude: true,
},
);
}
}
}
}

/// Separate the globals Vec into two. The first element in the tuple will be the
/// literal globals, except for arrays, and the second will be all other globals.
/// We exclude array literals as they can contain complex types
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub fn collect_defs(
) -> Vec<(CompilationError, FileId)> {
let mut collector = ModCollector { def_collector, file_id, module_id };
let mut errors: Vec<(CompilationError, FileId)> = vec![];

// First resolve the module declarations
for decl in ast.module_decls {
errors.extend(collector.parse_module_declaration(context, &decl, crate_id));
Expand All @@ -57,6 +58,7 @@ pub fn collect_defs(
module_id: collector.module_id,
path: import.path,
alias: import.alias,
is_prelude: false,
});
}

Expand Down
77 changes: 44 additions & 33 deletions compiler/noirc_frontend/src/hir/def_map/item_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ use crate::{
};
use std::collections::{hash_map::Entry, HashMap};

type Scope = HashMap<Option<TraitId>, (ModuleDefId, Visibility, bool /*is_prelude*/)>;

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Visibility {
Public,
}

#[derive(Default, Debug, PartialEq, Eq)]
pub struct ItemScope {
types: HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>,
values: HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>,
types: HashMap<Ident, Scope>,
values: HashMap<Ident, Scope>,

defs: Vec<ModuleDefId>,
}
Expand All @@ -25,7 +27,7 @@ impl ItemScope {
mod_def: ModuleDefId,
trait_id: Option<TraitId>,
) -> Result<(), (Ident, Ident)> {
self.add_item_to_namespace(name, mod_def, trait_id)?;
self.add_item_to_namespace(name, mod_def, trait_id, false)?;
self.defs.push(mod_def);
Ok(())
}
Expand All @@ -38,25 +40,31 @@ impl ItemScope {
name: Ident,
mod_def: ModuleDefId,
trait_id: Option<TraitId>,
is_prelude: bool,
) -> Result<(), (Ident, Ident)> {
let add_item =
|map: &mut HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>| {
if let Entry::Occupied(mut o) = map.entry(name.clone()) {
let trait_hashmap = o.get_mut();
if let Entry::Occupied(_) = trait_hashmap.entry(trait_id) {
let old_ident = o.key();
Err((old_ident.clone(), name))
} else {
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public));
let add_item = |map: &mut HashMap<Ident, Scope>| {
if let Entry::Occupied(mut o) = map.entry(name.clone()) {
let trait_hashmap = o.get_mut();
if let Entry::Occupied(mut n) = trait_hashmap.entry(trait_id) {
let is_prelude = std::mem::replace(&mut n.get_mut().2, is_prelude);
let old_ident = o.key();

if is_prelude {
Ok(())
} else {
Err((old_ident.clone(), name))
}
} else {
let mut trait_hashmap = HashMap::new();
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public));
map.insert(name, trait_hashmap);
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public, is_prelude));
Ok(())
}
};
} else {
let mut trait_hashmap = HashMap::new();
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public, is_prelude));
map.insert(name, trait_hashmap);
Ok(())
}
};

match mod_def {
ModuleDefId::ModuleId(_) => add_item(&mut self.types),
Expand All @@ -69,7 +77,7 @@ impl ItemScope {
}

pub fn find_module_with_name(&self, mod_name: &Ident) -> Option<&ModuleId> {
let (module_def, _) = self.types.get(mod_name)?.get(&None)?;
let (module_def, _, _) = self.types.get(mod_name)?.get(&None)?;
match module_def {
ModuleDefId::ModuleId(id) => Some(id),
_ => None,
Expand All @@ -81,13 +89,13 @@ impl ItemScope {
// methods introduced without trait take priority and hide methods with the same name that come from a trait
let a = trait_hashmap.get(&None);
match a {
Some((module_def, _)) => match module_def {
Some((module_def, _, _)) => match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
},
None => {
if trait_hashmap.len() == 1 {
let (module_def, _) = trait_hashmap.get(trait_hashmap.keys().last()?)?;
let (module_def, _, _) = trait_hashmap.get(trait_hashmap.keys().last()?)?;
match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
Expand All @@ -105,7 +113,7 @@ impl ItemScope {
func_name: &Ident,
trait_id: &Option<TraitId>,
) -> Option<FuncId> {
let (module_def, _) = self.values.get(func_name)?.get(trait_id)?;
let (module_def, _, _) = self.values.get(func_name)?.get(trait_id)?;
match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
Expand All @@ -115,20 +123,19 @@ impl ItemScope {
pub fn find_name(&self, name: &Ident) -> PerNs {
// Names, not associated with traits are searched first. If not found, we search for name, coming from a trait.
// If we find only one name from trait, we return it. If there are multiple traits, providing the same name, we return None.
let find_name_in =
|a: &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>| {
if let Some(t) = a.get(name) {
if let Some(tt) = t.get(&None) {
Some(*tt)
} else if t.len() == 1 {
t.values().last().cloned()
} else {
None
}
let find_name_in = |a: &HashMap<Ident, Scope>| {
if let Some(t) = a.get(name) {
if let Some(tt) = t.get(&None) {
Some(*tt)
} else if t.len() == 1 {
t.values().last().cloned()
} else {
None
}
};
} else {
None
}
};

PerNs { types: find_name_in(&self.types), values: find_name_in(&self.values) }
}
Expand All @@ -144,15 +151,19 @@ impl ItemScope {
}
}

pub fn names(&self) -> impl Iterator<Item = &Ident> {
self.types.keys().chain(self.values.keys())
}

pub fn definitions(&self) -> Vec<ModuleDefId> {
self.defs.clone()
}

pub fn types(&self) -> &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>> {
pub fn types(&self) -> &HashMap<Ident, Scope> {
&self.types
}

pub fn values(&self) -> &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>> {
pub fn values(&self) -> &HashMap<Ident, Scope> {
&self.values
}

Expand Down
17 changes: 13 additions & 4 deletions compiler/noirc_frontend/src/hir/def_map/module_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ impl ModuleData {
}
}

pub(crate) fn scope(&self) -> &ItemScope {
&self.scope
}

fn declare(
&mut self,
name: Ident,
Expand Down Expand Up @@ -104,21 +108,26 @@ impl ModuleData {
self.scope.find_func_with_name(name)
}

pub fn import(&mut self, name: Ident, id: ModuleDefId) -> Result<(), (Ident, Ident)> {
self.scope.add_item_to_namespace(name, id, None)
pub fn import(
&mut self,
name: Ident,
id: ModuleDefId,
is_prelude: bool,
) -> Result<(), (Ident, Ident)> {
self.scope.add_item_to_namespace(name, id, None, is_prelude)
}

pub fn find_name(&self, name: &Ident) -> PerNs {
self.scope.find_name(name)
}

pub fn type_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
self.definitions.types().values().flat_map(|a| a.values().map(|(id, _)| *id))
self.definitions.types().values().flat_map(|a| a.values().map(|(id, _, _)| *id))
}

/// Return an iterator over all definitions defined within this module,
/// excluding any type definitions.
pub fn value_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
self.definitions.values().values().flat_map(|a| a.values().map(|(id, _)| *id))
self.definitions.values().values().flat_map(|a| a.values().map(|(id, _, _)| *id))
}
}
7 changes: 7 additions & 0 deletions compiler/noirc_frontend/src/hir/def_map/module_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ impl ModuleDefId {
ModuleDefId::GlobalId(_) => "global",
}
}

pub fn as_module(&self) -> Option<ModuleId> {
match self {
Self::ModuleId(v) => Some(*v),
_ => None,
}
}
}

impl From<ModuleId> for ModuleDefId {
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_frontend/src/hir/def_map/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use super::{item_scope::Visibility, ModuleDefId};
// This works exactly the same as in r-a, just simplified
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct PerNs {
pub types: Option<(ModuleDefId, Visibility)>,
pub values: Option<(ModuleDefId, Visibility)>,
pub types: Option<(ModuleDefId, Visibility, bool)>,
pub values: Option<(ModuleDefId, Visibility, bool)>,
jfecher marked this conversation as resolved.
Show resolved Hide resolved
}

impl PerNs {
pub fn types(t: ModuleDefId) -> PerNs {
PerNs { types: Some((t, Visibility::Public)), values: None }
PerNs { types: Some((t, Visibility::Public, false)), values: None }
}

pub fn take_types(self) -> Option<ModuleDefId> {
Expand All @@ -24,7 +24,7 @@ impl PerNs {
self.types.map(|it| it.0).into_iter().chain(self.values.map(|it| it.0))
}

pub fn iter_items(self) -> impl Iterator<Item = (ModuleDefId, Visibility)> {
pub fn iter_items(self) -> impl Iterator<Item = (ModuleDefId, Visibility, bool)> {
self.types.into_iter().chain(self.values)
}

Expand Down
Loading
Loading