Skip to content

Commit fd7648d

Browse files
Shrink Path to 16 bytes
Thanks to the observation (supported by counting) that the vast majority paths have neither generics no type anchors, and thanks to a new datastructure `ThinVecWithHeader` that is essentially `(T, Box<[U]>)` but with the size of a single pointer, we are able to reach this feat. This (together with `ThinVecWithHeader`) makes the possibility to shrink `TypeRef`, because most types are paths.
1 parent 61d14ba commit fd7648d

File tree

13 files changed

+596
-107
lines changed

13 files changed

+596
-107
lines changed

src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -720,12 +720,8 @@ impl ExprCollector<'_> {
720720
fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
721721
e.path().and_then(|path| {
722722
let path = self.parse_path(path)?;
723-
let Path::Normal { type_anchor, mod_path, generic_args } = &path else {
724-
panic!("path parsing produced a non-normal path");
725-
};
726723
// Need to enable `mod_path.len() < 1` for `self`.
727-
let may_be_variable =
728-
type_anchor.is_none() && mod_path.len() <= 1 && generic_args.is_none();
724+
let may_be_variable = matches!(&path, Path::BarePath(mod_path) if mod_path.len() <= 1);
729725
let hygiene = if may_be_variable {
730726
self.hygiene_id_for(e.syntax().text_range().start())
731727
} else {
@@ -797,7 +793,7 @@ impl ExprCollector<'_> {
797793
ast::Expr::PathExpr(e) => {
798794
let (path, hygiene) = self
799795
.collect_expr_path(e.clone())
800-
.map(|(path, hygiene)| (Pat::Path(Box::new(path)), hygiene))
796+
.map(|(path, hygiene)| (Pat::Path(path), hygiene))
801797
.unwrap_or((Pat::Missing, HygieneId::ROOT));
802798
let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
803799
if !hygiene.is_root() {
@@ -1059,7 +1055,7 @@ impl ExprCollector<'_> {
10591055
syntax_ptr,
10601056
);
10611057
let none_arm = MatchArm {
1062-
pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
1058+
pat: self.alloc_pat_desugared(Pat::Path(option_none)),
10631059
guard: None,
10641060
expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr),
10651061
};
@@ -1561,7 +1557,7 @@ impl ExprCollector<'_> {
15611557
Pat::Ref { pat, mutability }
15621558
}
15631559
ast::Pat::PathPat(p) => {
1564-
let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new);
1560+
let path = p.path().and_then(|path| self.parse_path(path));
15651561
path.map(Pat::Path).unwrap_or(Pat::Missing)
15661562
}
15671563
ast::Pat::OrPat(p) => 'b: {

src/tools/rust-analyzer/crates/hir-def/src/generics.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121
item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree},
2222
lower::LowerCtx,
2323
nameres::{DefMap, MacroSubNs},
24-
path::{AssociatedTypeBinding, GenericArg, GenericArgs, Path},
24+
path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
2525
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
2626
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
2727
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
@@ -788,19 +788,16 @@ fn copy_path(
788788
to_source_map: &mut TypesSourceMap,
789789
) -> Path {
790790
match path {
791-
Path::Normal { type_anchor, mod_path, generic_args } => {
792-
let type_anchor = type_anchor
791+
Path::BarePath(mod_path) => Path::BarePath(mod_path.clone()),
792+
Path::Normal(path) => {
793+
let type_anchor = path
794+
.type_anchor()
793795
.map(|type_ref| copy_type_ref(type_ref, from, from_source_map, to, to_source_map));
794-
let mod_path = mod_path.clone();
795-
let generic_args = generic_args.as_ref().map(|generic_args| {
796-
generic_args
797-
.iter()
798-
.map(|generic_args| {
799-
copy_generic_args(generic_args, from, from_source_map, to, to_source_map)
800-
})
801-
.collect()
796+
let mod_path = path.mod_path().clone();
797+
let generic_args = path.generic_args().iter().map(|generic_args| {
798+
copy_generic_args(generic_args, from, from_source_map, to, to_source_map)
802799
});
803-
Path::Normal { type_anchor, mod_path, generic_args }
800+
Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args))
804801
}
805802
Path::LangItem(lang_item, name) => Path::LangItem(*lang_item, name.clone()),
806803
}

src/tools/rust-analyzer/crates/hir-def/src/hir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ pub enum Pat {
583583
suffix: Box<[PatId]>,
584584
},
585585
/// This might refer to a variable if a single segment path (specifically, on destructuring assignment).
586-
Path(Box<Path>),
586+
Path(Path),
587587
Lit(ExprId),
588588
Bind {
589589
id: BindingId,

src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ pub enum TypeBound {
201201
}
202202

203203
#[cfg(target_pointer_width = "64")]
204-
const _: [(); 48] = [(); ::std::mem::size_of::<TypeBound>()];
204+
const _: [(); 32] = [(); ::std::mem::size_of::<TypeBound>()];
205205

206206
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
207207
pub enum UseArgRef {

src/tools/rust-analyzer/crates/hir-def/src/path.rs

+87-63
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{
1414
use hir_expand::name::Name;
1515
use intern::Interned;
1616
use span::Edition;
17+
use stdx::thin_vec::thin_vec_with_header_struct;
1718
use syntax::ast;
1819

1920
pub use hir_expand::mod_path::{path, ModPath, PathKind};
@@ -47,20 +48,33 @@ impl Display for ImportAliasDisplay<'_> {
4748

4849
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4950
pub enum Path {
50-
/// A normal path
51-
Normal {
52-
/// Type based path like `<T>::foo`.
53-
/// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
54-
type_anchor: Option<TypeRefId>,
55-
mod_path: Interned<ModPath>,
56-
/// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
57-
generic_args: Option<Box<[Option<GenericArgs>]>>,
58-
},
51+
/// `BarePath` is used when the path has neither generics nor type anchor, since the vast majority of paths
52+
/// are in this category, and splitting `Path` this way allows it to be more thin. When the path has either generics
53+
/// or type anchor, it is `Path::Normal` with the generics filled with `None` even if there are none (practically
54+
/// this is not a problem since many more paths have generics than a type anchor).
55+
BarePath(Interned<ModPath>),
56+
/// `Path::Normal` may have empty generics and type anchor (but generic args will be filled with `None`).
57+
Normal(NormalPath),
5958
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
6059
/// links via a normal path since they might be private and not accessible in the usage place.
6160
LangItem(LangItemTarget, Option<Name>),
6261
}
6362

63+
// This type is being used a lot, make sure it doesn't grow unintentionally.
64+
#[cfg(target_arch = "x86_64")]
65+
const _: () = {
66+
assert!(size_of::<Path>() == 16);
67+
assert!(size_of::<Option<Path>>() == 16);
68+
};
69+
70+
thin_vec_with_header_struct! {
71+
pub new(pub(crate)) struct NormalPath, NormalPathHeader {
72+
pub generic_args: [Option<GenericArgs>],
73+
pub type_anchor: Option<TypeRefId>,
74+
pub mod_path: Interned<ModPath>; ref,
75+
}
76+
}
77+
6478
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
6579
/// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
6680
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -112,50 +126,49 @@ impl Path {
112126
}
113127

114128
/// Converts a known mod path to `Path`.
115-
pub fn from_known_path(
116-
path: ModPath,
117-
generic_args: impl Into<Box<[Option<GenericArgs>]>>,
118-
) -> Path {
119-
let generic_args = generic_args.into();
120-
assert_eq!(path.len(), generic_args.len());
121-
Path::Normal {
122-
type_anchor: None,
123-
mod_path: Interned::new(path),
124-
generic_args: Some(generic_args),
125-
}
129+
pub fn from_known_path(path: ModPath, generic_args: Vec<Option<GenericArgs>>) -> Path {
130+
Path::Normal(NormalPath::new(None, Interned::new(path), generic_args))
126131
}
127132

128133
/// Converts a known mod path to `Path`.
129134
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
130-
Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
135+
Path::BarePath(Interned::new(path))
131136
}
132137

138+
#[inline]
133139
pub fn kind(&self) -> &PathKind {
134140
match self {
135-
Path::Normal { mod_path, .. } => &mod_path.kind,
141+
Path::BarePath(mod_path) => &mod_path.kind,
142+
Path::Normal(path) => &path.mod_path().kind,
136143
Path::LangItem(..) => &PathKind::Abs,
137144
}
138145
}
139146

147+
#[inline]
140148
pub fn type_anchor(&self) -> Option<TypeRefId> {
141149
match self {
142-
Path::Normal { type_anchor, .. } => *type_anchor,
143-
Path::LangItem(..) => None,
150+
Path::Normal(path) => path.type_anchor(),
151+
Path::LangItem(..) | Path::BarePath(_) => None,
152+
}
153+
}
154+
155+
#[inline]
156+
pub fn generic_args(&self) -> Option<&[Option<GenericArgs>]> {
157+
match self {
158+
Path::Normal(path) => Some(path.generic_args()),
159+
Path::LangItem(..) | Path::BarePath(_) => None,
144160
}
145161
}
146162

147163
pub fn segments(&self) -> PathSegments<'_> {
148164
match self {
149-
Path::Normal { mod_path, generic_args, .. } => {
150-
let s = PathSegments {
151-
segments: mod_path.segments(),
152-
generic_args: generic_args.as_deref(),
153-
};
154-
if let Some(generic_args) = s.generic_args {
155-
assert_eq!(s.segments.len(), generic_args.len());
156-
}
157-
s
165+
Path::BarePath(mod_path) => {
166+
PathSegments { segments: mod_path.segments(), generic_args: None }
158167
}
168+
Path::Normal(path) => PathSegments {
169+
segments: path.mod_path().segments(),
170+
generic_args: Some(path.generic_args()),
171+
},
159172
Path::LangItem(_, seg) => PathSegments {
160173
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
161174
generic_args: None,
@@ -165,34 +178,55 @@ impl Path {
165178

166179
pub fn mod_path(&self) -> Option<&ModPath> {
167180
match self {
168-
Path::Normal { mod_path, .. } => Some(mod_path),
181+
Path::BarePath(mod_path) => Some(mod_path),
182+
Path::Normal(path) => Some(path.mod_path()),
169183
Path::LangItem(..) => None,
170184
}
171185
}
172186

173187
pub fn qualifier(&self) -> Option<Path> {
174-
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
175-
return None;
176-
};
177-
if mod_path.is_ident() {
178-
return None;
188+
match self {
189+
Path::BarePath(mod_path) => {
190+
if mod_path.is_ident() {
191+
return None;
192+
}
193+
Some(Path::BarePath(Interned::new(ModPath::from_segments(
194+
mod_path.kind,
195+
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
196+
))))
197+
}
198+
Path::Normal(path) => {
199+
let mod_path = path.mod_path();
200+
if mod_path.is_ident() {
201+
return None;
202+
}
203+
let type_anchor = path.type_anchor();
204+
let generic_args = path.generic_args();
205+
let qualifier_mod_path = Interned::new(ModPath::from_segments(
206+
mod_path.kind,
207+
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
208+
));
209+
let qualifier_generic_args = &generic_args[..generic_args.len() - 1];
210+
Some(Path::Normal(NormalPath::new(
211+
type_anchor,
212+
qualifier_mod_path,
213+
qualifier_generic_args.iter().cloned(),
214+
)))
215+
}
216+
Path::LangItem(..) => None,
179217
}
180-
let res = Path::Normal {
181-
type_anchor: *type_anchor,
182-
mod_path: Interned::new(ModPath::from_segments(
183-
mod_path.kind,
184-
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
185-
)),
186-
generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
187-
};
188-
Some(res)
189218
}
190219

191220
pub fn is_self_type(&self) -> bool {
192-
let Path::Normal { mod_path, generic_args, type_anchor } = self else {
193-
return false;
194-
};
195-
type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
221+
match self {
222+
Path::BarePath(mod_path) => mod_path.is_Self(),
223+
Path::Normal(path) => {
224+
path.type_anchor().is_none()
225+
&& path.mod_path().is_Self()
226+
&& path.generic_args().iter().all(|args| args.is_none())
227+
}
228+
Path::LangItem(..) => false,
229+
}
196230
}
197231
}
198232

@@ -268,16 +302,6 @@ impl GenericArgs {
268302

269303
impl From<Name> for Path {
270304
fn from(name: Name) -> Path {
271-
Path::Normal {
272-
type_anchor: None,
273-
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
274-
generic_args: None,
275-
}
276-
}
277-
}
278-
279-
impl From<Name> for Box<Path> {
280-
fn from(name: Name) -> Box<Path> {
281-
Box::new(Path::from(name))
305+
Path::BarePath(Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))))
282306
}
283307
}

src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::iter;
44

5-
use crate::{lower::LowerCtx, type_ref::ConstRef};
5+
use crate::{lower::LowerCtx, path::NormalPath, type_ref::ConstRef};
66

77
use hir_expand::{
88
mod_path::resolve_crate_root,
@@ -74,11 +74,9 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
7474
}
7575
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
7676
Some(trait_ref) => {
77-
let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
78-
Path::from_src(ctx, trait_ref.path()?)?
79-
else {
80-
return None;
81-
};
77+
let path = Path::from_src(ctx, trait_ref.path()?)?;
78+
let mod_path = path.mod_path()?;
79+
let path_generic_args = path.generic_args();
8280
let num_segments = mod_path.segments().len();
8381
kind = mod_path.kind;
8482

@@ -136,7 +134,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
136134
};
137135
}
138136
segments.reverse();
139-
if !generic_args.is_empty() {
137+
if !generic_args.is_empty() || type_anchor.is_some() {
140138
generic_args.resize(segments.len(), None);
141139
generic_args.reverse();
142140
}
@@ -165,11 +163,11 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
165163
}
166164

167165
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
168-
return Some(Path::Normal {
169-
type_anchor,
170-
mod_path,
171-
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
172-
});
166+
if type_anchor.is_none() && generic_args.is_empty() {
167+
return Some(Path::BarePath(mod_path));
168+
} else {
169+
return Some(Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args)));
170+
}
173171

174172
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
175173
if let Some(q) = path.qualifier() {

src/tools/rust-analyzer/crates/hir-def/src/resolver.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ impl Resolver {
167167
path: &Path,
168168
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
169169
let path = match path {
170-
Path::Normal { mod_path, .. } => mod_path,
170+
Path::BarePath(mod_path) => mod_path,
171+
Path::Normal(it) => it.mod_path(),
171172
Path::LangItem(l, seg) => {
172173
let type_ns = match *l {
173174
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
@@ -265,7 +266,8 @@ impl Resolver {
265266
mut hygiene_id: HygieneId,
266267
) -> Option<ResolveValueResult> {
267268
let path = match path {
268-
Path::Normal { mod_path, .. } => mod_path,
269+
Path::BarePath(mod_path) => mod_path,
270+
Path::Normal(it) => it.mod_path(),
269271
Path::LangItem(l, None) => {
270272
return Some(ResolveValueResult::ValueNs(
271273
match *l {

0 commit comments

Comments
 (0)