Skip to content

Commit 2ab3eba

Browse files
committed
Auto merge of #54201 - eddyb:reflexive-disambiguation, r=petrochenkov
rustc_resolve: don't treat uniform_paths canaries as ambiguities unless they resolve to distinct Def's. In particular, this allows this pattern that @cramertj mentioned in #53130 (comment): ```rust use log::{debug, log}; fn main() { use log::{debug, log}; debug!(...); } ``` The canaries for the inner `use log::...;`, *in the macro namespace*, see the `log` macro imported at the module scope, and the (same) `log` macro, imported in the block scope inside `main`. Previously, these two possible (macro namspace) `log` resolutions would be considered ambiguous (from a forwards-compat standpoint, where we might make imports aware of block scopes). With this PR, such a case is allowed *if and only if* all the possible resolutions refer to the same definition (more specifically, because the *same* `log` macro is being imported twice). This condition subsumes previous (weaker) checks like #54005 and the second commit of #54011. Only the last commit is the main change, the other two are cleanups. r? @petrochenkov cc @Centril @joshtriplett
2 parents 052d24e + 514c6b6 commit 2ab3eba

File tree

3 files changed

+89
-73
lines changed

3 files changed

+89
-73
lines changed

src/librustc_resolve/resolve_imports.rs

+73-73
Original file line numberDiff line numberDiff line change
@@ -630,15 +630,16 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
630630
self.finalize_resolutions_in(module);
631631
}
632632

633-
#[derive(Default)]
634-
struct UniformPathsCanaryResult<'a> {
633+
struct UniformPathsCanaryResults<'a> {
634+
name: Name,
635635
module_scope: Option<&'a NameBinding<'a>>,
636636
block_scopes: Vec<&'a NameBinding<'a>>,
637637
}
638+
638639
// Collect all tripped `uniform_paths` canaries separately.
639640
let mut uniform_paths_canaries: BTreeMap<
640-
(Span, NodeId),
641-
(Name, PerNS<UniformPathsCanaryResult>),
641+
(Span, NodeId, Namespace),
642+
UniformPathsCanaryResults,
642643
> = BTreeMap::new();
643644

644645
let mut errors = false;
@@ -665,21 +666,25 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
665666
import.module_path.len() > 0 &&
666667
import.module_path[0].name == keywords::SelfValue.name();
667668

668-
let (prev_name, canary_results) =
669-
uniform_paths_canaries.entry((import.span, import.id))
670-
.or_insert((name, PerNS::default()));
671-
672-
// All the canaries with the same `id` should have the same `name`.
673-
assert_eq!(*prev_name, name);
674-
675669
self.per_ns(|_, ns| {
676670
if let Some(result) = result[ns].get().ok() {
671+
let canary_results =
672+
uniform_paths_canaries.entry((import.span, import.id, ns))
673+
.or_insert(UniformPathsCanaryResults {
674+
name,
675+
module_scope: None,
676+
block_scopes: vec![],
677+
});
678+
679+
// All the canaries with the same `id` should have the same `name`.
680+
assert_eq!(canary_results.name, name);
681+
677682
if has_explicit_self {
678683
// There should only be one `self::x` (module-scoped) canary.
679-
assert!(canary_results[ns].module_scope.is_none());
680-
canary_results[ns].module_scope = Some(result);
684+
assert!(canary_results.module_scope.is_none());
685+
canary_results.module_scope = Some(result);
681686
} else {
682-
canary_results[ns].block_scopes.push(result);
687+
canary_results.block_scopes.push(result);
683688
}
684689
}
685690
});
@@ -720,77 +725,72 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
720725
}
721726

