Skip to content

Commit 6998b36

Browse files
committed
Auto merge of #52733 - pnkfelix:issue-51348-make-temp-for-each-candidate-in-arm, r=nikomatsakis
[NLL] make temp for each candidate in `match` arm In NLL, `ref mut` patterns leverage the two-phase borrow infrastructure to allow the shared borrows within a guard before the "activation" of the mutable borrow when we begin execution of the match arm's body. (There is further discussion of this on PR #50783.) To accommodate the restrictions we impose on two-phase borrows (namely that there is a one-to-one mapping between each activation and the original initialization), this PR is making separate temps for each candidate pattern. So in an arm like this: ```rust PatA(_, ref mut ident) | PatB(ref mut ident) | PatC(_, _, ref mut ident) | PatD(ref mut ident) if guard_stuff(ident) => ... ``` instead of 3 temps (two for the guard and one for the arm body), we now have 4 + 2 temps associated with `ident`: one for each candidate plus the actual temp that the guard uses directly, and then the sixth is the temp used in the arm body. Fix #51348
2 parents b18b9ed + 9462645 commit 6998b36

File tree

7 files changed

+137
-40
lines changed

7 files changed

+137
-40
lines changed

src/librustc_mir/borrow_check/borrow_set.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,21 @@ impl<'a, 'gcx, 'tcx> GatherBorrows<'a, 'gcx, 'tcx> {
333333

334334
// Consider the borrow not activated to start. When we find an activation, we'll update
335335
// this field.
336-
let borrow_data = &mut self.idx_vec[borrow_index];
337-
borrow_data.activation_location = TwoPhaseActivation::NotActivated;
336+
{
337+
let borrow_data = &mut self.idx_vec[borrow_index];
338+
borrow_data.activation_location = TwoPhaseActivation::NotActivated;
339+
}
338340

339341
// Insert `temp` into the list of pending activations. From
340342
// now on, we'll be on the lookout for a use of it. Note that
341343
// we are guaranteed that this use will come after the
342344
// assignment.
343345
let old_value = self.pending_activations.insert(temp, borrow_index);
344-
assert!(old_value.is_none());
346+
if let Some(old_index) = old_value {
347+
span_bug!(self.mir.source_info(start_location).span,
348+
"found already pending activation for temp: {:?} \
349+
at borrow_index: {:?} with associated data {:?}",
350+
temp, old_index, self.idx_vec[old_index]);
351+
}
345352
}
346353
}

src/librustc_mir/build/block.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use rustc::mir::*;
1616
use rustc::hir;
1717
use syntax_pos::Span;
1818

19+
use std::slice;
20+
1921
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
2022
pub fn ast_block(&mut self,
2123
destination: &Place<'tcx>,
@@ -126,7 +128,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
126128
None,
127129
remainder_span,
128130
lint_level,
129-
&pattern,
131+
slice::from_ref(&pattern),
130132
ArmHasGuard(false),
131133
Some((None, initializer_span)),
132134
);
@@ -138,8 +140,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
138140
})
139141
}));
140142
} else {
141-
scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
142-
ArmHasGuard(false), None);
143+
scope = this.declare_bindings(
144+
None, remainder_span, lint_level, slice::from_ref(&pattern),
145+
ArmHasGuard(false), None);
143146

144147
// FIXME(#47184): We currently only insert `UserAssertTy` statements for
145148
// patterns that are bindings, this is as we do not want to deconstruct

