Skip to content

Commit 80b3a13

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 640b309 commit 80b3a13

File tree

12 files changed

+591
-100
lines changed

12 files changed

+591
-100
lines changed

crates/hir-def/src/body/lower.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ impl ExprCollector<'_> {
859859
syntax_ptr,
860860
);
861861
let none_arm = MatchArm {
862-
pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
862+
pat: self.alloc_pat_desugared(Pat::Path(option_none)),
863863
guard: None,
864864
expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr),
865865
};
@@ -1315,7 +1315,7 @@ impl ExprCollector<'_> {
13151315
Pat::Ref { pat, mutability }
13161316
}
13171317
ast::Pat::PathPat(p) => {
1318-
let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new);
1318+
let path = p.path().and_then(|path| self.parse_path(path));
13191319
path.map(Pat::Path).unwrap_or(Pat::Missing)
13201320
}
13211321
ast::Pat::OrPat(p) => 'b: {

crates/hir-def/src/generics.rs

Lines changed: 9 additions & 12 deletions
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
}

crates/hir-def/src/hir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ pub enum Pat {
671671
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
672672
Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
673673
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
674-
Path(Box<Path>),
674+
Path(Path),
675675
Lit(ExprId),
676676
Bind { id: BindingId, subpat: Option<PatId> },
677677
TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<u32> },

crates/hir-def/src/path.rs

Lines changed: 84 additions & 63 deletions
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,30 @@ 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(Interned<ModPath>),
52+
/// You can **not** rely on `Path::Normal` to not have generic args and type anchor, it may be missing both
53+
/// (but generic args will be filled with `None`).
54+
Normal(NormalPath),
5955
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
6056
/// links via a normal path since they might be private and not accessible in the usage place.
6157
LangItem(LangItemTarget, Option<Name>),
6258
}
6359

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

114125
/// 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-
}
126+
pub fn from_known_path(path: ModPath, generic_args: Vec<Option<GenericArgs>>) -> Path {
127+
Path::Normal(NormalPath::new(None, Interned::new(path), generic_args))
126128
}
127129

128130
/// Converts a known mod path to `Path`.
129131
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 }
132+
Path::BarePath(Interned::new(path))
131133
}
132134

135+
#[inline]
133136
pub fn kind(&self) -> &PathKind {
134137
match self {
135-
Path::Normal { mod_path, .. } => &mod_path.kind,
138+
Path::BarePath(mod_path) => &mod_path.kind,
139+
Path::Normal(path) => &path.mod_path().kind,
136140
Path::LangItem(..) => &PathKind::Abs,
137141
}
138142
}
139143

144+
#[inline]
140145
pub fn type_anchor(&self) -> Option<TypeRefId> {
141146
match self {
142-
Path::Normal { type_anchor, .. } => *type_anchor,
143-
Path::LangItem(..) => None,
147+
Path::Normal(path) => path.type_anchor(),
148+
Path::LangItem(..) | Path::BarePath(_) => None,
149+
}
150+
}
151+
152+
#[inline]
153+
pub fn generic_args(&self) -> Option<&[Option<GenericArgs>]> {
154+
match self {
155+
Path::Normal(path) => Some(path.generic_args()),
156+
Path::LangItem(..) | Path::BarePath(_) => None,
144157
}
145158
}
146159

147160
pub fn segments(&self) -> PathSegments<'_> {
148161
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
162+
Path::BarePath(mod_path) => {
163+
PathSegments { segments: mod_path.segments(), generic_args: None }
158164
}
165+
Path::Normal(path) => PathSegments {
166+
segments: path.mod_path().segments(),
167+
generic_args: Some(path.generic_args()),
168+
},
159169
Path::LangItem(_, seg) => PathSegments {
160170
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
161171
generic_args: None,
@@ -165,34 +175,55 @@ impl Path {
165175

166176
pub fn mod_path(&self) -> Option<&ModPath> {
167177
match self {
168-
Path::Normal { mod_path, .. } => Some(mod_path),
178+
Path::BarePath(mod_path) => Some(mod_path),
179+
Path::Normal(path) => Some(path.mod_path()),
169180
Path::LangItem(..) => None,
170181
}
171182
}
172183

173184
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;
185+
match self {
186+
Path::BarePath(mod_path) => {
187+
if mod_path.is_ident() {
188+
return None;
189+
}
190+
Some(Path::BarePath(Interned::new(ModPath::from_segments(
191+
mod_path.kind,
192+
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
193+
))))
194+
}
195+
Path::Normal(path) => {
196+
let mod_path = path.mod_path();
197+
if mod_path.is_ident() {
198+
return None;
199+
}
200+
let type_anchor = path.type_anchor();
201+
let generic_args = path.generic_args();
202+
let qualifier_mod_path = Interned::new(ModPath::from_segments(
203+
mod_path.kind,
204+
mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
205+
));
206+
let qualifier_generic_args = &generic_args[..generic_args.len() - 1];
207+
Some(Path::Normal(NormalPath::new(
208+
type_anchor,
209+
qualifier_mod_path,
210+
qualifier_generic_args.iter().cloned(),
211+
)))
212+
}
213+
Path::LangItem(..) => None,
179214
}
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)
189215
}
190216