722727
let uniform_paths_feature = self.session.features_untracked().uniform_paths;
723-
for ((span, _), (name, results)) in uniform_paths_canaries {
724-
self.per_ns(|this, ns| {
725-
let external_crate = if ns == TypeNS && this.extern_prelude.contains(&name) {
726-
let crate_id =
727-
this.crate_loader.process_path_extern(name, span);
728-
Some(DefId { krate: crate_id, index: CRATE_DEF_INDEX })
729-
} else {
730-
None
731-
};
732-
let result_filter = |result: &&NameBinding| {
733-
// Ignore canaries that resolve to an import of the same crate.
734-
// That is, we allow `use crate_name; use crate_name::foo;`.
735-
if let Some(def_id) = external_crate {
736-
if let Some(module) = result.module() {
737-
if module.normal_ancestor_id == def_id {
738-
return false;
739-
}
740-
}
741-
}
728+
for ((span, _, ns), results) in uniform_paths_canaries {
729+
let name = results.name;
730+
let external_crate = if ns == TypeNS && self.extern_prelude.contains(&name) {
731+
let crate_id =
732+
self.crate_loader.process_path_extern(name, span);
733+
Some(Def::Mod(DefId { krate: crate_id, index: CRATE_DEF_INDEX }))
734+
} else {
735+
None
736+
};
742737

743-
true
744-
};
745-
let module_scope = results[ns].module_scope.filter(result_filter);
746-
let block_scopes = || {
747-
results[ns].block_scopes.iter().cloned().filter(result_filter)
748-
};
738+
// Currently imports can't resolve in non-module scopes,
739+
// we only have canaries in them for future-proofing.
740+
if external_crate.is_none() && results.module_scope.is_none() {
741+
return;
742+
}
743+
744+
{
745+
let mut all_results = external_crate.into_iter().chain(
746+
results.module_scope.iter()
747+
.chain(&results.block_scopes)
748+
.map(|binding| binding.def())
749+
);
750+
let first = all_results.next().unwrap();
749751

750-
// An ambiguity requires more than one possible resolution.
752+
// An ambiguity requires more than one *distinct* possible resolution.
751753
let possible_resultions =
752-
(external_crate.is_some() as usize) +
753-
(module_scope.is_some() as usize) +
754-
(block_scopes().next().is_some() as usize);
754+
1 + all_results.filter(|&def| def != first).count();
755755
if possible_resultions <= 1 {
756756
return;
757757
}
758+
}
758759

759-
errors = true;
760+
errors = true;
760761

761-
let msg = format!("`{}` import is ambiguous", name);
762-
let mut err = this.session.struct_span_err(span, &msg);
763-
let mut suggestion_choices = String::new();
764-
if external_crate.is_some() {
765-
write!(suggestion_choices, "`::{}`", name);
766-
err.span_label(span,
767-
format!("can refer to external crate `::{}`", name));
768-
}
769-
if let Some(result) = module_scope {
770-
if !suggestion_choices.is_empty() {
771-
suggestion_choices.push_str(" or ");
772-
}
773-
write!(suggestion_choices, "`self::{}`", name);
774-
if uniform_paths_feature {
775-
err.span_label(result.span,
776-
format!("can refer to `self::{}`", name));
777-
} else {
778-
err.span_label(result.span,
779-
format!("may refer to `self::{}` in the future", name));
780-
}
781-
}
782-
for result in block_scopes() {
783-
err.span_label(result.span,
784-
format!("shadowed by block-scoped `{}`", name));
762+
let msg = format!("`{}` import is ambiguous", name);
763+
let mut err = self.session.struct_span_err(span, &msg);
764+
let mut suggestion_choices = String::new();
765+
if external_crate.is_some() {
766+
write!(suggestion_choices, "`::{}`", name);
767+
err.span_label(span,
768+
format!("can refer to external crate `::{}`", name));
769+
}
770+
if let Some(result) = results.module_scope {
771+
if !suggestion_choices.is_empty() {
772+
suggestion_choices.push_str(" or ");
785773
}
786-
err.help(&format!("write {} explicitly instead", suggestion_choices));
774+
write!(suggestion_choices, "`self::{}`", name);
787775
if uniform_paths_feature {
788-
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
776+
err.span_label(result.span,
777+
format!("can refer to `self::{}`", name));
789778
} else {
790-
err.note("in the future, `#![feature(uniform_paths)]` may become the default");
779+
err.span_label(result.span,
780+
format!("may refer to `self::{}` in the future", name));
791781
}
792-
err.emit();
793-
});
782+
}
783+
for result in results.block_scopes {
784+
err.span_label(result.span,
785+
format!("shadowed by block-scoped `{}`", name));
786+
}
787+
err.help(&format!("write {} explicitly instead", suggestion_choices));
788+
if uniform_paths_feature {
789+
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
790+
} else {
791+
err.note("in the future, `#![feature(uniform_paths)]` may become the default");
792+
}
793+
err.emit();
794794
}
795795

796796
if !error_vec.is_empty() {

src/test/ui/run-pass/uniform-paths/basic-nested.rs

+8
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,12 @@ fn main() {
5959
bar::io::stdout();
6060
bar::std();
6161
bar::std!();
62+
63+
{
64+
// Test that having `io` in a module scope and a non-module
65+
// scope is allowed, when both resolve to the same definition.
66+
use std::io;
67+
use io::stdout;
68+
stdout();
69+
}
6270
}

src/test/ui/run-pass/uniform-paths/basic.rs

+8
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,12 @@ fn main() {
3333
Foo(());
3434
std_io::stdout();
3535
local_io(());
36+
37+
{
38+
// Test that having `std_io` in a module scope and a non-module
39+
// scope is allowed, when both resolve to the same definition.
40+
use std::io as std_io;
41+
use std_io::stdout;
42+
stdout();
43+
}
3644
}

0 commit comments

Comments
 (0)