Skip to content

Commit 4bdccab

Browse files
committed
Borrowck: Simplify SCC annotation computation, placeholder rewriting
This simplifies the `PlaceholderReachability` `enum` by replacing the case when no placeholders were reached with a standard `Option::None`. It also rewrites the API for `scc::Annotations` to be update-mut rather than a more Functional programming style. This showed some slight performance impact in early tests of the PR and definitely makes the implementation simpler.
1 parent 78df2f9 commit 4bdccab

3 files changed

Lines changed: 58 additions & 102 deletions

File tree

compiler/rustc_borrowck/src/handle_placeholders.rs

Lines changed: 42 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -62,65 +62,31 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
6262
}
6363

6464
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
65-
enum PlaceholderReachability {
66-
/// This SCC reaches no placeholders.
67-
NoPlaceholders,
68-
/// This SCC reaches at least one placeholder.
69-
Placeholders {
70-
/// The largest-universed placeholder we can reach
71-
max_universe: (UniverseIndex, RegionVid),
72-
73-
/// The placeholder with the smallest ID
74-
min_placeholder: RegionVid,
75-
76-
/// The placeholder with the largest ID
77-
max_placeholder: RegionVid,
78-
},
65+
struct PlaceholderReachability {
66+
/// The largest-universed placeholder we can reach
67+
max_universe: (UniverseIndex, RegionVid),
68+
69+
/// The placeholder with the smallest ID
70+
min_placeholder: RegionVid,
71+
72+
/// The placeholder with the largest ID
73+
max_placeholder: RegionVid,
7974
}
8075

8176
impl PlaceholderReachability {
8277
/// Merge the reachable placeholders of two graph components.
83-
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
84-
use PlaceholderReachability::*;
85-
match (self, other) {
86-
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
87-
(NoPlaceholders, p @ Placeholders { .. })
88-
| (p @ Placeholders { .. }, NoPlaceholders) => p,
89-
(
90-
Placeholders {
91-
min_placeholder: min_pl,
92-
max_placeholder: max_pl,
93-
max_universe: max_u,
94-
},
95-
Placeholders { min_placeholder, max_placeholder, max_universe },
96-
) => Placeholders {
97-
min_placeholder: min_pl.min(min_placeholder),
98-
max_placeholder: max_pl.max(max_placeholder),
99-
max_universe: max_u.max(max_universe),
100-
},
101-
}
102-
}
103-
104-
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
105-
match self {
106-
Self::NoPlaceholders => None,
107-
Self::Placeholders { max_universe, .. } => Some(*max_universe),
108-
}
109-
}
110-
111-
/// If we have reached placeholders, determine if they can
112-
/// be named from this universe.
113-
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
114-
self.max_universe()
115-
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
78+
fn merge(&mut self, other: &Self) {
79+
self.max_universe = self.max_universe.max(other.max_universe);
80+
self.min_placeholder = self.min_placeholder.min(other.min_placeholder);
81+
self.max_placeholder = self.max_placeholder.max(other.max_placeholder);
11682
}
11783
}
11884

11985
/// An annotation for region graph SCCs that tracks
12086
/// the values of its elements. This annotates a single SCC.
12187
#[derive(Copy, Debug, Clone)]
12288
pub(crate) struct RegionTracker {
123-
reachable_placeholders: PlaceholderReachability,
89+
reachable_placeholders: Option<PlaceholderReachability>,
12490

12591
/// The largest universe nameable from this SCC.
12692
/// It is the smallest nameable universes of all
@@ -135,13 +101,13 @@ impl RegionTracker {
135101
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
136102
let reachable_placeholders =
137103
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
138-
PlaceholderReachability::Placeholders {
104+
Some(PlaceholderReachability {
139105
max_universe: (definition.universe, rvid),
140106
min_placeholder: rvid,
141107
max_placeholder: rvid,
142-
}
108+
})
143109
} else {
144-
PlaceholderReachability::NoPlaceholders
110+
None
145111
};
146112

147113
Self {
@@ -159,43 +125,46 @@ impl RegionTracker {
159125
}
160126

161127
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
162-
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
163-
universe
164-
} else {
165-
UniverseIndex::ROOT
166-
}
128+
self.reachable_placeholders.map(|pls| pls.max_universe.0).unwrap_or(UniverseIndex::ROOT)
129+
}
130+
131+
/// Can all reachable placeholders be named from `from`?
132+
/// True vacuously in case no placeholders were reached.
133+
fn placeholders_can_be_named_by(&self, from: UniverseIndex) -> bool {
134+
self.reachable_placeholders.is_none_or(|pls| from.can_name(pls.max_universe.0))
167135
}
168136

169137
/// Determine if we can name all the placeholders in `other`.
170138
pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool {
171-
other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0)
139+
// HACK: We first check whether we can name the highest existential universe
140+
// of `other`. This only exists to avoid errors in case that scc already
141+
// depends on a placeholder it cannot name itself.
142+
self.max_nameable_universe().can_name(other.max_nameable_universe())
143+
|| other.placeholders_can_be_named_by(self.max_nameable_universe.0)
172144
}
173145

