From 8884655bd0057228dbdcf255e50dd4266cec7a39 Mon Sep 17 00:00:00 2001 From: Joao Matos Date: Mon, 26 Aug 2024 12:08:37 +0100 Subject: [PATCH] Rework parsed module storage and ordering algorithm. --- sway-core/src/concurrent_slab_mut.rs | 131 ++++++++++++++ sway-core/src/engine_threading.rs | 8 + sway-core/src/language/module.rs | 12 +- sway-core/src/language/parsed/mod.rs | 2 + sway-core/src/language/parsed/module.rs | 28 ++- .../language/parsed/parsed_module_engine.rs | 74 ++++++++ sway-core/src/language/parsed/program.rs | 6 +- sway-core/src/lib.rs | 170 +++++++++++------- sway-core/src/semantic_analysis/module.rs | 83 ++++++--- sway-core/src/semantic_analysis/program.rs | 10 +- .../src/semantic_analysis/symbol_resolve.rs | 6 +- sway-lsp/src/core/session.rs | 28 ++- sway-module-order/Forc.lock | 3 + sway-module-order/Forc.toml | 8 + sway-module-order/src/a.sw | 3 + sway-module-order/src/a/inner_a.sw | 3 + sway-module-order/src/b.sw | 3 + sway-module-order/src/b/inner_b.sw | 3 + sway-module-order/src/lib.sw | 4 + 19 files changed, 469 insertions(+), 116 deletions(-) create mode 100644 sway-core/src/concurrent_slab_mut.rs create mode 100644 sway-core/src/language/parsed/parsed_module_engine.rs create mode 100644 sway-module-order/Forc.lock create mode 100644 sway-module-order/Forc.toml create mode 100644 sway-module-order/src/a.sw create mode 100644 sway-module-order/src/a/inner_a.sw create mode 100644 sway-module-order/src/b.sw create mode 100644 sway-module-order/src/b/inner_b.sw create mode 100644 sway-module-order/src/lib.sw 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 e0e151249eb..8d772416714 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..6f16349770c 100644 --- a/sway-core/src/language/parsed/module.rs +++ b/sway-core/src/language/parsed/module.rs @@ -1,9 +1,9 @@ use crate::{ - language::{HasModule, HasSubmodules, ModName, Visibility}, + language::{HasModule, HasModuleId, HasSubmodules, ModName, Visibility}, transform, }; -use super::ParseTree; +use super::{ParseModuleId, ParseTree}; use sway_types::Span; pub type ModuleHash = u64; @@ -12,6 +12,9 @@ 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 +28,14 @@ pub struct ParseModule { pub span: Span, /// an hash used for caching the module pub hash: ModuleHash, + pub name: Option, +} + +impl HasModuleId for ParseModule +{ + fn module_id(&self) -> ParseModuleId { + return self.id + } } /// A library module that was declared as a `mod` of another module. @@ -32,13 +43,20 @@ 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..277e513aee1 --- /dev/null +++ b/sway-core/src/language/parsed/parsed_module_engine.rs @@ -0,0 +1,74 @@ +use std::sync::{Arc, RwLock}; + +use crate::concurrent_slab_mut::ConcurrentSlabMut; + +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) + } +} + +/// 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 c744255156d..f5a4ec047c6 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,6 +35,7 @@ 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 std::collections::hash_map::DefaultHasher; @@ -113,6 +115,7 @@ pub fn parse( config.include_tests, config.experimental, config.lsp_mode.as_ref(), + None, ) .map( |ParsedModuleTree { @@ -217,6 +220,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 +229,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 +240,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,13 +271,14 @@ 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()); module.submodules().for_each(|submod| { // Read the source code from the dependency. // If we cannot, record as an error, but continue with other files. - let submod_path = Arc::new(module_path(module_dir, module_name, submod)); + let submod_path = Arc::new(module_path(module_dir, module_name.clone(), submod)); let submod_str: Arc = match std::fs::read_to_string(&*submod_path) { Ok(s) => Arc::from(s), Err(e) => { @@ -290,6 +304,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 +342,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 +358,7 @@ fn parse_module_tree( include_tests: bool, experimental: ExperimentalFlags, lsp_mode: Option<&LspConfig>, + parent: Option, ) -> Result { let query_engine = engines.qe(); @@ -351,6 +367,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 +408,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 +452,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 +562,32 @@ fn module_path( pub fn build_module_dep_graph( handler: &Handler, - parse_module: &mut parsed::ParseModule, + engines: &Engines, + parse_module: 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 module_name = parse_module.read(engines, |m| m.name.clone()); + let module_dep_graph = ty::TyModule::build_dep_graph(handler, engines, &parse_module)?; + + module_dep_graph.visualize( + engines, + Some(format!( + "module_dep_graph_{}.dot", + module_name.clone().unwrap_or_default() + )), + ); + + // let order = module_dep_graph.compute_order(handler)?; + // parse_module.write(engines, |m| { + // m.module_eval_order = order.clone(); + // }); + + // parse_module.read(engines, |m| { + // m.submodules + // .iter() + // .map(|(_, submodule)| build_module_dep_graph(handler, engines, submodule.module)) + // .collect::>() + // })?; - for (_, submodule) in &mut parse_module.submodules { - build_module_dep_graph(handler, &mut submodule.module)?; - } Ok(()) } @@ -565,8 +609,8 @@ 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. @@ -1253,32 +1297,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/module.rs b/sway-core/src/semantic_analysis/module.rs index 8acdf1cac09..240fedda69b 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()) @@ -80,10 +83,19 @@ impl ModuleDepGraph { } } - pub fn add_node(&mut self, node: ModuleDepGraphNode) -> ModuleDepGraphNodeId { + pub fn add_node( + &mut self, + engines: &Engines, + node: ModuleDepGraphNode, + ) -> ModuleDepGraphNodeId { let node_id = self.dep_graph.add_node(node.clone()); match node { - ModuleDepGraphNode::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(); + self.node_name_map.insert(module_name, node_id); + } ModuleDepGraphNode::Submodule { name: mod_name } => { self.node_name_map.insert(mod_name.to_string(), node_id); } @@ -91,9 +103,12 @@ impl ModuleDepGraph { 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, + engines: &Engines, + id: ParseModuleId, + ) -> ModuleDepGraphNodeId { + self.add_node(engines, super::module::ModuleDepGraphNode::Module { id }) } fn get_node_id_for_module( @@ -155,7 +170,7 @@ impl ModuleDepGraph { for cycle in cycles.first().unwrap() { let node = self.dep_graph.node_weight(*cycle).unwrap(); match node { - ModuleDepGraphNode::Module {} => unreachable!(), + ModuleDepGraphNode::Module { .. } => unreachable!(), ModuleDepGraphNode::Submodule { name } => modules.push(name.clone()), }; } @@ -177,7 +192,7 @@ impl ModuleDepGraph { let node = self.dep_graph.node_weight(node_index); match node { Some(node) => match node { - ModuleDepGraphNode::Module {} => None, // root module + ModuleDepGraphNode::Module { .. } => None, // root module ModuleDepGraphNode::Submodule { name: mod_name } => Some(mod_name.clone()), }, None => None, @@ -194,26 +209,33 @@ impl ty::TyModule { /// Analyzes the given parsed module to produce a dependency graph. pub fn build_dep_graph( handler: &Handler, - parsed: &ParseModule, + engines: &Engines, + module_id: &ParseModuleId, ) -> Result { - let mut dep_graph = ModuleDepGraph::new(); - dep_graph.add_root_node(); + let module_arc = module_id.get(engines); + let module = module_arc.read().unwrap(); - let ParseModule { submodules, .. } = parsed; + let mut dep_graph = ModuleDepGraph::new(); + dep_graph.root = dep_graph.add_module_node(engines, *module_id); // Create graph nodes for each submodule. - submodules.iter().for_each(|(name, _submodule)| { + module.submodules.iter().for_each(|(_name, submodule)| { let sub_mod_node = - dep_graph.add_node(ModuleDepGraphNode::Submodule { name: name.clone() }); + dep_graph.add_module_node(engines, submodule.module); dep_graph .dep_graph .add_edge(dep_graph.root, 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().for_each(|(name, submodule)| { + let _ = ty::TySubmodule::build_dep_graph( + handler, + engines, + &mut dep_graph, + name.clone(), + submodule, + ); }); Ok(dep_graph) @@ -331,9 +353,13 @@ impl ty::TyModule { .find(|(submod_name, _)| eval_mod_name == submod_name) .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, ) { @@ -640,12 +666,15 @@ fn collect_fallback_fn( impl ty::TySubmodule { pub fn build_dep_graph( _handler: &Handler, + engines: &Engines, module_dep_graph: &mut ModuleDepGraph, mod_name: ModName, submodule: &ParseSubmodule, ) -> Result<(), ErrorEmitted> { let ParseSubmodule { module, .. } = submodule; let sub_mod_node = module_dep_graph.get_node_id_for_module(&mod_name).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) => { @@ -665,9 +694,11 @@ impl ty::TySubmodule { } } } + AstNodeContent::IncludeStatement(stmt) => { + // stmt.mod_name + } AstNodeContent::Declaration(_) => {} AstNodeContent::Expression(_) => {} - AstNodeContent::IncludeStatement(_) => {} AstNodeContent::Error(_, _) => {} } } @@ -686,12 +717,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 +742,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/program.rs b/sway-core/src/semantic_analysis/program.rs index c70a442fc1a..4a736605fee 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) } @@ -60,12 +61,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..5cb0f85d908 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()); + let _ = root.write(ctx.engines(), |root| { + root.resolve_symbols(handler, ctx.by_ref()); + }); } } @@ -49,7 +51,7 @@ impl ResolveSymbols for ParseModule { .iter_mut() .find(|(submod_name, _submodule)| eval_mod_name == submod_name) .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-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;