From bcdcabee75e5f0aad8811ce204f38e54174d26fd Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:15:58 +0300 Subject: [PATCH 01/10] 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 | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 669f045681b7d..8e5c15e88ca55 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -429,10 +429,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, @@ -445,7 +442,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { force, ignore_binding, ignore_import, - )? { + ) { + 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_results.is_empty() => { + return ControlFlow::Break(Err(determinacy)); + } + Err(determinacy) => Err(determinacy.into_value()), + }; + match res { Ok(binding) if sub_namespace_match(binding.macro_kinds(), macro_kind) => { // Below we report various ambiguity errors. // We do not need to report them if we are either in speculative resolution, @@ -504,8 +512,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { force: bool, ignore_binding: Option>, ignore_import: Option>, - ) -> ControlFlow, Determinacy>, Result, Determinacy>> - { + ) -> Result, ControlFlow> { let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); let ret = match scope { Scope::DeriveHelpers(expn_id) => { @@ -591,7 +598,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!(), @@ -680,7 +687,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, }; - ControlFlow::Continue(ret) + ret.map_err(ControlFlow::Continue) } fn maybe_push_ambiguity( From 75d90094e732948f4a45cb407f604239ae6a31ef Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:24:59 +0300 Subject: [PATCH 02/10] resolve: Split `Scope::Module` into two scopes for non-glob and glob bindings --- compiler/rustc_resolve/src/diagnostics.rs | 7 +- compiler/rustc_resolve/src/ident.rs | 71 +++++++++++++++---- compiler/rustc_resolve/src/lib.rs | 17 +++-- 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(+), 29 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 8f20b5fe5745f..716e1d5a2e7ff 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1206,9 +1206,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)| { @@ -2033,7 +2036,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) } - if let Scope::Module(module, _) = scope { + if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { if module == self.graph_root { help_msgs.push(format!( "use `crate::{ident}` to refer to this {thing} unambiguously" diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 8e5c15e88ca55..fcd058c5ae6d0 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -116,9 +116,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(); @@ -145,7 +145,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 => { @@ -186,20 +186,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()); @@ -560,7 +561,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) @@ -570,7 +571,51 @@ 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, + }, + ); + } + Ok(binding) + } + 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, @@ -717,12 +762,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else if res == derive_helper_compat && innermost_res != derive_helper { span_bug!(orig_ident.span, "impossible inner resolution kind") } else if matches!(innermost_scope, Scope::MacroRules(_)) - && matches!(scope, Scope::Module(..)) + && matches!(scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..)) && !self.disambiguate_macro_rules_vs_modularized(innermost_binding, binding) { Some(AmbiguityKind::MacroRulesVsModularized) } else if matches!(scope, Scope::MacroRules(_)) - && matches!(innermost_scope, Scope::Module(..)) + && matches!(innermost_scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..)) { // should be impossible because of visitation order in // visit_scopes @@ -1177,8 +1222,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, b1: binding, b2: shadowed_glob, - scope1: Scope::Module(self.empty_module, None), - scope2: Scope::Module(self.empty_module, None), + scope1: Scope::ModuleGlobs(self.empty_module, None), + scope2: Scope::ModuleGlobs(self.empty_module, None), warning: false, }); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7cbf2088efce5..fa53d57d175e8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -123,10 +123,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. @@ -1926,9 +1930,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); @@ -2061,8 +2068,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, b1: used_binding, b2, - scope1: Scope::Module(self.empty_module, None), - scope2: Scope::Module(self.empty_module, None), + scope1: Scope::ModuleGlobs(self.empty_module, None), + scope2: Scope::ModuleGlobs(self.empty_module, None), warning: warn_ambiguity, }; if !self.matches_previous_ambiguity_error(&ambiguity_error) { 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 bcfbb5619c5f2f9d69d8b60b820a2b1db266d4af Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 3 Dec 2025 22:46:42 +0300 Subject: [PATCH 03/10] 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 | 30 +++++++++-------------------- tests/ui/imports/macro-paths.rs | 1 - tests/ui/imports/macro-paths.stderr | 29 +++++----------------------- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index fcd058c5ae6d0..61e98c60a5c94 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -783,9 +783,18 @@ 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 + && let Scope::ModuleGlobs(m1, _) = scope + && let Scope::ModuleNonGlobs(m2, _) = innermost_scope + && m1 == m2 + { + // 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 }; + if let Some(kind) = ambiguity_error_kind { // Skip ambiguity errors for extern flag bindings "overridden" // by extern item bindings. @@ -1008,7 +1017,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, @@ -1070,7 +1078,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, @@ -1182,7 +1189,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, @@ -1210,24 +1216,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, - scope1: Scope::ModuleGlobs(self.empty_module, None), - scope2: Scope::ModuleGlobs(self.empty_module, None), - warning: false, - }); - } - 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 d7d49244ef9dafb7dff752b03561f369a9aab7e7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 4 Dec 2025 22:03:42 +0300 Subject: [PATCH 04/10] resolve: Introduce `ScopeSet::Module` for looking up a name in two scopes inside a module - non-glob and glob bindings. --- compiler/rustc_resolve/src/ident.rs | 187 ++++++++++++---------------- compiler/rustc_resolve/src/late.rs | 6 +- compiler/rustc_resolve/src/lib.rs | 2 + 3 files changed, 87 insertions(+), 108 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 61e98c60a5c94..82a87b8a9ed6f 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -103,20 +103,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), @@ -189,6 +192,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()); @@ -336,13 +340,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, ) @@ -395,12 +398,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)), }; @@ -468,6 +473,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, scope, @@ -562,21 +569,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, @@ -598,29 +611,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok(binding) } 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, @@ -642,11 +661,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok(binding) } 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() { @@ -680,13 +699,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, ) @@ -738,6 +756,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>, scope: Scope<'ra>, @@ -751,6 +771,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // FIXME: Use `scope` instead of `res` to detect built-in attrs and derive helpers, // it will exclude imports, make slightly more code legal, and will require lang approval. + 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); @@ -781,9 +802,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) && let Scope::ModuleGlobs(m1, _) = scope && let Scope::ModuleNonGlobs(m2, _) = innermost_scope && m1 == m2 @@ -887,18 +911,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), @@ -948,48 +969,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>, @@ -999,6 +978,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> { @@ -1156,28 +1137,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 dd80f5da508c3..290d21cad4b90 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -44,8 +44,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; @@ -1514,7 +1514,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 fa53d57d175e8..1d462f173f4d0 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -153,6 +153,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. From 0016a7174768082ea50187847e86b288ec28281a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Dec 2025 16:31:04 +0300 Subject: [PATCH 05/10] 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 +----- .../ambiguous-glob-vs-expanded-extern.rs | 4 +- .../ambiguous-glob-vs-expanded-extern.stderr | 53 ------------------- 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 +------- 10 files changed, 11 insertions(+), 147 deletions(-) delete mode 100644 tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 82a87b8a9ed6f..512b35310539b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -813,7 +813,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && m1 == m2 { // 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 4e0f3db59821f..a46ed5d8e69ef 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -389,20 +389,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/ambiguous-glob-vs-expanded-extern.rs b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs index de632119ecba0..0277da46f7505 100644 --- a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs +++ b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.rs @@ -1,6 +1,6 @@ +//@ check-pass //@ aux-crate: glob_vs_expanded=glob-vs-expanded.rs fn main() { - glob_vs_expanded::mac!(); //~ ERROR `mac` is ambiguous - //~| WARN this was previously accepted + glob_vs_expanded::mac!(); // OK } diff --git a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr b/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr deleted file mode 100644 index 4a9a6c99819b0..0000000000000 --- a/tests/ui/imports/ambiguous-glob-vs-expanded-extern.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: `mac` is ambiguous - --> $DIR/ambiguous-glob-vs-expanded-extern.rs:4:23 - | -LL | glob_vs_expanded::mac!(); - | ^^^ ambiguous name - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #114095 - = 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: `mac` could refer to the macro defined here - --> $DIR/auxiliary/glob-vs-expanded.rs:9:13 - | -LL | () => { pub macro mac() {} } - | ^^^^^^^^^^^^^ -LL | } -LL | define_mac!(); - | ------------- in this macro invocation -note: `mac` could also refer to the macro defined here - --> $DIR/auxiliary/glob-vs-expanded.rs:5:9 - | -LL | pub use inner::*; - | ^^^^^ - = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the macro `define_mac` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -Future incompatibility report: Future breakage diagnostic: -error: `mac` is ambiguous - --> $DIR/ambiguous-glob-vs-expanded-extern.rs:4:23 - | -LL | glob_vs_expanded::mac!(); - | ^^^ ambiguous name - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #114095 - = 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: `mac` could refer to the macro defined here - --> $DIR/auxiliary/glob-vs-expanded.rs:9:13 - | -LL | () => { pub macro mac() {} } - | ^^^^^^^^^^^^^ -LL | } -LL | define_mac!(); - | ------------- in this macro invocation -note: `mac` could also refer to the macro defined here - --> $DIR/auxiliary/glob-vs-expanded.rs:5:9 - | -LL | pub use inner::*; - | ^^^^^ - = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the macro `define_mac` (in Nightly builds, run with -Z macro-backtrace for more info) - 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 78c61beb4816a9f103429da9a5cf49eeb6610ec9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 5 Dec 2025 17:47:02 +0300 Subject: [PATCH 06/10] 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 512b35310539b..f400ae8f64394 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1249,6 +1249,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 2e123aef706ac12e1277b90c933055d0b4b79d9b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 2 Jan 2026 14:29:23 +0300 Subject: [PATCH 07/10] resolve: Avoid additional ambiguities from splitting modules into two scopes --- compiler/rustc_resolve/src/ident.rs | 15 ++++++++++++- tests/ui/imports/overwritten-glob-ambig.rs | 26 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/ui/imports/overwritten-glob-ambig.rs diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f400ae8f64394..c9f560d2a6fa5 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -828,8 +828,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && innermost_results[1..].iter().any(|(b, s)| { matches!(s, Scope::ExternPreludeItems) && *b != innermost_binding }); + // Skip ambiguity errors for nonglob module bindings "overridden" + // by glob module bindings in the same module. + // FIXME: Remove with lang team approval. + let issue_149681_hack = match scope { + Scope::ModuleGlobs(m1, _) + if innermost_results[1..] + .iter() + .any(|(_, s)| matches!(*s, Scope::ModuleNonGlobs(m2, _) if m1 == m2)) => + { + true + } + _ => false, + }; - if issue_145575_hack { + if issue_145575_hack || issue_149681_hack { self.issue_145575_hack_applied = true; } else { self.ambiguity_errors.push(AmbiguityError { diff --git a/tests/ui/imports/overwritten-glob-ambig.rs b/tests/ui/imports/overwritten-glob-ambig.rs new file mode 100644 index 0000000000000..a5918568c62a4 --- /dev/null +++ b/tests/ui/imports/overwritten-glob-ambig.rs @@ -0,0 +1,26 @@ +// Test for a regression introduced by splitting module scope into two scopes +// (similar to issue #145575). + +//@ check-pass +//@ edition: 2018.. + +#[macro_use] +mod one { + // Macro that is in a different module, but still in scope due to `macro_use` + macro_rules! mac { () => {} } + pub(crate) use mac; +} + +mod other { + macro_rules! mac { () => {} } + pub(crate) use mac; +} + +// Single import of the same in the current module. +use one::mac; +// Glob import of a different macro in the current module (should be an ambiguity). +use other::*; + +fn main() { + mac!(); // OK for now, the ambiguity is not reported +} From 3f3db936514dca81c1e6ca6d004da9921450d981 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 2 Jan 2026 15:40:21 +0300 Subject: [PATCH 08/10] metadata: Stop keeping `AmbiguityKind` in name bindings It can only be `GlobVsGlob` now. --- compiler/rustc_middle/src/metadata.rs | 7 ----- .../rustc_resolve/src/build_reduced_graph.rs | 22 ++++++--------- compiler/rustc_resolve/src/imports.rs | 28 ++++++------------- compiler/rustc_resolve/src/lib.rs | 10 +++---- 4 files changed, 22 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs index 2b0be9865799d..b7848bc261d8d 100644 --- a/compiler/rustc_middle/src/metadata.rs +++ b/compiler/rustc_middle/src/metadata.rs @@ -45,16 +45,9 @@ pub struct ModChild { pub reexport_chain: SmallVec<[Reexport; 2]>, } -#[derive(Debug, TyEncodable, TyDecodable, HashStable)] -pub enum AmbigModChildKind { - GlobVsGlob, - GlobVsExpanded, -} - /// Same as `ModChild`, however, it includes ambiguity error. #[derive(Debug, TyEncodable, TyDecodable, HashStable)] pub struct AmbigModChild { pub main: ModChild, pub second: ModChild, - pub kind: AmbigModChildKind, } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index b9c945a440f88..241c13663ae5d 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -22,7 +22,7 @@ use rustc_hir::def::{self, *}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::LoadedMacro; -use rustc_middle::metadata::{AmbigModChildKind, ModChild, Reexport}; +use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::ty::{Feed, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; @@ -36,9 +36,9 @@ use crate::imports::{ImportData, ImportKind}; use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::ref_mut::CmCell; use crate::{ - AmbiguityKind, BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, - ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, - ResolutionError, Resolver, Segment, Used, VisResolutionError, errors, + BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot, + NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, ResolutionError, + Resolver, Segment, Used, VisResolutionError, errors, }; type Res = def::Res; @@ -82,7 +82,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { vis: Visibility, span: Span, expansion: LocalExpnId, - ambiguity: Option<(NameBinding<'ra>, AmbiguityKind)>, + ambiguity: Option>, ) { let binding = self.arenas.alloc_name_binding(NameBindingData { kind: NameBindingKind::Res(res), @@ -254,7 +254,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &child.main, parent_scope, children.len() + i, - Some((&child.second, child.kind)), + Some(&child.second), ) } } @@ -265,7 +265,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { child: &ModChild, parent_scope: ParentScope<'ra>, child_index: usize, - ambig_child: Option<(&ModChild, AmbigModChildKind)>, + ambig_child: Option<&ModChild>, ) { let parent = parent_scope.module; let child_span = |this: &Self, reexport_chain: &[Reexport], res: def::Res<_>| { @@ -280,15 +280,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let span = child_span(self, reexport_chain, res); let res = res.expect_non_local(); let expansion = parent_scope.expansion; - let ambig = ambig_child.map(|(ambig_child, ambig_kind)| { + let ambig = ambig_child.map(|ambig_child| { let ModChild { ident: _, res, vis, ref reexport_chain } = *ambig_child; let span = child_span(self, reexport_chain, res); let res = res.expect_non_local(); - let ambig_kind = match ambig_kind { - AmbigModChildKind::GlobVsGlob => AmbiguityKind::GlobVsGlob, - AmbigModChildKind::GlobVsExpanded => AmbiguityKind::GlobVsExpanded, - }; - (self.arenas.new_res_binding(res, vis, span, expansion), ambig_kind) + self.arenas.new_res_binding(res, vis, span, expansion) }); // Record primary definitions. diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a46ed5d8e69ef..558f769eda191 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -9,7 +9,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir::def::{self, DefKind, PartialRes}; use rustc_hir::def_id::{DefId, LocalDefIdMap}; -use rustc_middle::metadata::{AmbigModChild, AmbigModChildKind, ModChild, Reexport}; +use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::Visibility; use rustc_session::lint::BuiltinLintDiag; @@ -32,10 +32,9 @@ use crate::errors::{ }; use crate::ref_mut::CmCell; use crate::{ - AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion, - Module, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, - PathResult, PerNS, ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string, - names_to_string, + AmbiguityError, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion, Module, + ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, + PerNS, ResolutionError, Resolver, ScopeSet, Segment, Used, module_to_string, names_to_string, }; type Res = def::Res; @@ -373,7 +372,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { resolution.glob_binding = Some(glob_binding); } else if res != old_glob_binding.res() { resolution.glob_binding = Some(this.new_ambiguity_binding( - AmbiguityKind::GlobVsGlob, old_glob_binding, glob_binding, warn_ambiguity, @@ -394,7 +392,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { assert!(old_glob_binding.is_glob_import()); if glob_binding.res() != old_glob_binding.res() { resolution.glob_binding = Some(this.new_ambiguity_binding( - AmbiguityKind::GlobVsGlob, old_glob_binding, glob_binding, false, @@ -424,12 +421,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn new_ambiguity_binding( &self, - ambiguity_kind: AmbiguityKind, primary_binding: NameBinding<'ra>, secondary_binding: NameBinding<'ra>, warn_ambiguity: bool, ) -> NameBinding<'ra> { - let ambiguity = Some((secondary_binding, ambiguity_kind)); + let ambiguity = Some(secondary_binding); let data = NameBindingData { ambiguity, warn_ambiguity, ..*primary_binding }; self.arenas.alloc_name_binding(data) } @@ -645,7 +641,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Some(binding) = resolution.best_binding() else { continue }; if let NameBindingKind::Import { import, .. } = binding.kind - && let Some((amb_binding, _)) = binding.ambiguity + && let Some(amb_binding) = binding.ambiguity && binding.res() != Res::Err && exported_ambiguities.contains(&binding) { @@ -1553,9 +1549,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { vis: binding.vis, reexport_chain, }; - if let Some((ambig_binding1, ambig_binding2, ambig_kind)) = - binding.descent_to_ambiguity() - { + if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { let main = child(ambig_binding1.reexport_chain(this)); let second = ModChild { ident: ident.0, @@ -1563,13 +1557,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { vis: ambig_binding2.vis, reexport_chain: ambig_binding2.reexport_chain(this), }; - let kind = match ambig_kind { - AmbiguityKind::GlobVsGlob => AmbigModChildKind::GlobVsGlob, - AmbiguityKind::GlobVsExpanded => AmbigModChildKind::GlobVsExpanded, - _ => unreachable!(), - }; - - ambig_children.push(AmbigModChild { main, second, kind }) + ambig_children.push(AmbigModChild { main, second }) } else { children.push(child(binding.reexport_chain(this))); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1d462f173f4d0..fbd16072c2c04 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -807,7 +807,7 @@ impl<'ra> fmt::Debug for Module<'ra> { #[derive(Clone, Copy, Debug)] struct NameBindingData<'ra> { kind: NameBindingKind<'ra>, - ambiguity: Option<(NameBinding<'ra>, AmbiguityKind)>, + ambiguity: Option>, /// Produce a warning instead of an error when reporting ambiguities inside this binding. /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required. warn_ambiguity: bool, @@ -937,9 +937,9 @@ impl<'ra> NameBindingData<'ra> { fn descent_to_ambiguity( self: NameBinding<'ra>, - ) -> Option<(NameBinding<'ra>, NameBinding<'ra>, AmbiguityKind)> { + ) -> Option<(NameBinding<'ra>, NameBinding<'ra>)> { match self.ambiguity { - Some((ambig_binding, ambig_kind)) => Some((self, ambig_binding, ambig_kind)), + Some(ambig_binding) => Some((self, ambig_binding)), None => match self.kind { NameBindingKind::Import { binding, .. } => binding.descent_to_ambiguity(), _ => None, @@ -2064,9 +2064,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { used: Used, warn_ambiguity: bool, ) { - if let Some((b2, kind)) = used_binding.ambiguity { + if let Some(b2) = used_binding.ambiguity { let ambiguity_error = AmbiguityError { - kind, + kind: AmbiguityKind::GlobVsGlob, ident, b1: used_binding, b2, From fa584faca5387010e37bc8172529be3b3776e0cc Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Sun, 4 Jan 2026 06:56:48 -0800 Subject: [PATCH 09/10] Update test and verify that tgt_(un)register_lib have the right type --- compiler/rustc_codegen_llvm/src/base.rs | 9 +++- .../src/builder/gpu_offload.rs | 5 ++- compiler/rustc_session/src/config.rs | 2 + compiler/rustc_session/src/options.rs | 10 ++++- tests/codegen-llvm/gpu_offload/gpu_host.rs | 42 +++++++++---------- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 388118f9b4f17..d00e70638b45a 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -93,8 +93,13 @@ pub(crate) fn compile_codegen_unit( // They are necessary for correct offload execution. We do this here to simplify the // `offload` intrinsic, avoiding the need for tracking whether it's the first // intrinsic call or not. - let has_host_offload = - cx.sess().opts.unstable_opts.offload.iter().any(|o| matches!(o, Offload::Host(_))); + let has_host_offload = cx + .sess() + .opts + .unstable_opts + .offload + .iter() + .any(|o| matches!(o, Offload::Host(_) | Offload::Test)); if has_host_offload && !cx.sess().target.is_like_gpu { cx.offload_globals.replace(Some(OffloadGlobals::declare(&cx))); } diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index fba92f996aa6b..b8eb4f0382167 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -49,8 +49,9 @@ impl<'ll> OffloadGlobals<'ll> { let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc"); cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false); - let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", mapper_fn_ty); - let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", mapper_fn_ty); + let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void()); + let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl); + let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl); let init_ty = cx.type_func(&[], cx.type_void()); let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index fe96dabf63302..8c492fcf8f15d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -196,6 +196,8 @@ pub enum Offload { Device, /// Second step in the offload pipeline, generates the host code to call kernels. Host(String), + /// Test is similar to Host, but allows testing without a device artifact. + Test, } /// The different settings that the `-Z autodiff` flag can have. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f11ad12fb9ddf..21fa3321a30ad 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -794,7 +794,8 @@ mod desc { pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`"; - pub(crate) const parse_offload: &str = "a comma separated list of settings: `Enable`"; + pub(crate) const parse_offload: &str = + "a comma separated list of settings: `Host=`, `Device`, `Test`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1471,6 +1472,13 @@ pub mod parse { } Offload::Device } + "Test" => { + if let Some(_) = arg { + // Test does not accept a value + return false; + } + Offload::Test + } _ => { // FIXME(ZuseZ4): print an error saying which value is not recognized return false; diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs index b4d17143720a7..dcbd65b144277 100644 --- a/tests/codegen-llvm/gpu_offload/gpu_host.rs +++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs @@ -1,15 +1,10 @@ -//@ compile-flags: -Zoffload=Enable -Zunstable-options -C opt-level=3 -Clto=fat +//@ compile-flags: -Zoffload=Test -Zunstable-options -C opt-level=3 -Clto=fat //@ no-prefer-dynamic -//@ needs-enzyme +//@ needs-offload // This test is verifying that we generate __tgt_target_data_*_mapper before and after a call to the // kernel_1. Better documentation to what each global or variable means is available in the gpu -// offlaod code, or the LLVM offload documentation. This code does not launch any GPU kernels yet, -// and will be rewritten once a proper offload frontend has landed. -// -// We currently only handle memory transfer for specific calls to functions named `kernel_{num}`, -// when inside of a function called main. This, too, is a temporary workaround for not having a -// frontend. +// offload code, or the LLVM offload documentation. #![feature(rustc_attrs)] #![feature(core_intrinsics)] @@ -22,6 +17,20 @@ fn main() { core::hint::black_box(&x); } +#[unsafe(no_mangle)] +#[inline(never)] +pub fn kernel_1(x: &mut [f32; 256]) { + core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], (x,)) +} + +#[unsafe(no_mangle)] +#[inline(never)] +pub fn _kernel_1(x: &mut [f32; 256]) { + for i in 0..256 { + x[i] = 21.0; + } +} + // CHECK: %struct.ident_t = type { i32, i32, i32, i32, ptr } // CHECK: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } // CHECK: %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } @@ -36,8 +45,9 @@ fn main() { // CHECK: @.offloading.entry_name._kernel_1 = internal unnamed_addr constant [10 x i8] c"_kernel_1\00", section ".llvm.rodata.offloading", align 1 // CHECK: @.offloading.entry._kernel_1 = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @._kernel_1.region_id, ptr @.offloading.entry_name._kernel_1, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8 -// CHECK: Function Attrs: nounwind // CHECK: declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) +// CHECK: declare void @__tgt_register_lib(ptr) local_unnamed_addr +// CHECK: declare void @__tgt_unregister_lib(ptr) local_unnamed_addr // CHECK: define{{( dso_local)?}} void @main() // CHECK-NEXT: start: @@ -94,17 +104,3 @@ fn main() { // CHECK-NEXT: call void @__tgt_unregister_lib(ptr nonnull %EmptyDesc) // CHECK-NEXT: ret void // CHECK-NEXT: } - -#[unsafe(no_mangle)] -#[inline(never)] -pub fn kernel_1(x: &mut [f32; 256]) { - core::intrinsics::offload(_kernel_1, [256, 1, 1], [32, 1, 1], (x,)) -} - -#[unsafe(no_mangle)] -#[inline(never)] -pub fn _kernel_1(x: &mut [f32; 256]) { - for i in 0..256 { - x[i] = 21.0; - } -} From 359f060175a99e73d576c08aa9f6140a13df0f4a Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Sun, 4 Jan 2026 17:06:39 +0000 Subject: [PATCH 10/10] relate.rs: tiny cleanup: eliminate temp vars --- compiler/rustc_type_ir/src/relate.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 4954ebc51cfce..9e14bf6b60ad5 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -155,11 +155,10 @@ impl Relate for ty::FnSig { let cx = relation.cx(); if a.c_variadic != b.c_variadic { - return Err(TypeError::VariadicMismatch({ - let a = a.c_variadic; - let b = b.c_variadic; - ExpectedFound::new(a, b) - })); + return Err(TypeError::VariadicMismatch(ExpectedFound::new( + a.c_variadic, + b.c_variadic, + ))); } if a.safety != b.safety {