Skip to content

Commit 91db9dc

Browse files
committed
Auto merge of #49870 - pnkfelix:issue-27282-immut-borrow-all-pat-ids-in-guards, r=nikomatsakis
Immutably and implicitly borrow all pattern ids for their guards (NLL only) This is an important piece of #27282. It applies only to NLL mode. It is a change to MIR codegen that is currently toggled on only when NLL is turned on. It thus affect MIR-borrowck but not the earlier static analyses (such as the type checker). This change makes it so that any pattern bindings of type T for a match arm will map to a `&T` within the context of the guard expression for that arm, but will continue to map to a `T` in the context of the arm body. To avoid surfacing this type distinction in the user source code (which would be a severe change to the language and would also require far more revision to the compiler internals), any occurrence of such an identifier in the guard expression will automatically get a deref op applied to it. So an input like: ```rust let place = (1, Foo::new()); match place { (1, foo) if inspect(foo) => feed(foo), ... } ``` will be treated as if it were really something like: ```rust let place = (1, Foo::new()); match place { (1, Foo { .. }) if { let tmp1 = &place.1; inspect(*tmp1) } => { let tmp2 = place.1; feed(tmp2) }, ... } ``` And an input like: ```rust let place = (2, Foo::new()); match place { (2, ref mut foo) if inspect(foo) => feed(foo), ... } ``` will be treated as if it were really something like: ```rust let place = (2, Foo::new()); match place { (2, Foo { .. }) if { let tmp1 = & &mut place.1; inspect(*tmp1) } => { let tmp2 = &mut place.1; feed(tmp2) }, ... } ``` In short, any pattern binding will always look like *some* kind of `&T` within the guard at least in terms of how the MIR-borrowck views it, and this will ensure that guard expressions cannot mutate their the match inputs via such bindings. (It also ensures that guard expressions can at most *copy* values from such bindings; non-Copy things cannot be moved via these pattern bindings in guard expressions, since one cannot move out of a `&T`.)
2 parents 0bfe307 + 930e76e commit 91db9dc

File tree

10 files changed

+505
-92
lines changed

10 files changed

+505
-92
lines changed

src/librustc/ty/context.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14671467
self.borrowck_mode().use_mir()
14681468
}
14691469

1470+
/// If true, pattern variables for use in guards on match arms
1471+
/// will be bound as references to the data, and occurrences of
1472+
/// those variables in the guard expression will implicitly
1473+
/// dereference those bindings. (See rust-lang/rust#27282.)
1474+
pub fn all_pat_vars_are_implicit_refs_within_guards(self) -> bool {
1475+
self.borrowck_mode().use_mir()
1476+
}
1477+
14701478
/// If true, we should enable two-phase borrows checks. This is
14711479
/// done with either `-Ztwo-phase-borrows` or with
14721480
/// `#![feature(nll)]`.

src/librustc_mir/borrow_check/error_reporting.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
471471
| (RegionKind::ReClosureBound(_), _)
472472
| (RegionKind::ReCanonical(_), _)
473473
| (RegionKind::ReErased, _) => {
474-
span_bug!(drop_span, "region does not make sense in this context");
474+
span_bug!(drop_span, "region {:?} does not make sense in this context",
475+
borrow.region);
475476
}
476477
}
477478
}

src/librustc_mir/build/block.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// except according to those terms.
1010

1111
use build::{BlockAnd, BlockAndExtension, Builder};
12+
use build::ForGuard::OutsideGuard;
13+
use build::matches::ArmHasGuard;
1214
use hair::*;
1315
use rustc::mir::*;
1416
use rustc::hir;
@@ -113,7 +115,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
113115
// Declare the bindings, which may create a visibility scope.
114116
let remainder_span = remainder_scope.span(this.hir.tcx(),
115117
&this.hir.region_scope_tree);
116-
let scope = this.declare_bindings(None, remainder_span, lint_level, &pattern);
118+
let scope = this.declare_bindings(None, remainder_span, lint_level, &pattern,
119+
ArmHasGuard(false));
117120

118121
// Evaluate the initializer, if present.
119122
if let Some(init) = initializer {
@@ -135,8 +138,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
135138
}
136139

137140
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
138-
this.storage_live_binding(block, node, span);
139-
this.schedule_drop_for_binding(node, span);
141+
this.storage_live_binding(block, node, span, OutsideGuard);
142+
this.schedule_drop_for_binding(node, span, OutsideGuard);
140143
})
141144
}
142145

src/librustc_mir/build/expr/as_place.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//! See docs in build/expr/mod.rs
1212
1313
use build::{BlockAnd, BlockAndExtension, Builder};
14+
use build::ForGuard::{OutsideGuard, WithinGuard};
1415
use build::expr::category::Category;
1516
use hair::*;
1617
use rustc::mir::*;
@@ -86,8 +87,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8687
block.and(Place::Local(Local::new(1)))
8788
}
8889
ExprKind::VarRef { id } => {
89-
let index = this.var_indices[&id];
90-
block.and(Place::Local(index))
90+
let place = if this.is_bound_var_in_guard(id) {
91+
let index = this.var_local_id(id, WithinGuard);
92+
if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() {
93+
Place::Local(index).deref()
94+
} else {
95+
Place::Local(index)
96+
}
97+
} else {
98+
let index = this.var_local_id(id, OutsideGuard);
99+
Place::Local(index)
100+
};
101+
block.and(place)
91102
}
92103
ExprKind::StaticRef { id } => {
93104
block.and(Place::Static(Box::new(Static { def_id: id, ty: expr.ty })))

0 commit comments

Comments
 (0)