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