Skip to content

Draft: consider importing submodules into namespace using glob imports #6847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 108 additions & 64 deletions sway-core/src/semantic_analysis/namespace/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![];

Expand Down Expand Up @@ -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()
Expand All @@ -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::<std::collections::HashMap<_, _>>();

let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?;
dst_mod.submodules_mut().extend(sub_mods);

Ok(())
}

Expand Down Expand Up @@ -378,74 +395,85 @@ impl Root {
alias: Option<Ident>,
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(())
}
Expand Down Expand Up @@ -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<Vec<CompileError>, ErrorEmitted> {
// Calculate the number of src prefixes whose privacy is ignored.
let mut ignored_prefixes = 0;

Expand All @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
license = "Apache-2.0"
name = "import_star_submodules"
entry = "main.sw"

[dependencies]
std = { path = "../../../../reduced_std_libs/sway-lib-std-assert" }
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library;

pub mod bar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
library;

pub fn func() -> u64 {
10
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
script;

mod foo;

use foo::*;

fn main() -> u64 {
bar::func()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "run"
expected_result = { action = "return", value = 10 }
expected_result_new_encoding = { action = "return_data", value = "000000000000000A" }
validate_abi = true