diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index c7e6439e300..99da8d36402 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -1600,18 +1600,19 @@ pub fn dependency_namespace( ) -> Result> { // TODO: Clean this up when config-time constants v1 are removed. let node_idx = &graph[node]; - let name = Some(Ident::new_no_span(node_idx.name.clone())); + let name = Ident::new_no_span(node_idx.name.clone()); let mut root_module = if let Some(contract_id_value) = contract_id_value { - namespace::default_with_contract_id(engines, name.clone(), contract_id_value, experimental)? + namespace::default_with_contract_id( + engines, + name.clone(), + Visibility::Public, + contract_id_value, + experimental, + )? } else { - namespace::Module::default() + namespace::Module::new(name, Visibility::Public, None) }; - root_module.write(engines, |root_module| { - root_module.name.clone_from(&name); - root_module.visibility = Visibility::Public; - }); - // Add direct dependencies. let mut core_added = false; for edge in graph.edges_directed(node, Direction::Outgoing) { @@ -1633,16 +1634,14 @@ pub fn dependency_namespace( // Construct namespace with contract id let contract_id_value = format!("0x{dep_contract_id}"); let node_idx = &graph[dep_node]; - let name = Some(Ident::new_no_span(node_idx.name.clone())); - let mut module = namespace::default_with_contract_id( + let name = Ident::new_no_span(node_idx.name.clone()); + namespace::default_with_contract_id( engines, name.clone(), + Visibility::Private, contract_id_value, experimental, - )?; - module.name = name; - module.visibility = Visibility::Public; - module + )? } }; dep_namespace.is_external = true; @@ -2477,9 +2476,6 @@ pub fn build( } if let TreeType::Library = compiled.tree_type { - compiled.root_module.write(&engines, |root_module| { - root_module.name = Some(Ident::new_no_span(pkg.name.clone())); - }); lib_namespace_map.insert(node, compiled.root_module); } source_map.insert_dependency(descriptor.manifest_file.dir()); @@ -2733,8 +2729,7 @@ pub fn check( .namespace .program_id(engines) .read(engines, |m| m.clone()); - module.name = Some(Ident::new_no_span(pkg.name.clone())); - module.span = Some( + module.set_span( Span::new( manifest.entry_string()?, 0, diff --git a/sway-core/src/abi_generation/abi_str.rs b/sway-core/src/abi_generation/abi_str.rs index bc3618abac3..b52af834f0c 100644 --- a/sway-core/src/abi_generation/abi_str.rs +++ b/sway-core/src/abi_generation/abi_str.rs @@ -3,7 +3,7 @@ use sway_types::integer_bits::IntegerBits; use crate::{language::CallPath, Engines, TypeArgument, TypeId, TypeInfo}; pub struct AbiStrContext { - pub program_name: Option, + pub program_name: String, pub abi_with_callpaths: bool, pub abi_with_fully_specified_types: bool, } @@ -182,9 +182,8 @@ fn call_path_display(ctx: &AbiStrContext, call_path: &CallPath) -> String { return call_path.suffix.as_str().to_string(); } let mut buf = String::new(); - let root_name = ctx.program_name.as_deref(); for (index, prefix) in call_path.prefixes.iter().enumerate() { - if index == 0 && Some(prefix.as_str()) == root_name { + if index == 0 && prefix.as_str() == ctx.program_name { continue; } buf.push_str(prefix.as_str()); diff --git a/sway-core/src/abi_generation/fuel_abi.rs b/sway-core/src/abi_generation/fuel_abi.rs index 1479dd2c155..f49e8eb6fea 100644 --- a/sway-core/src/abi_generation/fuel_abi.rs +++ b/sway-core/src/abi_generation/fuel_abi.rs @@ -26,7 +26,7 @@ impl<'a> AbiContext<'a> { .root .namespace .program_id(engines) - .read(engines, |m| m.name.clone().map(|v| v.as_str().to_string())), + .read(engines, |m| m.name().to_string()), abi_with_callpaths: self.abi_with_callpaths, abi_with_fully_specified_types, } diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index f16e36e5b97..157d103589e 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1316,12 +1316,11 @@ mod tests { }, ); let mut md_mgr = MetadataManager::default(); - let mut core_lib = namespace::Root::from(namespace::Module { - name: Some(sway_types::Ident::new_no_span( - "assert_is_constant_test".to_string(), - )), - ..Default::default() - }); + let mut core_lib = namespace::Root::from(namespace::Module::new( + sway_types::Ident::new_no_span("assert_is_constant_test".to_string()), + crate::Visibility::Private, + None, + )); let r = crate::compile_to_ast( &handler, diff --git a/sway-core/src/language/call_path.rs b/sway-core/src/language/call_path.rs index e71cef0aa93..7c6ba2502f7 100644 --- a/sway-core/src/language/call_path.rs +++ b/sway-core/src/language/call_path.rs @@ -336,9 +336,7 @@ impl CallPath { /// before the identifier is added to the environment. pub fn ident_to_fullpath(suffix: Ident, namespace: &Namespace) -> CallPath { let mut res: Self = suffix.clone().into(); - if let Some(ref pkg_name) = namespace.root_module().name { - res.prefixes.push(pkg_name.clone()) - }; + res.prefixes.push(namespace.root_module().name().clone()); for mod_path in namespace.mod_path() { res.prefixes.push(mod_path.clone()) } @@ -404,9 +402,7 @@ impl CallPath { let mut prefixes: Vec = vec![]; if !is_external { - if let Some(pkg_name) = &namespace.root_module().name { - prefixes.push(pkg_name.clone()); - } + prefixes.push(namespace.root_module().name().clone()); if !is_absolute { for mod_path in namespace.mod_path() { @@ -439,9 +435,8 @@ impl CallPath { } } else { let mut prefixes: Vec = vec![]; - if let Some(pkg_name) = &namespace.root_module().read(engines, |m| m.name.clone()) { - prefixes.push(pkg_name.clone()); - } + prefixes.push(namespace.root_module().name().clone()); + for mod_path in namespace.mod_path() { prefixes.push(mod_path.clone()); } @@ -473,7 +468,7 @@ impl CallPath { let converted = self.to_fullpath(engines, namespace); if let Some(first) = converted.prefixes.first() { - if namespace.root_module().read(engines, |m| m.name.clone()) == Some(first.clone()) { + if namespace.root_module().name() == first { return converted.lshift(); } } diff --git a/sway-core/src/language/ty/expression/intrinsic_function.rs b/sway-core/src/language/ty/expression/intrinsic_function.rs index a82ec310f9f..2d9466d3c0c 100644 --- a/sway-core/src/language/ty/expression/intrinsic_function.rs +++ b/sway-core/src/language/ty/expression/intrinsic_function.rs @@ -118,7 +118,7 @@ impl CollectTypesMetadata for TyIntrinsicFunctionKind { types_metadata.push(TypeMetadata::LoggedType( LogId::new(logged_type.get_abi_type_str( &AbiStrContext { - program_name: Some(ctx.program_name.clone()), + program_name: ctx.program_name.clone(), abi_with_callpaths: true, abi_with_fully_specified_types: true, }, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs index efb07857886..2731811f6a7 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl.rs @@ -401,8 +401,7 @@ where engines: &Engines, decl: &TyDecl, ) -> Option<(Option, Option)> { - if matches!(self.ctx.namespace.root().module.read(engines, |m| m.name.clone()).as_ref(), Some(x) if x.as_str() == "core") - { + if self.ctx.namespace.root().module.name().as_str() == "core" { return Some((None, None)); } @@ -437,8 +436,7 @@ where engines: &Engines, decl: &TyDecl, ) -> Option<(Option, Option)> { - if matches!(self.ctx.namespace.root().module.read(engines, |m| m.name.clone()).as_ref(), Some(x) if x.as_str() == "core") - { + if self.ctx.namespace.root().module.name().as_str() == "core" { return Some((None, None)); } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 88a027a343a..e5f5ca57586 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -2680,7 +2680,12 @@ mod tests { type_annotation: TypeId, experimental: ExperimentalFlags, ) -> Result { - let mut root_module = namespace::Root::from(namespace::Module::default()); + let root_module_name = sway_types::Ident::new_no_span("do_type_check_test".to_string()); + let mut root_module = namespace::Root::from(namespace::Module::new( + root_module_name, + Visibility::Private, + None, + )); let mut namespace = Namespace::init_root(&mut root_module); let ctx = TypeCheckContext::from_namespace(&mut namespace, engines, experimental) .with_type_annotation(type_annotation); diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 04e88818497..c281e67da0e 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -829,7 +829,7 @@ pub(crate) fn resolve_method_name( ctx.namespace().prepend_module_path(&call_path.prefixes) } else { let mut module_path = call_path.prefixes.clone(); - if let (Some(root_mod), Some(root_name)) = ( + if let (Some(root_mod), root_name) = ( module_path.first().cloned(), ctx.namespace().root_module_name().clone(), ) { diff --git a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs index eaa91f37820..9a58cd80c64 100644 --- a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs +++ b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs @@ -26,25 +26,34 @@ use super::{lexical_scope::SymbolMap, root::ResolvedDeclaration, Module, Root}; /// `CONTRACT_ID`-containing modules: https://github.com/FuelLabs/sway/issues/3077 pub fn default_with_contract_id( engines: &Engines, - name: Option, + name: Ident, + visibility: Visibility, contract_id_value: String, experimental: crate::ExperimentalFlags, ) -> Result> { let handler = <_>::default(); - default_with_contract_id_inner(&handler, engines, name, contract_id_value, experimental) - .map_err(|_| { - let (errors, warnings) = handler.consume(); - assert!(warnings.is_empty()); + default_with_contract_id_inner( + &handler, + engines, + name, + visibility, + contract_id_value, + experimental, + ) + .map_err(|_| { + let (errors, warnings) = handler.consume(); + assert!(warnings.is_empty()); - // Invariant: `.value == None` => `!errors.is_empty()`. - vec1::Vec1::try_from_vec(errors).unwrap() - }) + // Invariant: `.value == None` => `!errors.is_empty()`. + vec1::Vec1::try_from_vec(errors).unwrap() + }) } fn default_with_contract_id_inner( handler: &Handler, engines: &Engines, - ns_name: Option, + ns_name: Ident, + visibility: Visibility, contract_id_value: String, experimental: crate::ExperimentalFlags, ) -> Result { @@ -95,11 +104,9 @@ fn default_with_contract_id_inner( content: AstNodeContent::Declaration(Declaration::ConstantDeclaration(const_decl_id)), span: const_item_span.clone(), }; - let mut root = Root::from(Module::default()); + let mut root = Root::from(Module::new(ns_name.clone(), Visibility::Public, None)); let mut ns = Namespace::init_root(&mut root); // This is pretty hacky but that's okay because of this code is being removed pretty soon - ns.root.module.name = ns_name; - ns.root.module.visibility = Visibility::Public; let type_check_ctx = TypeCheckContext::from_namespace(&mut ns, engines, experimental); let typed_node = TyAstNode::type_check(handler, type_check_ctx, &ast_node).unwrap(); // get the decl out of the typed node: @@ -118,7 +125,7 @@ fn default_with_contract_id_inner( }; compiled_constants.insert(name, ResolvedDeclaration::Typed(typed_decl)); - let mut ret = Module::default(); + let mut ret = Module::new(ns_name, visibility, None); ret.current_lexical_scope_mut().items.symbols = compiled_constants; Ok(ret) } diff --git a/sway-core/src/semantic_analysis/namespace/module.rs b/sway-core/src/semantic_analysis/namespace/module.rs index b61daaa2c01..bfa1f683945 100644 --- a/sway-core/src/semantic_analysis/namespace/module.rs +++ b/sway-core/src/semantic_analysis/namespace/module.rs @@ -36,11 +36,11 @@ pub struct Module { pub current_lexical_scope_id: LexicalScopeId, /// Name of the module, package name for root module, module name for other modules. /// Module name used is the same as declared in `mod name;`. - pub name: Option, + name: Ident, /// Whether or not this is a `pub` module - pub visibility: Visibility, + visibility: Visibility, /// Empty span at the beginning of the file implementing the module - pub span: Option, + span: Option, /// Indicates whether the module is external to the current package. External modules are /// imported in the `Forc.toml` file. pub is_external: bool, @@ -51,22 +51,57 @@ pub struct Module { pub(crate) mod_path: ModulePathBuf, } -impl Default for Module { - fn default() -> Self { +impl Module { + pub fn new(name: Ident, visibility: Visibility, span: Option) -> Self { Self { - visibility: Visibility::Private, + visibility, submodules: Default::default(), lexical_scopes: vec![LexicalScope::default()], current_lexical_scope_id: 0, - name: Default::default(), - span: Default::default(), + name, + span, is_external: Default::default(), mod_path: Default::default(), } } -} -impl Module { + // Specialized constructor for cloning Namespace::init. Should not be used for anything else + pub(super) fn new_submodule_from_init( + &self, + name: Ident, + visibility: Visibility, + span: Option, + is_external: bool, + mod_path: ModulePathBuf, + ) -> Self { + Self { + visibility, + submodules: self.submodules.clone(), + lexical_scopes: self.lexical_scopes.clone(), + current_lexical_scope_id: self.current_lexical_scope_id, + name, + span, + is_external, + mod_path, + } + } + + pub fn name(&self) -> &Ident { + &self.name + } + + pub fn visibility(&self) -> &Visibility { + &self.visibility + } + + pub fn span(&self) -> &Option { + &self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = Some(span); + } + pub fn read(&self, _engines: &crate::Engines, mut f: impl FnMut(&Module) -> R) -> R { f(self) } diff --git a/sway-core/src/semantic_analysis/namespace/namespace.rs b/sway-core/src/semantic_analysis/namespace/namespace.rs index 9b2470bb3d3..7a8b00e4304 100644 --- a/sway-core/src/semantic_analysis/namespace/namespace.rs +++ b/sway-core/src/semantic_analysis/namespace/namespace.rs @@ -110,8 +110,8 @@ impl Namespace { } /// The name of the root module - pub fn root_module_name(&self) -> &Option { - &self.root.module.name + pub fn root_module_name(&self) -> &Ident { + self.root.module.name() } /// Access to the current [Module], i.e. the module at the inner `mod_path`. @@ -158,10 +158,7 @@ impl Namespace { ) -> bool { // `mod_path` does not contain the root name, so we have to separately check // that the root name is equal to the module package name. - let root_name = match &self.root.module.name { - Some(name) => name, - None => panic!("Root module must always have a name."), - }; + let root_name = self.root.module.name(); let (package_name, modules) = absolute_module_path.split_first().expect("Absolute module path must have at least one element, because it always contains the package name."); @@ -192,10 +189,7 @@ impl Namespace { /// Returns true if the module given by the `absolute_module_path` is external /// to the current package. External modules are imported in the `Forc.toml` file. pub(crate) fn module_is_external(&self, absolute_module_path: &ModulePath) -> bool { - let root_name = match &self.root.module.name { - Some(name) => name, - None => panic!("Root module must always have a name."), - }; + let root_name = self.root.module.name(); assert!(!absolute_module_path.is_empty(), "Absolute module path must have at least one element, because it always contains the package name."); @@ -292,24 +286,23 @@ impl Namespace { ) -> SubmoduleNamespace { let init = self.init.clone(); let is_external = self.module(engines).is_external; - self.module_mut(engines) - .submodules - .entry(mod_name.to_string()) - .or_insert(init); let submod_path: Vec<_> = self .mod_path .iter() .cloned() .chain(Some(mod_name.clone())) .collect(); + self.module_mut(engines) + .submodules + .entry(mod_name.to_string()) + .or_insert(init.new_submodule_from_init( + mod_name, + visibility, + Some(module_span), + is_external, + submod_path.clone(), + )); let parent_mod_path = std::mem::replace(&mut self.mod_path, submod_path.clone()); - // self.module() now refers to a different module, so refetch - let new_module = self.module_mut(engines); - new_module.name = Some(mod_name); - new_module.span = Some(module_span); - new_module.visibility = visibility; - new_module.is_external = is_external; - new_module.mod_path = submod_path; SubmoduleNamespace { namespace: self, parent_mod_path, @@ -324,16 +317,10 @@ impl Namespace { visibility: Visibility, module_span: Span, ) { - let module = Module { - name: Some(mod_name.clone()), - visibility, - span: Some(module_span), - ..Default::default() - }; self.module_mut(engines) .submodules .entry(mod_name.to_string()) - .or_insert(module); + .or_insert(Module::new(mod_name.clone(), visibility, Some(module_span))); self.mod_path.push(mod_name); } diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index 960909134ef..31480d5567a 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -668,7 +668,7 @@ impl Root { // we don't check the first prefix because direct children are always accessible for prefix in iter_prefixes(src).skip(1) { let module = self.module.lookup_submodule(handler, engines, prefix)?; - if module.visibility.is_private() { + if module.visibility().is_private() { let prefix_last = prefix[prefix.len() - 1].clone(); handler.emit_err(CompileError::ImportPrivateModule { span: prefix_last.span(), diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index daddfdaa947..621c8e8d838 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -774,7 +774,7 @@ impl<'a> TypeCheckContext<'a> { let module = self .namespace() .lookup_submodule_from_absolute_path(handler, engines, prefix)?; - if module.visibility.is_private() { + if module.visibility().is_private() { let prefix_last = prefix[prefix.len() - 1].clone(); handler.emit_err(CompileError::ImportPrivateModule { span: prefix_last.span(), diff --git a/sway-lsp/src/traverse/typed_tree.rs b/sway-lsp/src/traverse/typed_tree.rs index 58003027f20..5a8df1a32f4 100644 --- a/sway-lsp/src/traverse/typed_tree.rs +++ b/sway-lsp/src/traverse/typed_tree.rs @@ -113,7 +113,7 @@ impl Parse for ty::TySideEffect { if let Some(span) = ctx .namespace .submodule(ctx.engines, mod_path) - .and_then(|tgt_submod| tgt_submod.span.clone()) + .and_then(|tgt_submod| tgt_submod.span().clone()) { token.type_def = Some(TypeDefinition::Ident(Ident::new(span))); } @@ -169,7 +169,7 @@ impl Parse for ty::TySideEffect { if let Some(span) = ctx .namespace .submodule(ctx.engines, call_path) - .and_then(|tgt_submod| tgt_submod.span.clone()) + .and_then(|tgt_submod| tgt_submod.span().clone()) { token.type_def = Some(TypeDefinition::Ident(Ident::new(span))); } @@ -192,7 +192,7 @@ impl Parse for ty::TySideEffect { if let Some(span) = ctx .namespace .submodule(ctx.engines, &[mod_name.clone()]) - .and_then(|tgt_submod| tgt_submod.span.clone()) + .and_then(|tgt_submod| tgt_submod.span().clone()) { token.type_def = Some(TypeDefinition::Ident(Ident::new(span))); } @@ -1209,7 +1209,7 @@ fn collect_call_path_prefixes(ctx: &ParseContext, prefixes: &[Ident]) { if let Some(span) = ctx .namespace .submodule(ctx.engines, mod_path) - .and_then(|tgt_submod| tgt_submod.span.clone()) + .and_then(|tgt_submod| tgt_submod.span().clone()) { token.kind = SymbolKind::Module; token.type_def = Some(TypeDefinition::Ident(Ident::new(span))); diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index e79226326d5..0c6107aaccf 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -8,8 +8,8 @@ use std::{ use anyhow::Result; use colored::Colorize; use sway_core::{ - compile_ir_context_to_finalized_asm, compile_to_ast, ir_generation::compile_program, namespace, - BuildTarget, Engines, + compile_ir_context_to_finalized_asm, compile_to_ast, ir_generation::compile_program, + language::Visibility, namespace, BuildTarget, Engines, }; use sway_error::handler::Handler; @@ -171,16 +171,16 @@ pub(super) async fn run( // TODO the way modules are built for these tests, new_encoding is not working. experimental.new_encoding = false; - // Compile core library and reuse it when compiling tests. - let engines = Engines::default(); - let build_target = BuildTarget::default(); - let mut core_lib = compile_core(build_target, &engines, experimental); - // Create new initial namespace for every test by reusing the precompiled // standard libraries. The namespace, thus its root module, must have the // name set. const PACKAGE_NAME: &str = "test_lib"; - core_lib.name = Some(sway_types::Ident::new_no_span(PACKAGE_NAME.to_string())); + let core_lib_name = sway_types::Ident::new_no_span(PACKAGE_NAME.to_string()); + + // Compile core library and reuse it when compiling tests. + let engines = Engines::default(); + let build_target = BuildTarget::default(); + let core_lib = compile_core(core_lib_name, build_target, &engines, experimental); // Find all the tests. let all_tests = discover_test_files(); @@ -527,6 +527,7 @@ fn discover_test_files() -> Vec { } fn compile_core( + lib_name: sway_types::Ident, build_target: BuildTarget, engines: &Engines, experimental: ExperimentalFlags, @@ -563,7 +564,11 @@ fn compile_core( .submodules() .into_iter() .fold( - namespace::Module::default(), + namespace::Module::new( + sway_types::Ident::new_no_span("core".to_string()), + Visibility::Private, + None, + ), |mut core_mod, (name, sub_mod)| { core_mod.insert_submodule(name.clone(), sub_mod.clone()); core_mod @@ -571,7 +576,7 @@ fn compile_core( ); // Create a module for std and insert the core module. - let mut std_module = namespace::Module::default(); + let mut std_module = namespace::Module::new(lib_name, Visibility::Private, None); std_module.insert_submodule("core".to_owned(), core_module); std_module }