Skip to content

Commit 8db6881

Browse files
authored
Rollup merge of #141741 - nnethercote:overhaul-UsePath, r=petrochenkov
Overhaul `UsePath` It currently uses `SmallVec<[Res; 3]>` which is really weird. Details in the individual commits. r? `@petrochenkov`
2 parents aed1171 + 8747ccb commit 8db6881

File tree

23 files changed

+121
-96
lines changed

23 files changed

+121
-96
lines changed

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ use rustc_ast::ptr::P;
33
use rustc_ast::visit::AssocCtxt;
44
use rustc_ast::*;
55
use rustc_errors::ErrorGuaranteed;
6-
use rustc_hir::def::{DefKind, Res};
6+
use rustc_hir::def::{DefKind, PerNS, Res};
77
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
88
use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin};
99
use rustc_index::{IndexSlice, IndexVec};
10+
use rustc_middle::span_bug;
1011
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
1112
use rustc_span::edit_distance::find_best_match_for_name;
1213
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
@@ -527,7 +528,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
527528
}
528529
UseTreeKind::Glob => {
529530
let res = self.expect_full_res(id);
530-
let res = smallvec![self.lower_res(res)];
531+
let res = self.lower_res(res);
532+
// Put the result in the appropriate namespace.
533+
let res = match res {
534+
Res::Def(DefKind::Mod | DefKind::Trait, _) => {
535+
PerNS { type_ns: Some(res), value_ns: None, macro_ns: None }
536+
}
537+
Res::Def(DefKind::Enum, _) => {
538+
PerNS { type_ns: None, value_ns: Some(res), macro_ns: None }
539+
}
540+
Res::Err => {
541+
// Propagate the error to all namespaces, just to be sure.
542+
let err = Some(Res::Err);
543+
PerNS { type_ns: err, value_ns: err, macro_ns: err }
544+
}
545+
_ => span_bug!(path.span, "bad glob res {:?}", res),
546+
};
531547
let path = Path { segments, span: path.span, tokens: None };
532548
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
533549
hir::ItemKind::Use(path, hir::UseKind::Glob)
@@ -601,7 +617,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
601617
} else {
602618
// For non-empty lists we can just drop all the data, the prefix is already
603619
// present in HIR as a part of nested imports.
604-
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
620+
self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span })
605621
};
606622
hir::ItemKind::Use(path, hir::UseKind::ListStem)
607623
}

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
6464
use rustc_session::parse::{add_feature_diagnostics, feature_err};
6565
use rustc_span::symbol::{Ident, Symbol, kw, sym};
6666
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
67-
use smallvec::{SmallVec, smallvec};
67+
use smallvec::SmallVec;
6868
use thin_vec::ThinVec;
6969
use tracing::{debug, instrument, trace};
7070

@@ -705,14 +705,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
705705
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
706706
}
707707

