Skip to content

Commit e608d8f

Browse files
committed
Make DefIdForest cheaper to clone
Since `DefIdForest` contains 0 or 1 elements the large majority of the time, by allocating only in the >1 case we avoid almost all allocations, compared to `Arc<SmallVec<[DefId;1]>>`. This shaves off 0.2% on the benchmark that stresses uninhabitedness checking.
1 parent 8598c9f commit e608d8f

File tree

3 files changed

+81
-59
lines changed

3 files changed

+81
-59
lines changed

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,7 @@ rustc_queries! {
13141314
/// check whether the forest is empty.
13151315
query type_uninhabited_from(
13161316
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1317-
) -> Arc<ty::inhabitedness::DefIdForest> {
1317+
) -> ty::inhabitedness::DefIdForest {
13181318
desc { "computing the inhabitedness of `{:?}`", key }
13191319
}
13201320
}

compiler/rustc_middle/src/ty/inhabitedness/def_id_forest.rs

+76-51
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use crate::ty::{DefId, DefIdTree};
33
use rustc_hir::CRATE_HIR_ID;
44
use smallvec::SmallVec;
55
use std::mem;
6+
use std::sync::Arc;
7+
8+
use DefIdForest::*;
69

710
/// Represents a forest of `DefId`s closed under the ancestor relation. That is,
811
/// if a `DefId` representing a module is contained in the forest then all
@@ -11,45 +14,77 @@ use std::mem;
1114
///
1215
/// This is used to represent a set of modules in which a type is visibly
1316
/// uninhabited.
17+
///
18+
/// We store the minimal set of `DefId`s required to represent the whole set. If A and B are
19+
/// `DefId`s in the `DefIdForest`, and A is a parent of B, then only A will be stored. When this is
20+
/// used with `type_uninhabited_from`, there will very rarely be more than one `DefId` stored.
1421
#[derive(Clone, HashStable)]
15-
pub struct DefIdForest {
16-
/// The minimal set of `DefId`s required to represent the whole set.
17-
/// If A and B are DefIds in the `DefIdForest`, and A is a descendant
18-
/// of B, then only B will be in `root_ids`.
19-
/// We use a `SmallVec` here because (for its use for caching inhabitedness)
20-
/// it's rare that this will contain even two IDs.
21-
root_ids: SmallVec<[DefId; 1]>,
22+
pub enum DefIdForest {
23+
Empty,
24+
Single(DefId),
25+
/// This variant is very rare.
26+
/// Invariant: >1 elements
27+
/// We use `Arc` because this is used in the output of a query.
28+
Multiple(Arc<[DefId]>),
29+
}
30+
31+
/// Tests whether a slice of roots contains a given DefId.
32+
#[inline]
33+
fn slice_contains(tcx: TyCtxt<'tcx>, slice: &[DefId], id: DefId) -> bool {
34+
slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
2235
}
2336

2437
impl<'tcx> DefIdForest {
2538
/// Creates an empty forest.
2639
pub fn empty() -> DefIdForest {
27-
DefIdForest { root_ids: SmallVec::new() }
40+
DefIdForest::Empty
2841
}
2942

3043
/// Creates a forest consisting of a single tree representing the entire
3144
/// crate.
3245
#[inline]
3346
pub fn full(tcx: TyCtxt<'tcx>) -> DefIdForest {
34-
let crate_id = tcx.hir().local_def_id(CRATE_HIR_ID);
35-
DefIdForest::from_id(crate_id.to_def_id())
47+
DefIdForest::from_id(tcx.hir().local_def_id(CRATE_HIR_ID).to_def_id())
3648
}
3749

3850
/// Creates a forest containing a `DefId` and all its descendants.
3951
pub fn from_id(id: DefId) -> DefIdForest {
40-
let mut root_ids = SmallVec::new();
41-
root_ids.push(id);
42-
DefIdForest { root_ids }
52+
DefIdForest::Single(id)
53+
}
54+
55+
fn as_slice(&self) -> &[DefId] {
56+
match self {
57+
Empty => &[],
58+
Single(id) => std::slice::from_ref(id),
59+
Multiple(root_ids) => root_ids,
60+
}
61+
}
62+
63+
// Only allocates in the rare `Multiple` case.
64+
fn from_slice(root_ids: &[DefId]) -> DefIdForest {
65+
match root_ids {
66+
[] => Empty,
67+
[id] => Single(*id),
68+
_ => DefIdForest::Multiple(root_ids.into()),
69+
}
4370
}
4471

4572
/// Tests whether the forest is empty.
4673
pub fn is_empty(&self) -> bool {
47-
self.root_ids.is_empty()
74+
match self {
75+
Empty => true,
76+
Single(..) | Multiple(..) => false,
77+
}
78+
}
79+
80+
/// Iterate over the set of roots.
81+
fn iter(&self) -> impl Iterator<Item = DefId> + '_ {
82+
self.as_slice().iter().copied()
4883
}
4984

5085
/// Tests whether the forest contains a given DefId.
5186
pub fn contains(&self, tcx: TyCtxt<'tcx>, id: DefId) -> bool {
52-
self.root_ids.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
87+
slice_contains(tcx, self.as_slice(), id)
5388
}
5489

