summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-05-30 02:11:59 +0000
committerbors <bors@rust-lang.org>2018-05-30 02:11:59 +0000
commit8372e7b37eb9777edc8f7179fc6cbc39456ce735 (patch)
treee98eddebdd5679b3a73a5716cc7cd240d1203a7f
parentAuto merge of #50772 - nicokoch:fastcopy, r=alexcrichton (diff)
parentReview feedback: Adding test cases suggested by arielb1. (diff)
downloadgrust-8372e7b37eb9777edc8f7179fc6cbc39456ce735.tar.gz
grust-8372e7b37eb9777edc8f7179fc6cbc39456ce735.tar.bz2
grust-8372e7b37eb9777edc8f7179fc6cbc39456ce735.tar.xz
Auto merge of #50783 - pnkfelix:issue-27282-match-borrows-its-input-take-three, r=nikomatsakis
every match arm reads the match's borrowed input This PR changes the `match` codegen under NLL (and just NLL, at least for now) to make the following adjustments: * It adds a `-Z disable-ast-check-for-mutation-in-guard` which, as described, turns off the naive (conservative but also not 100% sound) check for mutation in guards of match arms. * We now borrow the match input at the outset and emit a special `ReadForMatch` statement (that, according to the *static* semantics, reads that borrowed match input) at the start of each match arm. The intent here is to catch cases where the match guard mutates the match input, either via an independent borrow or via `ref mut` borrows in that arm's pattern. * In order to ensure that `ref mut` borrows do not actually conflict with the emitted `ReadForMatch` statements, I expanded the two-phase-borrow system slightly, and also changed the MIR code gen so that under NLL, when there is a guard on a match arm, then each pattern variable ends up having *three* temporaries associated with it: 1. The first temporary will hold the substructure being matched; this is what we will move the (substructural) value into *if* the guard succeeds. 2. The second temporary also corresponds to the same value as the first, but we are just constructing this temporarily for use during the scope of the guard; it is unaliased and its sole referrer is the third temporary. 3. The third temporary is a reference to the second temporary. * (This sounds complicated, I know, but its actually *simpler* than what I was doing before and had checked into the repo, which was to sometimes construct the final value and then take a reference to it before evaluating the guard. See also PR #49870.) Fix #27282 This also provides a path towards resolving #24535 aka rust-lang/rfcs#1006, at least once the `-Z disable-ast-check-for-mutation-in-guard` is just turned on by default (under NLL, that is. It is not sound under AST-borrowck). * But I did not want to make `#![feature(nll)]` imply that as part of this PR; that seemed like too drastic a change to me.
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs6
-rw-r--r--src/librustc/mir/visit.rs5
-rw-r--r--src/librustc/session/config.rs6
-rw-r--r--src/librustc/ty/context.rs19
-rw-r--r--src/librustc_codegen_llvm/mir/statement.rs1
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs44
-rw-r--r--src/librustc_mir/borrow_check/mod.rs27
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs8
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs3
-rw-r--r--src/librustc_mir/borrow_check/path_utils.rs9
-rw-r--r--src/librustc_mir/build/expr/as_place.rs5
-rw-r--r--src/librustc_mir/build/matches/mod.rs233
-rw-r--r--src/librustc_mir/build/mod.rs21
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs1
-rw-r--r--src/librustc_mir/dataflow/move_paths/builder.rs3
-rw-r--r--src/librustc_mir/hair/pattern/check_match.rs4
-rw-r--r--src/librustc_mir/interpret/step.rs4
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs1
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs1
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs1
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs1
-rw-r--r--src/librustc_passes/mir_stats.rs1
-rw-r--r--src/test/mir-opt/match_false_edges.rs237
-rw-r--r--src/test/run-pass/issue-24535-allow-mutable-borrow-in-match-guard.rs68
-rw-r--r--src/test/ui/borrowck/issue-41962.rs5
-rw-r--r--src/test/ui/borrowck/issue-41962.stderr19
-rw-r--r--src/test/ui/issue-27282-move-match-input-into-guard.rs33
-rw-r--r--src/test/ui/issue-27282-move-match-input-into-guard.stderr32
-rw-r--r--src/test/ui/issue-27282-move-ref-mut-into-guard.rs28
-rw-r--r--src/test/ui/issue-27282-move-ref-mut-into-guard.stderr9
-rw-r--r--src/test/ui/issue-27282-mutate-before-diverging-arm-1.rs43
-rw-r--r--src/test/ui/issue-27282-mutate-before-diverging-arm-1.stderr25
-rw-r--r--src/test/ui/issue-27282-mutate-before-diverging-arm-2.rs52
-rw-r--r--src/test/ui/issue-27282-mutate-before-diverging-arm-2.stderr26
-rw-r--r--src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs32
-rw-r--r--src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr9
37 files changed, 790 insertions, 235 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index c71b10ce14..e77d38de58 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -241,6 +241,9 @@ for mir::StatementKind<'gcx> {
241 place.hash_stable(hcx, hasher); 241 place.hash_stable(hcx, hasher);
242 rvalue.hash_stable(hcx, hasher); 242 rvalue.hash_stable(hcx, hasher);
243 } 243 }
244 mir::StatementKind::ReadForMatch(ref place) => {
245 place.hash_stable(hcx, hasher);
246 }
244 mir::StatementKind::SetDiscriminant { ref place, variant_index } => { 247 mir::StatementKind::SetDiscriminant { ref place, variant_index } => {
245 place.hash_stable(hcx, hasher); 248 place.hash_stable(hcx, hasher);
246 variant_index.hash_stable(hcx, hasher); 249 variant_index.hash_stable(hcx, hasher);
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index d35884ec78..a94e5e793b 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1225,6 +1225,10 @@ pub enum StatementKind<'tcx> {
1225 /// Write the RHS Rvalue to the LHS Place. 1225 /// Write the RHS Rvalue to the LHS Place.
1226 Assign(Place<'tcx>, Rvalue<'tcx>), 1226 Assign(Place<'tcx>, Rvalue<'tcx>),
1227 1227
1228 /// This represents all the reading that a pattern match may do
1229 /// (e.g. inspecting constants and discriminant values).
1230 ReadForMatch(Place<'tcx>),
1231
1228 /// Write the discriminant for a variant to the enum Place. 1232 /// Write the discriminant for a variant to the enum Place.
1229 SetDiscriminant { place: Place<'tcx>, variant_index: usize }, 1233 SetDiscriminant { place: Place<'tcx>, variant_index: usize },
1230 1234
@@ -1327,6 +1331,7 @@ impl<'tcx> Debug for Statement<'tcx> {
1327 use self::StatementKind::*; 1331 use self::StatementKind::*;
1328 match self.kind { 1332 match self.kind {
1329 Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv), 1333 Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv),
1334 ReadForMatch(ref place) => write!(fmt, "ReadForMatch({:?})", place),
1330 // (reuse lifetime rendering policy from ppaux.) 1335 // (reuse lifetime rendering policy from ppaux.)
1331 EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)), 1336 EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
1332 Validate(ref op, ref places) => write!(fmt, "Validate({:?}, {:?})", op, places), 1337 Validate(ref op, ref places) => write!(fmt, "Validate({:?}, {:?})", op, places),
@@ -2212,6 +2217,7 @@ BraceStructTypeFoldableImpl! {
2212EnumTypeFoldableImpl! { 2217EnumTypeFoldableImpl! {
2213 impl<'tcx> TypeFoldable<'tcx> for StatementKind<'tcx> { 2218 impl<'tcx> TypeFoldable<'tcx> for StatementKind<'tcx> {
2214 (StatementKind::Assign)(a, b), 2219 (StatementKind::Assign)(a, b),
2220 (StatementKind::ReadForMatch)(place),
2215 (StatementKind::SetDiscriminant) { place, variant_index }, 2221 (StatementKind::SetDiscriminant) { place, variant_index },
2216 (StatementKind::StorageLive)(a), 2222 (StatementKind::StorageLive)(a),
2217 (StatementKind::StorageDead)(a), 2223 (StatementKind::StorageDead)(a),
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index b647ba553d..9dd1432167 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -355,6 +355,11 @@ macro_rules! make_mir_visitor {
355 ref $($mutability)* rvalue) => { 355 ref $($mutability)* rvalue) => {
356 self.visit_assign(block, place, rvalue, location); 356 self.visit_assign(block, place, rvalue, location);
357 } 357 }
358 StatementKind::ReadForMatch(ref $($mutability)* place) => {
359 self.visit_place(place,
360 PlaceContext::Inspect,
361 location);
362 }
358 StatementKind::EndRegion(_) => {} 363 StatementKind::EndRegion(_) => {}
359 StatementKind::Validate(_, ref $($mutability)* places) => { 364 StatementKind::Validate(_, ref $($mutability)* places) => {
360 for operand in places { 365 for operand in places {
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 755b4af1a3..35538e5d02 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1290,16 +1290,22 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
1290 useful for profiling / PGO."), 1290 useful for profiling / PGO."),
1291 relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED], 1291 relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
1292 "choose which RELRO level to use"), 1292 "choose which RELRO level to use"),
1293 disable_ast_check_for_mutation_in_guard: bool = (false, parse_bool, [UNTRACKED],
1294 "skip AST-based mutation-in-guard check (mir-borrowck provides more precise check)"),
1293 nll_subminimal_causes: bool = (false, parse_bool, [UNTRACKED], 1295 nll_subminimal_causes: bool = (false, parse_bool, [UNTRACKED],
1294 "when tracking region error causes, accept subminimal results for faster execution."), 1296 "when tracking region error causes, accept subminimal results for faster execution."),
1295 nll_facts: bool = (false, parse_bool, [UNTRACKED], 1297 nll_facts: bool = (false, parse_bool, [UNTRACKED],
1296 "dump facts from NLL analysis into side files"), 1298 "dump facts from NLL analysis into side files"),
1297 disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED], 1299 disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED],
1298 "disable user provided type assertion in NLL"), 1300 "disable user provided type assertion in NLL"),
1301 nll_dont_emit_read_for_match: bool = (false, parse_bool, [UNTRACKED],
1302 "in match codegen, do not include ReadForMatch statements (used by mir-borrowck)"),
1299 polonius: bool = (false, parse_bool, [UNTRACKED], 1303 polonius: bool = (false, parse_bool, [UNTRACKED],
1300 "enable polonius-based borrow-checker"), 1304 "enable polonius-based borrow-checker"),
1301 codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], 1305 codegen_time_graph: bool = (false, parse_bool, [UNTRACKED],
1302 "generate a graphical HTML report of time spent in codegen and LLVM"), 1306 "generate a graphical HTML report of time spent in codegen and LLVM"),
1307 trans_time_graph: bool = (false, parse_bool, [UNTRACKED],
1308 "generate a graphical HTML report of time spent in trans and LLVM"),
1303 thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], 1309 thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
1304 "enable ThinLTO when possible"), 1310 "enable ThinLTO when possible"),
1305 inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED], 1311 inline_in_all_cgus: Option<bool> = (None, parse_opt_bool, [TRACKED],
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index a533e1a5b9..68f55b4993 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -1344,12 +1344,31 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
1344 self.on_disk_query_result_cache.serialize(self.global_tcx(), encoder) 1344 self.on_disk_query_result_cache.serialize(self.global_tcx(), encoder)
1345 } 1345 }
1346 1346
1347 /// If true, we should use a naive AST walk to determine if match
1348 /// guard could perform bad mutations (or mutable-borrows).
1349 pub fn check_for_mutation_in_guard_via_ast_walk(self) -> bool {
1350 !self.sess.opts.debugging_opts.disable_ast_check_for_mutation_in_guard
1351 }
1352
1347 /// If true, we should use the MIR-based borrowck (we may *also* use 1353 /// If true, we should use the MIR-based borrowck (we may *also* use
1348 /// the AST-based borrowck). 1354 /// the AST-based borrowck).
1349 pub fn use_mir_borrowck(self) -> bool { 1355 pub fn use_mir_borrowck(self) -> bool {
1350 self.borrowck_mode().use_mir() 1356 self.borrowck_mode().use_mir()
1351 } 1357 }
1352 1358
1359 /// If true, make MIR codegen for `match` emit a temp that holds a
1360 /// borrow of the input to the match expression.
1361 pub fn generate_borrow_of_any_match_input(&self) -> bool {
1362 self.emit_read_for_match()
1363 }
1364
1365 /// If true, make MIR codegen for `match` emit ReadForMatch
1366 /// statements (which simulate the maximal effect of executing the
1367 /// patterns in a match arm).
1368 pub fn emit_read_for_match(&self) -> bool {
1369 self.use_mir_borrowck() && !self.sess.opts.debugging_opts.nll_dont_emit_read_for_match
1370 }
1371
1353 /// If true, pattern variables for use in guards on match arms 1372 /// If true, pattern variables for use in guards on match arms
1354 /// will be bound as references to the data, and occurrences of 1373 /// will be bound as references to the data, and occurrences of
1355 /// those variables in the guard expression will implicitly 1374 /// those variables in the guard expression will implicitly
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index 578481df15..c0cce297ef 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -82,6 +82,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
82 asm::codegen_inline_asm(&bx, asm, outputs, input_vals); 82 asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
83 bx 83 bx
84 } 84 }
85 mir::StatementKind::ReadForMatch(_) |
85 mir::StatementKind::EndRegion(_) | 86 mir::StatementKind::EndRegion(_) |
86 mir::StatementKind::Validate(..) | 87 mir::StatementKind::Validate(..) |
87 mir::StatementKind::UserAssertTy(..) | 88 mir::StatementKind::UserAssertTy(..) |
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index ccfb44a8b5..3d6f49c377 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -53,6 +53,17 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
53 } 53 }
54} 54}
55 55
56/// Every two-phase borrow has *exactly one* use (or else it is not a
57/// proper two-phase borrow under our current definition). However, not
58/// all uses are actually ones that activate the reservation.. In
59/// particular, a shared borrow of a `&mut` does not activate the
60/// reservation.
61#[derive(Copy, Clone, PartialEq, Eq, Debug)]
62crate enum TwoPhaseUse {
63 MutActivate,
64 SharedUse,
65}
66
56#[derive(Debug)] 67#[derive(Debug)]
57crate struct BorrowData<'tcx> { 68crate struct BorrowData<'tcx> {
58 /// Location where the borrow reservation starts. 69 /// Location where the borrow reservation starts.
@@ -60,7 +71,7 @@ crate struct BorrowData<'tcx> {
60 crate reserve_location: Location, 71 crate reserve_location: Location,
61 /// Location where the borrow is activated. None if this is not a 72 /// Location where the borrow is activated. None if this is not a
62 /// 2-phase borrow. 73 /// 2-phase borrow.
63 crate activation_location: Option<Location>, 74 crate activation_location: Option<(TwoPhaseUse, Location)>,
64 /// What kind of borrow this is 75 /// What kind of borrow this is
65 crate kind: mir::BorrowKind, 76 crate kind: mir::BorrowKind,
66 /// The region for which this borrow is live 77 /// The region for which this borrow is live
@@ -215,9 +226,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
215 Some(&borrow_index) => { 226 Some(&borrow_index) => {
216 let borrow_data = &mut self.idx_vec[borrow_index]; 227 let borrow_data = &mut self.idx_vec[borrow_index];
217 228
218 // Watch out: the use of TMP in the borrow 229 // Watch out: the use of TMP in the borrow itself
219 // itself doesn't count as an 230 // doesn't count as an activation. =)
220 // activation. =)
221 if borrow_data.reserve_location == location && context == PlaceContext::Store { 231 if borrow_data.reserve_location == location && context == PlaceContext::Store {
222 return; 232 return;
223 } 233 }
@@ -225,7 +235,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
225 if let Some(other_activation) = borrow_data.activation_location { 235 if let Some(other_activation) = borrow_data.activation_location {
226 span_bug!( 236 span_bug!(
227 self.mir.source_info(location).span, 237 self.mir.source_info(location).span,
228 "found two activations for 2-phase borrow temporary {:?}: \ 238 "found two uses for 2-phase borrow temporary {:?}: \
229 {:?} and {:?}", 239 {:?} and {:?}",
230 temp, 240 temp,
231 location, 241 location,
@@ -235,11 +245,25 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
235 245
236 // Otherwise, this is the unique later use 246 // Otherwise, this is the unique later use
237 // that we expect. 247 // that we expect.
238 borrow_data.activation_location = Some(location); 248
239 self.activation_map 249 let two_phase_use;
240 .entry(location) 250
241 .or_insert(Vec::new()) 251 match context {
242 .push(borrow_index); 252 // The use of TMP in a shared borrow does not
253 // count as an actual activation.
254 PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. } => {
255 two_phase_use = TwoPhaseUse::SharedUse;
256 }
257 _ => {
258 two_phase_use = TwoPhaseUse::MutActivate;
259 self.activation_map
260 .entry(location)
261 .or_insert(Vec::new())
262 .push(borrow_index);
263 }
264 }
265
266 borrow_data.activation_location = Some((two_phase_use, location));
243 } 267 }
244 268
245 None => {} 269 None => {}
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 9bfba219cc..20eb084e1a 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -423,6 +423,14 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
423 flow_state, 423 flow_state,
424 ); 424 );
425 } 425 }
426 StatementKind::ReadForMatch(ref place) => {
427 self.access_place(ContextKind::ReadForMatch.new(location),
428 (place, span),
429 (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
430 LocalMutationIsAllowed::No,
431 flow_state,
432 );
433 }
426 StatementKind::SetDiscriminant { 434 StatementKind::SetDiscriminant {
427 ref place, 435 ref place,
428 variant_index: _, 436 variant_index: _,
@@ -1689,14 +1697,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1689 ); 1697 );
1690 let mut error_reported = false; 1698 let mut error_reported = false;
1691 match kind { 1699 match kind {
1692 Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) 1700 Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique))
1693 | Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => { 1701 | Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. }))
1694 if let Err(_place_err) = self.is_mutable(place, LocalMutationIsAllowed::Yes) { 1702 | Write(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique))
1695 span_bug!(span, "&unique borrow for {:?} should not fail", place); 1703 | Write(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. })) =>
1696 } 1704 {
1697 } 1705 let is_local_mutation_allowed = match borrow_kind {
1698 Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { .. })) 1706 BorrowKind::Unique => LocalMutationIsAllowed::Yes,
1699 | Write(WriteKind::MutableBorrow(BorrowKind::Mut { .. })) => { 1707 BorrowKind::Mut { .. } => is_local_mutation_allowed,
1708 BorrowKind::Shared => unreachable!(),
1709 };
1700 match self.is_mutable(place, is_local_mutation_allowed) { 1710 match self.is_mutable(place, is_local_mutation_allowed) {
1701 Ok(root_place) => self.add_used_mut(root_place, flow_state), 1711 Ok(root_place) => self.add_used_mut(root_place, flow_state),
1702 Err(place_err) => { 1712 Err(place_err) => {
@@ -2090,6 +2100,7 @@ enum ContextKind {
2090 CallDest, 2100 CallDest,
2091 Assert, 2101 Assert,
2092 Yield, 2102 Yield,
2103 ReadForMatch,
2093 StorageDead, 2104 StorageDead,
2094} 2105}
2095 2106
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index 50aa1550fb..46026cdc94 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -93,6 +93,14 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
93 JustWrite 93 JustWrite
94 ); 94 );
95 } 95 }
96 StatementKind::ReadForMatch(ref place) => {
97 self.access_place(
98 ContextKind::ReadForMatch.new(location),
99 place,
100 (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
101 LocalMutationIsAllowed::No,
102 );
103 }
96 StatementKind::SetDiscriminant { 104 StatementKind::SetDiscriminant {
97 ref place, 105 ref place,
98 variant_index: _, 106 variant_index: _,
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 456aa1aa66..04f5024b76 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -836,7 +836,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
836 ); 836 );
837 } 837 }
838 } 838 }
839 StatementKind::StorageLive(_) 839 StatementKind::ReadForMatch(_)
840 | StatementKind::StorageLive(_)
840 | StatementKind::StorageDead(_) 841 | StatementKind::StorageDead(_)
841 | StatementKind::InlineAsm { .. } 842 | StatementKind::InlineAsm { .. }
842 | StatementKind::EndRegion(_) 843 | StatementKind::EndRegion(_)
diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs
index d8d160b73e..4871d427d0 100644
--- a/src/librustc_mir/borrow_check/path_utils.rs
+++ b/src/librustc_mir/borrow_check/path_utils.rs
@@ -12,7 +12,7 @@
12/// allowed to be split into separate Reservation and 12/// allowed to be split into separate Reservation and
13/// Activation phases. 13/// Activation phases.
14use borrow_check::ArtificialField; 14use borrow_check::ArtificialField;
15use borrow_check::borrow_set::{BorrowSet, BorrowData}; 15use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseUse};
16use borrow_check::{Context, Overlap}; 16use borrow_check::{Context, Overlap};
17use borrow_check::{ShallowOrDeep, Deep, Shallow}; 17use borrow_check::{ShallowOrDeep, Deep, Shallow};
18use dataflow::indexes::BorrowIndex; 18use dataflow::indexes::BorrowIndex;
@@ -431,10 +431,13 @@ pub(super) fn is_active<'tcx>(
431) -> bool { 431) -> bool {
432 debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location); 432 debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
433 433
434 // If this is not a 2-phase borrow, it is always active.
435 let activation_location = match borrow_data.activation_location { 434 let activation_location = match borrow_data.activation_location {
436 Some(v) => v, 435 // If this is not a 2-phase borrow, it is always active.
437 None => return true, 436 None => return true,
437 // And if the unique 2-phase use is not an activation, then it is *never* active.
438 Some((TwoPhaseUse::SharedUse, _)) => return false,
439 // Otherwise, we derive info from the activation point `v`:
440 Some((TwoPhaseUse::MutActivate, v)) => v,
438 }; 441 };
439 442
440 // Otherwise, it is active for every location *except* in between 443 // Otherwise, it is active for every location *except* in between
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 365b9babd0..964841e7a9 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -11,7 +11,7 @@
11//! See docs in build/expr/mod.rs 11//! See docs in build/expr/mod.rs
12 12
13use build::{BlockAnd, BlockAndExtension, Builder}; 13use build::{BlockAnd, BlockAndExtension, Builder};
14use build::ForGuard::{OutsideGuard, WithinGuard}; 14use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard};
15use build::expr::category::Category; 15use build::expr::category::Category;
16use hair::*; 16use hair::*;
17use rustc::mir::*; 17use rustc::mir::*;
@@ -88,10 +88,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
88 } 88 }
89 ExprKind::VarRef { id } => { 89 ExprKind::VarRef { id } => {
90 let place = if this.is_bound_var_in_guard(id) { 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() { 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() 93 Place::Local(index).deref()
94 } else { 94 } else {
95 let index = this.var_local_id(id, ValWithinGuard);
95 Place::Local(index) 96 Place::Local(index)
96 } 97 }
97 } else { 98 } else {
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index f3953d0877..a3c7bcfbd0 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -15,7 +15,7 @@
15 15
16use build::{BlockAnd, BlockAndExtension, Builder}; 16use build::{BlockAnd, BlockAndExtension, Builder};
17use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; 17use build::{GuardFrame, GuardFrameLocal, LocalsForNode};
18use build::ForGuard::{self, OutsideGuard, WithinGuard}; 18use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard};
19use rustc_data_structures::fx::FxHashMap; 19use rustc_data_structures::fx::FxHashMap;
20use rustc_data_structures::bitvec::BitVector; 20use rustc_data_structures::bitvec::BitVector;
21use rustc::ty::{self, Ty}; 21use rustc::ty::{self, Ty};
@@ -43,6 +43,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
43 discriminant: ExprRef<'tcx>, 43 discriminant: ExprRef<'tcx>,
44 arms: Vec<Arm<'tcx>>) 44 arms: Vec<Arm<'tcx>>)
45 -> BlockAnd<()> { 45 -> BlockAnd<()> {
46 let tcx = self.hir.tcx();
46 let discriminant_place = unpack!(block = self.as_place(block, discriminant)); 47 let discriminant_place = unpack!(block = self.as_place(block, discriminant));
47 48
48 // Matching on a `discriminant_place` with an uninhabited type doesn't 49 // Matching on a `discriminant_place` with an uninhabited type doesn't
@@ -55,12 +56,33 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
55 // HACK(eddyb) Work around the above issue by adding a dummy inspection 56 // HACK(eddyb) Work around the above issue by adding a dummy inspection
56 // of `discriminant_place`, specifically by applying `Rvalue::Discriminant` 57 // of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
57 // (which will work regardless of type) and storing the result in a temp. 58 // (which will work regardless of type) and storing the result in a temp.
59 //
60 // NOTE: Under NLL, the above issue should no longer occur because it
61 // injects a borrow of the matched input, which should have the same effect
62 // as eddyb's hack. Once NLL is the default, we can remove the hack.
63
58 let dummy_source_info = self.source_info(span); 64 let dummy_source_info = self.source_info(span);
59 let dummy_access = Rvalue::Discriminant(discriminant_place.clone()); 65 let dummy_access = Rvalue::Discriminant(discriminant_place.clone());
60 let dummy_ty = dummy_access.ty(&self.local_decls, self.hir.tcx()); 66 let dummy_ty = dummy_access.ty(&self.local_decls, tcx);
61 let dummy_temp = self.temp(dummy_ty, dummy_source_info.span); 67 let dummy_temp = self.temp(dummy_ty, dummy_source_info.span);
62 self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access); 68 self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access);
63 69
70 let source_info = self.source_info(span);
71 let borrowed_input_temp = if tcx.generate_borrow_of_any_match_input() {
72 // The region is unknown at this point; we rely on NLL
73 // inference to find an appropriate one. Therefore you can
74 // only use this when NLL is turned on.
75 assert!(tcx.use_mir_borrowck());
76 let borrowed_input =
77 Rvalue::Ref(tcx.types.re_empty, BorrowKind::Shared, discriminant_place.clone());
78 let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
79 let borrowed_input_temp = self.temp(borrowed_input_ty, span);
80 self.cfg.push_assign(block, source_info, &borrowed_input_temp, borrowed_input);
81 Some(borrowed_input_temp)
82 } else {
83 None
84 };
85
64 let mut arm_blocks = ArmBlocks { 86 let mut arm_blocks = ArmBlocks {
65 blocks: arms.iter() 87 blocks: arms.iter()
66 .map(|_| self.cfg.start_new_block()) 88 .map(|_| self.cfg.start_new_block())
@@ -99,6 +121,44 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
99 .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1))) 121 .zip(pre_binding_blocks.iter().zip(pre_binding_blocks.iter().skip(1)))
100 .map(|((arm_index, pattern, guard), 122 .map(|((arm_index, pattern, guard),
101 (pre_binding_block, next_candidate_pre_binding_block))| { 123 (pre_binding_block, next_candidate_pre_binding_block))| {
124
125 if let (true, Some(borrow_temp)) = (tcx.emit_read_for_match(),
126 borrowed_input_temp.clone()) {
127 // inject a fake read of the borrowed input at
128 // the start of each arm's pattern testing
129 // code.
130 //
131 // This should ensure that you cannot change
132 // the variant for an enum while you are in
133 // the midst of matching on it.
134
135 self.cfg.push(*pre_binding_block, Statement {
136 source_info,
137 kind: StatementKind::ReadForMatch(borrow_temp.clone()),
138 });
139 }
140
141 // One might ask: why not build up the match pair such that it
142 // matches via `borrowed_input_temp.deref()` instead of
143 // using the `discriminant_place` directly, as it is doing here?
144 //
145 // The basic answer is that if you do that, then you end up with
146 // accceses to a shared borrow of the input and that conflicts with
147 // any arms that look like e.g.
148 //
149 // match Some(&4) {
150 // ref mut foo => {
151 // ... /* mutate `foo` in arm body */ ...
152 // }
153 // }
154 //
155 // (Perhaps we could further revise the MIR
156 // construction here so that it only does a
157 // shared borrow at the outset and delays doing
158 // the mutable borrow until after the pattern is
159 // matched *and* the guard (if any) for the arm
160 // has been run.)
161
102 Candidate { 162 Candidate {
103 span: pattern.span, 163 span: pattern.span,
104 match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)], 164 match_pairs: vec![MatchPair::new(discriminant_place.clone(), pattern)],
@@ -229,7 +289,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
229 } 289 }
230 290
231 // now apply the bindings, which will also declare the variables 291 // now apply the bindings, which will also declare the variables
232 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); 292 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
233 293
234 block.unit() 294 block.unit()
235 } 295 }
@@ -870,22 +930,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
870 // (because all we have is the places associated with the 930 // (because all we have is the places associated with the
871 // match input itself; it is up to us to create a place 931 // match input itself; it is up to us to create a place
872 // holding a `&` or `&mut` that we can then borrow). 932 // holding a `&` or `&mut` that we can then borrow).
873 //
874 // * Therefore, when the binding is by-reference, we
875 // *eagerly* introduce the binding for the arm body
876 // (`tmp2`) and then borrow it (`tmp1`).
877 //
878 // * This is documented with "NOTE tricky business" below.
879 //
880 // FIXME The distinction in how `tmp2` is initialized is
881 // currently encoded in a pretty awkward fashion; namely, by
882 // passing a boolean to bind_matched_candidate_for_arm_body
883 // indicating whether all of the by-ref bindings were already
884 // initialized.
885 //
886 // * Also: pnkfelix thinks "laziness" is natural; but since
887 // MIR-borrowck did not complain with earlier (universally
888 // eager) MIR codegen, laziness might not be *necessary*.
889 933
890 let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards(); 934 let autoref = self.hir.tcx().all_pat_vars_are_implicit_refs_within_guards();
891 if let Some(guard) = candidate.guard { 935 if let Some(guard) = candidate.guard {
@@ -899,7 +943,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
899 debug!("Entering guard building context: {:?}", guard_frame); 943 debug!("Entering guard building context: {:?}", guard_frame);
900 self.guard_context.push(guard_frame); 944 self.guard_context.push(guard_frame);
901 } else { 945 } else {
902 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); 946 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
903 } 947 }
904 948
905 // the block to branch to if the guard fails; if there is no 949 // the block to branch to if the guard fails; if there is no
@@ -913,14 +957,47 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
913 } 957 }
914 958
915 let false_edge_block = self.cfg.start_new_block(); 959 let false_edge_block = self.cfg.start_new_block();
960
961 // We want to ensure that the matched candidates are bound
962 // after we have confirmed this candidate *and* any
963 // associated guard; Binding them on `block` is too soon,
964 // because that would be before we've checked the result
965 // from the guard.
966 //
967 // But binding them on `arm_block` is *too late*, because
968 // then all of the candidates for a single arm would be
969 // bound in the same place, that would cause a case like:
970 //
971 // ```rust
972 // match (30, 2) {
973 // (mut x, 1) | (2, mut x) if { true } => { ... }
974 // ... // ^^^^^^^ (this is `arm_block`)
975 // }
976 // ```
977 //
978 // would yield a `arm_block` something like:
979 //
980 // ```
981 // StorageLive(_4); // _4 is `x`
982 // _4 = &mut (_1.0: i32); // this is handling `(mut x, 1)` case
983 // _4 = &mut (_1.1: i32); // this is handling `(2, mut x)` case
984 // ```
985 //
986 // and that is clearly not correct.
987 let post_guard_block = self.cfg.start_new_block();
916 self.cfg.terminate(block, source_info, 988 self.cfg.terminate(block, source_info,
917 TerminatorKind::if_(self.hir.tcx(), cond, arm_block, 989 TerminatorKind::if_(self.hir.tcx(), cond, post_guard_block,
918 false_edge_block)); 990 false_edge_block));
919 991
920 let otherwise = self.cfg.start_new_block();
921 if autoref { 992 if autoref {
922 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, true); 993 self.bind_matched_candidate_for_arm_body(post_guard_block, &candidate.bindings);
923 } 994 }
995
996 self.cfg.terminate(post_guard_block, source_info,
997 TerminatorKind::Goto { target: arm_block });
998
999 let otherwise = self.cfg.start_new_block();
1000
924 self.cfg.terminate(false_edge_block, source_info, 1001 self.cfg.terminate(false_edge_block, source_info,
925 TerminatorKind::FalseEdges { 1002 TerminatorKind::FalseEdges {
926 real_target: otherwise, 1003 real_target: otherwise,
@@ -929,13 +1006,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
929 }); 1006 });
930 Some(otherwise) 1007 Some(otherwise)
931 } else { 1008 } else {
932 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); 1009 // (Here, it is not too early to bind the matched
1010 // candidate on `block`, because there is no guard result
1011 // that we have to inspect before we bind them.)
1012 self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
933 self.cfg.terminate(block, candidate_source_info, 1013 self.cfg.terminate(block, candidate_source_info,
934 TerminatorKind::Goto { target: arm_block }); 1014 TerminatorKind::Goto { target: arm_block });
935 None 1015 None
936 } 1016 }
937 } 1017 }
938 1018
1019 // Only called when all_pat_vars_are_implicit_refs_within_guards,
1020 // and thus all code/comments assume we are in that context.
939 fn bind_matched_candidate_for_guard(&mut self, 1021 fn bind_matched_candidate_for_guard(&mut self,
940 block: BasicBlock, 1022 block: BasicBlock,
941 bindings: &[Binding<'tcx>]) { 1023 bindings: &[Binding<'tcx>]) {
@@ -948,61 +1030,54 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
948 let re_empty = self.hir.tcx().types.re_empty; 1030 let re_empty = self.hir.tcx().types.re_empty;
949 for binding in bindings { 1031 for binding in bindings {
950 let source_info = self.source_info(binding.span); 1032 let source_info = self.source_info(binding.span);
951 let local_for_guard = self.storage_live_binding( 1033
952 block, binding.var_id, binding.span, WithinGuard); 1034 // For each pattern ident P of type T, `ref_for_guard` is
1035 // a reference R: &T pointing to the location matched by
1036 // the pattern, and every occurrence of P within a guard
1037 // denotes *R.
1038 let ref_for_guard = self.storage_live_binding(
1039 block, binding.var_id, binding.span, RefWithinGuard);
953 // Question: Why schedule drops if bindings are all 1040 // Question: Why schedule drops if bindings are all
954 // shared-&'s? Answer: Because schedule_drop_for_binding 1041 // shared-&'s? Answer: Because schedule_drop_for_binding
955 // also emits StorageDead's for those locals. 1042 // also emits StorageDead's for those locals.
956 self.schedule_drop_for_binding(binding.var_id, binding.span, WithinGuard); 1043 self.schedule_drop_for_binding(binding.var_id, binding.span, RefWithinGuard);
957 match binding.binding_mode { 1044 match binding.binding_mode {
958 BindingMode::ByValue => { 1045 BindingMode::ByValue => {
959 let rvalue = Rvalue::Ref(re_empty, BorrowKind::Shared, binding.source.clone()); 1046 let rvalue = Rvalue::Ref(re_empty, BorrowKind::Shared, binding.source.clone());
960 self.cfg.push_assign(block, source_info, &local_for_guard, rvalue); 1047 self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
961 } 1048 }
962 BindingMode::ByRef(region, borrow_kind) => { 1049 BindingMode::ByRef(region, borrow_kind) => {
963 // NOTE tricky business: For `ref id` and `ref mut 1050 // Tricky business: For `ref id` and `ref mut id`
964 // id` patterns, we want `id` within the guard to 1051 // patterns, we want `id` within the guard to
965 // correspond to a temp of type `& &T` or `& &mut 1052 // correspond to a temp of type `& &T` or `& &mut
966 // T`, while within the arm body it will 1053 // T` (i.e. a "borrow of a borrow") that is
967 // correspond to a temp of type `&T` or `&mut T`, 1054 // implicitly dereferenced.
968 // as usual.
969 //
970 // But to inject the level of indirection, we need
971 // something to point to.
972 //
973 // So:
974 //
975 // 1. First set up the local for the arm body
976 // (even though we have not yet evaluated the
977 // guard itself),
978 // 1055 //
979 // 2. Then setup the local for the guard, which is 1056 // To borrow a borrow, we need that inner borrow
980 // just a reference to the local from step 1. 1057 // to point to. So, create a temp for the inner
1058 // borrow, and then take a reference to it.
981 // 1059 //
982 // Note that since we are setting up the local for 1060 // Note: the temp created here is *not* the one
983 // the arm body a bit eagerly here (and likewise 1061 // used by the arm body itself. This eases
984 // scheduling its drop code), we should *not* do 1062 // observing two-phase borrow restrictions.
985 // it redundantly later on. 1063 let val_for_guard = self.storage_live_binding(
986 // 1064 block, binding.var_id, binding.span, ValWithinGuard);
987 // While we could have kept track of this with a 1065 self.schedule_drop_for_binding(binding.var_id, binding.span, ValWithinGuard);
988 // flag or collection of such bindings, the 1066
989 // treatment of all of these cases is uniform, so 1067 // rust-lang/rust#27282: We reuse the two-phase
990 // we should be safe just avoiding the code 1068 // borrow infrastructure so that the mutable
991 // without maintaining such state.) 1069 // borrow (whose mutabilty is *unusable* within
992 let local_for_arm_body = self.storage_live_binding( 1070 // the guard) does not conflict with the implicit
993 block, binding.var_id, binding.span, OutsideGuard); 1071 // borrow of the whole match input. See additional
994 self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); 1072 // discussion on rust-lang/rust#49870.
995 1073 let borrow_kind = match borrow_kind {
996 // rust-lang/rust#27282: this potentially mutable 1074 BorrowKind::Shared | BorrowKind::Unique => borrow_kind,
997 // borrow may require a cast in the future to 1075 BorrowKind::Mut { .. } => BorrowKind::Mut { allow_two_phase_borrow: true },
998 // avoid conflicting with an implicit borrow of 1076 };
999 // the whole match input; or maybe it just
1000 // requires an extension of our two-phase borrows
1001 // system. See discussion on rust-lang/rust#49870.
1002 let rvalue = Rvalue::Ref(region, borrow_kind, binding.source.clone()); 1077 let rvalue = Rvalue::Ref(region, borrow_kind, binding.source.clone());
1003 self.cfg.push_assign(block, source_info, &local_for_arm_body, rvalue); 1078 self.cfg.push_assign(block, source_info, &val_for_guard, rvalue);
1004 let rvalue = Rvalue::Ref(region, BorrowKind::Shared, local_for_arm_body); 1079 let rvalue = Rvalue::Ref(region, BorrowKind::Shared, val_for_guard);
1005 self.cfg.push_assign(block, source_info, &local_for_guard, rvalue); 1080 self.cfg.push_assign(block, source_info, &ref_for_guard, rvalue);
1006 } 1081 }
1007 } 1082 }
1008 } 1083 }
@@ -1010,19 +1085,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1010 1085
1011 fn bind_matched_candidate_for_arm_body(&mut self, 1086 fn bind_matched_candidate_for_arm_body(&mut self,
1012 block: BasicBlock, 1087 block: BasicBlock,
1013 bindings: &[Binding<'tcx>], 1088 bindings: &[Binding<'tcx>]) {
1014 already_initialized_state_for_refs: bool) { 1089 debug!("bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}", block, bindings);
1015 debug!("bind_matched_candidate_for_arm_body(block={:?}, bindings={:?}, \
1016 already_initialized_state_for_refs={:?})",
1017 block, bindings, already_initialized_state_for_refs);
1018 1090
1019 // Assign each of the bindings. This may trigger moves out of the candidate. 1091 // Assign each of the bindings. This may trigger moves out of the candidate.
1020 for binding in bindings { 1092 for binding in bindings {
1021 if let BindingMode::ByRef(..) = binding.binding_mode {
1022 // See "NOTE tricky business" above
1023 if already_initialized_state_for_refs { continue; }
1024 }
1025
1026 let source_info = self.source_info(binding.span); 1093 let source_info = self.source_info(binding.span);
1027 let local = self.storage_live_binding(block, binding.var_id, binding.span, 1094 let local = self.storage_live_binding(block, binding.var_id, binding.span,
1028 OutsideGuard); 1095 OutsideGuard);
@@ -1059,7 +1126,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1059 var_id, name, var_ty, source_info, syntactic_scope); 1126 var_id, name, var_ty, source_info, syntactic_scope);
1060 1127
1061 let tcx = self.hir.tcx(); 1128 let tcx = self.hir.tcx();
1062 let for_arm_body = self.local_decls.push(LocalDecl::<'tcx> { 1129 let local = LocalDecl::<'tcx> {
1063 mutability, 1130 mutability,
1064 ty: var_ty.clone(), 1131 ty: var_ty.clone(),
1065 name: Some(name), 1132 name: Some(name),
@@ -1067,9 +1134,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1067 syntactic_scope, 1134 syntactic_scope,
1068 internal: false, 1135 internal: false,
1069 is_user_variable: true, 1136 is_user_variable: true,
1070 }); 1137 };
1138 let for_arm_body = self.local_decls.push(local.clone());
1071 let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() { 1139 let locals = if has_guard.0 && tcx.all_pat_vars_are_implicit_refs_within_guards() {
1072 let for_guard = self.local_decls.push(LocalDecl::<'tcx> { 1140 let val_for_guard = self.local_decls.push(local);
1141 let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
1073 mutability, 1142 mutability,
1074 ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty), 1143 ty: tcx.mk_imm_ref(tcx.types.re_empty, var_ty),
1075 name: Some(name), 1144 name: Some(name),
@@ -1078,7 +1147,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1078 internal: false, 1147 internal: false,
1079 is_user_variable: true, 1148 is_user_variable: true,
1080 }); 1149 });
1081 LocalsForNode::Two { for_guard, for_arm_body } 1150 LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
1082 } else { 1151 } else {
1083 LocalsForNode::One(for_arm_body) 1152 LocalsForNode::One(for_arm_body)
1084 }; 1153 };
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 2da2d3f697..4822b9e4df 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -293,7 +293,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
293#[derive(Debug)] 293#[derive(Debug)]
294enum LocalsForNode { 294enum LocalsForNode {
295 One(Local), 295 One(Local),
296 Two { for_guard: Local, for_arm_body: Local }, 296 Three { val_for_guard: Local, ref_for_guard: Local, for_arm_body: Local },
297} 297}
298 298
299#[derive(Debug)] 299#[derive(Debug)]
@@ -325,12 +325,15 @@ struct GuardFrame {
325 locals: Vec<GuardFrameLocal>, 325 locals: Vec<GuardFrameLocal>,
326} 326}
327 327
328/// ForGuard is isomorphic to a boolean flag. It indicates whether we are 328/// ForGuard indicates whether we are talking about:
329/// talking about the temp for a local binding for a use within a guard expression, 329/// 1. the temp for a local binding used solely within guard expressions,
330/// or a temp for use outside of a guard expressions. 330/// 2. the temp that holds reference to (1.), which is actually what the
331/// guard expressions see, or
332/// 3. the temp for use outside of guard expressions.
331#[derive(Copy, Clone, Debug, PartialEq, Eq)] 333#[derive(Copy, Clone, Debug, PartialEq, Eq)]
332enum ForGuard { 334enum ForGuard {
333 WithinGuard, 335 ValWithinGuard,
336 RefWithinGuard,
334 OutsideGuard, 337 OutsideGuard,
335} 338}
336 339
@@ -338,11 +341,13 @@ impl LocalsForNode {
338 fn local_id(&self, for_guard: ForGuard) -> Local { 341 fn local_id(&self, for_guard: ForGuard) -> Local {
339 match (self, for_guard) { 342 match (self, for_guard) {
340 (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) | 343 (&LocalsForNode::One(local_id), ForGuard::OutsideGuard) |
341 (&LocalsForNode::Two { for_guard: local_id, .. }, ForGuard::WithinGuard) | 344 (&LocalsForNode::Three { val_for_guard: local_id, .. }, ForGuard::ValWithinGuard) |
342 (&LocalsForNode::Two { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) => 345 (&LocalsForNode::Three { ref_for_guard: local_id, .. }, ForGuard::RefWithinGuard) |
346 (&LocalsForNode::Three { for_arm_body: local_id, .. }, ForGuard::OutsideGuard) =>
343 local_id, 347 local_id,
344 348
345 (&LocalsForNode::One(_), ForGuard::WithinGuard) => 349 (&LocalsForNode::One(_), ForGuard::ValWithinGuard) |
350 (&LocalsForNode::One(_), ForGuard::RefWithinGuard) =>
346 bug!("anything with one local should never be within a guard."), 351 bug!("anything with one local should never be within a guard."),
347 } 352 }
348 } 353 }
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 04c62854c5..78886baf51 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -227,6 +227,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
227 } 227 }
228 } 228 }
229 229
230 mir::StatementKind::ReadForMatch(..) |
230 mir::StatementKind::SetDiscriminant { .. } | 231 mir::StatementKind::SetDiscriminant { .. } |
231 mir::StatementKind::StorageLive(..) | 232 mir::StatementKind::StorageLive(..) |
232 mir::StatementKind::Validate(..) | 233 mir::StatementKind::Validate(..) |
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index cbf4c82276..2ff2284214 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -278,6 +278,9 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
278 } 278 }
279 self.gather_rvalue(rval); 279 self.gather_rvalue(rval);
280 } 280 }
281 StatementKind::ReadForMatch(ref place) => {
282 self.create_move_path(place);
283 }
281 StatementKind::InlineAsm { ref outputs, ref inputs, ref asm } => { 284 StatementKind::InlineAsm { ref outputs, ref inputs, ref asm } => {
282 for (output, kind) in outputs.iter().zip(&asm.outputs) { 285 for (output, kind) in outputs.iter().zip(&asm.outputs) {
283 if !kind.is_indirect { 286 if !kind.is_indirect {
diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs
index 7c44a8d4d5..0a11397009 100644
--- a/src/librustc_mir/hair/pattern/check_match.rs
+++ b/src/librustc_mir/hair/pattern/check_match.rs
@@ -181,7 +181,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
181 // Second, if there is a guard on each arm, make sure it isn't 181 // Second, if there is a guard on each arm, make sure it isn't
182 // assigning or borrowing anything mutably. 182 // assigning or borrowing anything mutably.
183 if let Some(ref guard) = arm.guard { 183 if let Some(ref guard) = arm.guard {
184 check_for_mutation_in_guard(self, &guard); 184 if self.tcx.check_for_mutation_in_guard_via_ast_walk() {
185 check_for_mutation_in_guard(self, &guard);
186 }
185 } 187 }
186 188
187 // Third, perform some lints. 189 // Third, perform some lints.
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index 554d87a04e..ab15278219 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -79,6 +79,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
79 self.deallocate_local(old_val)?; 79 self.deallocate_local(old_val)?;
80 } 80 }
81 81
82 // No dynamic semantics attached to `ReadForMatch`; MIR
83 // interpreter is solely intended for borrowck'ed code.
84 ReadForMatch(..) => {}
85
82 // Validity checks. 86 // Validity checks.
83 Validate(op, ref places) => { 87 Validate(op, ref places) => {
84 for operand in places { 88 for operand in places {
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index fc3764e4f4..4081f827d4 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -100,6 +100,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
100 self.source_info = statement.source_info; 100 self.source_info = statement.source_info;
101 match statement.kind { 101 match statement.kind {
102 StatementKind::Assign(..) | 102 StatementKind::Assign(..) |
103 StatementKind::ReadForMatch(..) |
103 StatementKind::SetDiscriminant { .. } | 104 StatementKind::SetDiscriminant { .. } |
104 StatementKind::StorageLive(..) | 105 StatementKind::StorageLive(..) |
105 StatementKind::StorageDead(..) | 106 StatementKind::StorageDead(..) |
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index c249dc312f..7196301294 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1135,6 +1135,7 @@ This does not pose a problem by itself because they can't be accessed directly."
1135 StatementKind::Assign(ref place, ref rvalue) => { 1135 StatementKind::Assign(ref place, ref rvalue) => {
1136 this.visit_assign(bb, place, rvalue, location); 1136 this.visit_assign(bb, place, rvalue, location);
1137 } 1137 }
1138 StatementKind::ReadForMatch(..) |
1138 StatementKind::SetDiscriminant { .. } | 1139 StatementKind::SetDiscriminant { .. } |
1139 StatementKind::StorageLive(_) | 1140 StatementKind::StorageLive(_) |
1140 StatementKind::StorageDead(_) | 1141 StatementKind::StorageDead(_) |
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index bcc8fef18f..680b60b972 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -47,6 +47,7 @@ impl RemoveNoopLandingPads {
47 { 47 {
48 for stmt in &mir[bb].statements { 48 for stmt in &mir[bb].statements {
49 match stmt.kind { 49 match stmt.kind {
50 StatementKind::ReadForMatch(_) |
50 StatementKind::StorageLive(_) | 51 StatementKind::StorageLive(_) |
51 StatementKind::StorageDead(_) | 52 StatementKind::StorageDead(_) |
52 StatementKind::EndRegion(_) | 53 StatementKind::EndRegion(_) |
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 8f67b9e7c3..b23f056801 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -158,6 +158,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
158 mir::StatementKind::Assign(ref place, ref rvalue) => { 158 mir::StatementKind::Assign(ref place, ref rvalue) => {
159 (place, rvalue) 159 (place, rvalue)
160 } 160 }
161 mir::StatementKind::ReadForMatch(_) |
161 mir::StatementKind::StorageLive(_) | 162 mir::StatementKind::StorageLive(_) |
162 mir::StatementKind::StorageDead(_) | 163 mir::StatementKind::StorageDead(_) |
163 mir::StatementKind::InlineAsm { .. } | 164 mir::StatementKind::InlineAsm { .. } |
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index 45c6e89321..f7c8f8f43f 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -85,6 +85,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
85 self.record("Statement", statement); 85 self.record("Statement", statement);
86 self.record(match statement.kind { 86 self.record(match statement.kind {
87 StatementKind::Assign(..) => "StatementKind::Assign", 87 StatementKind::Assign(..) => "StatementKind::Assign",
88 StatementKind::ReadForMatch(..) => "StatementKind::ReadForMatch",
88 StatementKind::EndRegion(..) => "StatementKind::EndRegion", 89 StatementKind::EndRegion(..) => "StatementKind::EndRegion",
89 StatementKind::Validate(..) => "StatementKind::Validate", 90 StatementKind::Validate(..) => "StatementKind::Validate",
90 StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant", 91 StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs
index c2a40399ef..739cbc0a99 100644
--- a/src/test/mir-opt/match_false_edges.rs
+++ b/src/test/mir-opt/match_false_edges.rs
@@ -54,60 +54,65 @@ fn main() {
54// ... 54// ...
55// _2 = std::option::Option<i32>::Some(const 42i32,); 55// _2 = std::option::Option<i32>::Some(const 42i32,);
56// _3 = discriminant(_2); 56// _3 = discriminant(_2);
57// _7 = discriminant(_2); 57// _14 = promoted[1];
58// switchInt(move _7) -> [0isize: bb6, 1isize: bb4, otherwise: bb8]; 58// _4 = &(*_14);
59// _9 = discriminant(_2);
60// switchInt(move _9) -> [0isize: bb5, 1isize: bb3, otherwise: bb7];
59// } 61// }
60// bb1: { 62// bb1: {
61// resume; 63// resume;
62// } 64// }
63// bb2: { // arm1 65// bb2: { // arm1
64// StorageLive(_9); 66// _1 = (const 3i32, const 3i32);
65// _9 = _4;
66// _1 = (const 1i32, move _9);
67// StorageDead(_9);
68// goto -> bb13; 67// goto -> bb13;
69// } 68// }
70// bb3: { // binding3(empty) and arm3 69// bb3: { // binding3(empty) and arm3
71// _1 = (const 3i32, const 3i32); 70// ReadForMatch(_4);
72// goto -> bb13; 71// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
73// } 72// }
74// bb4: { 73// bb4: {
75// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 74// ReadForMatch(_4);
75// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding2
76// } 76// }
77// bb5: { 77// bb5: {
78// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding2 78// ReadForMatch(_4);
79// falseEdges -> [real: bb2, imaginary: bb6]; //pre_binding3
79// } 80// }
80// bb6: { 81// bb6: {
81// falseEdges -> [real: bb3, imaginary: bb7]; //pre_binding3 82// unreachable;
82// } 83// }
83// bb7: { 84// bb7: {
84// unreachable; 85// unreachable;
85// } 86// }
86// bb8: { 87// bb8: { // binding1 and guard
87// unreachable; 88// StorageLive(_7);
89// _13 = promoted[0];
90// _7 = &(((*_13) as Some).0: i32);
91// StorageLive(_10);
92// _10 = const guard() -> [return: bb9, unwind: bb1];
88// } 93// }
89// bb9: { // binding1 and guard 94// bb9: {
90// StorageLive(_5); 95// switchInt(move _10) -> [false: bb10, otherwise: bb11];
91// _11 = promoted[0];
92// _5 = &(((*_11) as Some).0: i32);
93// StorageLive(_8);
94// _8 = const guard() -> [return: bb10, unwind: bb1];
95// } 96// }
96// bb10: { // end of guard 97// bb10: { // to pre_binding2
97// StorageLive(_4); 98// falseEdges -> [real: bb4, imaginary: bb4];
98// _4 = ((_2 as Some).0: i32);
99// switchInt(move _8) -> [false: bb11, otherwise: bb2];
100// } 99// }
101// bb11: { // to pre_binding2 100// bb11: { // bindingNoLandingPads.before.mir2 and arm2
102// falseEdges -> [real: bb5, imaginary: bb5]; 101// StorageLive(_5);
102// _5 = ((_2 as Some).0: i32);
103// StorageLive(_11);
104// _11 = _5;
105// _1 = (const 1i32, move _11);
106// StorageDead(_11);
107// goto -> bb13;
103// } 108// }
104// bb12: { // bindingNoLandingPads.before.mir2 and arm2 109// bb12: {
105// StorageLive(_6); 110// StorageLive(_8);
106// _6 = ((_2 as Some).0: i32); 111// _8 = ((_2 as Some).0: i32);
107// StorageLive(_10); 112// StorageLive(_12);
108// _10 = _6; 113// _12 = _8;
109// _1 = (const 2i32, move _10); 114// _1 = (const 2i32, move_12);
110// StorageDead(_10); 115// StorageDead(_12);
111// goto -> bb13; 116// goto -> bb13;
112// } 117// }
113// bb13: { 118// bb13: {
@@ -121,59 +126,63 @@ fn main() {
121// ... 126// ...
122// _2 = std::option::Option<i32>::Some(const 42i32,); 127// _2 = std::option::Option<i32>::Some(const 42i32,);
123// _3 = discriminant(_2); 128// _3 = discriminant(_2);
124// _7 = discriminant(_2); 129// _4 = &_2;
125// switchInt(move _7) -> [0isize: bb5, 1isize: bb4, otherwise: bb8]; 130// _9 = discriminant(_2);
131// switchInt(move _9) -> [0isize: bb4, 1isize: bb3, otherwise: bb7];
126// } 132// }
127// bb1: { 133// bb1: {
128// resume; 134// resume;
129// } 135// }
130// bb2: { // arm1 136// bb2: { // arm2
131// StorageLive(_9);
132// _9 = _4;
133// _1 = (const 1i32, move _9);
134// StorageDead(_9);
135// goto -> bb13;
136// }
137// bb3: { // binding3(empty) and arm3
138// _1 = (const 3i32, const 3i32); 137// _1 = (const 3i32, const 3i32);
139// goto -> bb13; 138// goto -> bb13;
140// } 139// }
140// bb3: {
141// ReadForMatch(_4);
142// falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
143// }
141// bb4: { 144// bb4: {
142// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 145// ReadForMatch(_4);
146// falseEdges -> [real: bb2, imaginary: bb5]; //pre_binding2
143// } 147// }
144// bb5: { 148// bb5: {
145// falseEdges -> [real: bb3, imaginary: bb6]; //pre_binding2 149// ReadForMatch(_4);
150// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding3
146// } 151// }
147// bb6: { 152// bb6: {
148// falseEdges -> [real: bb12, imaginary: bb7]; //pre_binding3 153// unreachable;
149// } 154// }
150// bb7: { 155// bb7: {
151// unreachable; 156// unreachable;
152// } 157// }
153// bb8: { 158// bb8: { // binding1 and guard
154// unreachable; 159// StorageLive(_7);
160// _7 = &((_2 as Some).0: i32);
161// StorageLive(_10);
162// _10 = const guard() -> [return: bb9, unwind: bb1];
155// } 163// }
156// bb9: { // binding1 and guard 164// bb9: { // end of guard
157// StorageLive(_5); 165// switchInt(move _10) -> [false: bb10, otherwise: bb11];
158// _5 = &((_2 as Some).0: i32);
159// StorageLive(_8);
160// _8 = const guard() -> [return: bb10, unwind: bb1];
161// } 166// }
162// bb10: { // end of guard 167// bb10: { // to pre_binding3 (can skip 2 since this is `Some`)
163// StorageLive(_4); 168// falseEdges -> [real: bb5, imaginary: bb4];
164// _4 = ((_2 as Some).0: i32);
165// switchInt(move _8) -> [false: bb11, otherwise: bb2];
166// } 169// }
167// bb11: { // to pre_binding2 170// bb11: { // arm1
168// falseEdges -> [real: bb6, imaginary: bb5]; 171// StorageLive(_5);
172// _5 = ((_2 as Some).0: i32);
173// StorageLive(_11);
174// _11 = _5;
175// _1 = (const 1i32, move _11);
176// StorageDead(_11);
177// goto -> bb13;
169// } 178// }
170// bb12: { // binding2 and arm2 179// bb12: { // binding3 and arm3
171// StorageLive(_6); 180// StorageLive(_8);
172// _6 = ((_2 as Some).0: i32); 181// _8 = ((_2 as Some).0: i32);
173// StorageLive(_10); 182// StorageLive(_12);
174// _10 = _6; 183// _12 = _8;
175// _1 = (const 2i32, move _10); 184// _1 = (const 2i32, move _12);
176// StorageDead(_10); 185// StorageDead(_12);
177// goto -> bb13; 186// goto -> bb13;
178// } 187// }
179// bb13: { 188// bb13: {
@@ -187,76 +196,80 @@ fn main() {
187// ... 196// ...
188// _2 = std::option::Option<i32>::Some(const 1i32,); 197// _2 = std::option::Option<i32>::Some(const 1i32,);
189// _3 = discriminant(_2); 198// _3 = discriminant(_2);
190// _10 = discriminant(_2); 199// _4 = &_2;
191// switchInt(move _10) -> [1isize: bb4, otherwise: bb5]; 200// _13 = discriminant(_2);
201// switchInt(move _13) -> [1isize: bb2, otherwise: bb3];
192// } 202// }
193// bb1: { 203// bb1: {
194// resume; 204// resume;
195// } 205// }
196// bb2: { // arm1 206// bb2: {
197// _1 = const 1i32; 207// ReadForMatch(_4);
198// goto -> bb17; 208// falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1
199// } 209// }
200// bb3: { // arm3 210// bb3: {
201// _1 = const 3i32; 211// ReadForMatch(_4);
202// goto -> bb17; 212// falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2
203// } 213// }
204//
205// bb4: { 214// bb4: {
206// falseEdges -> [real: bb9, imaginary: bb5]; //pre_binding1 215// ReadForMatch(_4);
216// falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding3
207// } 217// }
208// bb5: { 218// bb5: {
209// falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding2 219// ReadForMatch(_4);
220// falseEdges -> [real: bb16, imaginary: bb6]; //pre_binding4
210// } 221// }
211// bb6: { 222// bb6: {
212// falseEdges -> [real: bb13, imaginary: bb7]; //pre_binding3
213// }
214// bb7: {
215// falseEdges -> [real: bb16, imaginary: bb8]; //pre_binding4
216// }
217// bb8: {
218// unreachable; 223// unreachable;
219// } 224// }
220// bb9: { // binding1: Some(w) if guard() 225// bb7: { // binding1: Some(w) if guard()
221// StorageLive(_5); 226// StorageLive(_7);
222// _5 = &((_2 as Some).0: i32); 227// _7 = &((_2 as Some).0: i32);
223// StorageLive(_11); 228// StorageLive(_14);
224// _11 = const guard() -> [return: bb10, unwind: bb1]; 229// _14 = const guard() -> [return: bb8, unwind: bb1];
225// } 230// }
226// bb10: { //end of guard 231// bb8: { //end of guard
227// StorageLive(_4); 232// switchInt(move _14) -> [false: bb9, otherwise: bb10];
228// _4 = ((_2 as Some).0: i32);
229// switchInt(move _11) -> [false: bb11, otherwise: bb2];
230// } 233// }
231// bb11: { // to pre_binding2 234// bb9: { // to pre_binding2
232// falseEdges -> [real: bb5, imaginary: bb5]; 235// falseEdges -> [real: bb3, imaginary: bb3];
233// } 236// }
234// bb12: { // binding2 & arm2 237// bb10: { // set up bindings for arm1
235// StorageLive(_6); 238// StorageLive(_5);
236// _6 = _2; 239// _5 = ((_2 as Some).0: i32);
237// _1 = const 2i32; 240// _1 = const 1i32;
238// goto -> bb17; 241// goto -> bb17;
239// } 242// }
240// bb13: { // binding3: Some(y) if guard2(y) 243// bb11: { // binding2 & arm2
241// StorageLive(_8); 244// StorageLive(_8);
242// _8 = &((_2 as Some).0: i32); 245// _8 = _2;
243// StorageLive(_13); 246// _1 = const 2i32;
244// StorageLive(_14); 247// goto -> bb17;
245// _14 = (*_8);
246// _13 = const guard2(move _14) -> [return: bb14, unwind: bb1];
247// } 248// }
248// bb14: { // end of guard2 249// bb12: { // binding3: Some(y) if guard2(y)
249// StorageDead(_14); 250// StorageLive(_11);
250// StorageLive(_7); 251// _11 = &((_2 as Some).0: i32);
251// _7 = ((_2 as Some).0: i32); 252// StorageLive(_16);
252// switchInt(move _13) -> [false: bb15, otherwise: bb3]; 253// StorageLive(_17);
254// _17 = (*_11);
255// _16 = const guard2(move _17) -> [return: bb13, unwind: bb1];
253// } 256// }
254// bb15: { // to pre_binding4 257// bb13: { // end of guard2
255// falseEdges -> [real: bb7, imaginary: bb7]; 258// StorageDead(_17);
259// switchInt(move _16) -> [false: bb14, otherwise: bb15];
256// } 260// }
257// bb16: { // binding4 & arm4 261// bb14: { // to pre_binding4
262// falseEdges -> [real: bb5, imaginary: bb5];
263// }
264// bb15: { // set up bindings for arm3
258// StorageLive(_9); 265// StorageLive(_9);
259// _9 = _2; 266// _9 = ((_2 as Some).0: i32);
267// _1 = const 3i32;
268// goto -> bb17;
269// }
270// bb16: { // binding4 & arm4
271// StorageLive(_12);
272// _12 = _2;
260// _1 = const 4i32; 273// _1 = const 4i32;
261// goto -> bb17; 274// goto -> bb17;
262// } 275// }
diff --git a/src/test/run-pass/issue-24535-allow-mutable-borrow-in-match-guard.rs b/src/test/run-pass/issue-24535-allow-mutable-borrow-in-match-guard.rs
new file mode 100644
index 0000000000..ac415e31f2
--- /dev/null
+++ b/src/test/run-pass/issue-24535-allow-mutable-borrow-in-match-guard.rs
@@ -0,0 +1,68 @@
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// This test illustrates that under NLL, we can remove our overly
12// conservative approach for disallowing mutations of match inputs.
13
14// See further discussion on rust-lang/rust#24535 and
15// rust-lang/rfcs#1006.
16
17// compile-flags: -Z disable-ast-check-for-mutation-in-guard
18
19#![feature(nll)]
20
21fn main() {
22 rust_issue_24535();
23 rfcs_issue_1006_1();
24 rfcs_issue_1006_2();
25}
26
27fn rust_issue_24535() {
28 fn compare(a: &u8, b: &mut u8) -> bool {
29 a == b
30 }
31
32 let a = 3u8;
33
34 match a {
35 0 => panic!("nope"),
36 3 if compare(&a, &mut 3) => (),
37 _ => panic!("nope"),
38 }
39}
40
41fn rfcs_issue_1006_1() {
42 let v = vec!["1".to_string(), "2".to_string(), "3".to_string()];
43 match Some(&v) {
44 Some(iv) if iv.iter().any(|x| &x[..]=="2") => true,
45 _ => panic!("nope"),
46 };
47}
48
49fn rfcs_issue_1006_2() {
50 #[inline(always)]
51 fn check<'a, I: Iterator<Item=&'a i32>>(mut i: I) -> bool {
52 i.any(|&x| x == 2)
53 }
54
55 let slice = [1, 2, 3];
56
57 match 42 {
58 _ if slice.iter().any(|&x| x == 2) => { true },
59 _ => { panic!("nope"); }
60 };
61
62 // (This match is just illustrating how easy it was to circumvent
63 // the checking performed for the previous `match`.)
64 match 42 {
65 _ if check(slice.iter()) => { true },
66 _ => { panic!("nope"); }
67 };
68}
diff --git a/src/test/ui/borrowck/issue-41962.rs b/src/test/ui/borrowck/issue-41962.rs
index f7c33691ad..29481dbe52 100644
--- a/src/test/ui/borrowck/issue-41962.rs
+++ b/src/test/ui/borrowck/issue-41962.rs
@@ -15,11 +15,12 @@ pub fn main(){
15 15
16 loop { 16 loop {
17 if let Some(thing) = maybe { 17 if let Some(thing) = maybe {
18 //~^ ERROR use of partially moved value: `maybe` (Ast) [E0382] 18 }
19 //~^^ ERROR use of partially moved value: `maybe` (Ast) [E0382]
19 //~| ERROR use of moved value: `(maybe as std::prelude::v1::Some).0` (Ast) [E0382] 20 //~| ERROR use of moved value: `(maybe as std::prelude::v1::Some).0` (Ast) [E0382]
20 //~| ERROR use of moved value: `maybe` (Mir) [E0382] 21 //~| ERROR use of moved value: `maybe` (Mir) [E0382]
21 //~| ERROR use of moved value: `maybe` (Mir) [E0382] 22 //~| ERROR use of moved value: `maybe` (Mir) [E0382]
22 //~| ERROR use of moved value: `maybe.0` (Mir) [E0382] 23 //~| ERROR use of moved value: `maybe.0` (Mir) [E0382]
23 } 24 //~| ERROR borrow of moved value: `maybe` (Mir) [E0382]
24 } 25 }
25} 26}
diff --git a/src/test/ui/borrowck/issue-41962.stderr b/src/test/ui/borrowck/issue-41962.stderr
index 39525d787b..e6eb3739d8 100644
--- a/src/test/ui/borrowck/issue-41962.stderr
+++ b/src/test/ui/borrowck/issue-41962.stderr
@@ -23,16 +23,23 @@ LL | if let Some(thing) = maybe {
23 | ^ ----- value moved here 23 | ^ ----- value moved here
24 | _________| 24 | _________|
25 | | 25 | |
26LL | | //~^ ERROR use of partially moved value: `maybe` (Ast) [E0382]
27LL | | //~| ERROR use of moved value: `(maybe as std::prelude::v1::Some).0` (Ast) [E0382]
28LL | | //~| ERROR use of moved value: `maybe` (Mir) [E0382]
29LL | | //~| ERROR use of moved value: `maybe` (Mir) [E0382]
30LL | | //~| ERROR use of moved value: `maybe.0` (Mir) [E0382]
31LL | | } 26LL | | }
32 | |_________^ value used here after move 27 | |_________^ value used here after move
33 | 28 |
34 = note: move occurs because `maybe` has type `std::option::Option<std::vec::Vec<bool>>`, which does not implement the `Copy` trait 29 = note: move occurs because `maybe` has type `std::option::Option<std::vec::Vec<bool>>`, which does not implement the `Copy` trait
35 30
31error[E0382]: borrow of moved value: `maybe` (Mir)
32 --> $DIR/issue-41962.rs:17:9
33 |
34LL | if let Some(thing) = maybe {
35 | ^ ----- value moved here
36 | _________|
37 | |
38LL | | }
39 | |_________^ value borrowed here after move
40 |
41 = note: move occurs because `maybe` has type `std::option::Option<std::vec::Vec<bool>>`, which does not implement the `Copy` trait
42
36error[E0382]: use of moved value: `maybe` (Mir) 43error[E0382]: use of moved value: `maybe` (Mir)
37 --> $DIR/issue-41962.rs:17:16 44 --> $DIR/issue-41962.rs:17:16
38 | 45 |
@@ -52,6 +59,6 @@ LL | if let Some(thing) = maybe {
52 | 59 |
53 = note: move occurs because `maybe.0` has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait 60 = note: move occurs because `maybe.0` has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
54 61
55error: aborting due to 5 previous errors 62error: aborting due to 6 previous errors
56 63
57For more information about this error, try `rustc --explain E0382`. 64For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/issue-27282-move-match-input-into-guard.rs b/src/test/ui/issue-27282-move-match-input-into-guard.rs
new file mode 100644
index 0000000000..b3be36e41e
--- /dev/null
+++ b/src/test/ui/issue-27282-move-match-input-into-guard.rs
@@ -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// Issue 27282: Example 2: This sidesteps the AST checks disallowing
12// mutable borrows in match guards by hiding the mutable borrow in a
13// guard behind a move (of the mutably borrowed match input) within a
14// closure.
15//
16// This example is not rejected by AST borrowck (and then reliably
17// reaches the panic code when executed, despite the compiler warning
18// about that match arm being unreachable.
19
20#![feature(nll)]
21
22fn main() {
23 let b = &mut true;
24 match b {
25 &mut false => {},
26 _ if { (|| { let bar = b; *bar = false; })();
27 //~^ ERROR cannot move out of `b` because it is borrowed [E0505]
28 false } => { },
29 &mut true => { println!("You might think we should get here"); },
30 //~^ ERROR use of moved value: `*b` [E0382]
31 _ => panic!("surely we could never get here, since rustc warns it is unreachable."),
32 }
33}
diff --git a/src/test/ui/issue-27282-move-match-input-into-guard.stderr b/src/test/ui/issue-27282-move-match-input-into-guard.stderr
new file mode 100644
index 0000000000..f89388f173
--- /dev/null
+++ b/src/test/ui/issue-27282-move-match-input-into-guard.stderr
@@ -0,0 +1,32 @@
1error[E0505]: cannot move out of `b` because it is borrowed
2 --> $DIR/issue-27282-move-match-input-into-guard.rs:26:16
3 |
4LL | match b {
5 | _____-
6 | |_____|
7 | ||
8LL | || &mut false => {},
9LL | || _ if { (|| { let bar = b; *bar = false; })();
10 | || ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `b` occurs here
11LL | || //~^ ERROR cannot move out of `b` because it is borrowed [E0505]
12... ||
13LL | || _ => panic!("surely we could never get here, since rustc warns it is unreachable."),
14LL | || }
15 | || -
16 | ||_____|
17 | |______borrow of `b` occurs here
18 | borrow later used here
19
20error[E0382]: use of moved value: `*b`
21 --> $DIR/issue-27282-move-match-input-into-guard.rs:29:14
22 |
23LL | _ if { (|| { let bar = b; *bar = false; })();
24 | ----------------------------------- value moved here
25...
26LL | &mut true => { println!("You might think we should get here"); },
27 | ^^^^ value used here after move
28
29error: aborting due to 2 previous errors
30
31Some errors occurred: E0382, E0505.
32For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/issue-27282-move-ref-mut-into-guard.rs b/src/test/ui/issue-27282-move-ref-mut-into-guard.rs
new file mode 100644
index 0000000000..5b4c746a1b
--- /dev/null
+++ b/src/test/ui/issue-27282-move-ref-mut-into-guard.rs
@@ -0,0 +1,28 @@
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// Issue 27282: Example 1: This sidesteps the AST checks disallowing
12// mutable borrows in match guards by hiding the mutable borrow in a
13// guard behind a move (of the ref mut pattern id) within a closure.
14//
15// This example is not rejected by AST borrowck (and then reliably
16// segfaults when executed).
17
18#![feature(nll)]
19
20fn main() {
21 match Some(&4) {
22 None => {},
23 ref mut foo
24 if { (|| { let bar = foo; bar.take() })(); false } => {},
25 //~^ ERROR cannot move out of borrowed content [E0507]
26 Some(s) => std::process::exit(*s),
27 }
28}
diff --git a/src/test/ui/issue-27282-move-ref-mut-into-guard.stderr b/src/test/ui/issue-27282-move-ref-mut-into-guard.stderr
new file mode 100644
index 0000000000..f6ffa90069
--- /dev/null
+++ b/src/test/ui/issue-27282-move-ref-mut-into-guard.stderr
@@ -0,0 +1,9 @@
1error[E0507]: cannot move out of borrowed content
2 --> $DIR/issue-27282-move-ref-mut-into-guard.rs:24:18
3 |
4LL | if { (|| { let bar = foo; bar.take() })(); false } => {},
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
6
7error: aborting due to previous error
8
9For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/issue-27282-mutate-before-diverging-arm-1.rs b/src/test/ui/issue-27282-mutate-before-diverging-arm-1.rs
new file mode 100644
index 0000000000..b575f4ebce
--- /dev/null
+++ b/src/test/ui/issue-27282-mutate-before-diverging-arm-1.rs
@@ -0,0 +1,43 @@
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// This is testing an attempt to corrupt the discriminant of the match
12// arm in a guard, followed by an attempt to continue matching on that
13// corrupted discriminant in the remaining match arms.
14//
15// Basically this is testing that our new NLL feature of emitting a
16// fake read on each match arm is catching cases like this.
17//
18// This case is interesting because it includes a guard that
19// diverges, and therefore a single final fake-read at the very end
20// after the final match arm would not suffice.
21
22#![feature(nll)]
23
24struct ForceFnOnce;
25
26fn main() {
27 let mut x = &mut Some(&2);
28 let force_fn_once = ForceFnOnce;
29 match x {
30 &mut None => panic!("unreachable"),
31 &mut Some(&_) if {
32 // ForceFnOnce needed to exploit #27282
33 (|| { *x = None; drop(force_fn_once); })();
34 //~^ ERROR closure requires unique access to `x` but it is already borrowed [E0500]
35 false
36 } => {}
37 &mut Some(&a) if { // this binds to garbage if we've corrupted discriminant
38 println!("{}", a);
39 panic!()
40 } => {}
41 _ => panic!("unreachable"),
42 }
43}
diff --git a/src/test/ui/issue-27282-mutate-before-diverging-arm-1.stderr b/src/test/ui/issue-27282-mutate-before-diverging-arm-1.stderr
new file mode 100644
index 0000000000..8f7fe9d33f
--- /dev/null
+++ b/src/test/ui/issue-27282-mutate-before-diverging-arm-1.stderr
@@ -0,0 +1,25 @@
1error[E0500]: closure requires unique access to `x` but it is already borrowed
2 --> $DIR/issue-27282-mutate-before-diverging-arm-1.rs:33:14
3 |
4LL | match x {
5 | _____-
6 | |_____|
7 | ||
8LL | || &mut None => panic!("unreachable"),
9LL | || &mut Some(&_) if {
10LL | || // ForceFnOnce needed to exploit #27282
11LL | || (|| { *x = None; drop(force_fn_once); })();
12 | || ^^ - borrow occurs due to use of `x` in closure
13 | || |
14 | || closure construction occurs here
15... ||
16LL | || _ => panic!("unreachable"),
17LL | || }
18 | || -
19 | ||_____|
20 | |______borrow occurs here
21 | borrow later used here
22
23error: aborting due to previous error
24
25For more information about this error, try `rustc --explain E0500`.
diff --git a/src/test/ui/issue-27282-mutate-before-diverging-arm-2.rs b/src/test/ui/issue-27282-mutate-before-diverging-arm-2.rs
new file mode 100644
index 0000000000..866fed1368
--- /dev/null
+++ b/src/test/ui/issue-27282-mutate-before-diverging-arm-2.rs
@@ -0,0 +1,52 @@
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// This is testing an attempt to corrupt the discriminant of the match
12// arm in a guard, followed by an attempt to continue matching on that
13// corrupted discriminant in the remaining match arms.
14//
15// Basically this is testing that our new NLL feature of emitting a
16// fake read on each match arm is catching cases like this.
17//
18// This case is interesting because it includes a guard that
19// diverges, and therefore a single final fake-read at the very end
20// after the final match arm would not suffice.
21//
22// It is also interesting because the access to the corrupted data
23// occurs in the pattern-match itself, and not in the guard
24// expression.
25
26#![feature(nll)]
27
28struct ForceFnOnce;
29
30fn main() {
31 let mut x = &mut Some(&2);
32 let force_fn_once = ForceFnOnce;
33 match x {
34 &mut None => panic!("unreachable"),
35 &mut Some(&_)
36 if {
37 // ForceFnOnce needed to exploit #27282
38 (|| { *x = None; drop(force_fn_once); })();
39 //~^ ERROR closure requires unique access to `x` but it is already borrowed [E0500]
40 false
41 } => {}
42
43 // this segfaults if we corrupted the discriminant, because
44 // the compiler gets to *assume* that it cannot be the `None`
45 // case, even though that was the effect of the guard.
46 &mut Some(&2)
47 if {
48 panic!()
49 } => {}
50 _ => panic!("unreachable"),
51 }
52}
diff --git a/src/test/ui/issue-27282-mutate-before-diverging-arm-2.stderr b/src/test/ui/issue-27282-mutate-before-diverging-arm-2.stderr
new file mode 100644
index 0000000000..df5e4300ce
--- /dev/null
+++ b/src/test/ui/issue-27282-mutate-before-diverging-arm-2.stderr
@@ -0,0 +1,26 @@
1error[E0500]: closure requires unique access to `x` but it is already borrowed
2 --> $DIR/issue-27282-mutate-before-diverging-arm-2.rs:38:18
3 |
4LL | match x {
5 | _____-
6 | |_____|
7 | ||
8LL | || &mut None => panic!("unreachable"),
9LL | || &mut Some(&_)
10LL | || if {
11LL | || // ForceFnOnce needed to exploit #27282
12LL | || (|| { *x = None; drop(force_fn_once); })();
13 | || ^^ - borrow occurs due to use of `x` in closure
14 | || |
15 | || closure construction occurs here
16... ||
17LL | || _ => panic!("unreachable"),
18LL | || }
19 | || -
20 | ||_____|
21 | |______borrow occurs here
22 | borrow later used here
23
24error: aborting due to previous error
25
26For more information about this error, try `rustc --explain E0500`.
diff --git a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs
new file mode 100644
index 0000000000..5d445c63ef
--- /dev/null
+++ b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs
@@ -0,0 +1,32 @@
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// Issue 27282: This is a variation on issue-27282-move-ref-mut-into-guard.rs
12//
13// It reborrows instead of moving the `ref mut` pattern borrow. This
14// means that our conservative check for mutation in guards will
15// reject it. But I want to make sure that we continue to reject it
16// (under NLL) even when that conservaive check goes away.
17
18// compile-flags: -Z disable-ast-check-for-mutation-in-guard
19
20#![feature(nll)]
21
22fn main() {
23 let mut b = &mut true;
24 match b {
25 &mut false => {},
26 ref mut r if { (|| { let bar = &mut *r; **bar = false; })();
27 //~^ ERROR cannot borrow immutable item `*r` as mutable
28 false } => { &mut *r; },
29 &mut true => { println!("You might think we should get here"); },
30 _ => panic!("surely we could never get here, since rustc warns it is unreachable."),
31 }
32}
diff --git a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr
new file mode 100644
index 0000000000..d767fdde9f
--- /dev/null
+++ b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr
@@ -0,0 +1,9 @@
1error[E0596]: cannot borrow immutable item `*r` as mutable
2 --> $DIR/issue-27282-reborrow-ref-mut-in-guard.rs:26:24
3 |
4LL | ref mut r if { (|| { let bar = &mut *r; **bar = false; })();
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
6
7error: aborting due to previous error
8
9For more information about this error, try `rustc --explain E0596`.