Skip to content

Commit e4c3571

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 756e97b commit e4c3571

File tree

3 files changed

+72
-54
lines changed

3 files changed

+72
-54
lines changed

compiler/rustc_middle/src/query/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ rustc_queries! {
12801280
/// check whether the forest is empty.
12811281
query type_uninhabited_from(
12821282
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
1283-
) -> Arc<ty::inhabitedness::DefIdForest> {
1283+
) -> ty::inhabitedness::DefIdForest {
12841284
desc { "computing the inhabitedness of `{:?}`", key }
12851285
}
12861286
}

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

+67-46
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,20 +14,30 @@ 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-
/// its 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
@@ -37,19 +50,42 @@ impl<'tcx> DefIdForest {
3750

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

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

5086
/// Tests whether the forest contains a given DefId.
5187
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))
88+
slice_contains(tcx, self.as_slice(), id)
5389
}
5490

5591
/// Calculate the intersection of a collection of forests.
@@ -58,65 +94,50 @@ impl<'tcx> DefIdForest {
5894
I: IntoIterator<Item = DefIdForest>,
5995
{
6096
let mut iter = iter.into_iter();
61-
let mut ret = if let Some(first) = iter.next() {
62-
first
97+
let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() {
98+
SmallVec::from_slice(first.as_slice())
6399
} else {
64100
return DefIdForest::full(tcx);
65101
};
66102

67-
let mut next_ret = SmallVec::new();
68-
let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new();
103+
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
69104
for next_forest in iter {
70105
// No need to continue if the intersection is already empty.
71106
if ret.is_empty() {
72107
break;
73108
}
74109

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-
110+
// We keep the elements in `ret` that are also in `next_forest`.
111+
next_ret.extend(ret.iter().copied().filter(|&id| next_forest.contains(tcx, id)));
87112
// 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);
113+
next_ret.extend(next_forest.iter().filter(|&id| slice_contains(tcx, &ret, id)));
114+
115+
mem::swap(&mut next_ret, &mut ret);
96116
next_ret.drain(..);
97117
}
98-
ret
118+
DefIdForest::from_slice(&ret)
99119
}
100120

101121
/// Calculate the union of a collection of forests.
102122
pub fn union<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest
103123
where
104124
I: IntoIterator<Item = DefIdForest>,
105125
{
106-
let mut ret = DefIdForest::empty();
107-
let mut next_ret = SmallVec::new();
126+
let mut ret: SmallVec<[_; 1]> = SmallVec::new();
127+
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
108128
for next_forest in iter {
109-
next_ret.extend(ret.root_ids.drain(..).filter(|&id| !next_forest.contains(tcx, id)));
110-
111-
for id in next_forest.root_ids {
112-
if !next_ret.contains(&id) {
129+
// We add everything in `ret` that is not in `next_forest`.
130+
next_ret.extend(ret.iter().copied().filter(|&id| !next_forest.contains(tcx, id)));
131+
// We add everything in `next_forest` that we haven't added yet.
132+
for id in next_forest.iter() {
133+
if !slice_contains(tcx, &next_ret, id) {
113134
next_ret.push(id);
114135
}
115136
}
116137

117-
mem::swap(&mut next_ret, &mut ret.root_ids);
138+
mem::swap(&mut next_ret, &mut ret);
118139
next_ret.drain(..);
119140
}
120-
ret
141+
DefIdForest::from_slice(&ret)
121142
}
122143
}

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)