Skip to content

Commit e5b2270

Browse files
committed
Handle Continue correctly
1 parent f462bca commit e5b2270

File tree

3 files changed

+107
-47
lines changed

3 files changed

+107
-47
lines changed

compiler/rustc_ast/src/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mod tests;
5454
/// ```
5555
///
5656
/// `'outer` is a label.
57-
#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic)]
57+
#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)]
5858
pub struct Label {
5959
pub ident: Ident,
6060
}

compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs

+89-46
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{
44
};
55
use hir::{
66
intravisit::{self, Visitor},
7-
Body, Expr, ExprKind, Guard, HirId,
7+
Body, Expr, ExprKind, Guard, HirId, LoopIdError,
88
};
99
use rustc_data_structures::fx::FxHashMap;
1010
use rustc_hir as hir;
@@ -85,6 +85,7 @@ struct DropRangeVisitor<'a, 'tcx> {
8585
expr_index: PostOrderId,
8686
tcx: TyCtxt<'tcx>,
8787
typeck_results: &'a TypeckResults<'tcx>,
88+
label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>,
8889
}
8990

9091
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
@@ -101,7 +102,15 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
101102
hir,
102103
num_exprs,
103104
);
104-
Self { hir, places, drop_ranges, expr_index: PostOrderId::from_u32(0), typeck_results, tcx }
105+
Self {
106+
hir,
107+
places,
108+
drop_ranges,
109+
expr_index: PostOrderId::from_u32(0),
110+
typeck_results,
111+
tcx,
112+
label_stack: vec![],
113+
}
105114
}
106115

107116
fn record_drop(&mut self, value: TrackedValue) {
@@ -210,46 +219,61 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
210219
}
211220
}
212221

213-
/// Break and continue expression targets might be another expression or a block,
214-
/// but this analysis only looks at expressions. In case a break has a block as a
215-
/// target, this will find the last expression in the block and return its HirId
216-
/// instead.
217-
fn find_target_expression(&self, hir_id: HirId) -> HirId {
218-
let node = self.hir.get(hir_id);
219-
match node {
220-
hir::Node::Expr(_) => hir_id,
221-
hir::Node::Block(b) => b.expr.map_or_else(
222-
// If there is no tail expression, there will be at least one statement in the
223-
// block because the block contains a break or continue statement.
224-
|| b.stmts.last().unwrap().hir_id,
225-
|expr| expr.hir_id,
226-
),
227-
hir::Node::Param(..)
228-
| hir::Node::Item(..)
229-
| hir::Node::ForeignItem(..)
230-
| hir::Node::TraitItem(..)
231-
| hir::Node::ImplItem(..)
232-
| hir::Node::Variant(..)
233-
| hir::Node::Field(..)
234-
| hir::Node::AnonConst(..)
235-
| hir::Node::Stmt(..)
236-
| hir::Node::PathSegment(..)
237-
| hir::Node::Ty(..)
238-
| hir::Node::TraitRef(..)
239-
| hir::Node::Binding(..)
240-
| hir::Node::Pat(..)
241-
| hir::Node::Arm(..)
242-
| hir::Node::Local(..)
243-
| hir::Node::Ctor(..)
244-
| hir::Node::Lifetime(..)
245-
| hir::Node::GenericParam(..)
246-
| hir::Node::Visibility(..)
247-
| hir::Node::Crate(..)
248-
| hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
249-
}
222+
/// Map a Destination to an equivalent expression node
223+
///
224+
/// The destination field of a Break or Continue expression can target either an
225+
/// expression or a block. The drop range analysis, however, only deals in
226+
/// expression nodes, so blocks that might be the destination of a Break or Continue
227+
/// will not have a PostOrderId.
228+
///
229+
/// If the destination is an expression, this function will simply return that expression's
230+
/// hir_id. If the destination is a block, this function will return the hir_id of last
231+
/// expression in the block.
232+
fn find_target_expression_from_destination(
233+
&self,
234+
destination: hir::Destination,
235+
) -> Result<HirId, LoopIdError> {
236+
destination.target_id.map(|target| {
237+
let node = self.hir.get(target);
238+
match node {
239+
hir::Node::Expr(_) => target,
240+
hir::Node::Block(b) => find_last_block_expression(b),
241+
hir::Node::Param(..)
242+
| hir::Node::Item(..)
243+
| hir::Node::ForeignItem(..)
244+
| hir::Node::TraitItem(..)
245+
| hir::Node::ImplItem(..)
246+
| hir::Node::Variant(..)
247+
| hir::Node::Field(..)
248+
| hir::Node::AnonConst(..)
249+
| hir::Node::Stmt(..)
250+
| hir::Node::PathSegment(..)
251+
| hir::Node::Ty(..)
252+
| hir::Node::TraitRef(..)
253+
| hir::Node::Binding(..)
254+
| hir::Node::Pat(..)
255+
| hir::Node::Arm(..)
256+
| hir::Node::Local(..)
257+
| hir::Node::Ctor(..)
258+
| hir::Node::Lifetime(..)
259+
| hir::Node::GenericParam(..)
260+
| hir::Node::Visibility(..)
261+
| hir::Node::Crate(..)
262+
| hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
263+
}
264+
})
250265
}
251266
}
252267

