From 079fe016d33d2fcd446c62b2524e10e414936289 Mon Sep 17 00:00:00 2001 From: Amir Mohammadi Date: Tue, 21 Jan 2025 22:38:34 +0330 Subject: [PATCH] consider importing submodules into namespace using glob imports --- .../src/semantic_analysis/namespace/root.rs | 172 +++++++++++------- .../language/import_star_submodules/Forc.lock | 13 ++ .../language/import_star_submodules/Forc.toml | 8 + .../json_abi_oracle.json | 25 +++ .../json_abi_oracle_new_encoding.json | 23 +++ .../import_star_submodules/src/foo.sw | 3 + .../import_star_submodules/src/foo/bar.sw | 5 + .../import_star_submodules/src/main.sw | 9 + .../language/import_star_submodules/test.toml | 4 + 9 files changed, 198 insertions(+), 64 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle_new_encoding.json create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo/bar.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/test.toml diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index f7e9fb3bea3..757eba3383c 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -181,7 +181,7 @@ impl Root { ) -> Result<(), ErrorEmitted> { self.check_module_privacy(handler, engines, src, dst)?; - let src_mod = self.module.lookup_submodule(handler, engines, src)?; + let src_mod = self.module.lookup_submodule(handler, engines, src)?.clone(); let mut decls_and_item_imports = vec![]; @@ -258,6 +258,7 @@ impl Root { } let implemented_traits = src_mod.root_items().implemented_traits.clone(); + let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?; dst_mod .current_items_mut() @@ -277,6 +278,22 @@ impl Root { ) }); + // Ignore invisible submodules + let sub_mods = src_mod + .submodules() + .iter() + .filter_map(|(name, sub_mod)| { + let prefix = [src, &[sub_mod.name().clone()]].concat(); + self.collect_module_privacy_errors(handler, engines, &prefix, dst) + .ok() + .filter(|e| e.is_empty()) + .map(|_| (name.clone(), sub_mod.clone())) + }) + .collect::>(); + + let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?; + dst_mod.submodules_mut().extend(sub_mods); + Ok(()) } @@ -378,74 +395,85 @@ impl Root { alias: Option, visibility: Visibility, ) -> Result<(), ErrorEmitted> { - self.check_module_privacy(handler, engines, src, dst)?; - let src_mod = self.module.lookup_submodule(handler, engines, src)?; + let src_mod = self.module.lookup_submodule(handler, engines, src)?.clone(); - let (decl, path) = self.item_lookup(handler, engines, item, src, dst)?; - - let mut impls_to_insert = TraitMap::default(); - if decl.is_typed() { - // We only handle trait imports when handling typed declarations, - // that is, when performing type-checking, and not when collecting. - // Update this once the type system is updated to refer to parsed - // declarations. - // 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 - .root_items() - .implemented_traits - .filter_by_type_item_import( - type_id, - engines, - super::CodeBlockFirstPass::No, - ), - engines, - ); - } - // if this is a trait, import its implementations - let decl_span = decl.span(engines); - if decl.is_trait() { - // 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 - .root_items() - .implemented_traits - .filter_by_trait_decl_span(decl_span), - engines, - ); - } - } + match src_mod.submodules.get(item.as_str()) { + Some(sub_mod) => { + let prefix = &[src, &[item.clone()]].concat(); + self.check_module_privacy(handler, engines, prefix, dst)?; - // no matter what, import it this way though. - let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?; - let check_name_clash = |name| { - if dst_mod.current_items().use_item_synonyms.contains_key(name) { - handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() }); - } - }; - match alias { - Some(alias) => { - check_name_clash(&alias); - dst_mod - .current_items_mut() - .use_item_synonyms - .insert(alias.clone(), (Some(item.clone()), path, decl, visibility)) + let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?; + dst_mod.insert_submodule(item.to_string(), sub_mod.clone()); } None => { - check_name_clash(item); + self.check_module_privacy(handler, engines, src, dst)?; + let (decl, path) = self.item_lookup(handler, engines, item, src, dst)?; + + let mut impls_to_insert = TraitMap::default(); + if decl.is_typed() { + // We only handle trait imports when handling typed declarations, + // that is, when performing type-checking, and not when collecting. + // Update this once the type system is updated to refer to parsed + // declarations. + // 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 + .root_items() + .implemented_traits + .filter_by_type_item_import( + type_id, + engines, + super::CodeBlockFirstPass::No, + ), + engines, + ); + } + // if this is a trait, import its implementations + let decl_span = decl.span(engines); + if decl.is_trait() { + // 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 + .root_items() + .implemented_traits + .filter_by_trait_decl_span(decl_span), + engines, + ); + } + } + + // no matter what, import it this way though. + let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?; + let check_name_clash = |name| { + if dst_mod.current_items().use_item_synonyms.contains_key(name) { + handler.emit_err(CompileError::ShadowsOtherSymbol { name: name.into() }); + } + }; + match alias { + Some(alias) => { + check_name_clash(&alias); + dst_mod + .current_items_mut() + .use_item_synonyms + .insert(alias.clone(), (Some(item.clone()), path, decl, visibility)) + } + None => { + check_name_clash(item); + dst_mod + .current_items_mut() + .use_item_synonyms + .insert(item.clone(), (None, path, decl, visibility)) + } + }; + dst_mod .current_items_mut() - .use_item_synonyms - .insert(item.clone(), (None, path, decl, visibility)) + .implemented_traits + .extend(impls_to_insert, engines); } - }; - - dst_mod - .current_items_mut() - .implemented_traits - .extend(impls_to_insert, engines); + } Ok(()) } @@ -715,6 +743,21 @@ impl Root { src: &ModulePath, dst: &ModulePath, ) -> Result<(), ErrorEmitted> { + self.collect_module_privacy_errors(handler, engines, src, dst)? + .iter() + .for_each(|e| { + handler.emit_err(e.to_owned()); + }); + Ok(()) + } + + fn collect_module_privacy_errors( + &self, + handler: &Handler, + engines: &Engines, + src: &ModulePath, + dst: &ModulePath, + ) -> Result, ErrorEmitted> { // Calculate the number of src prefixes whose privacy is ignored. let mut ignored_prefixes = 0; @@ -731,18 +774,19 @@ impl Root { } // Check visibility of remaining submodules in the source path + let mut errors = vec![]; for prefix in iter_prefixes(src).skip(ignored_prefixes) { let module = self.module.lookup_submodule(handler, engines, prefix)?; if module.visibility().is_private() { let prefix_last = prefix[prefix.len() - 1].clone(); - handler.emit_err(CompileError::ImportPrivateModule { + errors.push(CompileError::ImportPrivateModule { span: prefix_last.span(), name: prefix_last, }); } } - Ok(()) + Ok(errors) } } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.lock new file mode 100644 index 00000000000..a89caa5c5fc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "core" +source = "path+from-root-64A1AF0004AA7B4A" + +[[package]] +name = "import_star_submodules" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-64A1AF0004AA7B4A" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.toml new file mode 100644 index 00000000000..3dc7152532c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "import_star_submodules" +entry = "main.sw" + +[dependencies] +std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle.json new file mode 100644 index 00000000000..ad50b55d54c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle.json @@ -0,0 +1,25 @@ +{ + "configurables": [], + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + } + } + ], + "loggedTypes": [], + "messagesTypes": [], + "types": [ + { + "components": null, + "type": "u64", + "typeId": 0, + "typeParameters": null + } + ] +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle_new_encoding.json new file mode 100644 index 00000000000..05b0f04b2d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/json_abi_oracle_new_encoding.json @@ -0,0 +1,23 @@ +{ + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], + "configurables": [], + "encodingVersion": "1", + "functions": [ + { + "attributes": null, + "inputs": [], + "name": "main", + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" + } + ], + "loggedTypes": [], + "messagesTypes": [], + "metadataTypes": [], + "programType": "script", + "specVersion": "1" +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo.sw new file mode 100644 index 00000000000..9c0926b439e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo.sw @@ -0,0 +1,3 @@ +library; + +pub mod bar; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo/bar.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo/bar.sw new file mode 100644 index 00000000000..d28651c6ab4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/foo/bar.sw @@ -0,0 +1,5 @@ +library; + +pub fn func() -> u64 { + 10 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/main.sw new file mode 100644 index 00000000000..807a534dd38 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/src/main.sw @@ -0,0 +1,9 @@ +script; + +mod foo; + +use foo::*; + +fn main() -> u64 { + bar::func() +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/test.toml new file mode 100644 index 00000000000..4ce10cccb80 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_star_submodules/test.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return", value = 10 } +expected_result_new_encoding = { action = "return_data", value = "000000000000000A" } +validate_abi = true