From 1e002a5787f3c867a4244040be57b07fea64a186 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:15:58 +0300 Subject: [PATCH 1/7] resolve: Do not break from the scope visiting loop if we already found the innermost binding. Previously we could lose the already found binding and break with an error, if some blocking error was found in the shadowed scopes. Also, avoid some impossible state in the return type of `fn resolve_ident_in_scope`. --- compiler/rustc_resolve/src/ident.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f59b5a0aad9a7..150637e5889b0 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -443,10 +443,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { orig_ident.span.ctxt(), derive_fallback_lint_id, |this, scope, use_prelude, ctxt| { - // We can break with an error at this step, it means we cannot determine the - // resolution right now, but we must block and wait until we can instead of - // considering outer scopes. - match this.reborrow().resolve_ident_in_scope( + let res = match this.reborrow().resolve_ident_in_scope( orig_ident, ns, scope, @@ -461,7 +458,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import, &mut extern_prelude_item_binding, &mut extern_prelude_flag_binding, - )? { + ) { + Ok(binding) => Ok(binding), + // We can break with an error at this step, it means we cannot determine the + // resolution right now, but we must block and wait until we can, instead of + // considering outer scopes. Although there's no need to do that if we already + // have a better solution. + Err(ControlFlow::Break(determinacy)) if innermost_result.is_none() => { + return ControlFlow::Break(Err(determinacy)); + } + Err(determinacy) => Err(determinacy.into_value()), + }; + match res { Ok((binding, flags)) if sub_namespace_match(binding.macro_kinds(), macro_kind) => { @@ -528,10 +536,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, extern_prelude_item_binding: &mut Option>, extern_prelude_flag_binding: &mut Option>, - ) -> ControlFlow< - Result, Determinacy>, - Result<(NameBinding<'ra>, Flags), Determinacy>, - > { + ) -> Result<(NameBinding<'ra>, Flags), ControlFlow> { let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); let ret = match scope { Scope::DeriveHelpers(expn_id) => { @@ -624,7 +629,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), Err(ControlFlow::Break(Determinacy::Undetermined)) => { - return ControlFlow::Break(Err(Determinacy::determined(force))); + return Err(ControlFlow::Break(Determinacy::determined(force))); } // Privacy errors, do not happen during in scope resolution. Err(ControlFlow::Break(Determinacy::Determined)) => unreachable!(), @@ -719,7 +724,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, }; - ControlFlow::Continue(ret) + ret.map_err(ControlFlow::Continue) } fn maybe_push_ambiguity( From 38812a3dcbf1f685dd8abccaaccbea3e2c49a06c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:24:59 +0300 Subject: [PATCH 2/7] resolve: Split `Scope::Module` into two scopes for non-glob and glob bindings --- compiler/rustc_resolve/src/diagnostics.rs | 5 +- compiler/rustc_resolve/src/ident.rs | 70 ++++++++++++++++--- compiler/rustc_resolve/src/lib.rs | 13 +++- tests/ui/imports/issue-114682-1.rs | 1 + tests/ui/imports/issue-114682-1.stderr | 28 +++++++- .../local-modularized-tricky-fail-1.rs | 1 + .../local-modularized-tricky-fail-1.stderr | 32 ++++++++- tests/ui/imports/macro-paths.rs | 1 + tests/ui/imports/macro-paths.stderr | 28 ++++++-- 9 files changed, 157 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 33c111708e366..3d80efbcd4205 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1205,9 +1205,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } } - Scope::Module(module, _) => { + Scope::ModuleNonGlobs(module, _) => { this.add_module_candidates(module, suggestions, filter_fn, None); } + Scope::ModuleGlobs(..) => { + // Already handled in `ModuleNonGlobs`. + } Scope::MacroUsePrelude => { suggestions.extend(this.macro_use_prelude.iter().filter_map( |(name, binding)| { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 150637e5889b0..7aef752664508 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -127,9 +127,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)); let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude); let mut scope = match ns { - _ if module_and_extern_prelude => Scope::Module(module, None), + _ if module_and_extern_prelude => Scope::ModuleNonGlobs(module, None), _ if extern_prelude => Scope::ExternPreludeItems, - TypeNS | ValueNS => Scope::Module(module, None), + TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; let mut ctxt = ctxt.normalize_to_macros_2_0(); @@ -156,7 +156,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } true } - Scope::Module(..) => true, + Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, Scope::MacroUsePrelude => use_prelude || rust_2015, Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { @@ -197,20 +197,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Invocation(invoc_id) => { Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) } - MacroRulesScope::Empty => Scope::Module(module, None), + MacroRulesScope::Empty => Scope::ModuleNonGlobs(module, None), }, - Scope::Module(..) if module_and_extern_prelude => match ns { + Scope::ModuleNonGlobs(module, lint_id) => Scope::ModuleGlobs(module, lint_id), + Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns { TypeNS => { ctxt.adjust(ExpnId::root()); Scope::ExternPreludeItems } ValueNS | MacroNS => break, }, - Scope::Module(module, prev_lint_id) => { + Scope::ModuleGlobs(module, prev_lint_id) => { use_prelude = !module.no_implicit_prelude; match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) { Some((parent_module, lint_id)) => { - Scope::Module(parent_module, lint_id.or(prev_lint_id)) + Scope::ModuleNonGlobs(parent_module, lint_id.or(prev_lint_id)) } None => { ctxt.adjust(ExpnId::root()); @@ -584,7 +585,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), _ => Err(Determinacy::Determined), }, - Scope::Module(module, derive_fallback_lint_id) => { + Scope::ModuleNonGlobs(module, derive_fallback_lint_id) => { let (adjusted_parent_scope, adjusted_finalize) = if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { (parent_scope, finalize) @@ -594,7 +595,58 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.map(|f| Finalize { used: Used::Scope, ..f }), ) }; - let binding = self.reborrow().resolve_ident_in_module_unadjusted( + let binding = self.reborrow().resolve_ident_in_module_non_globs_unadjusted( + module, + ident, + ns, + adjusted_parent_scope, + Shadowing::Restricted, + adjusted_finalize, + ignore_binding, + ignore_import, + ); + match binding { + Ok(binding) => { + if let Some(lint_id) = derive_fallback_lint_id { + self.get_mut().lint_buffer.buffer_lint( + PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + lint_id, + orig_ident.span, + errors::ProcMacroDeriveResolutionFallback { + span: orig_ident.span, + ns_descr: ns.descr(), + ident, + }, + ); + } + let misc_flags = if module == self.graph_root { + Flags::MISC_SUGGEST_CRATE + } else if module.is_normal() { + Flags::MISC_SUGGEST_SELF + } else { + Flags::empty() + }; + Ok((binding, Flags::MODULE | misc_flags)) + } + Err(ControlFlow::Continue(determinacy)) => Err(determinacy), + Err(ControlFlow::Break(Determinacy::Undetermined)) => { + return Err(ControlFlow::Break(Determinacy::determined(force))); + } + // Privacy errors, do not happen during in scope resolution. + Err(ControlFlow::Break(Determinacy::Determined)) => unreachable!(), + } + } + Scope::ModuleGlobs(module, derive_fallback_lint_id) => { + let (adjusted_parent_scope, adjusted_finalize) = + if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { + (parent_scope, finalize) + } else { + ( + &ParentScope { module, ..*parent_scope }, + finalize.map(|f| Finalize { used: Used::Scope, ..f }), + ) + }; + let binding = self.reborrow().resolve_ident_in_module_globs_unadjusted( module, ident, ns, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ed47f3124f939..8799c84b73c11 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -122,10 +122,14 @@ enum Scope<'ra> { DeriveHelpersCompat, /// Textual `let`-like scopes introduced by `macro_rules!` items. MacroRules(MacroRulesScopeRef<'ra>), - /// Names declared in the given module. + /// Non-glob names declared in the given module. /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` /// lint if it should be reported. - Module(Module<'ra>, Option), + ModuleNonGlobs(Module<'ra>, Option), + /// Glob names declared in the given module. + /// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK` + /// lint if it should be reported. + ModuleGlobs(Module<'ra>, Option), /// Names introduced by `#[macro_use]` attributes on `extern crate` items. MacroUsePrelude, /// Built-in attributes. @@ -1898,9 +1902,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let scope_set = ScopeSet::All(TypeNS); self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |this, scope, _, _| { match scope { - Scope::Module(module, _) => { + Scope::ModuleNonGlobs(module, _) => { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); } + Scope::ModuleGlobs(..) => { + // Already handled in `ModuleNonGlobs` (but see #144993). + } Scope::StdLibPrelude => { if let Some(module) = this.prelude { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); diff --git a/tests/ui/imports/issue-114682-1.rs b/tests/ui/imports/issue-114682-1.rs index 88fe05e51444d..58b78508026d9 100644 --- a/tests/ui/imports/issue-114682-1.rs +++ b/tests/ui/imports/issue-114682-1.rs @@ -22,4 +22,5 @@ mac!(); fn main() { A!(); //~^ ERROR `A` is ambiguous + //~| ERROR `A` is ambiguous } diff --git a/tests/ui/imports/issue-114682-1.stderr b/tests/ui/imports/issue-114682-1.stderr index 85fb7f7919e4e..de8dc6cfb9ff5 100644 --- a/tests/ui/imports/issue-114682-1.stderr +++ b/tests/ui/imports/issue-114682-1.stderr @@ -23,6 +23,32 @@ LL | pub use m::*; = help: consider adding an explicit import of `A` to disambiguate = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error[E0659]: `A` is ambiguous + --> $DIR/issue-114682-1.rs:23:5 + | +LL | A!(); + | ^ ambiguous name + | + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `A` could refer to the macro defined here + --> $DIR/issue-114682-1.rs:7:9 + | +LL | / pub macro A() { +LL | | println!("non import") +LL | | } + | |_________^ +... +LL | mac!(); + | ------ in this macro invocation + = help: use `crate::A` to refer to this macro unambiguously +note: `A` could also refer to the macro imported here + --> $DIR/issue-114682-1.rs:19:9 + | +LL | pub use m::*; + | ^^^^ + = help: use `crate::A` to refer to this macro unambiguously + = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.rs b/tests/ui/imports/local-modularized-tricky-fail-1.rs index ce700ae0de9b1..bba26ee43a245 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.rs +++ b/tests/ui/imports/local-modularized-tricky-fail-1.rs @@ -27,6 +27,7 @@ mod inner1 { } exported!(); //~ ERROR `exported` is ambiguous + //~| ERROR `exported` is ambiguous mod inner2 { define_exported!(); diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.stderr b/tests/ui/imports/local-modularized-tricky-fail-1.stderr index 52a01e8bcdfe3..54d928f7c8121 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.stderr +++ b/tests/ui/imports/local-modularized-tricky-fail-1.stderr @@ -23,8 +23,34 @@ LL | use inner1::*; = help: consider adding an explicit import of `exported` to disambiguate = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0659]: `exported` is ambiguous + --> $DIR/local-modularized-tricky-fail-1.rs:29:1 + | +LL | exported!(); + | ^^^^^^^^ ambiguous name + | + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `exported` could refer to the macro defined here + --> $DIR/local-modularized-tricky-fail-1.rs:6:5 + | +LL | / macro_rules! exported { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_exported!(); + | ------------------ in this macro invocation + = help: use `crate::exported` to refer to this macro unambiguously +note: `exported` could also refer to the macro imported here + --> $DIR/local-modularized-tricky-fail-1.rs:23:5 + | +LL | use inner1::*; + | ^^^^^^^^^ + = help: use `crate::exported` to refer to this macro unambiguously + = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0659]: `panic` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:36:5 + --> $DIR/local-modularized-tricky-fail-1.rs:37:5 | LL | panic!(); | ^^^^^ ambiguous name @@ -45,7 +71,7 @@ LL | define_panic!(); = note: this error originates in the macro `define_panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `include` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:47:1 + --> $DIR/local-modularized-tricky-fail-1.rs:48:1 | LL | include!(); | ^^^^^^^ ambiguous name @@ -65,6 +91,6 @@ LL | define_include!(); = help: use `crate::include` to refer to this macro unambiguously = note: this error originates in the macro `define_include` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/imports/macro-paths.rs b/tests/ui/imports/macro-paths.rs index 916442a7c4eab..6fd426c34eff4 100644 --- a/tests/ui/imports/macro-paths.rs +++ b/tests/ui/imports/macro-paths.rs @@ -11,6 +11,7 @@ mod foo { fn f() { use foo::*; bar::m! { //~ ERROR ambiguous + //~| ERROR `bar` is ambiguous mod bar { pub use two_macros::m; } } } diff --git a/tests/ui/imports/macro-paths.stderr b/tests/ui/imports/macro-paths.stderr index 5f113ce2bee5d..5ba92072805e3 100644 --- a/tests/ui/imports/macro-paths.stderr +++ b/tests/ui/imports/macro-paths.stderr @@ -6,7 +6,7 @@ LL | bar::m! { | = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution note: `bar` could refer to the module defined here - --> $DIR/macro-paths.rs:14:9 + --> $DIR/macro-paths.rs:15:9 | LL | mod bar { pub use two_macros::m; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,20 +17,38 @@ LL | use foo::*; | ^^^^^^ = help: consider adding an explicit import of `bar` to disambiguate +error[E0659]: `bar` is ambiguous + --> $DIR/macro-paths.rs:13:5 + | +LL | bar::m! { + | ^^^ ambiguous name + | + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `bar` could refer to the module defined here + --> $DIR/macro-paths.rs:15:9 + | +LL | mod bar { pub use two_macros::m; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: `bar` could also refer to the module imported here + --> $DIR/macro-paths.rs:12:9 + | +LL | use foo::*; + | ^^^^^^ + error[E0659]: `baz` is ambiguous - --> $DIR/macro-paths.rs:23:5 + --> $DIR/macro-paths.rs:24:5 | LL | baz::m! { | ^^^ ambiguous name | = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution note: `baz` could refer to the module defined here - --> $DIR/macro-paths.rs:24:9 + --> $DIR/macro-paths.rs:25:9 | LL | mod baz { pub use two_macros::m; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `baz` could also refer to the module defined here - --> $DIR/macro-paths.rs:18:1 + --> $DIR/macro-paths.rs:19:1 | LL | / pub mod baz { LL | | pub use two_macros::m; @@ -38,6 +56,6 @@ LL | | } | |_^ = help: use `crate::baz` to refer to this module unambiguously -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0659`. From baf89338f62b461c984827196c70a1d3158d83d6 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:46:42 +0300 Subject: [PATCH 3/7] resolve: Migrate a special ambiguity for glob vs non-glob bindings in the same module to the usual ambiguity infra in `resolve_ident_in_scope_set` --- compiler/rustc_resolve/src/ident.rs | 31 ++++++++++------------------- tests/ui/imports/macro-paths.rs | 1 - tests/ui/imports/macro-paths.stderr | 29 +++++---------------------- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 7aef752664508..a9761eddece83 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -825,9 +825,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(AmbiguityKind::GlobVsOuter) } else if innermost_binding.may_appear_after(parent_scope.expansion, binding) { Some(AmbiguityKind::MoreExpandedVsOuter) + } else if innermost_binding.expansion != LocalExpnId::ROOT + && binding.is_glob_import() + && !innermost_binding.is_glob_import() + && self.binding_parent_modules.get(&innermost_binding) + == Some(&self.binding_parent_modules[&binding]) + { + // FIXME: this error is too conservative and technically unnecessary now when module + // scope is split into two scopes, remove it with lang team approval. + Some(AmbiguityKind::GlobVsExpanded) } else { None }; + // Skip ambiguity errors for extern flag bindings "overridden" // by extern item bindings. // FIXME: Remove with lang team approval. @@ -1057,7 +1067,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return self.get_mut().finalize_module_binding( ident, binding, - if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None }, parent_scope, module, finalize, @@ -1119,7 +1128,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return self.get_mut().finalize_module_binding( ident, binding, - if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None }, parent_scope, module, finalize, @@ -1231,7 +1239,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, ident: Ident, binding: Option>, - shadowed_glob: Option>, parent_scope: &ParentScope<'ra>, module: Module<'ra>, finalize: Finalize, @@ -1259,24 +1266,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - // Forbid expanded shadowing to avoid time travel. - if let Some(shadowed_glob) = shadowed_glob - && shadowing == Shadowing::Restricted - && finalize.stage == Stage::Early - && binding.expansion != LocalExpnId::ROOT - && binding.res() != shadowed_glob.res() - { - self.ambiguity_errors.push(AmbiguityError { - kind: AmbiguityKind::GlobVsExpanded, - ident, - b1: binding, - b2: shadowed_glob, - warning: false, - misc1: AmbiguityErrorMisc::None, - misc2: AmbiguityErrorMisc::None, - }); - } - if shadowing == Shadowing::Unrestricted && binding.expansion != LocalExpnId::ROOT && let NameBindingKind::Import { import, .. } = binding.kind diff --git a/tests/ui/imports/macro-paths.rs b/tests/ui/imports/macro-paths.rs index 6fd426c34eff4..916442a7c4eab 100644 --- a/tests/ui/imports/macro-paths.rs +++ b/tests/ui/imports/macro-paths.rs @@ -11,7 +11,6 @@ mod foo { fn f() { use foo::*; bar::m! { //~ ERROR ambiguous - //~| ERROR `bar` is ambiguous mod bar { pub use two_macros::m; } } } diff --git a/tests/ui/imports/macro-paths.stderr b/tests/ui/imports/macro-paths.stderr index 5ba92072805e3..56a40e908258b 100644 --- a/tests/ui/imports/macro-paths.stderr +++ b/tests/ui/imports/macro-paths.stderr @@ -1,22 +1,3 @@ -error[E0659]: `bar` is ambiguous - --> $DIR/macro-paths.rs:13:5 - | -LL | bar::m! { - | ^^^ ambiguous name - | - = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution -note: `bar` could refer to the module defined here - --> $DIR/macro-paths.rs:15:9 - | -LL | mod bar { pub use two_macros::m; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: `bar` could also refer to the module imported here - --> $DIR/macro-paths.rs:12:9 - | -LL | use foo::*; - | ^^^^^^ - = help: consider adding an explicit import of `bar` to disambiguate - error[E0659]: `bar` is ambiguous --> $DIR/macro-paths.rs:13:5 | @@ -25,7 +6,7 @@ LL | bar::m! { | = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution note: `bar` could refer to the module defined here - --> $DIR/macro-paths.rs:15:9 + --> $DIR/macro-paths.rs:14:9 | LL | mod bar { pub use two_macros::m; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,19 +17,19 @@ LL | use foo::*; | ^^^^^^ error[E0659]: `baz` is ambiguous - --> $DIR/macro-paths.rs:24:5 + --> $DIR/macro-paths.rs:23:5 | LL | baz::m! { | ^^^ ambiguous name | = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution note: `baz` could refer to the module defined here - --> $DIR/macro-paths.rs:25:9 + --> $DIR/macro-paths.rs:24:9 | LL | mod baz { pub use two_macros::m; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: `baz` could also refer to the module defined here - --> $DIR/macro-paths.rs:19:1 + --> $DIR/macro-paths.rs:18:1 | LL | / pub mod baz { LL | | pub use two_macros::m; @@ -56,6 +37,6 @@ LL | | } | |_^ = help: use `crate::baz` to refer to this module unambiguously -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0659`. From 0d65bf1bead13e5d5410a8a0b3bc0b3f9f2ee742 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 4 Dec 2025 22:03:42 +0300 Subject: [PATCH 4/7] resolve: Introduce `ScopeSet::Module` for looking up a name in two scopes insice a module - non-glob and glob bindings. --- compiler/rustc_resolve/src/ident.rs | 177 +++++++++------------- compiler/rustc_resolve/src/late.rs | 6 +- compiler/rustc_resolve/src/lib.rs | 2 + tests/ui/underscore-imports/shadow.rs | 1 + tests/ui/underscore-imports/shadow.stderr | 31 +++- 5 files changed, 107 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index a9761eddece83..df5609a4b1aa1 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -114,20 +114,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let rust_2015 = ctxt.edition().is_rust_2015(); let (ns, macro_kind) = match scope_set { - ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), + ScopeSet::All(ns) + | ScopeSet::Module(ns, _) + | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), ScopeSet::ExternPrelude => (TypeNS, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; let module = match scope_set { // Start with the specified module. - ScopeSet::ModuleAndExternPrelude(_, module) => module, + ScopeSet::Module(_, module) | ScopeSet::ModuleAndExternPrelude(_, module) => module, // Jump out of trait or enum modules, they do not act as scopes. _ => parent_scope.module.nearest_item_scope(), }; + let module_only = matches!(scope_set, ScopeSet::Module(..)); let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)); let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude); let mut scope = match ns { - _ if module_and_extern_prelude => Scope::ModuleNonGlobs(module, None), + _ if module_only || module_and_extern_prelude => Scope::ModuleNonGlobs(module, None), _ if extern_prelude => Scope::ExternPreludeItems, TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), @@ -200,6 +203,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MacroRulesScope::Empty => Scope::ModuleNonGlobs(module, None), }, Scope::ModuleNonGlobs(module, lint_id) => Scope::ModuleGlobs(module, lint_id), + Scope::ModuleGlobs(..) if module_only => break, Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns { TypeNS => { ctxt.adjust(ExpnId::root()); @@ -348,13 +352,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { diag_metadata, ))); } else if let RibKind::Block(Some(module)) = rib.kind - && let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted( - module, + && let Ok(binding) = self.cm().resolve_ident_in_scope_set( ident, - ns, + ScopeSet::Module(ns, module), parent_scope, - Shadowing::Unrestricted, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), + finalize.is_some(), ignore_binding, None, ) @@ -407,12 +410,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { assert!(force || finalize.is_none()); // `finalize` implies `force` // Make sure `self`, `super` etc produce an error when passed to here. - if orig_ident.is_path_segment_keyword() { + if orig_ident.is_path_segment_keyword() && !matches!(scope_set, ScopeSet::Module(..)) { return Err(Determinacy::Determined); } let (ns, macro_kind) = match scope_set { - ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), + ScopeSet::All(ns) + | ScopeSet::Module(ns, _) + | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None), ScopeSet::ExternPrelude => (TypeNS, None), ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)), }; @@ -586,21 +591,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => Err(Determinacy::Determined), }, Scope::ModuleNonGlobs(module, derive_fallback_lint_id) => { - let (adjusted_parent_scope, adjusted_finalize) = - if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { - (parent_scope, finalize) - } else { - ( - &ParentScope { module, ..*parent_scope }, - finalize.map(|f| Finalize { used: Used::Scope, ..f }), - ) - }; + let (adjusted_parent_scope, adjusted_finalize) = if matches!( + scope_set, + ScopeSet::Module(..) | ScopeSet::ModuleAndExternPrelude(..) + ) { + (parent_scope, finalize) + } else { + ( + &ParentScope { module, ..*parent_scope }, + finalize.map(|f| Finalize { used: Used::Scope, ..f }), + ) + }; let binding = self.reborrow().resolve_ident_in_module_non_globs_unadjusted( module, ident, ns, adjusted_parent_scope, - Shadowing::Restricted, + if matches!(scope_set, ScopeSet::Module(..)) { + Shadowing::Unrestricted + } else { + Shadowing::Restricted + }, adjusted_finalize, ignore_binding, ignore_import, @@ -629,29 +640,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok((binding, Flags::MODULE | misc_flags)) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(Determinacy::Undetermined)) => { - return Err(ControlFlow::Break(Determinacy::determined(force))); + Err(ControlFlow::Break(determinacy)) => { + return Err(ControlFlow::Break(Determinacy::determined( + determinacy == Determinacy::Determined || force, + ))); } - // Privacy errors, do not happen during in scope resolution. - Err(ControlFlow::Break(Determinacy::Determined)) => unreachable!(), } } Scope::ModuleGlobs(module, derive_fallback_lint_id) => { - let (adjusted_parent_scope, adjusted_finalize) = - if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { - (parent_scope, finalize) - } else { - ( - &ParentScope { module, ..*parent_scope }, - finalize.map(|f| Finalize { used: Used::Scope, ..f }), - ) - }; + let (adjusted_parent_scope, adjusted_finalize) = if matches!( + scope_set, + ScopeSet::Module(..) | ScopeSet::ModuleAndExternPrelude(..) + ) { + (parent_scope, finalize) + } else { + ( + &ParentScope { module, ..*parent_scope }, + finalize.map(|f| Finalize { used: Used::Scope, ..f }), + ) + }; let binding = self.reborrow().resolve_ident_in_module_globs_unadjusted( module, ident, ns, adjusted_parent_scope, - Shadowing::Restricted, + if matches!(scope_set, ScopeSet::Module(..)) { + Shadowing::Unrestricted + } else { + Shadowing::Restricted + }, adjusted_finalize, ignore_binding, ignore_import, @@ -680,11 +697,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok((binding, Flags::MODULE | misc_flags)) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(Determinacy::Undetermined)) => { - return Err(ControlFlow::Break(Determinacy::determined(force))); + Err(ControlFlow::Break(determinacy)) => { + return Err(ControlFlow::Break(Determinacy::determined( + determinacy == Determinacy::Determined || force, + ))); } - // Privacy errors, do not happen during in scope resolution. - Err(ControlFlow::Break(Determinacy::Determined)) => unreachable!(), } } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { @@ -724,13 +741,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); if let Some(prelude) = self.prelude - && let Ok(binding) = self.reborrow().resolve_ident_in_module_unadjusted( - prelude, + && let Ok(binding) = self.reborrow().resolve_ident_in_scope_set( ident, - ns, + ScopeSet::Module(ns, prelude), parent_scope, - Shadowing::Unrestricted, None, + false, ignore_binding, ignore_import, ) @@ -937,18 +953,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, Determinacy> { match module { - ModuleOrUniformRoot::Module(module) => self - .resolve_ident_in_module_unadjusted( - module, - ident, - ns, - parent_scope, - Shadowing::Unrestricted, - finalize, - ignore_binding, - ignore_import, - ) - .map_err(|determinacy| determinacy.into_value()), + ModuleOrUniformRoot::Module(module) => self.resolve_ident_in_scope_set( + ident, + ScopeSet::Module(ns, module), + parent_scope, + finalize, + finalize.is_some(), + ignore_binding, + ignore_import, + ), ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set( ident, ScopeSet::ModuleAndExternPrelude(ns, module), @@ -998,48 +1011,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - /// Attempts to resolve `ident` in namespace `ns` of `module`. - fn resolve_ident_in_module_unadjusted<'r>( - mut self: CmResolver<'r, 'ra, 'tcx>, - module: Module<'ra>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'ra>, - shadowing: Shadowing, - finalize: Option, - // This binding should be ignored during in-module resolution, so that we don't get - // "self-confirming" import resolutions during import validation and checking. - ignore_binding: Option>, - ignore_import: Option>, - ) -> Result, ControlFlow> { - let res = self.reborrow().resolve_ident_in_module_non_globs_unadjusted( - module, - ident, - ns, - parent_scope, - shadowing, - finalize, - ignore_binding, - ignore_import, - ); - - match res { - Ok(_) | Err(ControlFlow::Break(_)) => return res, - Err(ControlFlow::Continue(_)) => {} - } - - self.resolve_ident_in_module_globs_unadjusted( - module, - ident, - ns, - parent_scope, - shadowing, - finalize, - ignore_binding, - ignore_import, - ) - } - /// Attempts to resolve `ident` in namespace `ns` of non-glob bindings in `module`. fn resolve_ident_in_module_non_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, @@ -1049,6 +1020,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, shadowing: Shadowing, finalize: Option, + // This binding should be ignored during in-module resolution, so that we don't get + // "self-confirming" import resolutions during import validation and checking. ignore_binding: Option>, ignore_import: Option>, ) -> Result, ControlFlow> { @@ -1206,28 +1179,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => {} None => continue, }; - let result = self.reborrow().resolve_ident_in_module_unadjusted( - module, + let result = self.reborrow().resolve_ident_in_scope_set( ident, - ns, + ScopeSet::Module(ns, module), adjusted_parent_scope, - Shadowing::Unrestricted, None, + false, ignore_binding, ignore_import, ); match result { - Err(ControlFlow::Break(Determined) | ControlFlow::Continue(Determined)) => continue, + Err(Determined) => continue, Ok(binding) if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) => { continue; } - Ok(_) - | Err(ControlFlow::Break(Undetermined) | ControlFlow::Continue(Undetermined)) => { - return Err(ControlFlow::Continue(Undetermined)); - } + Ok(_) | Err(Undetermined) => return Err(ControlFlow::Continue(Undetermined)), } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f1a03d5a06109..2d76503660968 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -41,8 +41,8 @@ use tracing::{debug, instrument, trace}; use crate::{ BindingError, BindingKey, Finalize, LexicalScopeBinding, Module, ModuleOrUniformRoot, - NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, TyCtxt, UseError, - Used, errors, path_names_to_string, rustdoc, + NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Stage, TyCtxt, + UseError, Used, errors, path_names_to_string, rustdoc, }; mod diagnostics; @@ -1498,7 +1498,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { opt_ns, &self.parent_scope, Some(source), - finalize, + finalize.map(|finalize| Finalize { stage: Stage::Late, ..finalize }), Some(&self.ribs), None, None, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8799c84b73c11..06a4a2bea905f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -152,6 +152,8 @@ enum Scope<'ra> { enum ScopeSet<'ra> { /// All scopes with the given namespace. All(Namespace), + /// Two scopes inside a module, for non-glob and glob bindings. + Module(Namespace, Module<'ra>), /// A module, then extern prelude (used for mixed 2015-2018 mode in macros). ModuleAndExternPrelude(Namespace, Module<'ra>), /// Just two extern prelude scopes. diff --git a/tests/ui/underscore-imports/shadow.rs b/tests/ui/underscore-imports/shadow.rs index 325f2001b9ede..8a3162b1c72eb 100644 --- a/tests/ui/underscore-imports/shadow.rs +++ b/tests/ui/underscore-imports/shadow.rs @@ -14,6 +14,7 @@ mod b { mod c { use crate::b::Shadow as _; // Only imports the struct + //~^ ERROR `Shadow` is ambiguous fn f(x: &()) { x.deref(); //~ ERROR no method named `deref` found diff --git a/tests/ui/underscore-imports/shadow.stderr b/tests/ui/underscore-imports/shadow.stderr index 4743d14dfb9e6..883b10b926f84 100644 --- a/tests/ui/underscore-imports/shadow.stderr +++ b/tests/ui/underscore-imports/shadow.stderr @@ -1,5 +1,29 @@ +error[E0659]: `Shadow` is ambiguous + --> $DIR/shadow.rs:16:19 + | +LL | use crate::b::Shadow as _; // Only imports the struct + | ^^^^^^ ambiguous name + | + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `Shadow` could refer to the struct defined here + --> $DIR/shadow.rs:10:25 + | +LL | ($i:ident) => { pub struct $i; } + | ^^^^^^^^^^^^^^ +LL | } +LL | m!(Shadow); + | ---------- in this macro invocation + = help: use `self::Shadow` to refer to this struct unambiguously +note: `Shadow` could also refer to the trait imported here + --> $DIR/shadow.rs:8:13 + | +LL | pub use crate::a::*; + | ^^^^^^^^^^^ + = help: use `self::Shadow` to refer to this trait unambiguously + = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0599]: no method named `deref` found for reference `&()` in the current scope - --> $DIR/shadow.rs:19:11 + --> $DIR/shadow.rs:20:11 | LL | x.deref(); | ^^^^^ method not found in `&()` @@ -10,6 +34,7 @@ help: trait `Deref` which provides `deref` is implemented but not in scope; perh LL + use std::ops::Deref; | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0599, E0659. +For more information about an error, try `rustc --explain E0599`. From 47e96a9f92616f5577af82be263346e74cec54f5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Dec 2025 16:31:04 +0300 Subject: [PATCH 5/7] resolve: Migrate one more special ambiguity for glob vs non-glob bindings in the same module to the usual ambiguity infra in `resolve_ident_in_scope_set` --- compiler/rustc_resolve/src/ident.rs | 3 +- compiler/rustc_resolve/src/imports.rs | 15 +-------- tests/ui/imports/issue-114682-1.rs | 1 - tests/ui/imports/issue-114682-1.stderr | 27 +--------------- .../local-modularized-tricky-fail-1.rs | 1 - .../local-modularized-tricky-fail-1.stderr | 31 ++----------------- tests/ui/imports/macros.rs | 2 +- tests/ui/imports/macros.stderr | 21 +------------ 8 files changed, 9 insertions(+), 92 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index df5609a4b1aa1..d6703fb28d5ea 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -848,7 +848,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { == Some(&self.binding_parent_modules[&binding]) { // FIXME: this error is too conservative and technically unnecessary now when module - // scope is split into two scopes, remove it with lang team approval. + // scope is split into two scopes, at least when not resolving in `ScopeSet::Module`, + // remove it with lang team approval. Some(AmbiguityKind::GlobVsExpanded) } else { None diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index f844e7b9cc12f..e503f492cbfe2 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -390,20 +390,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (old_glob @ true, false) | (old_glob @ false, true) => { let (glob_binding, non_glob_binding) = if old_glob { (old_binding, binding) } else { (binding, old_binding) }; - if ns == MacroNS - && non_glob_binding.expansion != LocalExpnId::ROOT - && glob_binding.res() != non_glob_binding.res() - { - resolution.non_glob_binding = Some(this.new_ambiguity_binding( - AmbiguityKind::GlobVsExpanded, - non_glob_binding, - glob_binding, - false, - )); - } else { - resolution.non_glob_binding = Some(non_glob_binding); - } - + resolution.non_glob_binding = Some(non_glob_binding); if let Some(old_glob_binding) = resolution.glob_binding { assert!(old_glob_binding.is_glob_import()); if glob_binding.res() != old_glob_binding.res() { diff --git a/tests/ui/imports/issue-114682-1.rs b/tests/ui/imports/issue-114682-1.rs index 58b78508026d9..88fe05e51444d 100644 --- a/tests/ui/imports/issue-114682-1.rs +++ b/tests/ui/imports/issue-114682-1.rs @@ -22,5 +22,4 @@ mac!(); fn main() { A!(); //~^ ERROR `A` is ambiguous - //~| ERROR `A` is ambiguous } diff --git a/tests/ui/imports/issue-114682-1.stderr b/tests/ui/imports/issue-114682-1.stderr index de8dc6cfb9ff5..fd2776f50ad77 100644 --- a/tests/ui/imports/issue-114682-1.stderr +++ b/tests/ui/imports/issue-114682-1.stderr @@ -1,28 +1,3 @@ -error[E0659]: `A` is ambiguous - --> $DIR/issue-114682-1.rs:23:5 - | -LL | A!(); - | ^ ambiguous name - | - = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution -note: `A` could refer to the macro defined here - --> $DIR/issue-114682-1.rs:7:9 - | -LL | / pub macro A() { -LL | | println!("non import") -LL | | } - | |_________^ -... -LL | mac!(); - | ------ in this macro invocation -note: `A` could also refer to the macro imported here - --> $DIR/issue-114682-1.rs:19:9 - | -LL | pub use m::*; - | ^^^^ - = help: consider adding an explicit import of `A` to disambiguate - = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0659]: `A` is ambiguous --> $DIR/issue-114682-1.rs:23:5 | @@ -49,6 +24,6 @@ LL | pub use m::*; = help: use `crate::A` to refer to this macro unambiguously = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.rs b/tests/ui/imports/local-modularized-tricky-fail-1.rs index bba26ee43a245..ce700ae0de9b1 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.rs +++ b/tests/ui/imports/local-modularized-tricky-fail-1.rs @@ -27,7 +27,6 @@ mod inner1 { } exported!(); //~ ERROR `exported` is ambiguous - //~| ERROR `exported` is ambiguous mod inner2 { define_exported!(); diff --git a/tests/ui/imports/local-modularized-tricky-fail-1.stderr b/tests/ui/imports/local-modularized-tricky-fail-1.stderr index 54d928f7c8121..b5b3be5953f93 100644 --- a/tests/ui/imports/local-modularized-tricky-fail-1.stderr +++ b/tests/ui/imports/local-modularized-tricky-fail-1.stderr @@ -1,28 +1,3 @@ -error[E0659]: `exported` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:29:1 - | -LL | exported!(); - | ^^^^^^^^ ambiguous name - | - = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution -note: `exported` could refer to the macro defined here - --> $DIR/local-modularized-tricky-fail-1.rs:6:5 - | -LL | / macro_rules! exported { -LL | | () => () -LL | | } - | |_____^ -... -LL | define_exported!(); - | ------------------ in this macro invocation -note: `exported` could also refer to the macro imported here - --> $DIR/local-modularized-tricky-fail-1.rs:23:5 - | -LL | use inner1::*; - | ^^^^^^^^^ - = help: consider adding an explicit import of `exported` to disambiguate - = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0659]: `exported` is ambiguous --> $DIR/local-modularized-tricky-fail-1.rs:29:1 | @@ -50,7 +25,7 @@ LL | use inner1::*; = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `panic` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:37:5 + --> $DIR/local-modularized-tricky-fail-1.rs:36:5 | LL | panic!(); | ^^^^^ ambiguous name @@ -71,7 +46,7 @@ LL | define_panic!(); = note: this error originates in the macro `define_panic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0659]: `include` is ambiguous - --> $DIR/local-modularized-tricky-fail-1.rs:48:1 + --> $DIR/local-modularized-tricky-fail-1.rs:47:1 | LL | include!(); | ^^^^^^^ ambiguous name @@ -91,6 +66,6 @@ LL | define_include!(); = help: use `crate::include` to refer to this macro unambiguously = note: this error originates in the macro `define_include` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/imports/macros.rs b/tests/ui/imports/macros.rs index cf67e08c87a30..e1db42abd28f3 100644 --- a/tests/ui/imports/macros.rs +++ b/tests/ui/imports/macros.rs @@ -13,7 +13,7 @@ mod m1 { mod m2 { use two_macros::*; - m! { //~ ERROR ambiguous + m! { use crate::foo::m; } } diff --git a/tests/ui/imports/macros.stderr b/tests/ui/imports/macros.stderr index 25a678c6b3751..08ebfa5b81516 100644 --- a/tests/ui/imports/macros.stderr +++ b/tests/ui/imports/macros.stderr @@ -1,22 +1,3 @@ -error[E0659]: `m` is ambiguous - --> $DIR/macros.rs:16:5 - | -LL | m! { - | ^ ambiguous name - | - = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution -note: `m` could refer to the macro imported here - --> $DIR/macros.rs:17:13 - | -LL | use crate::foo::m; - | ^^^^^^^^^^^^^ -note: `m` could also refer to the macro imported here - --> $DIR/macros.rs:15:9 - | -LL | use two_macros::*; - | ^^^^^^^^^^^^^ - = help: consider adding an explicit import of `m` to disambiguate - error[E0659]: `m` is ambiguous --> $DIR/macros.rs:29:9 | @@ -36,6 +17,6 @@ LL | use two_macros::m; | ^^^^^^^^^^^^^ = help: use `self::m` to refer to this macro unambiguously -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0659`. From 37dc384cdf7e1896e32b49b4aa239332d8e05c55 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Dec 2025 17:47:02 +0300 Subject: [PATCH 6/7] resolve: Patch up an inconsistent resolution ICE revealed by the previous commit --- compiler/rustc_resolve/src/ident.rs | 8 ++++++++ tests/ui/imports/macros.rs | 2 +- tests/ui/imports/macros.stderr | 22 +++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index d6703fb28d5ea..76faa4b98aebc 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1291,6 +1291,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, ) -> bool { for single_import in &resolution.single_imports { + if let Some(binding) = resolution.non_glob_binding + && let NameBindingKind::Import { import, .. } = binding.kind + && import == *single_import + { + // Single import has already defined the name and we are aware of it, + // no need to block the globs. + continue; + } if ignore_import == Some(*single_import) { continue; } diff --git a/tests/ui/imports/macros.rs b/tests/ui/imports/macros.rs index e1db42abd28f3..121f1f7ae043e 100644 --- a/tests/ui/imports/macros.rs +++ b/tests/ui/imports/macros.rs @@ -13,7 +13,7 @@ mod m1 { mod m2 { use two_macros::*; - m! { + m! { //~ ERROR `m` is ambiguous use crate::foo::m; } } diff --git a/tests/ui/imports/macros.stderr b/tests/ui/imports/macros.stderr index 08ebfa5b81516..9c081cc7af8b1 100644 --- a/tests/ui/imports/macros.stderr +++ b/tests/ui/imports/macros.stderr @@ -1,3 +1,23 @@ +error[E0659]: `m` is ambiguous + --> $DIR/macros.rs:16:5 + | +LL | m! { + | ^ ambiguous name + | + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `m` could refer to the macro imported here + --> $DIR/macros.rs:17:13 + | +LL | use crate::foo::m; + | ^^^^^^^^^^^^^ + = help: use `self::m` to refer to this macro unambiguously +note: `m` could also refer to the macro imported here + --> $DIR/macros.rs:15:9 + | +LL | use two_macros::*; + | ^^^^^^^^^^^^^ + = help: use `self::m` to refer to this macro unambiguously + error[E0659]: `m` is ambiguous --> $DIR/macros.rs:29:9 | @@ -17,6 +37,6 @@ LL | use two_macros::m; | ^^^^^^^^^^^^^ = help: use `self::m` to refer to this macro unambiguously -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0659`. From 1b2b0daf97d52a0f23fd4d4e8195011dcd9dfd19 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Dec 2025 17:55:27 +0300 Subject: [PATCH 7/7] Match the old logic closer --- compiler/rustc_resolve/src/ident.rs | 10 +++++++- tests/ui/underscore-imports/shadow.rs | 1 - tests/ui/underscore-imports/shadow.stderr | 31 +++-------------------- 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 76faa4b98aebc..84637cfe213ea 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -491,6 +491,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Found another solution, if the first one was "weak", report an error. if this.get_mut().maybe_push_ambiguity( orig_ident, + ns, + scope_set, parent_scope, binding, innermost_binding, @@ -798,6 +800,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn maybe_push_ambiguity( &mut self, orig_ident: Ident, + ns: Namespace, + scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, binding: NameBinding<'ra>, innermost_binding: NameBinding<'ra>, @@ -811,6 +815,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return false; } + let module_only = matches!(scope_set, ScopeSet::Module(..)); let is_builtin = |res| matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..))); let derive_helper = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); let derive_helper_compat = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat); @@ -839,9 +844,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) } else if innermost_binding.is_glob_import() { Some(AmbiguityKind::GlobVsOuter) - } else if innermost_binding.may_appear_after(parent_scope.expansion, binding) { + } else if !module_only + && innermost_binding.may_appear_after(parent_scope.expansion, binding) + { Some(AmbiguityKind::MoreExpandedVsOuter) } else if innermost_binding.expansion != LocalExpnId::ROOT + && (!module_only || ns == MacroNS) && binding.is_glob_import() && !innermost_binding.is_glob_import() && self.binding_parent_modules.get(&innermost_binding) diff --git a/tests/ui/underscore-imports/shadow.rs b/tests/ui/underscore-imports/shadow.rs index 8a3162b1c72eb..325f2001b9ede 100644 --- a/tests/ui/underscore-imports/shadow.rs +++ b/tests/ui/underscore-imports/shadow.rs @@ -14,7 +14,6 @@ mod b { mod c { use crate::b::Shadow as _; // Only imports the struct - //~^ ERROR `Shadow` is ambiguous fn f(x: &()) { x.deref(); //~ ERROR no method named `deref` found diff --git a/tests/ui/underscore-imports/shadow.stderr b/tests/ui/underscore-imports/shadow.stderr index 883b10b926f84..4743d14dfb9e6 100644 --- a/tests/ui/underscore-imports/shadow.stderr +++ b/tests/ui/underscore-imports/shadow.stderr @@ -1,29 +1,5 @@ -error[E0659]: `Shadow` is ambiguous - --> $DIR/shadow.rs:16:19 - | -LL | use crate::b::Shadow as _; // Only imports the struct - | ^^^^^^ ambiguous name - | - = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution -note: `Shadow` could refer to the struct defined here - --> $DIR/shadow.rs:10:25 - | -LL | ($i:ident) => { pub struct $i; } - | ^^^^^^^^^^^^^^ -LL | } -LL | m!(Shadow); - | ---------- in this macro invocation - = help: use `self::Shadow` to refer to this struct unambiguously -note: `Shadow` could also refer to the trait imported here - --> $DIR/shadow.rs:8:13 - | -LL | pub use crate::a::*; - | ^^^^^^^^^^^ - = help: use `self::Shadow` to refer to this trait unambiguously - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0599]: no method named `deref` found for reference `&()` in the current scope - --> $DIR/shadow.rs:20:11 + --> $DIR/shadow.rs:19:11 | LL | x.deref(); | ^^^^^ method not found in `&()` @@ -34,7 +10,6 @@ help: trait `Deref` which provides `deref` is implemented but not in scope; perh LL + use std::ops::Deref; | -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0599, E0659. -For more information about an error, try `rustc --explain E0599`. +For more information about this error, try `rustc --explain E0599`.