5590
/// Calculate the intersection of a collection of forests.
@@ -58,65 +93,55 @@ impl<'tcx> DefIdForest {
5893
I: IntoIterator<Item = DefIdForest>,
5994
{
6095
let mut iter = iter.into_iter();
61-
let mut ret = if let Some(first) = iter.next() {
62-
first
96+
let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() {
97+
SmallVec::from_slice(first.as_slice())
6398
} else {
6499
return DefIdForest::full(tcx);
65100
};
66101

67-
let mut next_ret = SmallVec::new();
68-
let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new();
102+
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
69103
for next_forest in iter {
70104
// No need to continue if the intersection is already empty.
71-
if ret.is_empty() {
72-
break;
105+
if ret.is_empty() || next_forest.is_empty() {
106+
return DefIdForest::empty();
73107
}
74108

75-
// `next_ret` and `old_ret` are empty here.
76-
// We keep the elements in `ret` that are also in `next_forest`. Those that aren't are
77-
// put back in `ret` via `old_ret`.
78-
for id in ret.root_ids.drain(..) {
79-
if next_forest.contains(tcx, id) {
80-
next_ret.push(id);
81-
} else {
82-
old_ret.push(id);
83-
}
84-
}
85-
ret.root_ids.extend(old_ret.drain(..));
86-
109+
// We keep the elements in `ret` that are also in `next_forest`.
110+
next_ret.extend(ret.iter().copied().filter(|&id| next_forest.contains(tcx, id)));
87111
// We keep the elements in `next_forest` that are also in `ret`.
88-
// You'd think this is not needed because `next_ret` already contains `ret \inter
89-
// next_forest`. But those aren't just sets of things. If `ret = [a]`, `next_forest =
90-
// [b]` and `b` is a submodule of `a`, then `b` belongs in the intersection but we
91-
// didn't catch it in the loop above.
92-
next_ret.extend(next_forest.root_ids.into_iter().filter(|&id| ret.contains(tcx, id)));
93-
// `next_ret` now contains the intersection of the original `ret` and `next_forest`.
94-
95-
mem::swap(&mut next_ret, &mut ret.root_ids);
96-
next_ret.drain(..);
112+
next_ret.extend(next_forest.iter().filter(|&id| slice_contains(tcx, &ret, id)));
113+
114+
mem::swap(&mut next_ret, &mut ret);
115+
next_ret.clear();
97116
}
98-
ret
117+
DefIdForest::from_slice(&ret)
99118
}
100119

101120
/// Calculate the union of a collection of forests.
102121
pub fn union<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest
103122
where
104123
I: IntoIterator<Item = DefIdForest>,
105124
{
106-
let mut ret = DefIdForest::empty();
107-
let mut next_ret = SmallVec::new();
125+
let mut ret: SmallVec<[_; 1]> = SmallVec::new();
126+
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
108127
for next_forest in iter {
109-
next_ret.extend(ret.root_ids.drain(..).filter(|&id| !next_forest.contains(tcx, id)));
128+
// Union with the empty set is a no-op.
129+
if next_forest.is_empty() {
130+
continue;
131+
}
110132

111-
for id in next_forest.root_ids {
112-
if !next_ret.contains(&id) {
133+
// We add everything in `ret` that is not in `next_forest`.
134+
next_ret.extend(ret.iter().copied().filter(|&id| !next_forest.contains(tcx, id)));
135+
// We add everything in `next_forest` that we haven't added yet.
136+
for id in next_forest.iter() {
137+
if !slice_contains(tcx, &next_ret, id) {
113138
next_ret.push(id);
114139
}
115140
}
116141

117-
mem::swap(&mut next_ret, &mut ret.root_ids);
118-
next_ret.drain(..);
142+
mem::swap(&mut next_ret, &mut ret);
143+
next_ret.clear();
119144
}
120-
ret
145+
DefIdForest::from_slice(&ret)
121146
}
122147
}

compiler/rustc_middle/src/ty/inhabitedness/mod.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef};
77
use crate::ty::{AdtKind, Visibility};
88
use crate::ty::{DefId, SubstsRef};
99

10-
use std::sync::Arc;
11-
1210
mod def_id_forest;
1311

1412
// The methods in this module calculate `DefIdForest`s of modules in which a
@@ -193,18 +191,18 @@ impl<'tcx> TyS<'tcx> {
193191
tcx: TyCtxt<'tcx>,
194192
param_env: ty::ParamEnv<'tcx>,
195193
) -> DefIdForest {
196-
tcx.type_uninhabited_from(param_env.and(self)).as_ref().clone()
194+
tcx.type_uninhabited_from(param_env.and(self))
197195
}
198196
}
199197

200198
// Query provider for `type_uninhabited_from`.
201199
pub(crate) fn type_uninhabited_from<'tcx>(
202200
tcx: TyCtxt<'tcx>,
203201
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
204-
) -> Arc<DefIdForest> {
202+
) -> DefIdForest {
205203
let ty = key.value;
206204
let param_env = key.param_env;
207-
let forest = match *ty.kind() {
205+
match *ty.kind() {
208206
Adt(def, substs) => def.uninhabited_from(tcx, substs, param_env),
209207

210208
Never => DefIdForest::full(tcx),
@@ -229,6 +227,5 @@ pub(crate) fn type_uninhabited_from<'tcx>(
229227
Ref(..) => DefIdForest::empty(),
230228

231229
_ => DefIdForest::empty(),
232-
};
233-
Arc::new(forest)
230+
}
234231
}

0 commit comments

Comments
 (0)