diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 6ae375d4015..59f7c798dd4 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -1595,11 +1595,11 @@ pub fn dependency_namespace( engines: &Engines, contract_id_value: Option, experimental: sway_core::ExperimentalFlags, -) -> Result> { +) -> 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 mut namespace = if let Some(contract_id_value) = contract_id_value { + let mut root_module = if let Some(contract_id_value) = contract_id_value { namespace::Module::default_with_contract_id( engines, name.clone(), @@ -1610,9 +1610,9 @@ pub fn dependency_namespace( namespace::Module::default() }; - namespace.is_external = true; - namespace.name = name; - namespace.visibility = Visibility::Public; + root_module.is_external = true; + root_module.name = name; + root_module.visibility = Visibility::Public; // Add direct dependencies. let mut core_added = false; @@ -1647,7 +1647,7 @@ pub fn dependency_namespace( ns } }; - namespace.insert_submodule(dep_name, dep_namespace); + root_module.insert_submodule(dep_name, dep_namespace); let dep = &graph[dep_node]; if dep.name == CORE { core_added = true; @@ -1658,29 +1658,29 @@ pub fn dependency_namespace( if !core_added { if let Some(core_node) = find_core_dep(graph, node) { let core_namespace = &lib_namespace_map[&core_node]; - namespace.insert_submodule(CORE.to_string(), core_namespace.clone()); + root_module.insert_submodule(CORE.to_string(), core_namespace.clone()); } } - let _ = namespace.star_import_with_reexports( + let mut root = namespace::Root::from(root_module); + + let _ = root.star_import_with_reexports( &Handler::default(), engines, &[CORE, PRELUDE].map(|s| Ident::new_no_span(s.into())), &[], - true, ); if has_std_dep(graph, node) { - let _ = namespace.star_import_with_reexports( + let _ = root.star_import_with_reexports( &Handler::default(), engines, &[STD, PRELUDE].map(|s| Ident::new_no_span(s.into())), &[], - true, ); } - Ok(namespace) + Ok(root) } /// Find the `std` dependency, if it is a direct one, of the given node. @@ -1757,7 +1757,7 @@ pub fn compile( pkg: &PackageDescriptor, profile: &BuildProfile, engines: &Engines, - namespace: namespace::Module, + namespace: namespace::Root, source_map: &mut SourceMap, ) -> Result { let mut metrics = PerformanceData::default(); diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 35ef5cdbaaf..fb4cb968c8c 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1185,12 +1185,12 @@ mod tests { let handler = Handler::default(); let mut context = Context::new(engines.se(), sway_ir::ExperimentalFlags::default()); let mut md_mgr = MetadataManager::default(); - let core_lib = namespace::Module { + let core_lib = namespace::Root::from(namespace::Module { name: Some(sway_types::Ident::new_no_span( "assert_is_constant_test".to_string(), )), ..Default::default() - }; + }); 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 d19c5c404ed..1fd6bf49445 100644 --- a/sway-core/src/language/call_path.rs +++ b/sway-core/src/language/call_path.rs @@ -320,7 +320,7 @@ impl CallPath { .get(&self.suffix) { synonym_prefixes = use_synonym.0.clone(); - is_absolute = use_synonym.3; + is_absolute = true; let submodule = namespace.module().submodule(&[use_synonym.0[0].clone()]); if let Some(submodule) = submodule { is_external = submodule.is_external; diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index a30d2b92660..1f2615a1e42 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -474,7 +474,7 @@ pub fn parsed_to_ast( handler: &Handler, engines: &Engines, parse_program: &parsed::ParseProgram, - initial_namespace: namespace::Module, + initial_namespace: namespace::Root, build_config: Option<&BuildConfig>, package_name: &str, retrigger_compilation: Option>, @@ -638,7 +638,7 @@ pub fn compile_to_ast( handler: &Handler, engines: &Engines, input: Arc, - initial_namespace: namespace::Module, + initial_namespace: namespace::Root, build_config: Option<&BuildConfig>, package_name: &str, retrigger_compilation: Option>, @@ -735,7 +735,7 @@ pub fn compile_to_asm( handler: &Handler, engines: &Engines, input: Arc, - initial_namespace: namespace::Module, + initial_namespace: namespace::Root, build_config: BuildConfig, package_name: &str, ) -> Result { @@ -892,7 +892,7 @@ pub fn compile_to_bytecode( handler: &Handler, engines: &Engines, input: Arc, - initial_namespace: namespace::Module, + initial_namespace: namespace::Root, build_config: BuildConfig, source_map: &mut SourceMap, package_name: &str, 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 77b7b61193c..0d01bdea725 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 @@ -2433,7 +2433,8 @@ mod tests { type_annotation: TypeId, experimental: ExperimentalFlags, ) -> Result { - let mut namespace = Namespace::init_root(namespace::Module::default()); + let root_module = namespace::Root::from(namespace::Module::default()); + let mut namespace = Namespace::init_root(root_module); let ctx = TypeCheckContext::from_namespace(&mut namespace, engines, experimental) .with_type_annotation(type_annotation); ty::TyExpression::type_check(handler, ctx, expr) diff --git a/sway-core/src/semantic_analysis/ast_node/mod.rs b/sway-core/src/semantic_analysis/ast_node/mod.rs index 73e3650962e..84ae5481756 100644 --- a/sway-core/src/semantic_analysis/ast_node/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/mod.rs @@ -49,8 +49,7 @@ impl ty::TyAstNode { ImportType::Star => { // try a standard starimport first let star_import_handler = Handler::default(); - let import = - ctx.star_import(&star_import_handler, &path, a.is_absolute); + let import = ctx.star_import(&star_import_handler, &path); if import.is_ok() { handler.append(star_import_handler); import @@ -62,7 +61,6 @@ impl ty::TyAstNode { &variant_import_handler, path, enum_name, - a.is_absolute, ); if variant_import.is_ok() { handler.append(variant_import_handler); @@ -78,18 +76,13 @@ impl ty::TyAstNode { } } ImportType::SelfImport(_) => { - ctx.self_import(handler, &path, a.alias.clone(), a.is_absolute) + ctx.self_import(handler, &path, a.alias.clone()) } ImportType::Item(ref s) => { // try a standard item import first let item_import_handler = Handler::default(); - let import = ctx.item_import( - &item_import_handler, - &path, - s, - a.alias.clone(), - a.is_absolute, - ); + let import = + ctx.item_import(&item_import_handler, &path, s, a.alias.clone()); if import.is_ok() { handler.append(item_import_handler); @@ -104,7 +97,6 @@ impl ty::TyAstNode { enum_name, s, a.alias.clone(), - a.is_absolute, ); if variant_import.is_ok() { handler.append(variant_import_handler); diff --git a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs index f0665e1a670..932c3128707 100644 --- a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs +++ b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs @@ -28,8 +28,8 @@ pub(crate) enum GlobImport { } pub(super) type SymbolMap = im::OrdMap; -// The final `bool` field of `UseSynonyms` is true if the `Vec` path is absolute. -pub(super) type UseSynonyms = im::HashMap, GlobImport, ty::TyDecl, bool)>; +// The `Vec` path is absolute. +pub(super) type UseSynonyms = im::HashMap, GlobImport, ty::TyDecl)>; pub(super) type UseAliases = im::HashMap; /// Represents a lexical scope integer-based identifier, which can be used to reference @@ -261,8 +261,7 @@ impl Items { append_shadowing_error(ident, decl, false, false, &item, const_shadowing_mode); } - if let Some((ident, (_, GlobImport::No, decl, _))) = self.use_synonyms.get_key_value(&name) - { + if let Some((ident, (_, GlobImport::No, decl))) = self.use_synonyms.get_key_value(&name) { append_shadowing_error( ident, decl, diff --git a/sway-core/src/semantic_analysis/namespace/module.rs b/sway-core/src/semantic_analysis/namespace/module.rs index d0d03f5f511..4d43813e407 100644 --- a/sway-core/src/semantic_analysis/namespace/module.rs +++ b/sway-core/src/semantic_analysis/namespace/module.rs @@ -3,7 +3,7 @@ use crate::{ engine_threading::Engines, language::{ parsed::*, - ty::{self, TyDecl, TyTraitItem}, + ty::{self, TyTraitItem}, CallPath, Visibility, }, semantic_analysis::*, @@ -12,9 +12,8 @@ use crate::{ }; use super::{ - lexical_scope::{GlobImport, Items, LexicalScope, SymbolMap}, + lexical_scope::{Items, LexicalScope, SymbolMap}, root::Root, - trait_map::TraitMap, LexicalScopeId, ModuleName, Path, PathBuf, }; @@ -23,7 +22,6 @@ use sway_error::handler::Handler; use sway_error::{error::CompileError, handler::ErrorEmitted}; use sway_parse::{lex, Parser}; use sway_types::{span::Span, Spanned}; -use sway_utils::iter_prefixes; /// A single `Module` within a Sway project. /// @@ -162,7 +160,8 @@ impl Module { content: AstNodeContent::Declaration(Declaration::ConstantDeclaration(const_decl_id)), span: const_item_span.clone(), }; - let mut ns = Namespace::init_root(Default::default()); + let root = Root::from(Module::default()); + let mut ns = Namespace::init_root(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.is_external = true; @@ -288,446 +287,6 @@ impl Module { self.current_lexical_scope_id = parent_scope_id.unwrap_or(0); } - /// Given a path to a `src` module, create synonyms to every symbol in that module to the given - /// `dst` module. - /// - /// This is used when an import path contains an asterisk. - /// - /// Paths are assumed to be relative to `self`. - pub(crate) fn star_import( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - dst: &Path, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, src)?; - - let decl_engine = engines.de(); - - let src_mod = self.check_submodule(handler, src)?; - - let implemented_traits = src_mod.current_items().implemented_traits.clone(); - let mut symbols_and_decls = vec![]; - for (symbol, decl) in src_mod.current_items().symbols.iter() { - if is_ancestor(src, dst) || decl.visibility(decl_engine).is_public() { - symbols_and_decls.push((symbol.clone(), decl.clone())); - } - } - - let dst_mod = &mut self[dst]; - dst_mod - .current_items_mut() - .implemented_traits - .extend(implemented_traits, engines); - for symbol_and_decl in symbols_and_decls { - dst_mod.current_items_mut().use_synonyms.insert( - symbol_and_decl.0, - ( - src.to_vec(), - GlobImport::Yes, - symbol_and_decl.1, - is_src_absolute, - ), - ); - } - - Ok(()) - } - - /// Given a path to a `src` module, create synonyms to every symbol in that module to the given - /// `dst` module. - /// - /// This is used when an import path contains an asterisk. - /// - /// Paths are assumed to be relative to `self`. - pub fn star_import_with_reexports( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - dst: &Path, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, src)?; - - let decl_engine = engines.de(); - - let src_mod = self.check_submodule(handler, src)?; - - let implemented_traits = src_mod.current_items().implemented_traits.clone(); - let use_synonyms = src_mod.current_items().use_synonyms.clone(); - let mut symbols_and_decls = src_mod - .current_items() - .use_synonyms - .iter() - .map(|(symbol, (_, _, decl, _))| (symbol.clone(), decl.clone())) - .collect::>(); - for (symbol, decl) in src_mod.current_items().symbols.iter() { - if is_ancestor(src, dst) || decl.visibility(decl_engine).is_public() { - symbols_and_decls.push((symbol.clone(), decl.clone())); - } - } - - let mut symbols_paths_and_decls = vec![]; - for (symbol, (mod_path, _, decl, _)) in use_synonyms { - let mut is_external = false; - let submodule = src_mod.submodule(&[mod_path[0].clone()]); - if let Some(submodule) = submodule { - is_external = submodule.is_external - }; - - let mut path = src[..1].to_vec(); - if is_external { - path = mod_path; - } else { - path.extend(mod_path); - } - - symbols_paths_and_decls.push((symbol, path, decl)); - } - - let dst_mod = &mut self[dst]; - dst_mod - .current_items_mut() - .implemented_traits - .extend(implemented_traits, engines); - - let mut try_add = |symbol, path, decl: ty::TyDecl| { - dst_mod - .current_items_mut() - .use_synonyms - .insert(symbol, (path, GlobImport::Yes, decl, is_src_absolute)); - }; - - for (symbol, decl) in symbols_and_decls { - try_add(symbol, src.to_vec(), decl); - } - - for (symbol, path, decl) in symbols_paths_and_decls { - try_add(symbol, path, decl); - } - - Ok(()) - } - - /// Pull a single item from a `src` module and import it into the `dst` module. - /// - /// The item we want to import is basically the last item in path because this is a `self` - /// import. - pub(crate) fn self_import( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - dst: &Path, - alias: Option, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - let (last_item, src) = src.split_last().expect("guaranteed by grammar"); - self.item_import( - handler, - engines, - src, - last_item, - dst, - alias, - is_src_absolute, - ) - } - - /// Pull a single `item` from the given `src` module and import it into the `dst` module. - /// - /// Paths are assumed to be relative to `self`. - #[allow(clippy::too_many_arguments)] - pub(crate) fn item_import( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - item: &Ident, - dst: &Path, - alias: Option, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, src)?; - - let decl_engine = engines.de(); - - let src_mod = self.check_submodule(handler, src)?; - let mut impls_to_insert = TraitMap::default(); - match src_mod.current_items().symbols.get(item).cloned() { - Some(decl) => { - if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { - handler.emit_err(CompileError::ImportPrivateSymbol { - name: item.clone(), - span: item.span(), - }); - } - - // if this is an enum or struct or function, import its implementations - if let Ok(type_id) = decl.return_type(&Handler::default(), engines) { - impls_to_insert.extend( - src_mod - .current_items() - .implemented_traits - .filter_by_type_item_import(type_id, engines), - engines, - ); - } - // if this is a trait, import its implementations - let decl_span = decl.span(); - if let TyDecl::TraitDecl(_) = &decl { - // TODO: we only import local impls from the source namespace - // this is okay for now but we'll need to device some mechanism to collect all available trait impls - impls_to_insert.extend( - src_mod - .current_items() - .implemented_traits - .filter_by_trait_decl_span(decl_span), - engines, - ); - } - // no matter what, import it this way though. - let dst_mod = &mut self[dst]; - let add_synonym = |name| { - if let Some((_, GlobImport::No, _, _)) = - dst_mod.current_items().use_synonyms.get(name) - { - handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() }); - } - dst_mod.current_items_mut().use_synonyms.insert( - name.clone(), - (src.to_vec(), GlobImport::No, decl, is_src_absolute), - ); - }; - match alias { - Some(alias) => { - add_synonym(&alias); - dst_mod - .current_items_mut() - .use_aliases - .insert(alias.as_str().to_string(), item.clone()); - } - None => add_synonym(item), - }; - } - None => { - return Err(handler.emit_err(CompileError::SymbolNotFound { - name: item.clone(), - span: item.span(), - })); - } - }; - - let dst_mod = &mut self[dst]; - dst_mod - .current_items_mut() - .implemented_traits - .extend(impls_to_insert, engines); - - Ok(()) - } - - /// Pull a single variant `variant` from the enum `enum_name` from the given `src` module and import it into the `dst` module. - /// - /// Paths are assumed to be relative to `self`. - #[allow(clippy::too_many_arguments)] // TODO: remove lint bypass once private modules are no longer experimental - pub(crate) fn variant_import( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - enum_name: &Ident, - variant_name: &Ident, - dst: &Path, - alias: Option, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, src)?; - - let decl_engine = engines.de(); - - let src_mod = self.check_submodule(handler, src)?; - match src_mod.current_items().symbols.get(enum_name).cloned() { - Some(decl) => { - if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { - handler.emit_err(CompileError::ImportPrivateSymbol { - name: enum_name.clone(), - span: enum_name.span(), - }); - } - - if let TyDecl::EnumDecl(ty::EnumDecl { - decl_id, - subst_list: _, - .. - }) = decl - { - let enum_decl = decl_engine.get_enum(&decl_id); - let enum_ref = DeclRef::new( - enum_decl.call_path.suffix.clone(), - decl_id, - enum_decl.span(), - ); - - if let Some(variant_decl) = - enum_decl.variants.iter().find(|v| v.name == *variant_name) - { - // import it this way. - let dst_mod = &mut self[dst]; - let mut add_synonym = |name| { - if let Some((_, GlobImport::No, _, _)) = - dst_mod.current_items().use_synonyms.get(name) - { - handler.emit_err(CompileError::ShadowsOtherSymbol { - name: name.into(), - }); - } - dst_mod.current_items_mut().use_synonyms.insert( - name.clone(), - ( - src.to_vec(), - GlobImport::No, - TyDecl::EnumVariantDecl(ty::EnumVariantDecl { - enum_ref: enum_ref.clone(), - variant_name: variant_name.clone(), - variant_decl_span: variant_decl.span.clone(), - }), - is_src_absolute, - ), - ); - }; - match alias { - Some(alias) => { - add_synonym(&alias); - dst_mod - .current_items_mut() - .use_aliases - .insert(alias.as_str().to_string(), variant_name.clone()); - } - None => add_synonym(variant_name), - }; - } else { - return Err(handler.emit_err(CompileError::SymbolNotFound { - name: variant_name.clone(), - span: variant_name.span(), - })); - } - } else { - return Err(handler.emit_err(CompileError::Internal( - "Attempting to import variants of something that isn't an enum", - enum_name.span(), - ))); - } - } - None => { - return Err(handler.emit_err(CompileError::SymbolNotFound { - name: enum_name.clone(), - span: enum_name.span(), - })); - } - }; - - Ok(()) - } - - /// Pull all variants from the enum `enum_name` from the given `src` module and import them all into the `dst` module. - /// - /// Paths are assumed to be relative to `self`. - pub(crate) fn variant_star_import( - &mut self, - handler: &Handler, - engines: &Engines, - src: &Path, - dst: &Path, - enum_name: &Ident, - is_src_absolute: bool, - ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, src)?; - - let decl_engine = engines.de(); - - let src_mod = self.check_submodule(handler, src)?; - match src_mod.current_items().symbols.get(enum_name).cloned() { - Some(decl) => { - if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { - handler.emit_err(CompileError::ImportPrivateSymbol { - name: enum_name.clone(), - span: enum_name.span(), - }); - } - - if let TyDecl::EnumDecl(ty::EnumDecl { - decl_id, - subst_list: _, - .. - }) = decl - { - let enum_decl = decl_engine.get_enum(&decl_id); - let enum_ref = DeclRef::new( - enum_decl.call_path.suffix.clone(), - decl_id, - enum_decl.span(), - ); - - for variant_decl in enum_decl.variants.iter() { - let variant_name = &variant_decl.name; - - // import it this way. - let dst_mod = &mut self[dst]; - dst_mod.current_items_mut().use_synonyms.insert( - variant_name.clone(), - ( - src.to_vec(), - GlobImport::Yes, - TyDecl::EnumVariantDecl(ty::EnumVariantDecl { - enum_ref: enum_ref.clone(), - variant_name: variant_name.clone(), - variant_decl_span: variant_decl.span.clone(), - }), - is_src_absolute, - ), - ); - } - } else { - return Err(handler.emit_err(CompileError::Internal( - "Attempting to import variants of something that isn't an enum", - enum_name.span(), - ))); - } - } - None => { - return Err(handler.emit_err(CompileError::SymbolNotFound { - name: enum_name.clone(), - span: enum_name.span(), - })); - } - }; - - Ok(()) - } - - fn check_module_privacy(&self, handler: &Handler, src: &Path) -> Result<(), ErrorEmitted> { - let dst = &self.mod_path; - // you are always allowed to access your ancestor's symbols - if !is_ancestor(src, dst) { - // we don't check the first prefix because direct children are always accessible - for prefix in iter_prefixes(src).skip(1) { - let module = self.check_submodule(handler, prefix)?; - if module.visibility.is_private() { - let prefix_last = prefix[prefix.len() - 1].clone(); - handler.emit_err(CompileError::ImportPrivateModule { - span: prefix_last.span(), - name: prefix_last, - }); - } - } - } - Ok(()) - } - /// Resolve a symbol that is potentially prefixed with some path, e.g. `foo::bar::symbol`. /// /// This is short-hand for concatenating the `mod_path` with the `call_path`'s prefixes and @@ -1041,8 +600,8 @@ impl Module { .get(symbol.as_str()) .unwrap_or(symbol); match module.current_items().use_synonyms.get(symbol) { - Some((_, _, decl @ ty::TyDecl::EnumVariantDecl { .. }, _)) => Ok(decl.clone()), - Some((src_path, _, _, _)) if mod_path != src_path => { + Some((_, _, decl @ ty::TyDecl::EnumVariantDecl { .. })) => Ok(decl.clone()), + Some((src_path, _, _)) if mod_path != src_path => { // If the symbol is imported, before resolving to it, // we need to check if there is a local symbol withing the module with // the same name, and if yes resolve to the local symbol, because it @@ -1103,7 +662,3 @@ fn module_not_found(path: &[Ident]) -> CompileError { .join("::"), } } - -fn is_ancestor(src: &Path, dst: &Path) -> bool { - dst.len() >= src.len() && src.iter().zip(dst).all(|(src, dst)| src == dst) -} diff --git a/sway-core/src/semantic_analysis/namespace/namespace.rs b/sway-core/src/semantic_analysis/namespace/namespace.rs index 89d3b9bfae6..e548a65024c 100644 --- a/sway-core/src/semantic_analysis/namespace/namespace.rs +++ b/sway-core/src/semantic_analysis/namespace/namespace.rs @@ -42,11 +42,10 @@ pub struct Namespace { impl Namespace { /// Initialise the namespace at its root from the given initial namespace. - pub fn init_root(init: Module) -> Self { - let root = Root::from(init.clone()); + pub fn init_root(root: Root) -> Self { let mod_path = vec![]; Self { - init, + init: root.module.clone(), root, mod_path, } diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index c0840c05744..da552e68183 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -1,4 +1,18 @@ -use super::{module::Module, namespace::Namespace}; +use super::{ + lexical_scope::GlobImport, module::Module, namespace::Namespace, trait_map::TraitMap, Ident, +}; +use crate::{ + decl_engine::DeclRef, + engine_threading::*, + language::ty::{self, TyDecl}, + namespace::Path, +}; +use sway_error::{ + error::CompileError, + handler::{ErrorEmitted, Handler}, +}; +use sway_types::Spanned; +use sway_utils::iter_prefixes; /// The root module, from which all other modules can be accessed. /// @@ -13,6 +27,432 @@ pub struct Root { pub(crate) module: Module, } +impl Root { + /// Given a path to a `src` module, create synonyms to every symbol in that module to the given + /// `dst` module. + /// + /// This is used when an import path contains an asterisk. + /// + /// Paths are assumed to be absolute. + pub(crate) fn star_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + dst: &Path, + ) -> Result<(), ErrorEmitted> { + self.check_module_privacy(handler, src)?; + + let decl_engine = engines.de(); + + let src_mod = self.module.check_submodule(handler, src)?; + + let implemented_traits = src_mod.current_items().implemented_traits.clone(); + let mut symbols_and_decls = vec![]; + for (symbol, decl) in src_mod.current_items().symbols.iter() { + if is_ancestor(src, dst) || decl.visibility(decl_engine).is_public() { + symbols_and_decls.push((symbol.clone(), decl.clone())); + } + } + + let dst_mod = &mut self.module[dst]; + dst_mod + .current_items_mut() + .implemented_traits + .extend(implemented_traits, engines); // TODO: No difference made between imported and declared items + for symbol_and_decl in symbols_and_decls { + dst_mod.current_items_mut().use_synonyms.insert( + // TODO: No difference made between imported and declared items + symbol_and_decl.0, + (src.to_vec(), GlobImport::Yes, symbol_and_decl.1), + ); + } + + Ok(()) + } + + /// Pull a single item from a `src` module and import it into the `dst` module. + /// + /// The item we want to import is basically the last item in path because this is a `self` + /// import. + pub(crate) fn self_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + dst: &Path, + alias: Option, + ) -> Result<(), ErrorEmitted> { + let (last_item, src) = src.split_last().expect("guaranteed by grammar"); + self.item_import(handler, engines, src, last_item, dst, alias) + } + + /// Pull a single `item` from the given `src` module and import it into the `dst` module. + /// + /// Paths are assumed to be absolute. + #[allow(clippy::too_many_arguments)] + pub(crate) fn item_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + item: &Ident, + dst: &Path, + alias: Option, + ) -> Result<(), ErrorEmitted> { + self.check_module_privacy(handler, src)?; + + let decl_engine = engines.de(); + + let src_mod = self.module.check_submodule(handler, src)?; + let mut impls_to_insert = TraitMap::default(); + match src_mod.current_items().symbols.get(item).cloned() { + Some(decl) => { + if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { + handler.emit_err(CompileError::ImportPrivateSymbol { + name: item.clone(), + span: item.span(), + }); + } + + // if this is an enum or struct or function, import its implementations + if let Ok(type_id) = decl.return_type(&Handler::default(), engines) { + impls_to_insert.extend( + src_mod + .current_items() + .implemented_traits + .filter_by_type_item_import(type_id, engines), + engines, + ); + } + // if this is a trait, import its implementations + let decl_span = decl.span(); + if let TyDecl::TraitDecl(_) = &decl { + // TODO: we only import local impls from the source namespace + // this is okay for now but we'll need to device some mechanism to collect all available trait impls + impls_to_insert.extend( + src_mod + .current_items() + .implemented_traits + .filter_by_trait_decl_span(decl_span), + engines, + ); + } + // no matter what, import it this way though. + let dst_mod = &mut self.module[dst]; + let add_synonym = |name| { + if let Some((_, GlobImport::No, _)) = + dst_mod.current_items().use_synonyms.get(name) + { + handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() }); + } + dst_mod.current_items_mut().use_synonyms.insert( + // TODO: No difference made between imported and declared items + name.clone(), + (src.to_vec(), GlobImport::No, decl), + ); + }; + match alias { + Some(alias) => { + add_synonym(&alias); + dst_mod + .current_items_mut() + .use_aliases + .insert(alias.as_str().to_string(), item.clone()); // TODO: No difference made between imported and declared items + } + None => add_synonym(item), + }; + } + None => { + return Err(handler.emit_err(CompileError::SymbolNotFound { + name: item.clone(), + span: item.span(), + })); + } + }; + + let dst_mod = &mut self.module[dst]; + dst_mod + .current_items_mut() + .implemented_traits + .extend(impls_to_insert, engines); // TODO: No difference made between imported and declared items + + Ok(()) + } + + /// Pull a single variant `variant` from the enum `enum_name` from the given `src` module and import it into the `dst` module. + /// + /// Paths are assumed to be absolute. + #[allow(clippy::too_many_arguments)] // TODO: remove lint bypass once private modules are no longer experimental + pub(crate) fn variant_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + enum_name: &Ident, + variant_name: &Ident, + dst: &Path, + alias: Option, + ) -> Result<(), ErrorEmitted> { + self.check_module_privacy(handler, src)?; + + let decl_engine = engines.de(); + + let src_mod = self.module.check_submodule(handler, src)?; + match src_mod.current_items().symbols.get(enum_name).cloned() { + Some(decl) => { + if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { + handler.emit_err(CompileError::ImportPrivateSymbol { + name: enum_name.clone(), + span: enum_name.span(), + }); + } + + if let TyDecl::EnumDecl(ty::EnumDecl { + decl_id, + subst_list: _, + .. + }) = decl + { + let enum_decl = decl_engine.get_enum(&decl_id); + let enum_ref = DeclRef::new( + enum_decl.call_path.suffix.clone(), + decl_id, + enum_decl.span(), + ); + + if let Some(variant_decl) = + enum_decl.variants.iter().find(|v| v.name == *variant_name) + { + // import it this way. + let dst_mod = &mut self.module[dst]; + let mut add_synonym = |name| { + if let Some((_, GlobImport::No, _)) = + dst_mod.current_items().use_synonyms.get(name) + { + handler.emit_err(CompileError::ShadowsOtherSymbol { + name: name.into(), + }); + } + dst_mod.current_items_mut().use_synonyms.insert( + // TODO: No difference made between imported and declared items + name.clone(), + ( + src.to_vec(), + GlobImport::No, + TyDecl::EnumVariantDecl(ty::EnumVariantDecl { + enum_ref: enum_ref.clone(), + variant_name: variant_name.clone(), + variant_decl_span: variant_decl.span.clone(), + }), + ), + ); + }; + match alias { + Some(alias) => { + add_synonym(&alias); + dst_mod + .current_items_mut() + .use_aliases + .insert(alias.as_str().to_string(), variant_name.clone()); + // TODO: No difference made between imported and declared items + } + None => add_synonym(variant_name), + }; + } else { + return Err(handler.emit_err(CompileError::SymbolNotFound { + name: variant_name.clone(), + span: variant_name.span(), + })); + } + } else { + return Err(handler.emit_err(CompileError::Internal( + "Attempting to import variants of something that isn't an enum", + enum_name.span(), + ))); + } + } + None => { + return Err(handler.emit_err(CompileError::SymbolNotFound { + name: enum_name.clone(), + span: enum_name.span(), + })); + } + }; + + Ok(()) + } + + /// Pull all variants from the enum `enum_name` from the given `src` module and import them all into the `dst` module. + /// + /// Paths are assumed to be absolute. + pub(crate) fn variant_star_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + dst: &Path, + enum_name: &Ident, + ) -> Result<(), ErrorEmitted> { + self.check_module_privacy(handler, src)?; + + let decl_engine = engines.de(); + + let src_mod = self.module.check_submodule(handler, src)?; + match src_mod.current_items().symbols.get(enum_name).cloned() { + Some(decl) => { + if !decl.visibility(decl_engine).is_public() && !is_ancestor(src, dst) { + handler.emit_err(CompileError::ImportPrivateSymbol { + name: enum_name.clone(), + span: enum_name.span(), + }); + } + + if let TyDecl::EnumDecl(ty::EnumDecl { + decl_id, + subst_list: _, + .. + }) = decl + { + let enum_decl = decl_engine.get_enum(&decl_id); + let enum_ref = DeclRef::new( + enum_decl.call_path.suffix.clone(), + decl_id, + enum_decl.span(), + ); + + for variant_decl in enum_decl.variants.iter() { + let variant_name = &variant_decl.name; + + // import it this way. + let dst_mod = &mut self.module[dst]; + dst_mod.current_items_mut().use_synonyms.insert( + // TODO: No difference made between imported and declared items + variant_name.clone(), + ( + src.to_vec(), + GlobImport::Yes, + TyDecl::EnumVariantDecl(ty::EnumVariantDecl { + enum_ref: enum_ref.clone(), + variant_name: variant_name.clone(), + variant_decl_span: variant_decl.span.clone(), + }), + ), + ); + } + } else { + return Err(handler.emit_err(CompileError::Internal( + "Attempting to import variants of something that isn't an enum", + enum_name.span(), + ))); + } + } + None => { + return Err(handler.emit_err(CompileError::SymbolNotFound { + name: enum_name.clone(), + span: enum_name.span(), + })); + } + }; + + Ok(()) + } + + /// Given a path to a `src` module, create synonyms to every symbol in that module to the given + /// `dst` module. + /// + /// This is used when an import path contains an asterisk. + /// + /// Paths are assumed to be absolute. + pub fn star_import_with_reexports( + &mut self, + handler: &Handler, + engines: &Engines, + src: &Path, + dst: &Path, + ) -> Result<(), ErrorEmitted> { + self.check_module_privacy(handler, src)?; + + let decl_engine = engines.de(); + + let src_mod = self.module.check_submodule(handler, src)?; + + let implemented_traits = src_mod.current_items().implemented_traits.clone(); + let use_synonyms = src_mod.current_items().use_synonyms.clone(); + let mut symbols_and_decls = src_mod + .current_items() + .use_synonyms + .iter() + .map(|(symbol, (_, _, decl))| (symbol.clone(), decl.clone())) + .collect::>(); + for (symbol, decl) in src_mod.current_items().symbols.iter() { + if is_ancestor(src, dst) || decl.visibility(decl_engine).is_public() { + symbols_and_decls.push((symbol.clone(), decl.clone())); + } + } + + let mut symbols_paths_and_decls = vec![]; + for (symbol, (mod_path, _, decl)) in use_synonyms { + let mut is_external = false; + let submodule = src_mod.submodule(&[mod_path[0].clone()]); + if let Some(submodule) = submodule { + is_external = submodule.is_external + }; + + let mut path = src[..1].to_vec(); + if is_external { + path = mod_path; + } else { + path.extend(mod_path); + } + + symbols_paths_and_decls.push((symbol, path, decl)); + } + + let dst_mod = &mut self.module[dst]; + dst_mod + .current_items_mut() + .implemented_traits + .extend(implemented_traits, engines); // TODO: No difference made between imported and declared items + + let mut try_add = |symbol, path, decl: ty::TyDecl| { + dst_mod + .current_items_mut() + .use_synonyms + .insert(symbol, (path, GlobImport::Yes, decl)); // TODO: No difference made between imported and declared items + }; + + for (symbol, decl) in symbols_and_decls { + try_add(symbol, src.to_vec(), decl); + } + + for (symbol, path, decl) in symbols_paths_and_decls { + try_add(symbol, path, decl); + } + + Ok(()) + } + + fn check_module_privacy(&self, handler: &Handler, src: &Path) -> Result<(), ErrorEmitted> { + let dst = &self.module.mod_path; + // you are always allowed to access your ancestor's symbols + if !is_ancestor(src, dst) { + // 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.check_submodule(handler, prefix)?; + if module.visibility.is_private() { + let prefix_last = prefix[prefix.len() - 1].clone(); + handler.emit_err(CompileError::ImportPrivateModule { + span: prefix_last.span(), + name: prefix_last, + }); + } + } + } + Ok(()) + } +} + impl From for Root { fn from(module: Module) -> Self { Root { module } @@ -24,3 +464,7 @@ impl From for Root { namespace.root } } + +fn is_ancestor(src: &Path, dst: &Path) -> bool { + dst.len() >= src.len() && src.iter().zip(dst).all(|(src, dst)| src == dst) +} diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 4244abc3eda..021f7a9fefd 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -27,7 +27,7 @@ impl TyProgram { handler: &Handler, engines: &Engines, parsed: &ParseProgram, - initial_namespace: namespace::Module, + initial_namespace: namespace::Root, package_name: &str, build_config: Option<&BuildConfig>, ) -> Result { diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index 20a0f2a29a3..43cb1eaad27 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -1317,14 +1317,12 @@ impl<'a> TypeCheckContext<'a> { &mut self, handler: &Handler, src: &Path, - is_absolute: bool, ) -> Result<(), ErrorEmitted> { let engines = self.engines; let mod_path = self.namespace().mod_path.clone(); self.namespace_mut() .root - .module - .star_import(handler, engines, src, &mod_path, is_absolute) + .star_import(handler, engines, src, &mod_path) } /// Short-hand for performing a [Module::variant_star_import] with `mod_path` as the destination. @@ -1333,18 +1331,12 @@ impl<'a> TypeCheckContext<'a> { handler: &Handler, src: &Path, enum_name: &Ident, - is_absolute: bool, ) -> Result<(), ErrorEmitted> { let engines = self.engines; let mod_path = self.namespace().mod_path.clone(); - self.namespace_mut().root.module.variant_star_import( - handler, - engines, - src, - &mod_path, - enum_name, - is_absolute, - ) + self.namespace_mut() + .root + .variant_star_import(handler, engines, src, &mod_path, enum_name) } /// Short-hand for performing a [Module::self_import] with `mod_path` as the destination. @@ -1353,18 +1345,12 @@ impl<'a> TypeCheckContext<'a> { handler: &Handler, src: &Path, alias: Option, - is_absolute: bool, ) -> Result<(), ErrorEmitted> { let engines = self.engines; let mod_path = self.namespace().mod_path.clone(); - self.namespace_mut().root.module.self_import( - handler, - engines, - src, - &mod_path, - alias, - is_absolute, - ) + self.namespace_mut() + .root + .self_import(handler, engines, src, &mod_path, alias) } /// Short-hand for performing a [Module::item_import] with `mod_path` as the destination. @@ -1374,19 +1360,12 @@ impl<'a> TypeCheckContext<'a> { src: &Path, item: &Ident, alias: Option, - is_absolute: bool, ) -> Result<(), ErrorEmitted> { let engines = self.engines; let mod_path = self.namespace().mod_path.clone(); - self.namespace_mut().root.module.item_import( - handler, - engines, - src, - item, - &mod_path, - alias, - is_absolute, - ) + self.namespace_mut() + .root + .item_import(handler, engines, src, item, &mod_path, alias) } /// Short-hand for performing a [Module::variant_import] with `mod_path` as the destination. @@ -1398,11 +1377,10 @@ impl<'a> TypeCheckContext<'a> { enum_name: &Ident, variant_name: &Ident, alias: Option, - is_absolute: bool, ) -> Result<(), ErrorEmitted> { let engines = self.engines; let mod_path = self.namespace().mod_path.clone(); - self.namespace_mut().root.module.variant_import( + self.namespace_mut().root.variant_import( handler, engines, src, @@ -1410,7 +1388,6 @@ impl<'a> TypeCheckContext<'a> { variant_name, &mod_path, alias, - is_absolute, ) } diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 855282968a9..a723a2efd16 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -171,7 +171,12 @@ pub(super) async fn run( // Compile core library and reuse it when compiling tests. let engines = Engines::default(); let build_target = BuildTarget::default(); - let core_lib = compile_core(build_target, &engines, experimental); + 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())); // Find all the tests. let all_tests = discover_test_files(); @@ -226,12 +231,7 @@ pub(super) async fn run( let sway_str = String::from_utf8_lossy(&sway_str); let handler = Handler::default(); - // 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"; - let mut initial_namespace = core_lib.clone(); - initial_namespace.name = Some(sway_types::Ident::new_no_span(PACKAGE_NAME.to_string())); + let initial_namespace = namespace::Root::from(core_lib.clone()); let compile_res = compile_to_ast( &handler, &engines,