Skip to content

Commit f506aea

Browse files
committed
Give match arms a drop/region scope
Also give arms the correct lint scope in MIR.
1 parent af6a9a2 commit f506aea

File tree

10 files changed

+299
-145
lines changed

10 files changed

+299
-145
lines changed

src/librustc/cfg/construct.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
419419
for arm in arms {
420420
// Add an exit node for when we've visited all the
421421
// patterns and the guard (if there is one) in the arm.
422-
let arm_exit = self.add_dummy_node(&[]);
422+
let bindings_exit = self.add_dummy_node(&[]);
423423

424424
for pat in &arm.pats {
425425
// Visit the pattern, coming from the discriminant exit
@@ -453,14 +453,16 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
453453

454454
// Add an edge from the exit of this pattern to the
455455
// exit of the arm
456-
self.add_contained_edge(pat_exit, arm_exit);
456+
self.add_contained_edge(pat_exit, bindings_exit);
457457
}
458458

459459
// Visit the body of this arm
460-
let body_exit = self.expr(&arm.body, arm_exit);
460+
let body_exit = self.expr(&arm.body, bindings_exit);
461+
462+
let arm_exit = self.add_ast_node(arm.hir_id.local_id, &[body_exit]);
461463

462464
// Link the body to the exit of the expression
463-
self.add_contained_edge(body_exit, expr_exit);
465+
self.add_contained_edge(arm_exit, expr_exit);
464466
}
465467

466468
expr_exit

src/librustc/middle/region.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,18 @@ impl fmt::Debug for Scope {
119119
pub enum ScopeData {
120120
Node,
121121

122-
// Scope of the call-site for a function or closure
123-
// (outlives the arguments as well as the body).
122+
/// Scope of the call-site for a function or closure
123+
/// (outlives the arguments as well as the body).
124124
CallSite,
125125

126-
// Scope of arguments passed to a function or closure
127-
// (they outlive its body).
126+
/// Scope of arguments passed to a function or closure
127+
/// (they outlive its body).
128128
Arguments,
129129

130-
// Scope of destructors for temporaries of node-id.
130+
/// Scope of destructors for temporaries of node-id.
131131
Destruction,
132132

133-
// Scope following a `let id = expr;` binding in a block.
133+
/// Scope following a `let id = expr;` binding in a block.
134134
Remainder(FirstStatementIndex)
135135
}
136136

@@ -152,11 +152,11 @@ newtype_index! {
152152
///
153153
/// * The subscope with `first_statement_index == 1` is scope of `c`,
154154
/// and thus does not include EXPR_2, but covers the `...`.
155-
pub struct FirstStatementIndex { .. }
155+
pub struct FirstStatementIndex {
156+
derive [HashStable]
157+
}
156158
}
157159

158-
impl_stable_hash_for!(struct crate::middle::region::FirstStatementIndex { private });
159-
160160
// compilation error if size of `ScopeData` is not the same as a `u32`
161161
static_assert_size!(ScopeData, 4);
162162

@@ -814,13 +814,25 @@ fn resolve_block<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, blk:
814814
}
815815

816816
fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) {
817+
let prev_cx = visitor.cx;
818+
819+
visitor.enter_scope(
820+
Scope {
821+
id: arm.hir_id.local_id,
822+
data: ScopeData::Node,
823+
}
824+
);
825+
visitor.cx.var_parent = visitor.cx.parent;
826+
817827
visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
818828

819829
if let Some(hir::Guard::If(ref expr)) = arm.guard {
820830
visitor.terminating_scopes.insert(expr.hir_id.local_id);
821831
}
822832

823833
intravisit::walk_arm(visitor, arm);
834+
835+
visitor.cx = prev_cx;
824836
}
825837

826838
fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &'tcx hir::Pat) {
@@ -893,10 +905,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
893905
terminating(body.hir_id.local_id);
894906
}
895907

