diff --git a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs index f35ee74514b..50422cd7fe8 100644 --- a/sway-core/src/semantic_analysis/namespace/lexical_scope.rs +++ b/sway-core/src/semantic_analysis/namespace/lexical_scope.rs @@ -43,6 +43,7 @@ pub(super) type SymbolUniqueMap = HashMap; type SourceIdent = Ident; +pub(super) type PreludeSynonyms = HashMap; pub(super) type GlobSynonyms = HashMap>; pub(super) type ItemSynonyms = HashMap< @@ -94,6 +95,13 @@ pub struct Items { pub(crate) symbols_unique_while_collecting_unifications: Arc>, pub(crate) implemented_traits: TraitMap, + /// Contains symbols imported from the standard library preludes. + /// + /// The import are asserted to never have a name clash. The imported names are always private + /// rather than public (`use ...` rather than `pub use ...`), since the bindings cannot be + /// accessed from outside the importing module. The preludes are asserted to not contain name + /// clashes. + pub(crate) prelude_synonyms: PreludeSynonyms, /// Contains symbols imported using star imports (`use foo::*`.). /// /// When star importing from multiple modules the same name may be imported more than once. This @@ -206,6 +214,11 @@ impl Items { } } + // Check prelude imports + if let Some((decl_path, decl)) = self.prelude_synonyms.get(symbol) { + return Ok(Some((decl.clone(), decl_path.clone()))); + } + Ok(None) } diff --git a/sway-core/src/semantic_analysis/namespace/namespace.rs b/sway-core/src/semantic_analysis/namespace/namespace.rs index ab6ad42a89f..984e3105694 100644 --- a/sway-core/src/semantic_analysis/namespace/namespace.rs +++ b/sway-core/src/semantic_analysis/namespace/namespace.rs @@ -222,34 +222,31 @@ impl Namespace { } else if package_name == STD { // Import core::prelude::* assert!(self.root.exists_as_external(&core_string)); - self.root.star_import( + self.root.prelude_import( handler, engines, &[core_ident, prelude_ident], &self.current_mod_path, - Visibility::Private, )? } else { // Import core::prelude::* and std::prelude::* if self.root.exists_as_external(&core_string) { - self.root.star_import( + self.root.prelude_import( handler, engines, &[core_ident, prelude_ident.clone()], &self.current_mod_path, - Visibility::Private, )?; } let std_string = STD.to_string(); // Only import std::prelude::* if std exists as a dependency if self.root.exists_as_external(&std_string) { - self.root.star_import( + self.root.prelude_import( handler, engines, &[Ident::new_no_span(std_string), prelude_ident], &self.current_mod_path, - Visibility::Private, )? } } diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index c67bf74101b..d1522f2b2fd 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -351,6 +351,85 @@ impl Root { ////// IMPORT ////// + /// Given a path to a prelude in the standard library, create synonyms to every symbol in that prelude to the given + /// `dst` module. + /// + /// This is used when a new module is created in order to pupulate the module with implicit + /// imports from the standard library preludes. + /// + /// Paths are assumed to be absolute. + pub(super) fn prelude_import( + &mut self, + handler: &Handler, + engines: &Engines, + src: &ModulePath, + dst: &ModulePath, + ) -> Result<(), ErrorEmitted> { + let src_mod = self.require_module(handler, &src.to_vec())?; + + let mut imports = vec![]; + + // A prelude should not declare its own items + assert!(src_mod.root_items().symbols.is_empty()); + + // Collect those item-imported items that the source module reexports + let mut symbols = src_mod + .root_items() + .use_item_synonyms + .keys() + .clone() + .collect::>(); + symbols.sort(); + for symbol in symbols { + let (_, path, decl, src_visibility) = &src_mod.root_items().use_item_synonyms[symbol]; + // Preludes reexport all their imports + // EXCEPT: In our IR generation tests we compile core in a way that causes forc-pkg to + // insert a CONTRACT_ID declaration into core. This gets imported into core::prelude, + // but does not get reexported. Hence, in those particular tests we do in fact have a + // symbol in core::prelude that does not get reexported... + assert!( + matches!(src_visibility, Visibility::Public) || symbol.as_str() == "CONTRACT_ID" + ); + imports.push((symbol.clone(), decl.clone(), path.clone())) + } + + // Collect those glob-imported items that the source module reexports. There should be no + // name clashes in a prelude, so item reexports and glob reexports can be treated the same + // way. + let mut symbols = src_mod + .root_items() + .use_glob_synonyms + .keys() + .clone() + .collect::>(); + symbols.sort(); + for symbol in symbols { + let bindings = &src_mod.root_items().use_glob_synonyms[symbol]; + for (path, decl, src_visibility) in bindings.iter() { + // Preludes reexport all their imports + assert!(matches!(src_visibility, Visibility::Public)); + imports.push((symbol.clone(), decl.clone(), path.clone())) + } + } + + let implemented_traits = src_mod.root_items().implemented_traits.clone(); + let dst_mod = self.require_module_mut_in_current_package(handler, &dst.to_vec())?; + + dst_mod + .current_items_mut() + .implemented_traits + .extend(implemented_traits, engines); + + let dst_prelude_synonyms = &mut dst_mod.current_items_mut().prelude_synonyms; + imports.iter().for_each(|(symbol, decl, path)| { + // Preludes should not contain name clashes + assert!(!dst_prelude_synonyms.contains_key(symbol)); + dst_prelude_synonyms.insert(symbol.clone(), (path.clone(), decl.clone())); + }); + + Ok(()) + } + /// Given a path to a `src` module, create synonyms to every symbol in that module to the given /// `dst` module. /// @@ -523,6 +602,8 @@ impl Root { span: item.span(), })); } + } else if let Some((path, decl)) = src_items.prelude_synonyms.get(item) { + (decl.clone(), path.clone(), Visibility::Private) } else { // Symbol not found return Err(handler.emit_err(CompileError::SymbolNotFound {