diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bd0296751a535..1a47eddc7cbff 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -4,10 +4,10 @@ use crate::diagnostics::Suggestion; use crate::Determinacy::{self, *}; use crate::Namespace::{self, MacroNS, TypeNS}; use crate::{module_to_string, names_to_string}; +use crate::{AllowResolveBlocks, BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; -use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment}; use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak}; -use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding}; +use crate::{ModuleData, NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding}; use rustc_ast::unwrap_or; use rustc_ast::NodeId; @@ -777,13 +777,15 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // For better failure detection, pretend that the import will // not define any names while resolving its module path. let orig_vis = import.vis.replace(ty::Visibility::Invisible); - let path_res = self.r.resolve_path( + let path_res = self.r.resolve_path_with_ribs( &import.module_path, None, &import.parent_scope, false, import.span, import.crate_lint(), + None, + AllowResolveBlocks::Yes, ); import.vis.set(orig_vis); @@ -818,7 +820,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // For better failure detection, pretend that the import will // not define any names while resolving its module path. let orig_vis = import.vis.replace(ty::Visibility::Invisible); - let binding = this.resolve_ident_in_module( + let mut binding = this.resolve_ident_in_module( module, source, ns, @@ -826,8 +828,29 @@ impl<'a, 'b> ImportResolver<'a, 'b> { false, import.span, ); - import.vis.set(orig_vis); + let mut module = module; + while binding.is_err() { + match module { + ModuleOrUniformRoot::Module(m @ ModuleData { parent: Some(p), .. }) + if m.is_block() => + { + // item not found in opaque block module; try inside module containing the block + module = ModuleOrUniformRoot::Module(*p); + binding = this.resolve_ident_in_module( + module, + source, + ns, + &import.parent_scope, + false, + import.span, + ); + } + _ => break, + } + } + + import.vis.set(orig_vis); source_bindings[ns].set(binding); } else { return; @@ -878,13 +901,15 @@ impl<'a, 'b> ImportResolver<'a, 'b> { _ => None, }; let prev_ambiguity_errors_len = self.r.ambiguity_errors.len(); - let path_res = self.r.resolve_path( + let path_res = self.r.resolve_path_with_ribs( &import.module_path, None, &import.parent_scope, true, import.span, import.crate_lint(), + None, + AllowResolveBlocks::Yes, ); let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len; if let Some(orig_unusable_binding) = orig_unusable_binding { @@ -1010,7 +1035,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let orig_unusable_binding = mem::replace(&mut this.unusable_binding, target_bindings[ns].get()); let orig_last_import_segment = mem::replace(&mut this.last_import_segment, true); - let binding = this.resolve_ident_in_module( + let mut binding = this.resolve_ident_in_module( module, ident, ns, @@ -1018,6 +1043,28 @@ impl<'a, 'b> ImportResolver<'a, 'b> { true, import.span, ); + + let mut module = module; + while binding.is_err() { + match module { + ModuleOrUniformRoot::Module(m @ ModuleData { parent: Some(p), .. }) + if m.is_block() => + { + // item not found in opaque block module; try inside module containing the block + module = ModuleOrUniformRoot::Module(*p); + binding = this.resolve_ident_in_module( + module, + ident, + ns, + &import.parent_scope, + false, + import.span, + ); + } + _ => break, + } + } + this.last_import_segment = orig_last_import_segment; this.unusable_binding = orig_unusable_binding; import.vis.set(orig_vis); @@ -1323,7 +1370,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } fn resolve_glob_import(&mut self, import: &'b Import<'b>) { - let module = match import.imported_module.get().unwrap() { + let mut module = match import.imported_module.get().unwrap() { ModuleOrUniformRoot::Module(module) => module, _ => { self.r.session.span_err(import.span, "cannot glob-import all possible crates"); @@ -1346,7 +1393,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. - let bindings = self + let mut bindings = self .r .resolutions(module) .borrow() @@ -1355,6 +1402,53 @@ impl<'a, 'b> ImportResolver<'a, 'b> { resolution.borrow().binding().map(|binding| (*key, binding)) }) .collect::>(); + + if module.is_block() { + // Glob imports should see "through" an opaque module. + // Prefer items in the opaque module to items in the parent. + + let mut imported_items = FxHashSet::default(); + + while module.is_block() && module.parent.is_some() { + // import these bindings + for (mut key, binding) in bindings { + let scope = + match key.ident.span.reverse_glob_adjust(module.expansion, import.span) { + Some(Some(def)) => self.r.macro_def_scope(def), + Some(None) => import.parent_scope.module, + None => continue, + }; + if self.r.is_accessible_from(binding.vis, scope) { + let imported_binding = self.r.import(binding, import); + let _ = + self.r.try_define(import.parent_scope.module, key, imported_binding); + imported_items.insert(key); + } + } + + // This was an opaque module; repeat with parent module. + module = module.parent.unwrap(); + + // Add to module's glob_importers + module.glob_importers.borrow_mut().push(import); + + // Ensure that `resolutions` isn't borrowed during `try_define`, + // since it might get updated via a glob cycle. + bindings = self + .r + .resolutions(module) + .borrow() + .iter() + .filter_map(|(key, resolution)| { + if imported_items.contains(key) { + return None; + } + resolution.borrow().binding().map(|binding| (*key, binding)) + }) + .collect::>(); + } + } + for (mut key, binding) in bindings { let scope = match key.ident.span.reverse_glob_adjust(module.expansion, import.span) { Some(Some(def)) => self.r.macro_def_scope(def), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6f24d7e941351..04802a30c0ed2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -747,6 +747,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { path_span, crate_lint, Some(&self.ribs), + crate::AllowResolveBlocks::No, ) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index e63fd4ce6357d..f89d3db133476 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -12,6 +12,7 @@ #![feature(box_patterns)] #![feature(bool_to_option)] #![feature(control_flow_enum)] +#![feature(bindings_after_at)] #![feature(crate_visibility_modifier)] #![feature(format_args_capture)] #![feature(nll)] @@ -597,6 +598,10 @@ impl<'a> ModuleData<'a> { matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _)) } + fn is_block(&self) -> bool { + matches!(self.kind, ModuleKind::Block(_)) + } + fn nearest_item_scope(&'a self) -> Module<'a> { match self.kind { ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => { @@ -839,6 +844,13 @@ enum BuiltinMacroState { AlreadySeen(Span), } +/// Whether resolve_path_with_ribs may resolve to anonymous block modules +#[derive(Copy, Clone, Debug)] +enum AllowResolveBlocks { + No, + Yes, +} + /// The main resolver class. /// /// This is the visitor that walks the whole crate. @@ -2177,6 +2189,7 @@ impl<'a> Resolver<'a> { path_span, crate_lint, None, + AllowResolveBlocks::No, ) } @@ -2189,6 +2202,7 @@ impl<'a> Resolver<'a> { path_span: Span, crate_lint: CrateLint, ribs: Option<&PerNS>>>, + resolve_opaque: AllowResolveBlocks, ) -> PathResult<'a> { let mut module = None; let mut allow_super = true; @@ -2231,9 +2245,11 @@ impl<'a> Resolver<'a> { }; if let Some(self_module) = self_module { if let Some(parent) = self_module.parent { - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent), - )); + let resolved = match (resolve_opaque, &parent.kind) { + (AllowResolveBlocks::Yes, ModuleKind::Block(_)) => parent, + _ => self.resolve_self(&mut ctxt, parent), + }; + module = Some(ModuleOrUniformRoot::Module(resolved)); continue; } } diff --git a/src/test/rustdoc/mod-in-doctest.rs b/src/test/rustdoc/mod-in-doctest.rs new file mode 100644 index 0000000000000..09573a3a4e828 --- /dev/null +++ b/src/test/rustdoc/mod-in-doctest.rs @@ -0,0 +1,13 @@ +// compile-flags:--test +// edition:2018 + +//! ``` +//! fn foo() {} +//! +//! mod bar { +//! use super::foo; +//! fn bar() { +//! foo() +//! } +//! } +//! ``` diff --git a/src/test/ui/resolve/module-in-block-build-pass.rs b/src/test/ui/resolve/module-in-block-build-pass.rs new file mode 100644 index 0000000000000..c7d5c16d9328e --- /dev/null +++ b/src/test/ui/resolve/module-in-block-build-pass.rs @@ -0,0 +1,98 @@ +// build-pass + +const CONST: &str = "OUTER"; +fn bar() -> &'static str { "outer" } + +fn module_in_function_use_super() { + mod inner { + use super::{bar, CONST}; + fn call_bar() { + bar(); + } + + fn get_const() -> &'static str { + CONST + } + } +} + +fn module_in_function_resolve_super() { + mod inner { + fn call_bar() { + super::bar(); + } + + fn get_const() -> &'static str { + super::CONST + } + } +} + + +fn module_in_function_use_super_glob() { + mod inner { + use super::*; + fn call_bar() { + bar(); + } + + fn get_const() -> &'static str { + CONST + } + } +} + +fn module_in_block_use_super() { + { + mod inner { + use super::{bar, CONST}; + fn call_bar() { + bar(); + } + + fn get_const() -> &'static str { + CONST + } + } + } +} + +fn module_in_block_resolve_super() { + { + mod inner { + fn call_bar() { + super::bar(); + } + + fn get_const() -> &'static str { + super::CONST + } + } + } +} + + +fn module_in_block_use_super_glob() { + { + mod inner { + use super::*; + fn call_bar() { + bar(); + } + + fn get_const() -> &'static str { + CONST + } + } + } +} + +fn main() { + module_in_function_use_super(); + module_in_function_resolve_super(); + module_in_function_use_super_glob(); + + module_in_block_use_super(); + module_in_block_resolve_super(); + module_in_block_use_super_glob(); +} diff --git a/src/test/ui/resolve/module-in-block-compile-fail.rs b/src/test/ui/resolve/module-in-block-compile-fail.rs new file mode 100644 index 0000000000000..537a4d860ccc8 --- /dev/null +++ b/src/test/ui/resolve/module-in-block-compile-fail.rs @@ -0,0 +1,12 @@ +fn module_in_function_cannot_access_variables() { + let x: i32 = 5; + + mod inner { + use super::x; //~ ERROR unresolved import `super::x` + fn get_x() -> i32 { + x + } + } +} + +fn main() { } diff --git a/src/test/ui/resolve/module-in-block-compile-fail.stderr b/src/test/ui/resolve/module-in-block-compile-fail.stderr new file mode 100644 index 0000000000000..f2fd1e0990322 --- /dev/null +++ b/src/test/ui/resolve/module-in-block-compile-fail.stderr @@ -0,0 +1,9 @@ +error[E0432]: unresolved import `super::x` + --> $DIR/module-in-block-compile-fail.rs:5:13 + | +LL | use super::x; + | ^^^^^^^^ no `x` in `` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/src/test/ui/resolve/module-in-block-run-pass.rs b/src/test/ui/resolve/module-in-block-run-pass.rs new file mode 100644 index 0000000000000..e61c2e86bcfc6 --- /dev/null +++ b/src/test/ui/resolve/module-in-block-run-pass.rs @@ -0,0 +1,134 @@ +// run-pass +// compile-flags:--test + +// These two items are marked as allow(dead_code) because none of the +// test code should ever resolve to them. +#[allow(dead_code)] +const CONST: &str = "OUTER"; +#[allow(dead_code)] +fn bar() -> &'static str { "outer" } + +#[test] +fn module_in_function_prefer_inner() { + + const CONST: &str = "INNER"; + fn bar() -> &'static str { "inner" } + + mod inner { + use super::{bar, CONST}; + pub fn call_bar() -> &'static str { + bar() + } + + pub fn get_const() -> &'static str { + CONST + } + } + + assert_eq!(inner::call_bar(), "inner"); + assert_eq!(inner::get_const(), "INNER") +} + +#[test] +fn module_in_function_prefer_inner_glob() { + + const CONST: &str = "INNER"; + fn bar() -> &'static str { "inner" } + + mod inner { + use super::*; + pub fn call_bar() -> &'static str { + bar() + } + + pub fn get_const() -> &'static str { + CONST + } + } + + assert_eq!(inner::call_bar(), "inner"); + assert_eq!(inner::get_const(), "INNER"); +} + +#[test] +fn module_in_block_prefer_inner() { + + const CONST: &str = "INNER"; + + // anonymous block + { + fn bar() -> &'static str { "inner_block" } + + mod inner { + use super::{CONST, bar}; + pub fn call_bar() -> &'static str { + bar() + } + + pub fn get_const() -> &'static str { + CONST + } + } + + assert_eq!(inner::call_bar(), "inner_block"); + assert_eq!(inner::get_const(), "INNER"); + } +} + + +#[test] +fn module_in_block_prefer_inner_glob() { + const CONST: &str = "INNER"; + + // anonymous block + { + fn bar() -> &'static str { "inner_block" } + + mod inner { + use super::*; + pub fn call_bar() -> &'static str { + bar() + } + + pub fn get_const() -> &'static str { + CONST + } + } + + assert_eq!(inner::call_bar(), "inner_block"); + assert_eq!(inner::get_const(), "INNER"); + } +} + +#[test] +fn module_in_block_does_not_use_variables() { + #[allow(unused_variables)] + let bar = || "inner_block"; + + + // anonymous block + mod inner { + use super::bar; + pub fn call_bar() -> &'static str { + bar() + } + } + + assert_eq!(inner::call_bar(), "outer"); +} + +#[test] +fn module_in_block_does_not_use_variables_glob() { + #[allow(unused_variables)] + let bar = || "inner_block"; + + // anonymous block + mod inner { + use super::*; + pub fn call_bar() -> &'static str { + bar() + } + } + + assert_eq!(inner::call_bar(), "outer"); +}