diff options
author | bors <bors@rust-lang.org> | 2018-05-30 02:11:59 +0000 |
---|---|---|
committer | bors <bors@rust-lang.org> | 2018-05-30 02:11:59 +0000 |
commit | 8372e7b37eb9777edc8f7179fc6cbc39456ce735 (patch) | |
tree | e98eddebdd5679b3a73a5716cc7cd240d1203a7f | |
parent | Auto merge of #50772 - nicokoch:fastcopy, r=alexcrichton (diff) | |
parent | Review feedback: Adding test cases suggested by arielb1. (diff) | |
download | grust-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.
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! { | |||
2212 | EnumTypeFoldableImpl! { | 2217 | EnumTypeFoldableImpl! { |
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)] | ||
62 | crate enum TwoPhaseUse { | ||
63 | MutActivate, | ||
64 | SharedUse, | ||
65 | } | ||
66 | |||
56 | #[derive(Debug)] | 67 | #[derive(Debug)] |
57 | crate struct BorrowData<'tcx> { | 68 | crate 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. |
14 | use borrow_check::ArtificialField; | 14 | use borrow_check::ArtificialField; |
15 | use borrow_check::borrow_set::{BorrowSet, BorrowData}; | 15 | use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseUse}; |
16 | use borrow_check::{Context, Overlap}; | 16 | use borrow_check::{Context, Overlap}; |
17 | use borrow_check::{ShallowOrDeep, Deep, Shallow}; | 17 | use borrow_check::{ShallowOrDeep, Deep, Shallow}; |
18 | use dataflow::indexes::BorrowIndex; | 18 | use 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 | ||
13 | use build::{BlockAnd, BlockAndExtension, Builder}; | 13 | use build::{BlockAnd, BlockAndExtension, Builder}; |
14 | use build::ForGuard::{OutsideGuard, WithinGuard}; | 14 | use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard}; |
15 | use build::expr::category::Category; | 15 | use build::expr::category::Category; |
16 | use hair::*; | 16 | use hair::*; |
17 | use rustc::mir::*; | 17 | use 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 | ||
16 | use build::{BlockAnd, BlockAndExtension, Builder}; | 16 | use build::{BlockAnd, BlockAndExtension, Builder}; |
17 | use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; | 17 | use build::{GuardFrame, GuardFrameLocal, LocalsForNode}; |
18 | use build::ForGuard::{self, OutsideGuard, WithinGuard}; | 18 | use build::ForGuard::{self, OutsideGuard, RefWithinGuard, ValWithinGuard}; |
19 | use rustc_data_structures::fx::FxHashMap; | 19 | use rustc_data_structures::fx::FxHashMap; |
20 | use rustc_data_structures::bitvec::BitVector; | 20 | use rustc_data_structures::bitvec::BitVector; |
21 | use rustc::ty::{self, Ty}; | 21 | use 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)] |
294 | enum LocalsForNode { | 294 | enum 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)] |
332 | enum ForGuard { | 334 | enum 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 | |||
21 | fn main() { | ||
22 | rust_issue_24535(); | ||
23 | rfcs_issue_1006_1(); | ||
24 | rfcs_issue_1006_2(); | ||
25 | } | ||
26 | |||
27 | fn 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 | |||
41 | fn 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 | |||
49 | fn 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 | | | |
26 | LL | | //~^ ERROR use of partially moved value: `maybe` (Ast) [E0382] | ||
27 | LL | | //~| ERROR use of moved value: `(maybe as std::prelude::v1::Some).0` (Ast) [E0382] | ||
28 | LL | | //~| ERROR use of moved value: `maybe` (Mir) [E0382] | ||
29 | LL | | //~| ERROR use of moved value: `maybe` (Mir) [E0382] | ||
30 | LL | | //~| ERROR use of moved value: `maybe.0` (Mir) [E0382] | ||
31 | LL | | } | 26 | LL | | } |
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 | ||
31 | error[E0382]: borrow of moved value: `maybe` (Mir) | ||
32 | --> $DIR/issue-41962.rs:17:9 | ||
33 | | | ||
34 | LL | if let Some(thing) = maybe { | ||
35 | | ^ ----- value moved here | ||
36 | | _________| | ||
37 | | | | ||
38 | LL | | } | ||
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 | |||
36 | error[E0382]: use of moved value: `maybe` (Mir) | 43 | error[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 | ||
55 | error: aborting due to 5 previous errors | 62 | error: aborting due to 6 previous errors |
56 | 63 | ||
57 | For more information about this error, try `rustc --explain E0382`. | 64 | For 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 | |||
22 | fn 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 @@ | |||
1 | error[E0505]: cannot move out of `b` because it is borrowed | ||
2 | --> $DIR/issue-27282-move-match-input-into-guard.rs:26:16 | ||
3 | | | ||
4 | LL | match b { | ||
5 | | _____- | ||
6 | | |_____| | ||
7 | | || | ||
8 | LL | || &mut false => {}, | ||
9 | LL | || _ if { (|| { let bar = b; *bar = false; })(); | ||
10 | | || ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `b` occurs here | ||
11 | LL | || //~^ ERROR cannot move out of `b` because it is borrowed [E0505] | ||
12 | ... || | ||
13 | LL | || _ => panic!("surely we could never get here, since rustc warns it is unreachable."), | ||
14 | LL | || } | ||
15 | | || - | ||
16 | | ||_____| | ||
17 | | |______borrow of `b` occurs here | ||
18 | | borrow later used here | ||
19 | |||
20 | error[E0382]: use of moved value: `*b` | ||
21 | --> $DIR/issue-27282-move-match-input-into-guard.rs:29:14 | ||
22 | | | ||
23 | LL | _ if { (|| { let bar = b; *bar = false; })(); | ||
24 | | ----------------------------------- value moved here | ||
25 | ... | ||
26 | LL | &mut true => { println!("You might think we should get here"); }, | ||
27 | | ^^^^ value used here after move | ||
28 | |||
29 | error: aborting due to 2 previous errors | ||
30 | |||
31 | Some errors occurred: E0382, E0505. | ||
32 | For 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 | |||
20 | fn 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 @@ | |||
1 | error[E0507]: cannot move out of borrowed content | ||
2 | --> $DIR/issue-27282-move-ref-mut-into-guard.rs:24:18 | ||
3 | | | ||
4 | LL | if { (|| { let bar = foo; bar.take() })(); false } => {}, | ||
5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content | ||
6 | |||
7 | error: aborting due to previous error | ||
8 | |||
9 | For 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 | |||
24 | struct ForceFnOnce; | ||
25 | |||
26 | fn 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 @@ | |||
1 | error[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 | | | ||
4 | LL | match x { | ||
5 | | _____- | ||
6 | | |_____| | ||
7 | | || | ||
8 | LL | || &mut None => panic!("unreachable"), | ||
9 | LL | || &mut Some(&_) if { | ||
10 | LL | || // ForceFnOnce needed to exploit #27282 | ||
11 | LL | || (|| { *x = None; drop(force_fn_once); })(); | ||
12 | | || ^^ - borrow occurs due to use of `x` in closure | ||
13 | | || | | ||
14 | | || closure construction occurs here | ||
15 | ... || | ||
16 | LL | || _ => panic!("unreachable"), | ||
17 | LL | || } | ||
18 | | || - | ||
19 | | ||_____| | ||
20 | | |______borrow occurs here | ||
21 | | borrow later used here | ||
22 | |||
23 | error: aborting due to previous error | ||
24 | |||
25 | For 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 | |||
28 | struct ForceFnOnce; | ||
29 | |||
30 | fn 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 @@ | |||
1 | error[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 | | | ||
4 | LL | match x { | ||
5 | | _____- | ||
6 | | |_____| | ||
7 | | || | ||
8 | LL | || &mut None => panic!("unreachable"), | ||
9 | LL | || &mut Some(&_) | ||
10 | LL | || if { | ||
11 | LL | || // ForceFnOnce needed to exploit #27282 | ||
12 | LL | || (|| { *x = None; drop(force_fn_once); })(); | ||
13 | | || ^^ - borrow occurs due to use of `x` in closure | ||
14 | | || | | ||
15 | | || closure construction occurs here | ||
16 | ... || | ||
17 | LL | || _ => panic!("unreachable"), | ||
18 | LL | || } | ||
19 | | || - | ||
20 | | ||_____| | ||
21 | | |______borrow occurs here | ||
22 | | borrow later used here | ||
23 | |||
24 | error: aborting due to previous error | ||
25 | |||
26 | For 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 | |||
22 | fn 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 @@ | |||
1 | error[E0596]: cannot borrow immutable item `*r` as mutable | ||
2 | --> $DIR/issue-27282-reborrow-ref-mut-in-guard.rs:26:24 | ||
3 | | | ||
4 | LL | ref mut r if { (|| { let bar = &mut *r; **bar = false; })(); | ||
5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable | ||
6 | |||
7 | error: aborting due to previous error | ||
8 | |||
9 | For more information about this error, try `rustc --explain E0596`. | ||