Skip to content

Commit c7afaa1

Browse files
committed
Handle break and continue. Change fixpoint computation to handle unreachable nodes.
1 parent ff0e8f4 commit c7afaa1

File tree

2 files changed

+134
-64
lines changed

2 files changed

+134
-64
lines changed

compiler/rustc_typeck/src/check/generator_interior.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,20 @@ pub fn resolve_interior<'a, 'tcx>(
246246
)
247247
.consume_body(body);
248248

249-
let mut drop_range_visitor = DropRangeVisitor::from(expr_use_visitor);
249+
let region_scope_tree = fcx.tcx.region_scope_tree(def_id);
250+
251+
let mut drop_range_visitor = DropRangeVisitor::from(
252+
expr_use_visitor,
253+
region_scope_tree.body_expr_count(body.id()).unwrap_or(0),
254+
);
250255
intravisit::walk_body(&mut drop_range_visitor, body);
251256

252257
drop_range_visitor.drop_ranges.propagate_to_fixpoint();
253258

254259
InteriorVisitor {
255260
fcx,
256261
types: FxIndexSet::default(),
257-
region_scope_tree: fcx.tcx.region_scope_tree(def_id),
262+
region_scope_tree,
258263
expr_count: 0,
259264
kind,
260265
prev_unresolved_span: None,
@@ -699,11 +704,12 @@ struct DropRangeVisitor<'tcx> {
699704
}
700705