src/librustc_mir/build/expr/as_place.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! See docs in build/expr/mod.rs
1212
1313
use build::{BlockAnd, BlockAndExtension, Builder};
14-
use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard};
14+
use build::ForGuard::{OutsideGuard, RefWithinGuard};
1515
use build::expr::category::Category;
1616
use hair::*;
1717
use rustc::mir::*;
@@ -87,14 +87,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8787
block.and(Place::Local(Local::new(1)))
8888
}
8989
ExprKind::VarRef { id } => {
90-
let place = if this.is_bound_var_in_guard(id) {
91-
if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() {
92-
let index = this.var_local_id(id, RefWithinGuard);
93-
Place::Local(index).deref()
94-
} else {
95-
let index = this.var_local_id(id, ValWithinGuard);
96-
Place::Local(index)
97-
}
90+
let place = if this.is_bound_var_in_guard(id) &&
91+
this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards()
92+
{
93+
let index = this.var_local_id(id, RefWithinGuard);
94+
Place::Local(index).deref()
9895
} else {
9996
let index = this.var_local_id(id, OutsideGuard);
10097
Place::Local(index)

src/librustc_mir/build/matches/mod.rs

+44-18
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9797
let body = self.hir.mirror(arm.body.clone());
9898
let scope = self.declare_bindings(None, body.span,
9999
LintLevel::Inherited,
100-
&arm.patterns[0],
100+
&arm.patterns[..],
101101
ArmHasGuard(arm.guard.is_some()),
102102
Some((Some(&discriminant_place), discriminant_span)));
103103
(body, scope.unwrap_or(self.source_scope))
@@ -118,11 +118,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
118118
arms.iter()
119119
.enumerate()
120120
.flat_map(|(arm_index, arm)| {
121-
arm.patterns.iter()
122-
.map(move |pat| (arm_index, pat, arm.guard.clone()))
121+
arm.patterns.iter().enumerate()
122+
.map(move |(pat_index, pat)| {
123+
(arm_index, pat_index, pat, arm.guard.clone())
124+
})
123125
})
124126
.zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
125-
.map(|((arm_index, pattern, guard),
127+
.map(|((arm_index, pat_index, pattern, guard),
126128
(pre_binding_block, next_candidate_pre_binding_block))| {
127129

128130
if let (true, Some(borrow_temp)) = (tcx.emit_read_for_match(),
@@ -168,6 +170,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
168170
bindings: vec![],
169171
guard,
170172
arm_index,
173+
pat_index,
171174
pre_binding_block: *pre_binding_block,
172175
next_candidate_pre_binding_block: *next_candidate_pre_binding_block,
173176
}
@@ -277,6 +280,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
277280

278281
// since we don't call `match_candidates`, next fields is unused
279282
arm_index: 0,
283+
pat_index: 0,
280284
pre_binding_block: block,
281285
next_candidate_pre_binding_block: block
282286
};
@@ -324,14 +328,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
324328
mut visibility_scope: Option<SourceScope>,
325329
scope_span: Span,
326330
lint_level: LintLevel,
327-
pattern: &Pattern<'tcx>,
331+
patterns: &[Pattern<'tcx>],
328332
has_guard: ArmHasGuard,
329333
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>)
330334
-> Option<SourceScope> {
331335
assert!(!(visibility_scope.is_some() && lint_level.is_explicit()),
332336
"can't have both a visibility and a lint scope at the same time");
333337
let mut scope = self.source_scope;
334-
self.visit_bindings(pattern, &mut |this, mutability, name, mode, var, span, ty| {
338+
let num_patterns = patterns.len();
339+
self.visit_bindings(&patterns[0], &mut |this, mutability, name, mode, var, span, ty| {
335340
if visibility_scope.is_none() {
336341
visibility_scope = Some(this.new_source_scope(scope_span,
337342
LintLevel::Inherited,
@@ -349,8 +354,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
349354
scope,
350355
};
351356
let visibility_scope = visibility_scope.unwrap();
352-
this.declare_binding(source_info, visibility_scope, mutability, name, mode, var,
353-
ty, has_guard, opt_match_place.map(|(x, y)| (x.cloned(), y)));
357+
this.declare_binding(source_info, visibility_scope, mutability, name, mode,
358+
num_patterns, var, ty, has_guard,
359+
opt_match_place.map(|(x, y)| (x.cloned(), y)));
354360
});
355361
visibility_scope
356362
}
@@ -453,6 +459,9 @@ pub struct Candidate<'pat, 'tcx:'pat> {
453459
// ...and the blocks for add false edges between candidates
454460
pre_binding_block: BasicBlock,
455461
next_candidate_pre_binding_block: BasicBlock,
462+
463+
// This uniquely identifies this candidate *within* the arm.
464+
pat_index: usize,
456465
}
457466

458467
#[derive(Clone, Debug)]
@@ -972,7 +981,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
972981
let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards();
973982
if let Some(guard) = candidate.guard {
974983
if autoref {
975-
self.bind_matched_candidate_for_guard(block, &candidate.bindings);
984+
self.bind_matched_candidate_for_guard(
985+
block, candidate.pat_index, &candidate.bindings);
976986
let guard_frame = GuardFrame {
977987
locals: candidate.bindings.iter()
978988
.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode))
@@ -1058,9 +1068,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10581068
// and thus all code/comments assume we are in that context.
10591069
fn bind_matched_candidate_for_guard(&mut self,
10601070
block: BasicBlock,
1071+
pat_index: usize,
10611072
bindings: &[Binding<'tcx>]) {
1062-
debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})",
1063-
block, bindings);
1073+
debug!("bind_matched_candidate_for_guard(block={:?}, pat_index={:?}, bindings={:?})",
1074+
block, pat_index, bindings);
10641075

10651076
// Assign each of the bindings. Since we are binding for a
10661077
// guard expression, this will never trigger moves out of the
@@ -1099,8 +1110,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10991110
// used by the arm body itself. This eases
11001111
// observing two-phase borrow restrictions.
11011112
let val_for_guard = self.storage_live_binding(
1102-
block, binding.var_id, binding.span, ValWithinGuard);
1103-
self.schedule_drop_for_binding(binding.var_id, binding.span, ValWithinGuard);
1113+
block, binding.var_id, binding.span, ValWithinGuard(pat_index));
1114+
self.schedule_drop_for_binding(
1115+
binding.var_id, binding.span, ValWithinGuard(pat_index));
11041116

11051117
// rust-lang/rust#27282: We reuse the two-phase
11061118
// borrow infrastructure so that the mutable
@@ -1146,16 +1158,26 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11461158

11471159
/// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where
11481160
/// the bound `var` has type `T` in the arm body) in a pattern
1149-
/// maps to *two* locals. The first local is a binding for
1161+
/// maps to `2+N` locals. The first local is a binding for
11501162
/// occurrences of `var` in the guard, which will all have type
1151-
/// `&T`. The second local is a binding for occurrences of `var`
1152-
/// in the arm body, which will have type `T`.
1163+
/// `&T`. The N locals are bindings for the `T` that is referenced
1164+
/// by the first local; they are not used outside of the
1165+
/// guard. The last local is a binding for occurrences of `var` in
1166+
/// the arm body, which will have type `T`.
1167+
///
1168+
/// The reason we have N locals rather than just 1 is to
1169+
/// accommodate rust-lang/rust#51348: If the arm has N candidate
1170+
/// patterns, then in general they can correspond to distinct
1171+
/// parts of the matched data, and we want them to be distinct
1172+
/// temps in order to simplify checks performed by our internal
1173+
/// leveraging of two-phase borrows).
11531174
fn declare_binding(&mut self,
11541175
source_info: SourceInfo,
11551176
visibility_scope: SourceScope,
11561177
mutability: Mutability,
11571178
name: Name,
11581179
mode: BindingMode,
1180+
num_patterns: usize,
11591181
var_id: NodeId,
11601182
var_ty: Ty<'tcx>,
11611183
has_guard: ArmHasGuard,
@@ -1189,7 +1211,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11891211
};
11901212
let for_arm_body = self.local_decls.push(local.clone());
11911213
let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() {
1192-
let val_for_guard = self.local_decls.push(local);
1214+
let mut vals_for_guard = Vec::with_capacity(num_patterns);
1215+
for _ in 0..num_patterns {
1216+
let val_for_guard_idx = self.local_decls.push(local.clone());
1217+
vals_for_guard.push(val_for_guard_idx);
1218+
}
11931219
let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
11941220
mutability,
11951221
ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty),
@@ -1200,7 +1226,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12001226
internal: false,
12011227
is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
12021228
});
1203-
LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
1229+
LocalsForNode::ForGuard { vals_for_guard, ref_for_guard, for_arm_body }
12041230
} else {
12051231
LocalsForNode::One(for_arm_body)
12061232
};

src/librustc_mir/build/matches/test.rs

+2
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
631631
bindings: candidate.bindings.clone(),
632632
guard: candidate.guard.clone(),
633633
arm_index: candidate.arm_index,
634+
pat_index: candidate.pat_index,
634635
pre_binding_block: candidate.pre_binding_block,
635636
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
636637
}
@@ -694,6 +695,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
694695
bindings: candidate.bindings.clone(),
695696
guard: candidate.guard.clone(),
696697
arm_index: candidate.arm_index,
698+
pat_index: candidate.pat_index,
697699
pre_binding_block: candidate.pre_binding_block,
698700
next_candidate_pre_binding_block: candidate.next_candidate_pre_binding_block,
699701
}

src/librustc_mir/build/mod.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
310310

311311
#[derive(Debug)]
312312
enum LocalsForNode {
313+
/// In the usual case, a node-id for an identifier maps to at most
314+
/// one Local declaration.
313315
One(Local),
314-
Three { val_for_guard: Local, ref_for_guard: Local, for_arm_body: Local },
316+
317+
/// The exceptional case is identifiers in a match arm's pattern
318+
/// that are referenced in a guard of that match arm. For these,
319+
/// we can have `2+k` Locals, where `k` is the number of candidate
320+
/// patterns (separated by `|`) in the arm.
321+
///
322+
/// * `for_arm_body` is the Local used in the arm body (which is
323+
/// just like the `One` case above),
324+
///
325+
/// * `ref_for_guard` is the Local used in the arm's guard (which
326+
/// is a reference to a temp that is an alias of
327+
/// `for_arm_body`).
328+
///
329+
/// * `vals_for_guard` is the `k` Locals; at most one of them will
330+
/// get initialized by the arm's execution, and after it is
331+
/// initialized, `ref_for_guard` will be assigned a reference to
332+
/// it.
333+
///
334+
/// There reason we have `k` Locals rather than just 1 is to
335+
/// accommodate some restrictions imposed by two-phase borrows,
336+
/// which apply when we have a `ref mut` pattern.
337+
ForGuard { vals_for_guard: Vec<Local>, ref_for_guard: Local, for_arm_body: Local },
315338
}
316339

317340
#[derive(Debug)]
@@ -350,7 +373,10 @@ struct GuardFrame {
350373
/// 3. the temp for use outside of guard expressions.
351374
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
352375
enum ForGuard {
353-
ValWithinGuard,
376+
/// The `usize` identifies for which candidate pattern we want the
377+
/// local binding. We keep a temp per-candidate to accommodate
378+
/// two-phase borrows (see `LocalsForNode` documentation).
379+
ValWithinGuard(usize),
354380
RefWithinGuard,
355381
OutsideGuard,
356382
}
@@ -359,12 +385,15 @@ impl LocalsForNode {
359385
fn local_id(&self, for_guard: ForGuard) -> Local {
360386
match (self, for_guard) {
361387
(&LocalsForNode::One(local_id), ForGuard::OutsideGuard) |
362-
(&LocalsForNode::Three { val_for_guard: local_id, .. }, ForGuard::ValWithinGuard) |
363-
(&LocalsForNode::Three { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) |
364-
(&LocalsForNode::Three { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) =>
388+
(&LocalsForNode::ForGuard { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) |
389+
(&LocalsForNode::ForGuard { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) =>
365390
local_id,
366391

367-
(&LocalsForNode::One(_), ForGuard::ValWithinGuard) |
392+
(&LocalsForNode::ForGuard { ref vals_for_guard, .. },
393+
ForGuard::ValWithinGuard(pat_idx)) =>
394+
vals_for_guard[pat_idx],
395+
396+
(&LocalsForNode::One(_), ForGuard::ValWithinGuard(_)) |
368397
(&LocalsForNode::One(_), ForGuard::RefWithinGuard) =>
369398
bug!("anything with one local should never be within a guard."),
370399
}
@@ -740,7 +769,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
740769
}
741770
_ => {
742771
scope = self.declare_bindings(scope, ast_body.span,
743-
LintLevel::Inherited, &pattern,
772+
LintLevel::Inherited, &[pattern.clone()],
744773
matches::ArmHasGuard(false),
745774
Some((Some(&place), span)));
746775
unpack!(block = self.place_into_pattern(block, pattern, &place, false));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// We used to ICE if you had a single match arm with multiple
12+
// candidate patterns with `ref mut` identifiers used in the arm's
13+
// guard.
14+
//
15+
// Also, this test expands on the original bug's example by actually
16+
// trying to double check that we are matching against the right part
17+
// of the input data based on which candidate pattern actually fired.
18+
19+
// run-pass
20+
21+
#![feature(nll)]
22+
23+
fn foo(x: &mut Result<(u32, u32), (u32, u32)>) -> u32 {
24+
match *x {
25+
Ok((ref mut v, _)) | Err((_, ref mut v)) if *v > 0 => { *v }
26+
_ => { 0 }
27+
}
28+
}
29+
30+
fn main() {
31+
assert_eq!(foo(&mut Ok((3, 4))), 3);
32+
assert_eq!(foo(&mut Err((3, 4))), 4);
33+
}

0 commit comments

Comments
 (0)