Skip to content
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

Name clash in star imports should result in error when the name is used #5963

Merged
merged 35 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b01280a
Move name resolution to Root
jjcnn Apr 25, 2024
a7510d9
fmt, clippy
jjcnn Apr 25, 2024
0856eb9
Added tests of alias reexports, remove alias map from LexicalScope
jjcnn Apr 26, 2024
070f884
Added tests of alias reexports, remove alias map from LexicalScope
jjcnn Apr 26, 2024
0546ab6
Fix failing test
jjcnn May 1, 2024
b2bffcd
Merge branch 'master' of github.com:FuelLabs/sway into jjcnn/name-cla…
jjcnn May 2, 2024
ce9fda8
EOD stash - still need to resolve test issues
jjcnn May 2, 2024
179c948
Use TyDecl::eq instead of mod_path to determine if the same TyDecl ha…
jjcnn May 2, 2024
869cad8
Fixed tests
jjcnn May 3, 2024
049e78e
Updated test
jjcnn May 3, 2024
4f3989b
Merge branch 'master' into jjcnn/name-clash-on-star-imports
jjcnn May 6, 2024
c1c9bbd
Fix module paths
jjcnn May 7, 2024
57a2bfc
Roll back changes to TyDecl::eq
jjcnn May 7, 2024
d3175cd
clippy, fmt
jjcnn May 7, 2024
636dc4c
Merge branch 'master' of github.com:FuelLabs/sway into jjcnn/name-cla…
jjcnn May 7, 2024
0ccffc9
Change order of star-imported items
jjcnn May 7, 2024
6364b2f
Revert order change of star-imported items
jjcnn May 7, 2024
93ea3cb
Allow bindings from std::prelude to override core::prelude and core::…
jjcnn May 7, 2024
497e4dd
Updated test
jjcnn May 8, 2024
068778e
Merge branch 'master' of github.com:FuelLabs/sway into jjcnn/name-cla…
jjcnn May 8, 2024
a4b408d
Move enum variant check back to TyDecl::eq
jjcnn May 8, 2024
bcec129
Code cleanup
jjcnn May 8, 2024
c11b88d
Temporary save while switching machines
jjcnn May 8, 2024
494cbf4
Workaround to deal with incorrect prelude reexports
jjcnn May 8, 2024
75b5f77
Merge branch 'master' of github.com:FuelLabs/sway into jjcnn/name-cla…
jjcnn May 8, 2024
4eb5ad0
Merge branch 'master' into jjcnn/name-clash-on-star-imports
JoshuaBatty May 9, 2024
8590ec1
Added implicit-std to tests
jjcnn May 10, 2024
6b5ccc9
Review comments addressed
jjcnn May 10, 2024
48fb955
Merge branch 'jjcnn/name-clash-on-star-imports' of github.com:FuelLab…
jjcnn May 10, 2024
c218e67
Fixed unhelpful error message for enum variants, updated test
jjcnn May 10, 2024
fa6c794
Merge branch 'master' into jjcnn/name-clash-on-star-imports
jjcnn May 10, 2024
f44d4a8
clippy, fmt
jjcnn May 10, 2024
9810f95
Fixed clippy error
jjcnn May 10, 2024
458e07c
clippy complains again
jjcnn May 10, 2024
14dd040
Merge branch 'master' into jjcnn/name-clash-on-star-imports
IGI-111 May 11, 2024
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
8 changes: 6 additions & 2 deletions sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,17 @@ impl CallPath {
.cloned()
{
Some(path)
} else if let Some((path, _)) = m
} else if let Some(paths_and_decls) = m
.current_items()
.use_glob_synonyms
.get(&self.suffix)
.cloned()
{
Some(path)
if paths_and_decls.len() == 1 {
Some(paths_and_decls[0].0.clone())
} else {
None
}
} else {
None
}
Expand Down
17 changes: 17 additions & 0 deletions sway-core/src/language/ty/declaration/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,23 @@ impl PartialEqWithEngines for TyDecl {
TyDecl::EnumDecl(EnumDecl { decl_id: lid, .. }),
TyDecl::EnumDecl(EnumDecl { decl_id: rid, .. }),
) => decl_engine.get(lid).eq(&decl_engine.get(rid), ctx),
(
TyDecl::EnumVariantDecl(EnumVariantDecl {
enum_ref: l_enum,
variant_name: ln,
..
}),
TyDecl::EnumVariantDecl(EnumVariantDecl {
enum_ref: r_enum,
variant_name: rn,
..
}),
) => {
ln == rn
&& decl_engine
.get_enum(l_enum)
.eq(&decl_engine.get_enum(r_enum), ctx)
}
(
TyDecl::ImplTrait(ImplTrait { decl_id: lid, .. }),
TyDecl::ImplTrait(ImplTrait { decl_id: rid, .. }),
Expand Down
58 changes: 56 additions & 2 deletions sway-core/src/semantic_analysis/namespace/lexical_scope.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
decl_engine::{parsed_id::ParsedDeclId, *},
engine_threading::Engines,
engine_threading::{Engines, PartialEqWithEngines, PartialEqWithEnginesContext},
language::{
parsed::{Declaration, FunctionDeclaration},
ty::{self, StructAccessInfo, TyDecl, TyStorageDecl},
Expand Down Expand Up @@ -38,7 +38,7 @@ impl ResolvedFunctionDecl {
pub(super) type ParsedSymbolMap = im::OrdMap<Ident, Declaration>;
pub(super) type SymbolMap = im::OrdMap<Ident, ty::TyDecl>;
type SourceIdent = Ident;
pub(super) type GlobSynonyms = im::HashMap<Ident, (ModulePathBuf, ty::TyDecl)>;
pub(super) type GlobSynonyms = im::HashMap<Ident, Vec<(ModulePathBuf, ty::TyDecl)>>;
pub(super) type ItemSynonyms = im::HashMap<Ident, (Option<SourceIdent>, ModulePathBuf, ty::TyDecl)>;

/// Represents a lexical scope integer-based identifier, which can be used to reference
Expand Down Expand Up @@ -75,6 +75,12 @@ pub struct Items {
/// path `foo::bar::Baz`.
///
/// use_glob_synonyms contains symbols imported using star imports (`use foo::*`.).
///
/// When star importing from multiple modules the same name may be imported more than once. This
/// is not an error, but it is an error to use the name without a module path. To represent
/// this, use_glob_synonyms maps identifiers to a vector of (module path, type declaration)
/// tuples.
///
/// use_item_synonyms contains symbols imported using item imports (`use foo::bar`).
///
/// For aliased item imports `use ::foo::bar::Baz as Wiz` the map key is `Wiz`. `Baz` is stored
Expand Down Expand Up @@ -304,6 +310,54 @@ impl Items {
Ok(())
}

// Insert a symbol into use_glob_synonyms if the symbol has not yet been inserted
jjcnn marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) fn insert_glob_use_symbol(
&mut self,
engines: &Engines,
symbol: Ident,
src_path: ModulePathBuf,
decl: &ty::TyDecl,
) {
if let Some(cur_decls) = self.use_glob_synonyms.get_mut(&symbol) {
// Name already bound. Check if the decl is already imported
let ctx = PartialEqWithEnginesContext::new(engines);
match cur_decls.iter().position(|(cur_path, cur_decl)| {
cur_decl.eq(decl, &ctx)
// For some reason the equality check is not sufficient. In some cases items that
// are actually identical fail the eq check, so we have to add heuristics for these
// cases.
//
// These edge occur because core and std preludes are not reexported correctly. Once
// reexports are implemented we can handle the preludes correctly, and then these
// edge cases should go away.
// See https://github.com/FuelLabs/sway/issues/3113
//
// As a heuristic we replace any bindings from std and core if the new binding is
// also from std or core. This does not work if the user has declared an item with
// the same name as an item in one of the preludes, but this is an edge case that we
// will have to live with for now.
|| ((cur_path[0].as_str() == "core" || cur_path[0].as_str() == "std")
&& (src_path[0].as_str() == "core" || src_path[0].as_str() == "std"))
}) {
Some(index) => {
// The name is already bound to this decl, but
// we need to replace the binding to make the paths work out.
// This appears to be an issue with the core prelude, and will probably no
// longer be necessary once reexports are implemented:
// https://github.com/FuelLabs/sway/issues/3113
cur_decls[index] = (src_path.to_vec(), decl.clone());
}
None => {
// New decl for this name. Add it to the end
cur_decls.push((src_path.to_vec(), decl.clone()));
}
}
} else {
let new_vec = vec![(src_path.to_vec(), decl.clone())];
self.use_glob_synonyms.insert(symbol, new_vec);
}
}

pub(crate) fn check_symbol(&self, name: &Ident) -> Result<ResolvedDeclaration, CompileError> {
self.symbols
.get(name)
Expand Down
3 changes: 2 additions & 1 deletion sway-core/src/semantic_analysis/namespace/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,14 @@ impl Namespace {
.cloned()
.chain(Some(mod_name.clone()))
.collect();
let parent_mod_path = std::mem::replace(&mut self.mod_path, submod_path);
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 = false;
new_module.mod_path = submod_path;
SubmoduleNamespace {
namespace: self,
parent_mod_path,
Expand Down
91 changes: 64 additions & 27 deletions sway-core/src/semantic_analysis/namespace/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@ impl Root {
.current_items_mut()
.implemented_traits
.extend(implemented_traits, engines);
for symbol_and_decl in symbols_and_decls {
dst_mod
.current_items_mut()
.use_glob_synonyms
.insert(symbol_and_decl.0, (src.to_vec(), symbol_and_decl.1));
}

symbols_and_decls.iter().for_each(|(symbol, decl)| {
dst_mod.current_items_mut().insert_glob_use_symbol(
engines,
symbol.clone(),
src.to_vec(),
decl,
)
});

Ok(())
}
Expand Down Expand Up @@ -338,20 +341,22 @@ impl Root {

for variant_decl in enum_decl.variants.iter() {
let variant_name = &variant_decl.name;
let decl = TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
enum_ref: enum_ref.clone(),
variant_name: variant_name.clone(),
variant_decl_span: variant_decl.span.clone(),
});

// import it this way.
let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?;
dst_mod.current_items_mut().use_glob_synonyms.insert(
variant_name.clone(),
(
self.module
.lookup_submodule_mut(handler, engines, dst)?
.current_items_mut()
.insert_glob_use_symbol(
engines,
variant_name.clone(),
src.to_vec(),
TyDecl::EnumVariantDecl(ty::EnumVariantDecl {
enum_ref: enum_ref.clone(),
variant_name: variant_name.clone(),
variant_decl_span: variant_decl.span.clone(),
}),
),
);
&decl,
);
}
} else {
return Err(handler.emit_err(CompileError::Internal(
Expand Down Expand Up @@ -396,8 +401,10 @@ impl Root {

// collect all declared and reexported symbols from the source module
let mut all_symbols_and_decls = vec![];
for (symbol, (_, decl)) in src_mod.current_items().use_glob_synonyms.iter() {
all_symbols_and_decls.push((symbol.clone(), decl.clone()));
for (symbol, decls) in src_mod.current_items().use_glob_synonyms.iter() {
decls
.iter()
.for_each(|(_, decl)| all_symbols_and_decls.push((symbol.clone(), decl.clone())));
}
for (symbol, (_, _, decl)) in src_mod.current_items().use_item_synonyms.iter() {
all_symbols_and_decls.push((symbol.clone(), decl.clone()));
Expand Down Expand Up @@ -428,8 +435,14 @@ impl Root {
for (symbol, (_, mod_path, decl)) in use_item_synonyms {
symbols_paths_and_decls.push((symbol, get_path(mod_path), decl));
}
for (symbol, (mod_path, decl)) in use_glob_synonyms {
symbols_paths_and_decls.push((symbol, get_path(mod_path), decl));
for (symbol, decls) in use_glob_synonyms {
decls.iter().for_each(|(mod_path, decl)| {
symbols_paths_and_decls.push((
symbol.clone(),
get_path(mod_path.clone()),
decl.clone(),
))
});
}

let dst_mod = self.module.lookup_submodule_mut(handler, engines, dst)?;
Expand All @@ -441,16 +454,15 @@ impl Root {
let mut try_add = |symbol, path, decl: ty::TyDecl| {
dst_mod
.current_items_mut()
.use_glob_synonyms
.insert(symbol, (path, decl));
.insert_glob_use_symbol(engines, symbol, path, &decl);
};

for (symbol, decl) in all_symbols_and_decls {
try_add(symbol, src.to_vec(), decl);
try_add(symbol.clone(), src.to_vec(), decl);
}

for (symbol, path, decl) in symbols_paths_and_decls {
try_add(symbol.clone(), path, decl.clone());
try_add(symbol.clone(), path, decl);
}

Ok(())
Expand Down Expand Up @@ -813,8 +825,33 @@ impl Root {
return Ok(ResolvedDeclaration::Typed(decl.clone()));
}
// Check glob imports
if let Some((_, decl)) = module.current_items().use_glob_synonyms.get(symbol) {
return Ok(ResolvedDeclaration::Typed(decl.clone()));
if let Some(decls) = module.current_items().use_glob_synonyms.get(symbol) {
if decls.len() == 1 {
return Ok(ResolvedDeclaration::Typed(decls[0].1.clone()));
} else if decls.is_empty() {
return Err(handler.emit_err(CompileError::Internal(
"The name {symbol} was bound in a star import, but no corresponding module paths were found",
symbol.span(),
)));
} else {
// Symbol not found
return Err(handler.emit_err(CompileError::SymbolWithMultipleBindings {
jjcnn marked this conversation as resolved.
Show resolved Hide resolved
name: symbol.clone(),
path_1: decls[0]
.0
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.join("::"),
path_2: decls[1]
.0
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.join("::"),
span: symbol.span(),
}));
}
}
// Symbol not found
Err(handler.emit_err(CompileError::SymbolNotFound {
Expand Down
8 changes: 8 additions & 0 deletions sway-error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ pub enum CompileError {
DeclIsNotATypeAlias { actually: String, span: Span },
#[error("Could not find symbol \"{name}\" in this scope.")]
SymbolNotFound { name: Ident, span: Span },
#[error("Found multiple bindings for \"{name}\" in this scope: {path_1}::{name} and {path_2}::{name} are both valid.")]
SymbolWithMultipleBindings {
name: Ident,
path_1: String,
path_2: String,
span: Span,
},
#[error("Symbol \"{name}\" is private.")]
ImportPrivateSymbol { name: Ident, span: Span },
#[error("Module \"{name}\" is private.")]
Expand Down Expand Up @@ -1019,6 +1026,7 @@ impl Spanned for CompileError {
NotIndexable { span, .. } => span.clone(),
FieldAccessOnNonStruct { span, .. } => span.clone(),
SymbolNotFound { span, .. } => span.clone(),
SymbolWithMultipleBindings { span, .. } => span.clone(),
ImportPrivateSymbol { span, .. } => span.clone(),
ImportPrivateModule { span, .. } => span.clone(),
NoElseBranch { span, .. } => span.clone(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[[package]]
name = "core"
source = "path+from-root-95C7DDCD726BD0EA"

[[package]]
name = "import_star_name_clash"
source = "member"
dependencies = ["std"]

[[package]]
name = "std"
source = "path+from-root-95C7DDCD726BD0EA"
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_name_clash"
entry = "main.sw"

jjcnn marked this conversation as resolved.
Show resolved Hide resolved
[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
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": [],
"type": "()",
"typeId": 0,
"typeParameters": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"configurables": [],
"encoding": "1",
"functions": [
{
"attributes": null,
"inputs": [],
"name": "main",
"output": {
"name": "",
"type": 0,
"typeArguments": null
}
}
],
"loggedTypes": [],
"messagesTypes": [],
"types": [
{
"components": [],
"type": "()",
"typeId": 0,
"typeParameters": null
}
]
}
Loading
Loading