191217
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()
218+
match self {
219+
Path::BarePath(mod_path) => mod_path.is_Self(),
220+
Path::Normal(path) => {
221+
path.type_anchor().is_none()
222+
&& path.mod_path().is_Self()
223+
&& path.generic_args().iter().all(|args| args.is_none())
224+
}
225+
Path::LangItem(..) => false,
226+
}
196227
}
197228
}
198229

@@ -268,16 +299,6 @@ impl GenericArgs {
268299

269300
impl From<Name> for Path {
270301
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))
302+
Path::BarePath(Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))))
282303
}
283304
}

crates/hir-def/src/path/lower.rs

Lines changed: 10 additions & 12 deletions
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() {

crates/hir-def/src/resolver.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ impl Resolver {
162162
path: &Path,
163163
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
164164
let path = match path {
165-
Path::Normal { mod_path, .. } => mod_path,
165+
Path::BarePath(mod_path) => mod_path,
166+
Path::Normal(it) => it.mod_path(),
166167
Path::LangItem(l, seg) => {
167168
let type_ns = match *l {
168169
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
@@ -259,7 +260,8 @@ impl Resolver {
259260
path: &Path,
260261
) -> Option<ResolveValueResult> {
261262
let path = match path {
262-
Path::Normal { mod_path, .. } => mod_path,
263+
Path::BarePath(mod_path) => mod_path,
264+
Path::Normal(it) => it.mod_path(),
263265
Path::LangItem(l, None) => {
264266
return Some(ResolveValueResult::ValueNs(
265267
match *l {

crates/hir-ty/src/infer/expr.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ impl InferenceContext<'_> {
195195
match &self.body[expr] {
196196
// Lang item paths cannot currently be local variables or statics.
197197
Expr::Path(Path::LangItem(_, _)) => false,
198-
Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
198+
Expr::Path(Path::Normal(path)) => path.type_anchor().is_none(),
199199
Expr::Path(path) => self
200200
.resolver
201201
.resolve_path_in_value_ns_fully(self.db.upcast(), path)
@@ -614,7 +614,8 @@ impl InferenceContext<'_> {
614614
let ty = match self.infer_path(p, tgt_expr.into()) {
615615
Some(ty) => ty,
616616
None => {
617-
if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
617+
if p.mod_path()
618+
.is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self())
618619
{
619620
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
620621
expr: tgt_expr,

crates/hir-ty/src/infer/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ impl InferenceContext<'_> {
221221

222222
let _d;
223223
let (resolved_segment, remaining_segments) = match path {
224-
Path::Normal { .. } => {
224+
Path::Normal { .. } | Path::BarePath(_) => {
225225
assert!(remaining_index < path.segments().len());
226226
(
227227
path.segments().get(remaining_index - 1).unwrap(),

crates/hir-ty/src/mir/lower.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,12 +1417,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
14171417
),
14181418
};
14191419
let edition = self.edition();
1420-
let unresolved_name = || {
1421-
MirLowerError::unresolved_path(self.db, c.as_ref(), edition, &self.body.types)
1422-
};
1420+
let unresolved_name =
1421+
|| MirLowerError::unresolved_path(self.db, c, edition, &self.body.types);
14231422
let resolver = self.owner.resolver(self.db.upcast());
14241423
let pr = resolver
1425-
.resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
1424+
.resolve_path_in_value_ns(self.db.upcast(), c)
14261425
.ok_or_else(unresolved_name)?;
14271426
match pr {
14281427
ResolveValueResult::ValueNs(v, _) => {

0 commit comments

Comments
 (0)