diff --git a/sway-core/src/concurrent_slab_mut.rs b/sway-core/src/concurrent_slab_mut.rs new file mode 100644 index 00000000000..03d22121d07 --- /dev/null +++ b/sway-core/src/concurrent_slab_mut.rs @@ -0,0 +1,131 @@ +use std::{ + fmt, + sync::{Arc, RwLock}, +}; + +#[derive(Debug, Clone)] +pub struct Inner { + pub items: Vec>>>, + pub free_list: Vec, +} + +impl Default for Inner { + fn default() -> Self { + Self { + items: Default::default(), + free_list: Default::default(), + } + } +} + +#[derive(Debug)] +pub(crate) struct ConcurrentSlabMut { + pub inner: RwLock>, +} + +impl Clone for ConcurrentSlabMut +where + T: Clone, +{ + fn clone(&self) -> Self { + let inner = self.inner.read().unwrap(); + Self { + inner: RwLock::new(inner.clone()), + } + } +} + +impl Default for ConcurrentSlabMut { + fn default() -> Self { + Self { + inner: Default::default(), + } + } +} + +pub struct ListDisplay { + pub list: I, +} + +impl fmt::Display for ListDisplay +where + I::Item: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let fmt_elems = self + .list + .clone() + .into_iter() + .enumerate() + .map(|(i, value)| format!("{i:<10}\t->\t{value}")) + .collect::>(); + write!(f, "{}", fmt_elems.join("\n")) + } +} + +impl ConcurrentSlabMut +where + T: Clone, +{ + #[allow(dead_code)] + pub fn len(&self) -> usize { + let inner = self.inner.read().unwrap(); + inner.items.len() + } + + #[allow(dead_code)] + pub fn values(&self) -> Vec>> { + let inner = self.inner.read().unwrap(); + inner.items.iter().filter_map(|x| x.clone()).collect() + } + + pub fn insert(&self, value: T) -> usize { + self.insert_arc(Arc::new(RwLock::new(value))) + } + + pub fn insert_arc(&self, value: Arc>) -> usize { + let mut inner = self.inner.write().unwrap(); + + if let Some(free) = inner.free_list.pop() { + assert!(inner.items[free].is_none()); + inner.items[free] = Some(value); + free + } else { + inner.items.push(Some(value)); + inner.items.len() - 1 + } + } + + pub fn get(&self, index: usize) -> Arc> { + let inner = self.inner.read().unwrap(); + inner.items[index] + .as_ref() + .expect("invalid slab index for ConcurrentSlab::get") + .clone() + } + + #[allow(dead_code)] + pub fn retain(&self, predicate: impl Fn(&usize, &mut Arc>) -> bool) { + let mut inner = self.inner.write().unwrap(); + + let Inner { items, free_list } = &mut *inner; + for (idx, item) in items.iter_mut().enumerate() { + if let Some(arc) = item { + if !predicate(&idx, arc) { + free_list.push(idx); + item.take(); + } + } + } + } + + #[allow(dead_code)] + pub fn clear(&self) { + let mut inner = self.inner.write().unwrap(); + inner.items.clear(); + inner.items.shrink_to(0); + + inner.free_list.clear(); + inner.free_list.shrink_to(0); + } +} diff --git a/sway-core/src/engine_threading.rs b/sway-core/src/engine_threading.rs index f4ad12632ef..1e245eac7ac 100644 --- a/sway-core/src/engine_threading.rs +++ b/sway-core/src/engine_threading.rs @@ -1,5 +1,6 @@ use crate::{ decl_engine::{parsed_engine::ParsedDeclEngine, DeclEngine}, + language::parsed::ParsedModuleEngine, query_engine::QueryEngine, type_system::TypeEngine, }; @@ -15,6 +16,7 @@ pub struct Engines { type_engine: TypeEngine, decl_engine: DeclEngine, parsed_decl_engine: ParsedDeclEngine, + parsed_module_engine: ParsedModuleEngine, query_engine: QueryEngine, source_engine: SourceEngine, } @@ -32,6 +34,10 @@ impl Engines { &self.parsed_decl_engine } + pub fn pme(&self) -> &ParsedModuleEngine { + &self.parsed_module_engine + } + pub fn qe(&self) -> &QueryEngine { &self.query_engine } @@ -46,6 +52,7 @@ impl Engines { self.type_engine.clear_program(program_id); self.decl_engine.clear_program(program_id); self.parsed_decl_engine.clear_program(program_id); + // self.parsed_module_engine.clear_program(program_id); } /// Removes all data associated with `source_id` from the declaration and type engines. @@ -54,6 +61,7 @@ impl Engines { self.type_engine.clear_module(source_id); self.decl_engine.clear_module(source_id); self.parsed_decl_engine.clear_module(source_id); + // self.parsed_module_engine.clear_module(source_id); } /// Helps out some `thing: T` by adding `self` as context. diff --git a/sway-core/src/language/module.rs b/sway-core/src/language/module.rs index 2649ddd416e..0bd5be93262 100644 --- a/sway-core/src/language/module.rs +++ b/sway-core/src/language/module.rs @@ -1,14 +1,23 @@ use sway_types::Ident; +use super::parsed::ParseModuleId; + /// The name used within a module to refer to one of its submodules. /// /// If an alias was given to the `mod`, this will be the alias. If not, this is the submodule's /// library name. pub type ModName = Ident; +pub type ModPath = Vec; + +pub trait HasModuleId +{ + /// Returns the associated module id. + fn module_id(&self) -> ParseModuleId; +} + pub trait HasModule where - T: HasSubmodules, Self: Sized, { /// Returns the module of this submodule. @@ -17,7 +26,6 @@ where pub trait HasSubmodules where - E: HasModule, Self: Sized, { /// Returns the submodules of this module. diff --git a/sway-core/src/language/parsed/mod.rs b/sway-core/src/language/parsed/mod.rs index bf19ed600c3..9c70b2574ce 100644 --- a/sway-core/src/language/parsed/mod.rs +++ b/sway-core/src/language/parsed/mod.rs @@ -4,6 +4,7 @@ pub mod declaration; mod expression; mod include_statement; mod module; +mod parsed_module_engine; mod program; mod use_statement; @@ -12,6 +13,7 @@ pub use declaration::*; pub use expression::*; pub use include_statement::IncludeStatement; pub use module::{ModuleEvaluationOrder, ParseModule, ParseSubmodule}; +pub use parsed_module_engine::*; pub use program::{ParseProgram, TreeType}; use sway_error::handler::ErrorEmitted; use sway_types::span::Span; diff --git a/sway-core/src/language/parsed/module.rs b/sway-core/src/language/parsed/module.rs index 7a29701a040..18fe5ac051c 100644 --- a/sway-core/src/language/parsed/module.rs +++ b/sway-core/src/language/parsed/module.rs @@ -1,17 +1,25 @@ use crate::{ - language::{HasModule, HasSubmodules, ModName, Visibility}, - transform, + language::{HasModule, HasModuleId, HasSubmodules, ModName, Visibility}, + namespace::ModulePath, + transform, Engines, }; -use super::ParseTree; +use super::{ParseModuleId, ParseTree}; +use sway_error::{ + error::CompileError, + handler::{ErrorEmitted, Handler}, +}; use sway_types::Span; pub type ModuleHash = u64; -pub type ModuleEvaluationOrder = Vec; +pub type ModuleEvaluationOrder = Vec; /// A module and its submodules in the form of a tree. #[derive(Debug, Clone)] pub struct ParseModule { + pub id: ParseModuleId, + /// Parent module id or `None` if its a root module. + pub parent: Option, /// The content of this module in the form of a `ParseTree`. pub tree: ParseTree, /// Submodules introduced within this module using the `dep` syntax in order of declaration. @@ -25,6 +33,40 @@ pub struct ParseModule { pub span: Span, /// an hash used for caching the module pub hash: ModuleHash, + pub name: Option, +} + +impl ParseModule { + /// Lookup the submodule at the given path. + pub fn lookup_submodule( + &self, + handler: &Handler, + engines: &Engines, + path: &ModulePath, + ) -> Result { + let pme = engines.pme(); + let mut module_id = self.id; + for ident in path.iter() { + let module_arc = pme.get(&module_id); + let module = module_arc.read().unwrap(); + match module.submodules.iter().find(|(name, _)| name == ident) { + Some((_name, submod)) => module_id = submod.module, + None => { + return Err(handler.emit_err(CompileError::Internal( + "Cannot find submodule", + Span::dummy(), + ))) + } + } + } + Ok(module_id) + } +} + +impl HasModuleId for ParseModule { + fn module_id(&self) -> ParseModuleId { + self.id + } } /// A library module that was declared as a `mod` of another module. @@ -32,13 +74,19 @@ pub struct ParseModule { /// Only submodules are guaranteed to be a `library`. #[derive(Debug, Clone)] pub struct ParseSubmodule { - pub module: ParseModule, + pub module: ParseModuleId, pub mod_name_span: Span, pub visibility: Visibility, } -impl HasModule for ParseSubmodule { - fn module(&self) -> &ParseModule { +impl HasModuleId for ParseSubmodule { + fn module_id(&self) -> ParseModuleId { + self.module + } +} + +impl HasModule for ParseSubmodule { + fn module(&self) -> &ParseModuleId { &self.module } } diff --git a/sway-core/src/language/parsed/parsed_module_engine.rs b/sway-core/src/language/parsed/parsed_module_engine.rs new file mode 100644 index 00000000000..472d0335291 --- /dev/null +++ b/sway-core/src/language/parsed/parsed_module_engine.rs @@ -0,0 +1,81 @@ +use std::sync::{Arc, RwLock}; + +use crate::{concurrent_slab_mut::ConcurrentSlabMut, engine_threading::DebugWithEngines}; + +use super::ParseModule; + +/// A identifier to uniquely refer to our parsed modules. +#[derive(Default, PartialEq, Eq, Hash, Clone, Copy, Ord, PartialOrd, Debug)] +pub struct ParseModuleId(usize); + +impl ParseModuleId { + pub fn new(index: usize) -> Self { + ParseModuleId(index) + } + + /// Returns the index that identifies the type. + pub fn index(&self) -> usize { + self.0 + } + + pub(crate) fn get(&self, engines: &crate::Engines) -> Arc> { + engines.pme().get(self) + } + + pub fn read(&self, engines: &crate::Engines, f: impl Fn(&ParseModule) -> R) -> R { + let value = self.get(engines); + let value = value.read().unwrap(); + f(&value) + } + + pub fn write( + &self, + engines: &crate::Engines, + mut f: impl FnMut(&mut ParseModule) -> R, + ) -> R { + let value = self.get(engines); + let mut value = value.write().unwrap(); + f(&mut value) + } +} + +impl DebugWithEngines for ParseModuleId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, engines: &crate::Engines) -> std::fmt::Result { + let name = self.read(engines, |m| m.name.clone()); + write!(f, "{:?}", name) + } +} + +/// The Parsed Module Engine manages a relationship between module ids and their corresponding +/// parsed module structures. +#[derive(Debug, Default, Clone)] +pub struct ParsedModuleEngine { + slab: ConcurrentSlabMut, +} + +impl ParsedModuleEngine { + /// This function provides the namespace module corresponding to a specified module ID. + pub fn get(&self, module_id: &ParseModuleId) -> Arc> { + self.slab.get(module_id.index()) + } + + pub fn read(&self, module_id: &ParseModuleId, f: impl Fn(&ParseModule) -> R) -> R { + let value = self.slab.get(module_id.index()); + let value = value.read().unwrap(); + f(&value) + } + + pub fn write(&self, module_id: &ParseModuleId, f: impl Fn(&mut ParseModule) -> R) -> R { + let value = self.slab.get(module_id.index()); + let mut value = value.write().unwrap(); + f(&mut value) + } + + pub fn insert(&self, value: ParseModule) -> ParseModuleId { + let id = ParseModuleId(self.slab.insert(value)); + self.write(&id, |m| { + m.id = id; + }); + id + } +} diff --git a/sway-core/src/language/parsed/program.rs b/sway-core/src/language/parsed/program.rs index 29311c20705..f36455cda9e 100644 --- a/sway-core/src/language/parsed/program.rs +++ b/sway-core/src/language/parsed/program.rs @@ -2,7 +2,7 @@ use strum::EnumString; use crate::Engines; -use super::ParseModule; +use super::ParseModuleId; /// A parsed, but not yet type-checked, Sway program. /// @@ -10,7 +10,7 @@ use super::ParseModule; #[derive(Debug, Clone)] pub struct ParseProgram { pub kind: TreeType, - pub root: ParseModule, + pub root: ParseModuleId, } /// A Sway program can be either a contract, script, predicate, or a library. @@ -46,6 +46,6 @@ impl std::fmt::Display for TreeType { impl ParseProgram { /// Excludes all test functions from the parse tree. pub(crate) fn exclude_tests(&mut self, engines: &Engines) { - self.root.tree.exclude_tests(engines) + self.root.write(engines, |m| m.tree.exclude_tests(engines)) } } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index fcc524ce8a0..19e9019c264 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -12,6 +12,7 @@ mod asm_lang; mod build_config; pub mod compiler_generated; mod concurrent_slab; +mod concurrent_slab_mut; mod control_flow_analysis; mod debug_generation; pub mod decl_engine; @@ -34,9 +35,12 @@ pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; use indexmap::IndexMap; +use language::parsed::ParseModuleId; use metadata::MetadataManager; use query_engine::{ModuleCacheKey, ModuleCommonInfo, ParsedModuleInfo, ProgramsCacheEntry}; +use semantic_analysis::module::ModuleDepGraph; use std::collections::hash_map::DefaultHasher; +use std::collections::VecDeque; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -113,6 +117,7 @@ pub fn parse( config.include_tests, config.experimental, config.lsp_mode.as_ref(), + None, ) .map( |ParsedModuleTree { @@ -217,6 +222,8 @@ fn parse_in_memory( let submodules = Vec::default(); let attributes = module_attrs_to_map(handler, &module.attribute_list)?; let root = parsed::ParseModule { + id: ParseModuleId::default(), + parent: None, span: span::Span::dummy(), module_kind_span, module_eval_order: vec![], @@ -224,7 +231,9 @@ fn parse_in_memory( submodules, attributes, hash, + name: None, }; + let root_id = engines.pme().insert(root); let lexed_program = lexed::LexedProgram::new( kind, lexed::LexedModule { @@ -233,7 +242,13 @@ fn parse_in_memory( }, ); - Ok((lexed_program, parsed::ParseProgram { kind, root })) + Ok(( + lexed_program, + parsed::ParseProgram { + kind, + root: root_id, + }, + )) } pub struct Submodule { @@ -258,6 +273,7 @@ fn parse_submodules( include_tests: bool, experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, + parent: Option, ) -> Submodules { // Assume the happy path, so there'll be as many submodules as dependencies, but no more. let mut submods = Vec::with_capacity(module.submodules().count()); @@ -290,6 +306,7 @@ fn parse_submodules( include_tests, experimental, lsp_mode, + parent, ) { if !matches!(kind, parsed::TreeType::Library) { let source_id = engines.se().get_source_id(submod_path.as_ref()); @@ -327,7 +344,7 @@ pub type SourceHash = u64; pub struct ParsedModuleTree { pub tree_type: parsed::TreeType, pub lexed_module: lexed::LexedModule, - pub parse_module: parsed::ParseModule, + pub parse_module: ParseModuleId, } /// Given the source of the module along with its path, @@ -343,6 +360,7 @@ fn parse_module_tree( include_tests: bool, experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, + parent: Option, ) -> Result { let query_engine = engines.qe(); @@ -351,6 +369,35 @@ fn parse_module_tree( let source_id = engines.se().get_source_id(&path.clone()); let module = sway_parse::parse_file(handler, src.clone(), Some(source_id))?; + // Convert from the raw parsed module to the `ParseTree` ready for type-check. + let (kind, tree) = to_parsed_lang::convert_parse_tree( + &mut to_parsed_lang::Context::new(build_target, experimental), + handler, + engines, + module.value.clone(), + )?; + let module_kind_span = module.value.kind.span(); + let attributes = module_attrs_to_map(handler, &module.attribute_list)?; + + let mut hasher = DefaultHasher::new(); + src.hash(&mut hasher); + let hash = hasher.finish(); + + let parse_module = parsed::ParseModule { + id: ParseModuleId::default(), + parent, + span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(), + module_kind_span, + module_eval_order: vec![], + tree, + submodules: vec![], + attributes, + hash, + name: module_name.map(|n| n.to_string()), + }; + + let parse_module_id = engines.pme().insert(parse_module); + // Parse all submodules before converting to the `ParseTree`. // This always recovers on parse errors for the file itself by skipping that file. let submodules = parse_submodules( @@ -363,44 +410,25 @@ fn parse_module_tree( include_tests, experimental, lsp_mode, + Some(parse_module_id), ); - // Convert from the raw parsed module to the `ParseTree` ready for type-check. - let (kind, tree) = to_parsed_lang::convert_parse_tree( - &mut to_parsed_lang::Context::new(build_target, experimental), - handler, - engines, - module.value.clone(), - )?; - let module_kind_span = module.value.kind.span(); - let attributes = module_attrs_to_map(handler, &module.attribute_list)?; - let lexed_submodules = submodules .iter() .map(|s| (s.name.clone(), s.lexed.clone())) .collect::>(); - let lexed = lexed::LexedModule { + let lexed_module = lexed::LexedModule { tree: module.value, submodules: lexed_submodules, }; - let mut hasher = DefaultHasher::new(); - src.hash(&mut hasher); - let hash = hasher.finish(); - - let parsed_submodules = submodules - .iter() - .map(|s| (s.name.clone(), s.parsed.clone())) - .collect::>(); - let parsed = parsed::ParseModule { - span: span::Span::new(src, 0, 0, Some(source_id)).unwrap(), - module_kind_span, - module_eval_order: vec![], - tree, - submodules: parsed_submodules, - attributes, - hash, - }; + parse_module_id.write(engines, |m| { + let parsed_submodules = submodules + .iter() + .map(|s| (s.name.clone(), s.parsed.clone())) + .collect::>(); + m.submodules = parsed_submodules; + }); // Let's prime the cache with the module dependency and hash data. let modified_time = std::fs::metadata(path.as_path()) @@ -426,8 +454,8 @@ fn parse_module_tree( Ok(ParsedModuleTree { tree_type: kind, - lexed_module: lexed, - parse_module: parsed, + lexed_module, + parse_module: parse_module_id, }) } @@ -536,14 +564,62 @@ fn module_path( pub fn build_module_dep_graph( handler: &Handler, - parse_module: &mut parsed::ParseModule, + engines: &Engines, + root_module_id: ParseModuleId, ) -> Result<(), ErrorEmitted> { - let module_dep_graph = ty::TyModule::build_dep_graph(handler, parse_module)?; - parse_module.module_eval_order = module_dep_graph.compute_order(handler)?; + let mut dep_graph = ModuleDepGraph::new(); + + { + let root_module_arc = root_module_id.get(engines); + let root_module = root_module_arc.read().unwrap(); + let module_name = root_module.name.clone(); + + ty::TyModule::build_dep_graph( + handler, + engines, + &mut dep_graph, + &root_module, + &root_module_id, + )?; - for (_, submodule) in &mut parse_module.submodules { - build_module_dep_graph(handler, &mut submodule.module)?; + dep_graph.visualize( + engines, + Some(format!( + "module_dep_graph_{}.dot", + module_name.clone().unwrap_or_default() + )), + ); } + + let ordered_modules = dep_graph.compute_order(handler, engines)?; + + //println!("global order {:?}", engines.help_out(global_order.clone())); + + // Set the modules with their computed submodules evaluation order + let mut modules = VecDeque::new(); + modules.push_back(root_module_id); + + while let Some(module_id) = modules.pop_front() { + module_id.write(engines, |m| { + let submodules = m.submodules.iter().map(|(_, m)| m.module); + + // Queue up the submodules for further processing + modules.extend(submodules.clone()); + + // Sort submodules based on their position in the computed order + let mut sorted = submodules.collect::>(); + + let position_in_order = + |module_id: &ParseModuleId| ordered_modules.iter().position(|id| id == module_id); + + sorted.sort_by_key(|module| position_in_order(module)); + + //println!("position_in_order {:?} {:?}", m.name, engines.help_out(sorted.clone())); + + m.module_eval_order = sorted.clone(); + }); + } + Ok(()) } @@ -565,10 +641,11 @@ pub fn parsed_to_ast( }); let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default(); - // Build the dependency graph for the submodules. - build_module_dep_graph(handler, &mut parse_program.root)?; + // Build the dependency graph starting from the root module. + build_module_dep_graph(handler, engines, parse_program.root)?; let namespace = Namespace::init_root(initial_namespace); + // Collect the program symbols. let mut collection_ctx = ty::TyProgram::collect(handler, engines, parse_program, namespace.clone())?; @@ -1254,32 +1331,34 @@ fn test_unary_ordering() { let (.., prog) = prog.unwrap(); // this should parse as `(!a) && b`, not `!(a && b)`. So, the top level // expression should be `&&` - if let parsed::AstNode { - content: - parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)), - .. - } = &prog.root.tree.root_nodes[0] - { - let fn_decl = engines.pe().get_function(decl_id); + prog.root.read(&engines, |root| { if let parsed::AstNode { content: - parsed::AstNodeContent::Expression(parsed::Expression { - kind: - parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression { - op, .. - }), - .. - }), + parsed::AstNodeContent::Declaration(parsed::Declaration::FunctionDeclaration(decl_id)), .. - } = &fn_decl.body.contents[2] + } = &root.tree.root_nodes[0] { - assert_eq!(op, &language::LazyOp::And) + let fn_decl = engines.pe().get_function(decl_id); + if let parsed::AstNode { + content: + parsed::AstNodeContent::Expression(parsed::Expression { + kind: + parsed::ExpressionKind::LazyOperator(parsed::LazyOperatorExpression { + op, .. + }), + .. + }), + .. + } = &fn_decl.body.contents[2] + { + assert_eq!(op, &language::LazyOp::And) + } else { + panic!("Was not lazy operator.") + } } else { - panic!("Was not lazy operator.") - } - } else { - panic!("Was not ast node") - }; + panic!("Was not ast node") + }; + }) } #[test] diff --git a/sway-core/src/semantic_analysis.rs b/sway-core/src/semantic_analysis.rs index 2d3bcdcc6c3..e82317da4a9 100644 --- a/sway-core/src/semantic_analysis.rs +++ b/sway-core/src/semantic_analysis.rs @@ -2,7 +2,7 @@ pub mod ast_node; pub(crate) mod cei_pattern_analysis; pub(crate) mod coins_analysis; -mod module; +pub mod module; pub mod namespace; mod node_dependencies; mod program; diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 8acdf1cac09..8266538ed5c 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -44,15 +44,18 @@ pub type ModuleDepGraphNodeId = petgraph::graph::NodeIndex; #[derive(Clone, Debug)] pub enum ModuleDepGraphNode { - Module {}, + Module { id: ParseModuleId }, Submodule { name: ModName }, } impl DebugWithEngines for ModuleDepGraphNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _engines: &Engines) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>, engines: &Engines) -> std::fmt::Result { let text = match self { - ModuleDepGraphNode::Module { .. } => { - format!("{:?}", "Root module") + ModuleDepGraphNode::Module { id } => { + let module_arc = engines.pme().get(id); + let module = module_arc.read().unwrap(); + let module_name = module.name.clone().unwrap_or_default(); + format!("{:?}", module_name.as_str()) } ModuleDepGraphNode::Submodule { name: mod_name } => { format!("{:?}", mod_name.as_str()) @@ -68,7 +71,7 @@ pub type ModuleDepNodeGraph = petgraph::graph::DiGraph, + node_id_map: HashMap, } impl ModuleDepGraph { @@ -76,31 +79,34 @@ impl ModuleDepGraph { Self { dep_graph: Default::default(), root: Default::default(), - node_name_map: Default::default(), + node_id_map: Default::default(), + } + } + + pub fn get_or_add_node(&mut self, id: ParseModuleId) -> ModuleDepGraphNodeId { + match self.get_node_id_for_module(id) { + Some(node) => node, + None => self.add_node(ModuleDepGraphNode::Module { id }), } } pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId { let node_id = self.dep_graph.add_node(node.clone()); match node { - ModuleDepGraphNode::Module {} => {} - ModuleDepGraphNode::Submodule { name: mod_name } => { - self.node_name_map.insert(mod_name.to_string(), node_id); + ModuleDepGraphNode::Module { id } => { + self.node_id_map.insert(id, node_id); } + ModuleDepGraphNode::Submodule { .. } => unreachable!(), }; node_id } - pub fn add_root_node(&mut self) -> ModuleDepGraphNodeId { - self.root = self.add_node(super::module::ModuleDepGraphNode::Module {}); - self.root + pub fn add_module_node(&mut self, id: ParseModuleId) -> ModuleDepGraphNodeId { + self.add_node(ModuleDepGraphNode::Module { id }) } - fn get_node_id_for_module( - &self, - mod_name: &sway_types::BaseIdent, - ) -> Option { - self.node_name_map.get(&mod_name.to_string()).copied() + fn get_node_id_for_module(&self, id: ParseModuleId) -> Option { + self.node_id_map.get(&id).copied() } /// Prints out GraphViz DOT format for the dependency graph. @@ -147,16 +153,23 @@ impl ModuleDepGraph { pub(crate) fn compute_order( &self, handler: &Handler, + engines: &Engines, ) -> Result { // Check for dependency cycles in the graph by running the Johnson's algorithm. let cycles = self.dep_graph.cycles(); if !cycles.is_empty() { let mut modules = Vec::new(); for cycle in cycles.first().unwrap() { + if *cycle == self.root { + continue; + } let node = self.dep_graph.node_weight(*cycle).unwrap(); match node { - ModuleDepGraphNode::Module {} => unreachable!(), - ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()), + ModuleDepGraphNode::Module { id } => { + let name = id.read(engines, |m| m.name.clone()).unwrap_or_default(); + modules.push(name) + } + ModuleDepGraphNode::Submodule { .. } => unreachable!(), }; } return Err(handler.emit_err(CompileError::ModuleDepGraphCyclicReference { modules })); @@ -177,8 +190,8 @@ impl ModuleDepGraph { let node = self.dep_graph.node_weight(node_index); match node { Some(node) => match node { - ModuleDepGraphNode::Module {} => None, // root module - ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()), + ModuleDepGraphNode::Module { id } => Some(*id), + ModuleDepGraphNode::Submodule { .. } => unreachable!(), }, None => None, } @@ -194,29 +207,45 @@ impl ty::TyModule { /// Analyzes the given parsed module to produce a dependency graph. pub fn build_dep_graph( handler: &Handler, - parsed: &ParseModule, - ) -> Result { - let mut dep_graph = ModuleDepGraph::new(); - dep_graph.add_root_node(); + engines: &Engines, + dep_graph: &mut ModuleDepGraph, + root_module: &ParseModule, + module_id: &ParseModuleId, + ) -> Result<(), ErrorEmitted> { + let module_arc = module_id.get(engines); + let module = module_arc.read().unwrap(); - let ParseModule { submodules, .. } = parsed; + let node = dep_graph.get_or_add_node(*module_id); + if module.parent.is_none() { + dep_graph.root = node; + } // Create graph nodes for each submodule. - submodules.iter().for_each(|(name, _submodule)| { - let sub_mod_node = - dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() }); + module.submodules.iter().for_each(|(_name, submodule)| { + let sub_mod_node = dep_graph.get_or_add_node(submodule.module); dep_graph .dep_graph - .add_edge(dep_graph.root, sub_mod_node, ModuleDepGraphEdge {}); + .add_edge(node, sub_mod_node, ModuleDepGraphEdge {}); }); // Analyze submodules first in order of declaration. - submodules.iter().for_each(|(name, submodule)| { - let _ = - ty::TySubmodule::build_dep_graph(handler, &mut dep_graph, name.clone(), submodule); - }); + module + .submodules + .iter() + .map(|(_name, submodule)| { + let _ = ty::TySubmodule::build_dep_graph( + handler, + engines, + dep_graph, + root_module, + submodule, + ); + + Self::build_dep_graph(handler, engines, dep_graph, root_module, &submodule.module) + }) + .collect::, ErrorEmitted>>()?; - Ok(dep_graph) + Ok(()) } /// Collects the given parsed module to produce a module symbol map. @@ -242,7 +271,7 @@ impl ty::TyModule { module_eval_order.iter().for_each(|eval_mod_name| { let (name, submodule) = submodules .iter() - .find(|(submod_name, _submodule)| eval_mod_name == submod_name) + .find(|(_name, submodule)| *eval_mod_name == submodule.module) .unwrap(); let _ = ty::TySubmodule::collect(handler, engines, ctx, name.clone(), submodule); }); @@ -325,15 +354,19 @@ impl ty::TyModule { // Type-check submodules first in order of evaluation previously computed by the dependency graph. let submodules_res = module_eval_order .iter() - .map(|eval_mod_name| { + .map(|eval_mod_id| { let (name, submodule) = submodules .iter() - .find(|(submod_name, _)| eval_mod_name == submod_name) + .find(|(_name, submodule)| *eval_mod_id == submodule.module) .unwrap(); + let source_id = submodule + .module + .read(engines, |m| m.span.source_id().cloned()); + // Try to get the cached submodule if let Some(cached_module) = ty::TyModule::get_cached_ty_module_if_up_to_date( - submodule.module.span.source_id(), + source_id.as_ref(), engines, build_config, ) { @@ -639,30 +672,35 @@ fn collect_fallback_fn( impl ty::TySubmodule { pub fn build_dep_graph( - _handler: &Handler, + handler: &Handler, + engines: &Engines, module_dep_graph: &mut ModuleDepGraph, - mod_name: ModName, + root_module: &ParseModule, submodule: &ParseSubmodule, ) -> Result<(), ErrorEmitted> { let ParseSubmodule { module, .. } = submodule; - let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).unwrap(); + let submod_node = module_dep_graph.get_node_id_for_module(*module).unwrap(); + let module_arc = module.get(engines); + let module = module_arc.read().unwrap(); for node in module.tree.root_nodes.iter() { match &node.content { AstNodeContent::UseStatement(use_stmt) => { - if let Some(use_mod_ident) = use_stmt.call_path.first() { - if let Some(mod_name_node) = - module_dep_graph.get_node_id_for_module(use_mod_ident) - { - // Prevent adding edge loops between the same node as that will throw off - // the cyclic dependency analysis. - if sub_mod_node != mod_name_node { - module_dep_graph.dep_graph.add_edge( - sub_mod_node, - mod_name_node, - ModuleDepGraphEdge {}, - ); - } - } + let used_submod_module_id = root_module.lookup_submodule( + handler, + engines, + &vec![use_stmt.call_path.first().unwrap().clone()], + )?; + + let used_submod_node = module_dep_graph.get_or_add_node(used_submod_module_id); + + // Prevent adding edge loops between the same node as that will throw off + // the cyclic dependency analysis. + if submod_node != used_submod_node { + module_dep_graph.dep_graph.add_edge( + submod_node, + used_submod_node, + ModuleDepGraphEdge {}, + ); } } AstNodeContent::Declaration(_) => {} @@ -686,12 +724,14 @@ impl ty::TySubmodule { mod_name_span: _, visibility, } = submodule; + let module_arc = module.get(engines); + let module = module_arc.read().unwrap(); parent_ctx.enter_submodule( engines, mod_name, *visibility, module.span.clone(), - |submod_ctx| ty::TyModule::collect(handler, engines, submod_ctx, module), + |submod_ctx| ty::TyModule::collect(handler, engines, submod_ctx, &module), ) } @@ -709,9 +749,11 @@ impl ty::TySubmodule { mod_name_span, visibility, } = submodule; + let module_arc = module.get(engines); + let module = module_arc.read().unwrap(); parent_ctx.enter_submodule(mod_name, *visibility, module.span.clone(), |submod_ctx| { let module_res = - ty::TyModule::type_check(handler, submod_ctx, engines, kind, module, build_config); + ty::TyModule::type_check(handler, submod_ctx, engines, kind, &module, build_config); module_res.map(|module| ty::TySubmodule { module, mod_name_span: mod_name_span.clone(), diff --git a/sway-core/src/semantic_analysis/namespace/module.rs b/sway-core/src/semantic_analysis/namespace/module.rs index 36f14f2050f..d7edfc7b376 100644 --- a/sway-core/src/semantic_analysis/namespace/module.rs +++ b/sway-core/src/semantic_analysis/namespace/module.rs @@ -18,7 +18,7 @@ use sway_types::{span::Span, Spanned}; /// script/predicate/contract file or some library dependency whether introduced via `mod` or the /// `[dependencies]` table of a `forc` manifest. /// -/// A `Module` contains a set of all items that exist within the lexical scope via declaration or +/// A `Module` contains a set of all items that exist within its lexical scopes via declaration or /// importing, along with a map of each of its submodules. #[derive(Clone, Debug)] pub struct Module { diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 581a630d9b6..f49e6de2952 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -31,8 +31,9 @@ impl TyProgram { ) -> Result { let mut ctx = SymbolCollectionContext::new(namespace); let ParseProgram { root, kind: _ } = parsed; - - ty::TyModule::collect(handler, engines, &mut ctx, root)?; + root.write(engines, |root| { + ty::TyModule::collect(handler, engines, &mut ctx, root) + })?; Ok(ctx) } @@ -62,12 +63,15 @@ impl TyProgram { let ParseProgram { root, kind } = parsed; + let root_arc = root.get(engines); + let root = root_arc.read().unwrap(); + let root = ty::TyModule::type_check( handler, ctx.by_ref(), engines, parsed.kind, - root, + &root, build_config, )?; diff --git a/sway-core/src/semantic_analysis/symbol_resolve.rs b/sway-core/src/semantic_analysis/symbol_resolve.rs index 60ac3da6a53..9e9443f439e 100644 --- a/sway-core/src/semantic_analysis/symbol_resolve.rs +++ b/sway-core/src/semantic_analysis/symbol_resolve.rs @@ -27,7 +27,9 @@ pub trait ResolveSymbols { impl ResolveSymbols for ParseProgram { fn resolve_symbols(&mut self, handler: &Handler, mut ctx: SymbolResolveContext) { let ParseProgram { root, .. } = self; - root.resolve_symbols(handler, ctx.by_ref()); + root.write(ctx.engines(), |root| { + root.resolve_symbols(handler, ctx.by_ref()); + }); } } @@ -44,12 +46,12 @@ impl ResolveSymbols for ParseModule { } = self; // Analyze submodules first in order of evaluation previously computed by the dependency graph. - module_eval_order.iter().for_each(|eval_mod_name| { + module_eval_order.iter().for_each(|eval_mod_id| { let (_name, submodule) = submodules .iter_mut() - .find(|(submod_name, _submodule)| eval_mod_name == submod_name) + .find(|(_name, submodule)| *eval_mod_id == submodule.module) .unwrap(); - submodule.module.resolve_symbols(handler, ctx.by_ref()); + submodule.module.write(ctx.engines(), |m| m.resolve_symbols(handler, ctx.by_ref())); }); tree.root_nodes diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index ec85cf3298b..2e00484e567 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -58,7 +58,7 @@ pub enum CompileError { modules.iter().map(|ident| ident.as_str().to_string()) .collect::>() .join(", "))] - ModuleDepGraphCyclicReference { modules: Vec }, + ModuleDepGraphCyclicReference { modules: Vec }, #[error("Variable \"{var_name}\" does not exist in this scope.")] UnknownVariable { var_name: Ident, span: Span }, diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 7f4ed2a9153..ec928fa1814 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -535,21 +535,19 @@ fn parse_ast_to_tokens( .unwrap_or(true) }; - parse_program - .root - .tree - .root_nodes - .iter() - .chain( - parse_program - .root - .submodules_recursive() - .flat_map(|(_, submodule)| &submodule.module.tree.root_nodes), - ) - .filter(should_process) - .collect::>() - .par_iter() - .for_each(|n| f(n, ctx)); + parse_program.root.read(ctx.engines, |root| { + root.tree + .root_nodes + .iter() + .chain(parse_program.root.read(ctx.engines, |root| { + root.submodules_recursive() + .flat_map(|(_, submodule)| &submodule.module.tree.root_nodes) + })) + .filter(should_process) + .collect::>() + .par_iter() + .for_each(|n| f(n, ctx)); + }); } /// Parse the [ty::TyProgram] AST to populate the [TokenMap] with typed AST nodes. diff --git a/sway-module-order/Forc.lock b/sway-module-order/Forc.lock new file mode 100644 index 00000000000..ff3c1438a03 --- /dev/null +++ b/sway-module-order/Forc.lock @@ -0,0 +1,3 @@ +[[package]] +name = "sway-module-order" +source = "member" diff --git a/sway-module-order/Forc.toml b/sway-module-order/Forc.toml new file mode 100644 index 00000000000..4baf46dd8ab --- /dev/null +++ b/sway-module-order/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Harsh Bajpai"] +entry = "lib.sw" +license = "Apache-2.0" +name = "sway-module-order" +implicit-std=false + +[dependencies] diff --git a/sway-module-order/src/a.sw b/sway-module-order/src/a.sw new file mode 100644 index 00000000000..2e4eb22fb20 --- /dev/null +++ b/sway-module-order/src/a.sw @@ -0,0 +1,3 @@ +library; + +pub mod inner_a; diff --git a/sway-module-order/src/a/inner_a.sw b/sway-module-order/src/a/inner_a.sw new file mode 100644 index 00000000000..a04af80a19a --- /dev/null +++ b/sway-module-order/src/a/inner_a.sw @@ -0,0 +1,3 @@ +library; + +use ::b::inner_b::foo; diff --git a/sway-module-order/src/b.sw b/sway-module-order/src/b.sw new file mode 100644 index 00000000000..3d4de365869 --- /dev/null +++ b/sway-module-order/src/b.sw @@ -0,0 +1,3 @@ +library; + +pub mod inner_b; diff --git a/sway-module-order/src/b/inner_b.sw b/sway-module-order/src/b/inner_b.sw new file mode 100644 index 00000000000..47f8176ee24 --- /dev/null +++ b/sway-module-order/src/b/inner_b.sw @@ -0,0 +1,3 @@ +library; + +pub fn foo() {} diff --git a/sway-module-order/src/lib.sw b/sway-module-order/src/lib.sw new file mode 100644 index 00000000000..d8009c7b5a3 --- /dev/null +++ b/sway-module-order/src/lib.sw @@ -0,0 +1,4 @@ +library; + +pub mod a; +pub mod b;