896-
hir::ExprKind::Match(..) => {
897-
visitor.cx.var_parent = visitor.cx.parent;
898-
}
899-
900908
hir::ExprKind::DropTemps(ref expr) => {
901909
// `DropTemps(expr)` does not denote a conditional scope.
902910
// Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.

src/librustc_mir/build/matches/mod.rs

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
1212
use crate::hair::{self, *};
1313
use rustc::hir::HirId;
1414
use rustc::mir::*;
15+
use rustc::middle::region;
1516
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
1617
use rustc::ty::layout::VariantIdx;
1718
use rustc_data_structures::bit_set::BitSet;
@@ -251,45 +252,47 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
251252

252253
// Step 5. Create everything else: the guards and the arms.
253254

254-
let outer_source_info = self.source_info(span);
255255
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| {
256-
let mut arm_block = self.cfg.start_new_block();
257-
258-
let body = self.hir.mirror(arm.body.clone());
259-
let scope = self.declare_bindings(
260-
None,
261-
body.span,
262-
&arm.patterns[0],
263-
ArmHasGuard(arm.guard.is_some()),
264-
Some((Some(&scrutinee_place), scrutinee_span)),
265-
);
266-
267-
if let Some(source_scope) = scope {
268-
this.source_scope = source_scope;
269-
}
270-
271-
for candidate in candidates {
272-
self.bind_and_guard_matched_candidate(
273-
candidate,
274-
arm.guard.clone(),
275-
arm_block,
276-
&fake_borrow_temps,
277-
scrutinee_span,
256+
let arm_source_info = self.source_info(arm.span);
257+
let region_scope = (arm.scope, arm_source_info);
258+
self.in_scope(region_scope, arm.lint_level, |this| {
259+
let arm_block = this.cfg.start_new_block();
260+
261+
let body = this.hir.mirror(arm.body.clone());
262+
let scope = this.declare_bindings(
263+
None,
264+
arm.span,
265+
&arm.patterns[0],
266+
ArmHasGuard(arm.guard.is_some()),
267+
Some((Some(&scrutinee_place), scrutinee_span)),
278268
);
279-
}
280269

270+
if let Some(source_scope) = scope {
271+
this.source_scope = source_scope;
272+
}
281273

282-
unpack!(arm_block = self.into(destination, arm_block, body));
274+
for candidate in candidates {
275+
this.clear_top_scope(arm.scope);
276+
this.bind_and_guard_matched_candidate(
277+
candidate,
278+
arm.guard.clone(),
279+
arm_block,
280+
&fake_borrow_temps,
281+
scrutinee_span,
282+
region_scope,
283+
);
284+
}
283285

284-
arm_block
286+
this.into(destination, arm_block, body)
287+
})
285288
}).collect();
286289

287290
// all the arm blocks will rejoin here
288291
let end_block = self.cfg.start_new_block();
289292

290293
for arm_block in arm_end_blocks {
291294
self.cfg.terminate(
292-
arm_block,
295+
unpack!(arm_block),
293296
outer_source_info,
294297
TerminatorKind::Goto { target: end_block },
295298
);
@@ -502,7 +505,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
502505
visibility_scope =
503506
Some(this.new_source_scope(scope_span, LintLevel::Inherited, None));
504507
}
505-
let source_info = SourceInfo { span, this.source_scope };
508+
let source_info = SourceInfo { span, scope: this.source_scope };
506509
let visibility_scope = visibility_scope.unwrap();
507510
this.declare_binding(
508511
source_info,
@@ -1315,6 +1318,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13151318
arm_block: BasicBlock,
13161319
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13171320
scrutinee_span: Span,
1321+
region_scope: (region::Scope, SourceInfo),
13181322
) {
13191323
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13201324

@@ -1497,17 +1501,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
14971501
//
14981502
// and that is clearly not correct.
14991503
let post_guard_block = self.cfg.start_new_block();
1504+
let otherwise_post_guard_block = self.cfg.start_new_block();
15001505
self.cfg.terminate(
15011506
block,
15021507
source_info,
15031508
TerminatorKind::if_(
15041509
self.hir.tcx(),
1505-
cond,
1510+
cond.clone(),
15061511
post_guard_block,
1507-
candidate.otherwise_block.unwrap()
1512+
otherwise_post_guard_block,
15081513
),
15091514
);
15101515

1516+
self.exit_scope(
1517+
source_info.span,
1518+
region_scope,
1519+
otherwise_post_guard_block,
1520+
candidate.otherwise_block.unwrap(),
1521+
);
1522+
1523+
if let Operand::Copy(cond_place) | Operand::Move(cond_place) = cond {
1524+
if let Place::Base(PlaceBase::Local(cond_temp)) = cond_place {
1525+
// We will call `clear_top_scope` if there's another guard. So
1526+
// we have to drop this variable now or it will be "storage
1527+
// leaked".
1528+
self.pop_variable(
1529+
post_guard_block,
1530+
region_scope.0,
1531+
cond_temp
1532+
);
1533+
} else {
1534+
bug!("Expected as_local_operand to produce a temporary");
1535+
}
1536+
}
1537+
15111538
let by_value_bindings = candidate.bindings.iter().filter(|binding| {
15121539
if let BindingMode::ByValue = binding.binding_mode { true } else { false }
15131540
});

src/librustc_mir/build/scope.rs

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ paragraph). This is because region scopes are tied to
1919
them. Eventually, when we shift to non-lexical lifetimes, there should
2020
be no need to remember this mapping.
2121
22-
There is one additional wrinkle, actually, that I wanted to hide from
23-
you but duty compels me to mention. In the course of building
24-
matches, it sometimes happen that certain code (namely guards) gets
25-
executed multiple times. This means that the scope lexical scope may
26-
in fact correspond to multiple, disjoint SEME regions. So in fact our
22+
### Not so SEME Regions
23+
24+
In the course of building matches, it sometimes happens that certain code
25+
(namely guards) gets executed multiple times. This means that the scope lexical
26+
scope may in fact correspond to multiple, disjoint SEME regions. So in fact our
2727
mapping is from one scope to a vector of SEME regions.
2828
29+
Also in matches, the scopes assigned to arms are not even SEME regions! Each
30+
arm has a single region with one entry for each pattern. We manually
31+
manipulate the scheduled drops in this scope to avoid dropping things multiple
32+
times, although drop elaboration would clean this up for value drops.
33+
2934
### Drops
3035
3136
The primary purpose for scopes is to insert drops: while building
@@ -731,7 +736,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
731736
// Note that this code iterates scopes from the inner-most to the outer-most,
732737
// invalidating caches of each scope visited. This way bare minimum of the
733738
// caches gets invalidated. i.e., if a new drop is added into the middle scope, the
734-
// cache of outer scpoe stays intact.
739+
// cache of outer scope stays intact.
735740
scope.invalidate_cache(!needs_drop, this_scope);
736741
if this_scope {
737742
if let DropKind::Value { .. } = drop_kind {
@@ -873,6 +878,73 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
873878

874879
success_block
875880
}
881+
882+
// `match` arm scopes
883+
// ==================
884+
/// Unschedules any drops in the top scope.
885+
///
886+
/// This is only needed for `match` arm scopes, because they have one
887+
/// entrance per pattern, but only one exit.
888+
pub fn clear_top_scope(&mut self, region_scope: region::Scope) {
889+
let top_scope = self.scopes.last_mut().unwrap();
890+
891+
assert_eq!(top_scope.region_scope, region_scope);
892+
893+
top_scope.drops.clear();
894+
top_scope.invalidate_cache(false, true);
895+
}
896+
897+
/// Drops the single variable provided
898+
///
899+
/// * The scope must be the top scope.
900+
/// * The variable must be in that scope.
901+
/// * The variable must be at the top of that scope: it's the next thing
902+
/// scheduled to drop.
903+
/// * The drop must be of DropKind::Storage.
904+
///
905+
/// This is used for the boolean holding the result of the match guard. We
906+
/// do this because:
907+
///
908+
/// * The boolean is different for each pattern
909+
/// * There is only one exit for the arm scope
910+
/// * The guard expression scope is too short, it ends just before the
911+
/// boolean is tested.
912+
pub fn pop_variable(
913+
&mut self,
914+
block: BasicBlock,
915+
region_scope: region::Scope,
916+
variable: Local,
917+
) {
918+
let top_scope = self.scopes.last_mut().unwrap();
919+
920+
assert_eq!(top_scope.region_scope, region_scope);
921+
922+
let top_drop_data = top_scope.drops.pop().unwrap();
923+
924+
match top_drop_data.kind {
925+
DropKind::Value { .. } => {
926+
bug!("Should not be calling pop_top_variable on non-copy type!")
927+
}
928+
DropKind::Storage => {
929+
// Drop the storage for both value and storage drops.
930+
// Only temps and vars need their storage dead.
931+
match top_drop_data.location {
932+
Place::Base(PlaceBase::Local(index)) => {
933+
let source_info = top_scope.source_info(top_drop_data.span);
934+
assert_eq!(index, variable);
935+
self.cfg.push(block, Statement {
936+
source_info,
937+
kind: StatementKind::StorageDead(index)
938+
});
939+
}
940+
_ => unreachable!(),
941+
}
942+
}
943+
}
944+
945+
top_scope.invalidate_cache(true, true);
946+
}
947+
876948
}
877949

878950
/// Builds drops for pop_scope and exit_scope.

src/librustc_mir/hair/cx/expr.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,12 @@ fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm)
879879
_ => None,
880880
},
881881
body: arm.body.to_ref(),
882-
// BUG: fix this
883-
lint_level: LintLevel::Inherited,
882+
lint_level: LintLevel::Explicit(arm.hir_id),
883+
scope: region::Scope {
884+
id: arm.hir_id.local_id,
885+
data: region::ScopeData::Node
886+
},
887+
span: arm.span,
884888
}
885889
}
886890

0 commit comments

Comments
 (0)