174146
/// If this SCC reaches a placeholder it can't name, return it.
175147
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
176-
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
177-
!self.max_nameable_universe().can_name(placeholder_universe)
178-
})
148+
self.reachable_placeholders
149+
.filter(|pls| !self.max_nameable_universe().can_name(pls.max_universe.0))
150+
.map(|pls| pls.max_universe)
179151
}
180152
}
181153

182154
impl scc::Annotation for RegionTracker {
183-
fn merge_scc(self, other: Self) -> Self {
155+
fn update_scc(&mut self, other: &Self) {
184156
trace!("{:?} << {:?}", self.representative, other.representative);
185-
186-
Self {
187-
representative: self.representative.min(other.representative),
188-
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
189-
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
190-
}
157+
self.representative = self.representative.min(other.representative);
158+
self.update_reachable(other);
191159
}
192160

193-
fn merge_reached(self, other: Self) -> Self {
194-
Self {
195-
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
196-
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
197-
representative: self.representative,
198-
}
161+
fn update_reachable(&mut self, other: &Self) {
162+
self.max_nameable_universe = self.max_nameable_universe.min(other.max_nameable_universe);
163+
match (self.reachable_placeholders.as_mut(), other.reachable_placeholders.as_ref()) {
164+
(None, None) | (Some(_), None) => (),
165+
(None, Some(theirs)) => self.reachable_placeholders = Some(*theirs),
166+
(Some(ours), Some(theirs)) => ours.merge(theirs),
167+
};
199168
}
200169
}
201170

compiler/rustc_data_structures/src/graph/scc/mod.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,18 @@ mod tests;
2727
/// the max/min element of the SCC, or all of the above.
2828
///
2929
/// Concretely, the both merge operations must commute, e.g. where `merge`
30-
/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)`
30+
/// is `update_scc` and `update_reached`: `a.merge(b) == b.merge(a)`
3131
///
3232
/// In general, what you want is probably always min/max according
3333
/// to some ordering, potentially with side constraints (min x such
3434
/// that P holds).
3535
pub trait Annotation: Debug + Copy {
3636
/// Merge two existing annotations into one during
37-
/// path compression.o
38-
fn merge_scc(self, other: Self) -> Self;
37+
/// path compression.
38+
fn update_scc(&mut self, other: &Self);
3939

4040
/// Merge a successor into this annotation.
41-
fn merge_reached(self, other: Self) -> Self;
42-
43-
fn update_scc(&mut self, other: Self) {
44-
*self = self.merge_scc(other)
45-
}
46-
47-
fn update_reachable(&mut self, other: Self) {
48-
*self = self.merge_reached(other)
49-
}
41+
fn update_reachable(&mut self, other: &Self);
5042
}
5143

5244
/// An accumulator for annotations.
@@ -70,12 +62,8 @@ impl<N: Idx, S: Idx + Ord> Annotations<N> for NoAnnotations<S> {
7062

7163
/// The empty annotation, which does nothing.
7264
impl Annotation for () {
73-
fn merge_reached(self, _other: Self) -> Self {
74-
()
75-
}
76-
fn merge_scc(self, _other: Self) -> Self {
77-
()
78-
}
65+
fn update_reachable(&mut self, _other: &Self) {}
66+
fn update_scc(&mut self, _other: &Self) {}
7967
}
8068

8169
/// Strongly connected components (SCC) of a graph. The type `N` is
@@ -614,7 +602,7 @@ where
614602
*min_depth = successor_min_depth;
615603
*min_cycle_root = successor_node;
616604
}
617-
current_component_annotation.update_scc(successor_annotation);
605+
current_component_annotation.update_scc(&successor_annotation);
618606
}
619607
// The starting node `node` is succeeded by a fully identified SCC
620608
// which is now added to the set under `scc_index`.
@@ -629,7 +617,7 @@ where
629617
// the `successors_stack` for later.
630618
trace!(?node, ?successor_scc_index);
631619
successors_stack.push(successor_scc_index);
632-
current_component_annotation.update_reachable(successor_annotation);
620+
current_component_annotation.update_reachable(&successor_annotation);
633621
}
634622
// `node` has no more (direct) successors; search recursively.
635623
None => {

compiler/rustc_data_structures/src/graph/scc/tests.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ impl Maxes {
3232
}
3333

3434
impl Annotation for MaxReached {
35-
fn merge_scc(self, other: Self) -> Self {
36-
Self(std::cmp::max(other.0, self.0))
35+
fn update_scc(&mut self, other: &Self) {
36+
self.0 = self.0.max(other.0);
3737
}
3838

39-
fn merge_reached(self, other: Self) -> Self {
40-
Self(std::cmp::max(other.0, self.0))
39+
fn update_reachable(&mut self, other: &Self) {
40+
self.0 = self.0.max(other.0);
4141
}
4242
}
4343

@@ -75,13 +75,12 @@ impl Annotations<usize> for MinMaxes {
7575
}
7676

7777
impl Annotation for MinMaxIn {
78-
fn merge_scc(self, other: Self) -> Self {
79-
Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) }
78+
fn update_scc(&mut self, other: &Self) {
79+
self.min = self.min.min(other.min);
80+
self.max = self.max.max(other.max);
8081
}
8182

82-
fn merge_reached(self, _other: Self) -> Self {
83-
self
84-
}
83+
fn update_reachable(&mut self, _other: &Self) {}
8584
}
8685

8786
#[test]

0 commit comments

Comments
 (0)