708-
fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> {
709-
let res = self.resolver.get_import_res(id).present_items();
710-
let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect();
711-
if res.is_empty() {
708+
fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS<Option<Res>> {
709+
let per_ns = self.resolver.get_import_res(id);
710+
let per_ns = per_ns.map(|res| res.map(|res| self.lower_res(res)));
711+
if per_ns.is_empty() {
712+
// Propagate the error to all namespaces, just to be sure.
712713
self.dcx().span_delayed_bug(span, "no resolution for an import");
713-
return smallvec![Res::Err];
714+
let err = Some(Res::Err);
715+
return PerNS { type_ns: err, value_ns: err, macro_ns: err };
714716
}
715-
res
717+
per_ns
716718
}
717719

718720
fn make_lang_item_qpath(

compiler/rustc_ast_lowering/src/path.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use std::sync::Arc;
22

33
use rustc_ast::{self as ast, *};
4-
use rustc_hir::def::{DefKind, PartialRes, Res};
4+
use rustc_hir::def::{DefKind, PartialRes, PerNS, Res};
55
use rustc_hir::def_id::DefId;
66
use rustc_hir::{self as hir, GenericArg};
77
use rustc_middle::{span_bug, ty};
88
use rustc_session::parse::add_feature_diagnostics;
99
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
10-
use smallvec::{SmallVec, smallvec};
10+
use smallvec::smallvec;
1111
use tracing::{debug, instrument};
1212

1313
use super::errors::{
@@ -226,11 +226,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
226226

227227
pub(crate) fn lower_use_path(
228228
&mut self,
229-
res: SmallVec<[Res; 3]>,
229+
res: PerNS<Option<Res>>,
230230
p: &Path,
231231
param_mode: ParamMode,
232232
) -> &'hir hir::UsePath<'hir> {
233-
assert!((1..=3).contains(&res.len()));
233+
assert!(!res.is_empty());
234234
self.arena.alloc(hir::UsePath {
235235
res,
236236
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {

compiler/rustc_hir/src/arena.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ macro_rules! arena_types {
88
[] asm_template: rustc_ast::InlineAsmTemplatePiece,
99
[] attribute: rustc_hir::Attribute,
1010
[] owner_info: rustc_hir::OwnerInfo<'tcx>,
11-
[] use_path: rustc_hir::UsePath<'tcx>,
1211
[] lit: rustc_hir::Lit,
1312
[] macro_def: rustc_ast::MacroDef,
1413
]);

compiler/rustc_hir/src/def.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace {
584584
}
585585

586586
/// Just a helper ‒ separate structure for each namespace.
587-
#[derive(Copy, Clone, Default, Debug)]
587+
#[derive(Copy, Clone, Default, Debug, HashStable_Generic)]
588588
pub struct PerNS<T> {
589589
pub value_ns: T,
590590
pub type_ns: T,
@@ -596,10 +596,16 @@ impl<T> PerNS<T> {
596596
PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
597597
}
598598

599+
/// Note: Do you really want to use this? Often you know which namespace a
600+
/// name will belong in, and you can consider just that namespace directly,
601+
/// rather than iterating through all of them.
599602
pub fn into_iter(self) -> IntoIter<T, 3> {
600603
[self.value_ns, self.type_ns, self.macro_ns].into_iter()
601604
}
602605

606+
/// Note: Do you really want to use this? Often you know which namespace a
607+
/// name will belong in, and you can consider just that namespace directly,
608+
/// rather than iterating through all of them.
603609
pub fn iter(&self) -> IntoIter<&T, 3> {
604610
[&self.value_ns, &self.type_ns, &self.macro_ns].into_iter()
605611
}
@@ -634,6 +640,10 @@ impl<T> PerNS<Option<T>> {
634640
}
635641

636642
/// Returns an iterator over the items which are `Some`.
643+
///
644+
/// Note: Do you really want to use this? Often you know which namespace a
645+
/// name will belong in, and you can consider just that namespace directly,
646+
/// rather than iterating through all of them.
637647
pub fn present_items(self) -> impl Iterator<Item = T> {
638648
[self.type_ns, self.value_ns, self.macro_ns].into_iter().flatten()
639649
}

compiler/rustc_hir/src/hir.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use thin_vec::ThinVec;
3030
use tracing::debug;
3131

3232
use crate::LangItem;
33-
use crate::def::{CtorKind, DefKind, Res};
33+
use crate::def::{CtorKind, DefKind, PerNS, Res};
3434
use crate::def_id::{DefId, LocalDefIdMap};
3535
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
3636
use crate::intravisit::{FnKind, VisitorExt};
@@ -347,7 +347,7 @@ pub struct Path<'hir, R = Res> {
347347
}
348348

349349
/// Up to three resolutions for type, value and macro namespaces.
350-
pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>;
350+
pub type UsePath<'hir> = Path<'hir, PerNS<Option<Res>>>;
351351

352352
impl Path<'_> {
353353
pub fn is_global(&self) -> bool {

compiler/rustc_hir/src/intravisit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ pub fn walk_use<'v, V: Visitor<'v>>(
11481148
hir_id: HirId,
11491149
) -> V::Result {
11501150
let UsePath { segments, ref res, span } = *path;
1151-
for &res in res {
1151+
for res in res.present_items() {
11521152
try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id));
11531153
}
11541154
V::Result::output()

compiler/rustc_lint/src/internal.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -328,16 +328,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr {
328328
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
329329
let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return };
330330

331-
let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id);
331+
let is_mod_inherent = |res: Res| {
332+
res.opt_def_id()
333+
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id))
334+
};
332335

333336
// Path segments except for the final.
334-
if let Some(seg) =
335-
path.segments.iter().find(|seg| seg.res.opt_def_id().is_some_and(is_mod_inherent))
336-
{
337+
if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) {
337338
cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage);
338339
}
339340
// Final path resolutions, like `use rustc_type_ir::inherent`
340-
else if path.res.iter().any(|res| res.opt_def_id().is_some_and(is_mod_inherent)) {
341+
else if let Some(type_ns) = path.res.type_ns
342+
&& is_mod_inherent(type_ns)
343+
{
341344
cx.emit_span_lint(
342345
USAGE_OF_TYPE_IR_INHERENT,
343346
path.segments.last().unwrap().ident.span,
@@ -346,13 +349,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr {
346349
}
347350

348351
let (lo, hi, snippet) = match path.segments {
349-
[.., penultimate, segment]
350-
if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) =>
351-
{
352+
[.., penultimate, segment] if is_mod_inherent(penultimate.res) => {
352353
(segment.ident.span, item.kind.ident().unwrap().span, "*")
353354
}
354355
[.., segment]
355-
if path.res.iter().flat_map(Res::opt_def_id).any(is_mod_inherent)
356+
if let Some(type_ns) = path.res.type_ns
357+
&& is_mod_inherent(type_ns)
356358
&& let rustc_hir::UseKind::Single(ident) = kind =>
357359
{
358360
let (lo, snippet) =

compiler/rustc_lint/src/unqualified_local_imports.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use rustc_hir::def::{DefKind, Res};
21
use rustc_hir::{self as hir};
32
use rustc_session::{declare_lint, declare_lint_pass};
43
use rustc_span::kw;
@@ -47,17 +46,15 @@ declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]);
4746
impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports {
4847
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
4948
let hir::ItemKind::Use(path, _kind) = item.kind else { return };
50-
// `path` has three resolutions for the type, module, value namespaces.
51-
// Check if any of them qualifies: local crate, and not a macro.
52-
// (Macros can't be imported any other way so we don't complain about them.)
53-
let is_local_import = |res: &Res| {
54-
matches!(
55-
res,
56-
hir::def::Res::Def(def_kind, def_id)
57-
if def_id.is_local() && !matches!(def_kind, DefKind::Macro(_)),
58-
)
59-
};
60-
if !path.res.iter().any(is_local_import) {
49+
// Check the type and value namespace resolutions for a local crate.
50+
let is_local_import = matches!(
51+
path.res.type_ns,
52+
Some(hir::def::Res::Def(_, def_id)) if def_id.is_local()
53+
) || matches!(
54+
path.res.value_ns,
55+
Some(hir::def::Res::Def(_, def_id)) if def_id.is_local()
56+
);
57+
if !is_local_import {
6158
return;
6259
}
6360
// So this does refer to something local. Let's check whether it starts with `self`,

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,26 +1047,21 @@ fn find_fallback_pattern_typo<'tcx>(
10471047
let hir::ItemKind::Use(path, _) = item.kind else {
10481048
continue;
10491049
};
1050-
for res in &path.res {
1051-
if let Res::Def(DefKind::Const, id) = res
1052-
&& infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
1053-
{
1054-
if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1055-
// The original const is accessible, suggest using it directly.
1056-
let item_name = cx.tcx.item_name(*id);
1057-
accessible.push(item_name);
1058-
accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1059-
} else if cx
1060-
.tcx
1061-
.visibility(item.owner_id)
1062-
.is_accessible_from(parent, cx.tcx)
1063-
{
1064-
// The const is accessible only through the re-export, point at
1065-
// the `use`.
1066-
let ident = item.kind.ident().unwrap();
1067-
imported.push(ident.name);
1068-
imported_spans.push(ident.span);
1069-
}
1050+
if let Some(value_ns) = path.res.value_ns
1051+
&& let Res::Def(DefKind::Const, id) = value_ns
1052+
&& infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
1053+
{
1054+
if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1055+
// The original const is accessible, suggest using it directly.
1056+
let item_name = cx.tcx.item_name(id);
1057+
accessible.push(item_name);
1058+
accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1059+
} else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) {
1060+
// The const is accessible only through the re-export, point at
1061+
// the `use`.
1062+
let ident = item.kind.ident().unwrap();
1063+
imported.push(ident.name);
1064+
imported_spans.push(ident.span);
10701065
}
10711066
}
10721067
}

compiler/rustc_passes/src/check_export.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> {
132132
self.add_exportable(def_id);
133133
}
134134
hir::ItemKind::Use(path, _) => {
135-
for res in &path.res {
135+
for res in path.res.present_items() {
136136
// Only local items are exportable.
137137
if let Some(res_id) = res.opt_def_id()
138138
&& let Some(res_id) = res_id.as_local()

compiler/rustc_resolve/src/check_unused.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,10 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
118118
ast::UseTreeKind::Simple(Some(ident)) => {
119119
if ident.name == kw::Underscore
120120
&& !self.r.import_res_map.get(&id).is_some_and(|per_ns| {
121-
per_ns.iter().filter_map(|res| res.as_ref()).any(|res| {
122-
matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
123-
})
121+
matches!(
122+
per_ns.type_ns,
123+
Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
124+
)
124125
})
125126
{
126127
self.unused_import(self.base_id).add(id);

src/librustdoc/clean/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ fn first_non_private<'tcx>(
16061606
&& let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id)
16071607
&& let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind
16081608
{
1609-
for res in &path.res {
1609+
for res in path.res.present_items() {
16101610
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
16111611
continue;
16121612
}
@@ -3014,7 +3014,7 @@ fn clean_use_statement<'tcx>(
30143014
) -> Vec<Item> {
30153015
let mut items = Vec::new();
30163016
let hir::UsePath { segments, ref res, span } = *path;
3017-
for &res in res {
3017+
for res in res.present_items() {
30183018
let path = hir::Path { segments, res, span };
30193019
items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
30203020
}

src/librustdoc/visit_ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
440440
hir::ItemKind::GlobalAsm { .. } => {}
441441
hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
442442
hir::ItemKind::Use(path, kind) => {
443-
for &res in &path.res {
443+
for res in path.res.present_items() {
444444
// Struct and variant constructors and proc macro stubs always show up alongside
445445
// their definitions, we've already processed them so just discard these.
446446
if should_ignore_res(res) {

src/tools/clippy/clippy_lints/src/disallowed_types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
106106
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
107107
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
108108
if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind {
109-
for res in &path.res {
110-
self.check_res_emit(cx, res, item.span);
109+
if let Some(res) = path.res.type_ns {
110+
self.check_res_emit(cx, &res, item.span);
111111
}
112112
}
113113
}

src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
5151
// so lint on the `use` statement directly.
5252
if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind
5353
&& !item.span.in_external_macro(cx.sess().source_map())
54-
&& let Some(def_id) = path.res[0].opt_def_id()
54+
// use `present_items` because it could be in either type_ns or value_ns
55+
&& let Some(res) = path.res.present_items().next()
56+
&& let Some(def_id) = res.opt_def_id()
5557
&& self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
5658
{
5759
let module = if is_integer_module(cx, def_id) {

src/tools/clippy/clippy_lints/src/macro_use.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,7 @@ impl LateLintPass<'_> for MacroUseImports {
100100
&& let hir_id = item.hir_id()
101101
&& let attrs = cx.tcx.hir_attrs(hir_id)
102102
&& let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use))
103-
&& let Some(id) = path.res.iter().find_map(|res| match res {
104-
Res::Def(DefKind::Mod, id) => Some(id),
105-
_ => None,
106-
})
103+
&& let Some(Res::Def(DefKind::Mod, id)) = path.res.type_ns
107104
&& !id.is_local()
108105
{
109106
for kid in cx.tcx.module_children(id) {

src/tools/clippy/clippy_lints/src/min_ident_chars.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,9 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
131131
// If however the identifier is different, this means it is an alias (`use foo::bar as baz`). In
132132
// this case, we need to emit the warning for `baz`.
133133
if let Some(imported_item_path) = usenode
134-
&& let Some(Res::Def(_, imported_item_defid)) = imported_item_path.res.first()
135-
&& cx.tcx.item_name(*imported_item_defid).as_str() == str
134+
// use `present_items` because it could be in any of type_ns, value_ns, macro_ns
135+
&& let Some(Res::Def(_, imported_item_defid)) = imported_item_path.res.value_ns
136+
&& cx.tcx.item_name(imported_item_defid).as_str() == str
136137
{
137138
return;
138139
}

0 commit comments

Comments
 (0)