268+
fn find_last_block_expression(block: &hir::Block<'_>) -> HirId {
269+
block.expr.map_or_else(
270+
// If there is no tail expression, there will be at least one statement in the
271+
// block because the block contains a break or continue statement.
272+
|| block.stmts.last().unwrap().hir_id,
273+
|expr| expr.hir_id,
274+
)
275+
}
276+
253277
impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
254278
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
255279
let mut reinit = None;
@@ -359,8 +383,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
359383
});
360384
}
361385

362-
ExprKind::Loop(body, ..) => {
386+
ExprKind::Loop(body, label, ..) => {
363387
let loop_begin = self.expr_index + 1;
388+
self.label_stack.push((label, loop_begin));
364389
if body.stmts.is_empty() && body.expr.is_none() {
365390
// For empty loops we won't have updated self.expr_index after visiting the
366391
// body, meaning we'd get an edge from expr_index to expr_index + 1, but
@@ -370,11 +395,31 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
370395
self.visit_block(body);
371396
self.drop_ranges.add_control_edge(self.expr_index, loop_begin);
372397
}
398+
self.label_stack.pop();
373399
}
374-
ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..)
375-
| ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => {
376-
self.drop_ranges
377-
.add_control_edge_hir_id(self.expr_index, self.find_target_expression(target));
400+
// Find the loop entry by searching through the label stack for either the last entry
401+
// (if label is none), or the first entry where the label matches this one. The Loop
402+
// case maintains this stack mapping labels to the PostOrderId for the loop entry.
403+
ExprKind::Continue(hir::Destination { label, .. }, ..) => self
404+
.label_stack
405+
.iter()
406+
.rev()
407+
.find(|(loop_label, _)| label.is_none() || *loop_label == label)
408+
.map_or((), |(_, target)| {
409+
self.drop_ranges.add_control_edge(self.expr_index, *target)
410+
}),
411+
412+
ExprKind::Break(destination, ..) => {
413+
// destination either points to an expression or to a block. We use
414+
// find_target_expression_from_destination to use the last expression of the block
415+
// if destination points to a block.
416+
//
417+
// We add an edge to the hir_id of the expression/block we are breaking out of, and
418+
// then in process_deferred_edges we will map this hir_id to its PostOrderId, which
419+
// will refer to the end of the block due to the post order traversal.
420+
self.find_target_expression_from_destination(destination).map_or((), |target| {
421+
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
422+
})
378423
}
379424

380425
ExprKind::Call(f, args) => {
@@ -399,11 +444,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
399444
| ExprKind::Binary(..)
400445
| ExprKind::Block(..)
401446
| ExprKind::Box(..)
402-
| ExprKind::Break(..)
403447
| ExprKind::Cast(..)
404448
| ExprKind::Closure(..)
405449
| ExprKind::ConstBlock(..)
406-
| ExprKind::Continue(..)
407450
| ExprKind::DropTemps(..)
408451
| ExprKind::Err
409452
| ExprKind::Field(..)

src/test/ui/generator/drop-control-flow.rs

+17
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,22 @@ fn nested_loop() {
110110
};
111111
}
112112

113+
fn loop_continue(b: bool) {
114+
let _ = || {
115+
let mut arr = [Ptr];
116+
let mut count = 0;
117+
drop(arr);
118+
while count < 3 {
119+
count += 1;
120+
yield;
121+
if b {
122+
arr = [Ptr];
123+
continue;
124+
}
125+
}
126+
};
127+
}
128+
113129
fn main() {
114130
one_armed_if(true);
115131
if_let(Some(41));
@@ -118,4 +134,5 @@ fn main() {
118134
reinit();
119135
loop_uninit();
120136
nested_loop();
137+
loop_continue(true);
121138
}

0 commit comments

Comments
 (0)