701706
impl<'tcx> DropRangeVisitor<'tcx> {
702-
fn from(uses: ExprUseDelegate<'tcx>) -> Self {
707+
fn from(uses: ExprUseDelegate<'tcx>, num_exprs: usize) -> Self {
703708
debug!("consumed_places: {:?}", uses.consumed_places);
704709
let drop_ranges = DropRanges::new(
705710
uses.consumed_places.iter().flat_map(|(_, places)| places.iter().copied()),
706711
&uses.hir,
712+
num_exprs,
707713
);
708714
Self {
709715
hir: uses.hir,
@@ -851,18 +857,18 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
851857
ExprKind::If(test, if_true, if_false) => {
852858
self.visit_expr(test);
853859

854-
let fork = self.expr_count - 1;
860+
let fork = self.expr_count;
855861

856-
self.drop_ranges.add_control_edge(fork, self.expr_count);
862+
self.drop_ranges.add_control_edge(fork, self.expr_count + 1);
857863
self.visit_expr(if_true);
858-
let true_end = self.expr_count - 1;
864+
let true_end = self.expr_count;
859865

866+
self.drop_ranges.add_control_edge(fork, self.expr_count + 1);
860867
if let Some(if_false) = if_false {
861-
self.drop_ranges.add_control_edge(fork, self.expr_count);
862868
self.visit_expr(if_false);
863869
}
864870

865-
self.drop_ranges.add_control_edge(true_end, self.expr_count);
871+
self.drop_ranges.add_control_edge(true_end, self.expr_count + 1);
866872
}
867873
ExprKind::Assign(lhs, rhs, _) => {
868874
self.visit_expr(lhs);
@@ -873,13 +879,13 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
873879
ExprKind::Loop(body, ..) => {
874880
let loop_begin = self.expr_count;
875881
self.visit_block(body);
876-
self.drop_ranges.add_control_edge(self.expr_count - 1, loop_begin);
882+
self.drop_ranges.add_control_edge(self.expr_count, loop_begin);
877883
}
878884
ExprKind::Match(scrutinee, arms, ..) => {
879885
self.visit_expr(scrutinee);
880886

881887
let fork = self.expr_count - 1;
882-
let arm_drops = arms
888+
let arm_end_ids = arms
883889
.iter()
884890
.map(|Arm { pat, body, guard, .. }| {
885891
self.drop_ranges.add_control_edge(fork, self.expr_count);
@@ -893,16 +899,22 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
893899
None => (),
894900
}
895901
self.visit_expr(body);
896-
self.expr_count - 1
902+
self.expr_count
897903
})
898904
.collect::<Vec<_>>();
899-
arm_drops.into_iter().for_each(|arm_end| {
900-
self.drop_ranges.add_control_edge(arm_end, self.expr_count)
905+
arm_end_ids.into_iter().for_each(|arm_end| {
906+
self.drop_ranges.add_control_edge(arm_end, self.expr_count + 1)
901907
});
902908
}
909+
ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..)
910+
| ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => {
911+
self.drop_ranges.add_control_edge_hir_id(self.expr_count, target);
912+
}
913+
903914
_ => intravisit::walk_expr(self, expr),
904915
}
905916

917+
self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_count);
906918
self.expr_count += 1;
907919
self.consume_expr(expr);
908920
if let Some(expr) = reinit {
Lines changed: 109 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::collections::BTreeMap;
2+
use std::fmt::Debug;
3+
use std::mem::swap;
4+
15
use rustc_hir::{HirId, HirIdMap};
26
use rustc_index::bit_set::BitSet;
37
use rustc_index::vec::IndexVec;
@@ -20,6 +24,19 @@ rustc_index::newtype_index! {
2024
pub struct DropRanges {
2125
hir_id_map: HirIdMap<HirIdIndex>,
2226
nodes: IndexVec<PostOrderId, NodeInfo>,
27+
deferred_edges: Vec<(usize, HirId)>,
28+
// FIXME: This should only be used for loops and break/continue.
29+
post_order_map: HirIdMap<usize>,
30+
}
31+
32+
impl Debug for DropRanges {
33+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34+
f.debug_struct("DropRanges")
35+
.field("hir_id_map", &self.hir_id_map)
36+
.field("post_order_maps", &self.post_order_map)
37+
.field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>())
38+
.finish()
39+
}
2340
}
2441

2542
/// DropRanges keeps track of what values are definitely dropped at each point in the code.
@@ -29,7 +46,7 @@ pub struct DropRanges {
2946
/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely
3047
/// dropped at the point of the node identified by post_order_id.
3148
impl DropRanges {
32-
pub fn new(hir_ids: impl Iterator<Item = HirId>, hir: &Map<'_>) -> Self {
49+
pub fn new(hir_ids: impl Iterator<Item = HirId>, hir: &Map<'_>, num_exprs: usize) -> Self {
3350
let mut hir_id_map = HirIdMap::<HirIdIndex>::default();
3451
let mut next = <_>::from(0u32);
3552
for hir_id in hir_ids {
@@ -41,7 +58,13 @@ impl DropRanges {
4158
});
4259
}
4360
debug!("hir_id_map: {:?}", hir_id_map);
44-
Self { hir_id_map, nodes: <_>::default() }
61+
let num_values = hir_id_map.len();
62+
Self {
63+
hir_id_map,
64+
nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1),
65+
deferred_edges: <_>::default(),
66+
post_order_map: <_>::default(),
67+
}
4568
}
4669

4770
fn hidx(&self, hir_id: HirId) -> HirIdIndex {
@@ -52,17 +75,23 @@ impl DropRanges {
5275
self.hir_id_map
5376
.get(&hir_id)
5477
.copied()
55-
.map_or(false, |hir_id| self.node(location.into()).drop_state.contains(hir_id))
78+
.map_or(false, |hir_id| self.expect_node(location.into()).drop_state.contains(hir_id))
5679
}
5780

5881
/// Returns the number of values (hir_ids) that are tracked
5982
fn num_values(&self) -> usize {
6083
self.hir_id_map.len()
6184
}
6285

63-
fn node(&mut self, id: PostOrderId) -> &NodeInfo {
64-
let size = self.num_values();
65-
self.nodes.ensure_contains_elem(id, || NodeInfo::new(size));
86+
/// Adds an entry in the mapping from HirIds to PostOrderIds
87+
///
88+
/// Needed so that `add_control_edge_hir_id` can work.
89+
pub fn add_node_mapping(&mut self, hir_id: HirId, post_order_id: usize) {
90+
self.post_order_map.insert(hir_id, post_order_id);
91+
}
92+
93+
/// Returns a reference to the NodeInfo for a node, panicking if it does not exist
94+
fn expect_node(&self, id: PostOrderId) -> &NodeInfo {
6695
&self.nodes[id]
6796
}
6897

@@ -73,9 +102,32 @@ impl DropRanges {
73102
}
74103

75104
pub fn add_control_edge(&mut self, from: usize, to: usize) {
105+
trace!("adding control edge from {} to {}", from, to);
76106
self.node_mut(from.into()).successors.push(to.into());
77107
}
78108

109+
/// Like add_control_edge, but uses a hir_id as the target.
110+
///
111+
/// This can be used for branches where we do not know the PostOrderId of the target yet,
112+
/// such as when handling `break` or `continue`.
113+
pub fn add_control_edge_hir_id(&mut self, from: usize, to: HirId) {
114+
self.deferred_edges.push((from, to));
115+
}
116+
117+
/// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them.
118+
///
119+
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
120+
/// edges will be missed.
121+
fn process_deferred_edges(&mut self) {
122+
let mut edges = vec![];
123+
swap(&mut edges, &mut self.deferred_edges);
124+
edges.into_iter().for_each(|(from, to)| {
125+
let to = *self.post_order_map.get(&to).expect("Expression ID not found");
126+
trace!("Adding deferred edge from {} to {}", from, to);
127+
self.add_control_edge(from, to)
128+
});
129+
}
130+
79131
pub fn drop_at(&mut self, value: HirId, location: usize) {
80132
let value = self.hidx(value);
81133
self.node_mut(location.into()).drops.push(value);
@@ -92,40 +144,65 @@ impl DropRanges {
92144
}
93145

94146
pub fn propagate_to_fixpoint(&mut self) {
95-
while self.propagate() {}
96-
}
147+
trace!("before fixpoint: {:#?}", self);
148+
self.process_deferred_edges();
149+
let preds = self.compute_predecessors();
150+
151+
trace!("predecessors: {:#?}", preds.iter_enumerated().collect::<BTreeMap<_, _>>());
152+
153+
let mut propagate = || {
154+
let mut changed = false;
155+
for id in self.nodes.indices() {
156+
let old_state = self.nodes[id].drop_state.clone();
157+
if preds[id].len() != 0 {
158+
self.nodes[id].drop_state = self.nodes[preds[id][0]].drop_state.clone();
159+
for pred in &preds[id][1..] {
160+
let state = self.nodes[*pred].drop_state.clone();
161+
self.nodes[id].drop_state.intersect(&state);
162+
}
163+
} else {
164+
self.nodes[id].drop_state = if id.index() == 0 {
165+
BitSet::new_empty(self.num_values())
166+
} else {
167+
// If we are not the start node and we have no predecessors, treat
168+
// everything as dropped because there's no way to get here anyway.
169+
BitSet::new_filled(self.num_values())
170+
};
171+
};
172+
for drop in &self.nodes[id].drops.clone() {
173+
self.nodes[id].drop_state.insert(*drop);
174+
}
175+
for reinit in &self.nodes[id].reinits.clone() {
176+
self.nodes[id].drop_state.remove(*reinit);
177+
}
97178

98-
fn propagate(&mut self) -> bool {
99-
let mut visited = BitSet::new_empty(self.nodes.len());
179+
changed |= old_state != self.nodes[id].drop_state;
180+
}
100181

101-
self.visit(&mut visited, PostOrderId::from(0usize), PostOrderId::from(0usize), false)
102-
}
182+
changed
183+
};
103184

104-
fn visit(
105-
&mut self,
106-
visited: &mut BitSet<PostOrderId>,
107-
id: PostOrderId,
108-
pred_id: PostOrderId,
109-
mut changed: bool,
110-
) -> bool {
111-
if visited.contains(id) {
112-
return changed;
113-
}
114-
visited.insert(id);
185+
while propagate() {}
115186

116-
changed &= self.nodes[id].merge_with(&self.nodes[pred_id]);
187+
trace!("after fixpoint: {:#?}", self);
188+
}
117189

118-
if self.nodes[id].successors.len() == 0 {
119-
self.visit(visited, PostOrderId::from(id.index() + 1), id, changed)
120-
} else {
121-
self.nodes[id]
122-
.successors
123-
.iter()
124-
.fold(changed, |changed, &succ| self.visit(visited, succ, id, changed))
190+
fn compute_predecessors(&self) -> IndexVec<PostOrderId, Vec<PostOrderId>> {
191+
let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len());
192+
for (id, node) in self.nodes.iter_enumerated() {
193+
if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 {
194+
preds[<_>::from(id.index() + 1)].push(id);
195+
} else {
196+
for succ in &node.successors {
197+
preds[*succ].push(id);
198+
}
199+
}
125200
}
201+
preds
126202
}
127203
}
128204

205+
#[derive(Debug)]
129206
struct NodeInfo {
130207
/// IDs of nodes that can follow this one in the control flow
131208
///
@@ -148,26 +225,7 @@ impl NodeInfo {
148225
successors: vec![],
149226
drops: vec![],
150227
reinits: vec![],
151-
drop_state: BitSet::new_empty(num_values),
228+
drop_state: BitSet::new_filled(num_values),
152229
}
153230
}
154-
155-
fn merge_with(&mut self, other: &NodeInfo) -> bool {
156-
let mut changed = false;
157-
for place in &self.drops {
158-
if !self.drop_state.contains(place) && !self.reinits.contains(&place) {
159-
changed = true;
160-
self.drop_state.insert(place);
161-
}
162-
}
163-
164-
for place in &self.reinits {
165-
if self.drop_state.contains(place) {
166-
changed = true;
167-
self.drop_state.remove(place);
168-
}
169-
}
170-
171-
changed
172-
}
173231
}

0 commit comments

Comments
 (0)