mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-02 01:52:51 +00:00
Rollup merge of #123324 - Nadrieril:false-edges2, r=matthewjasper
match lowering: make false edges more precise When lowering match expressions, we add false edges to hide details of the lowering from borrowck. Morally we pretend we're testing the patterns (and guards) one after the other in order. See the tests for examples. Problem is, the way we implement this today is too coarse for deref patterns. In deref patterns, a pattern like `deref [1, x]` matches on a `Vec` by creating a temporary to store the output of the call to `deref()` and then uses that to continue matching. Here the pattern has a binding, which we set up after the pre-binding block. Problem is, currently the false edges tell borrowck that the pre-binding block can be reached from a previous arm as well, so the `deref()` temporary may not be initialized. This triggers an error when we try to use the binding `x`. We could call `deref()` a second time, but this opens the door to soundness issues if the deref impl is weird. Instead in this PR I rework false edges a little bit. What we need from false edges is a (fake) path from each candidate to the next, specifically from candidate C's pre-binding block to next candidate D's pre-binding block. Today, we link the pre-binding blocks directly. In this PR, I link them indirectly by choosing an earlier node on D's success path. Specifically, I choose the earliest block on D's success path that doesn't make a loop (if I chose e.g. the start block of the whole match (which is on the success path of all candidates), that would make a loop). This turns out to be rather straightforward to implement. r? `@matthewjasper` if you have the bandwidth, otherwise let me know
This commit is contained in:
commit
504a78e2f2
@ -214,12 +214,77 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
///
|
||||
/// ## False edges
|
||||
///
|
||||
/// We don't want to have the exact structure of the decision tree be
|
||||
/// visible through borrow checking. False edges ensure that the CFG as
|
||||
/// seen by borrow checking doesn't encode this. False edges are added:
|
||||
/// We don't want to have the exact structure of the decision tree be visible through borrow
|
||||
/// checking. Specifically we want borrowck to think that:
|
||||
/// - at any point, any or none of the patterns and guards seen so far may have been tested;
|
||||
/// - after the match, any of the patterns may have matched.
|
||||
///
|
||||
/// * From each pre-binding block to the next pre-binding block.
|
||||
/// * From each otherwise block to the next pre-binding block.
|
||||
/// For example, all of these would fail to error if borrowck could see the real CFG (examples
|
||||
/// taken from `tests/ui/nll/match-cfg-fake-edges.rs`):
|
||||
/// ```ignore (too many errors, this is already in the test suite)
|
||||
/// let x = String::new();
|
||||
/// let _ = match true {
|
||||
/// _ => {},
|
||||
/// _ => drop(x),
|
||||
/// };
|
||||
/// // Borrowck must not know the second arm is never run.
|
||||
/// drop(x); //~ ERROR use of moved value
|
||||
///
|
||||
/// let x;
|
||||
/// # let y = true;
|
||||
/// match y {
|
||||
/// _ if { x = 2; true } => {},
|
||||
/// // Borrowck must not know the guard is always run.
|
||||
/// _ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized
|
||||
/// };
|
||||
///
|
||||
/// let x = String::new();
|
||||
/// # let y = true;
|
||||
/// match y {
|
||||
/// false if { drop(x); true } => {},
|
||||
/// // Borrowck must not know the guard is not run in the `true` case.
|
||||
/// true => drop(x), //~ ERROR use of moved value: `x`
|
||||
/// false => {},
|
||||
/// };
|
||||
///
|
||||
/// # let mut y = (true, true);
|
||||
/// let r = &mut y.1;
|
||||
/// match y {
|
||||
/// //~^ ERROR cannot use `y.1` because it was mutably borrowed
|
||||
/// (false, true) => {}
|
||||
/// // Borrowck must not know we don't test `y.1` when `y.0` is `true`.
|
||||
/// (true, _) => drop(r),
|
||||
/// (false, _) => {}
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// We add false edges to act as if we were naively matching each arm in order. What we need is
|
||||
/// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding
|
||||
/// block to next candidate D's pre-binding block. For maximum precision (needed for deref
|
||||
/// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to
|
||||
/// avoid loops).
|
||||
///
|
||||
/// This turns out to be easy to compute: that block is the `start_block` of the first call to
|
||||
/// `match_candidates` where D is the first candidate in the list.
|
||||
///
|
||||
/// For example:
|
||||
/// ```rust
|
||||
/// # let (x, y) = (true, true);
|
||||
/// match (x, y) {
|
||||
/// (true, true) => 1,
|
||||
/// (false, true) => 2,
|
||||
/// (true, false) => 3,
|
||||
/// _ => 4,
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
/// In this example, the pre-binding block of arm 1 has a false edge to the block for result
|
||||
/// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks
|
||||
/// of the next arm.
|
||||
///
|
||||
/// On top of this, we also add a false edge from the otherwise_block of each guard to the
|
||||
/// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which
|
||||
/// guards may have run.
|
||||
#[instrument(level = "debug", skip(self, arms))]
|
||||
pub(crate) fn match_expr(
|
||||
&mut self,
|
||||
@ -365,7 +430,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
for candidate in candidates {
|
||||
candidate.visit_leaves(|leaf_candidate| {
|
||||
if let Some(ref mut prev) = previous_candidate {
|
||||
prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block;
|
||||
assert!(leaf_candidate.false_edge_start_block.is_some());
|
||||
prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
|
||||
}
|
||||
previous_candidate = Some(leaf_candidate);
|
||||
});
|
||||
@ -1010,8 +1076,12 @@ struct Candidate<'pat, 'tcx> {
|
||||
|
||||
/// The block before the `bindings` have been established.
|
||||
pre_binding_block: Option<BasicBlock>,
|
||||
/// The pre-binding block of the next candidate.
|
||||
next_candidate_pre_binding_block: Option<BasicBlock>,
|
||||
|
||||
/// The earliest block that has only candidates >= this one as descendents. Used for false
|
||||
/// edges, see the doc for [`Builder::match_expr`].
|
||||
false_edge_start_block: Option<BasicBlock>,
|
||||
/// The `false_edge_start_block` of the next candidate.
|
||||
next_candidate_start_block: Option<BasicBlock>,
|
||||
}
|
||||
|
||||
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
@ -1033,7 +1103,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|
||||
or_span: None,
|
||||
otherwise_block: None,
|
||||
pre_binding_block: None,
|
||||
next_candidate_pre_binding_block: None,
|
||||
false_edge_start_block: None,
|
||||
next_candidate_start_block: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1325,6 +1396,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
otherwise_block: BasicBlock,
|
||||
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||
) {
|
||||
if let [first, ..] = candidates {
|
||||
if first.false_edge_start_block.is_none() {
|
||||
first.false_edge_start_block = Some(start_block);
|
||||
}
|
||||
}
|
||||
|
||||
match candidates {
|
||||
[] => {
|
||||
// If there are no candidates that still need testing, we're done. Since all matches are
|
||||
@ -1545,6 +1622,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
.into_iter()
|
||||
.map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
|
||||
.collect();
|
||||
candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block;
|
||||
}
|
||||
|
||||
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
|
||||
@ -1564,6 +1642,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let any_matches = self.cfg.start_new_block();
|
||||
let or_span = candidate.or_span.take().unwrap();
|
||||
let source_info = self.source_info(or_span);
|
||||
if candidate.false_edge_start_block.is_none() {
|
||||
candidate.false_edge_start_block =
|
||||
candidate.subcandidates[0].false_edge_start_block;
|
||||
}
|
||||
for subcandidate in mem::take(&mut candidate.subcandidates) {
|
||||
let or_block = subcandidate.pre_binding_block.unwrap();
|
||||
self.cfg.goto(or_block, source_info, any_matches);
|
||||
@ -1979,12 +2061,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
let mut block = candidate.pre_binding_block.unwrap();
|
||||
|
||||
if candidate.next_candidate_pre_binding_block.is_some() {
|
||||
if candidate.next_candidate_start_block.is_some() {
|
||||
let fresh_block = self.cfg.start_new_block();
|
||||
self.false_edges(
|
||||
block,
|
||||
fresh_block,
|
||||
candidate.next_candidate_pre_binding_block,
|
||||
candidate.next_candidate_start_block,
|
||||
candidate_source_info,
|
||||
);
|
||||
block = fresh_block;
|
||||
@ -2132,7 +2214,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
self.false_edges(
|
||||
otherwise_post_guard_block,
|
||||
otherwise_block,
|
||||
candidate.next_candidate_pre_binding_block,
|
||||
candidate.next_candidate_start_block,
|
||||
source_info,
|
||||
);
|
||||
|
||||
|
@ -48,7 +48,7 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb15, imaginary: bb6];
|
||||
falseEdge -> [real: bb15, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
@ -40,7 +40,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb9];
|
||||
falseEdge -> [real: bb12, imaginary: bb7];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
@ -48,7 +48,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb16, imaginary: bb3];
|
||||
falseEdge -> [real: bb16, imaginary: bb1];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
@ -60,7 +60,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb9: {
|
||||
falseEdge -> [real: bb15, imaginary: bb6];
|
||||
falseEdge -> [real: bb15, imaginary: bb5];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
@ -89,7 +89,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
|
||||
bb14: {
|
||||
StorageDead(_10);
|
||||
falseEdge -> [real: bb5, imaginary: bb9];
|
||||
falseEdge -> [real: bb5, imaginary: bb7];
|
||||
}
|
||||
|
||||
bb15: {
|
||||
|
@ -23,7 +23,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb9, imaginary: bb4];
|
||||
falseEdge -> [real: bb9, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
@ -32,7 +32,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb6];
|
||||
falseEdge -> [real: bb12, imaginary: bb5];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
@ -69,7 +69,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
|
||||
bb11: {
|
||||
StorageDead(_8);
|
||||
falseEdge -> [real: bb1, imaginary: bb4];
|
||||
falseEdge -> [real: bb1, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
|
@ -60,11 +60,11 @@
|
||||
}
|
||||
|
||||
- bb5: {
|
||||
- falseEdge -> [real: bb13, imaginary: bb3];
|
||||
- falseEdge -> [real: bb13, imaginary: bb2];
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
- falseEdge -> [real: bb8, imaginary: bb5];
|
||||
- falseEdge -> [real: bb8, imaginary: bb1];
|
||||
- }
|
||||
-
|
||||
- bb7: {
|
||||
@ -127,7 +127,7 @@
|
||||
StorageDead(_9);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb1, imaginary: bb5];
|
||||
- falseEdge -> [real: bb1, imaginary: bb1];
|
||||
+ goto -> bb1;
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@
|
||||
StorageDead(_12);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb2, imaginary: bb3];
|
||||
- falseEdge -> [real: bb2, imaginary: bb2];
|
||||
+ goto -> bb2;
|
||||
}
|
||||
|
||||
|
@ -60,11 +60,11 @@
|
||||
}
|
||||
|
||||
- bb5: {
|
||||
- falseEdge -> [real: bb13, imaginary: bb3];
|
||||
- falseEdge -> [real: bb13, imaginary: bb2];
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
- falseEdge -> [real: bb8, imaginary: bb5];
|
||||
- falseEdge -> [real: bb8, imaginary: bb1];
|
||||
- }
|
||||
-
|
||||
- bb7: {
|
||||
@ -127,7 +127,7 @@
|
||||
StorageDead(_9);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb1, imaginary: bb5];
|
||||
- falseEdge -> [real: bb1, imaginary: bb1];
|
||||
+ goto -> bb1;
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@
|
||||
StorageDead(_12);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb2, imaginary: bb3];
|
||||
- falseEdge -> [real: bb2, imaginary: bb2];
|
||||
+ goto -> bb2;
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,57 @@
|
||||
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn all_patterns_are_tested() {
|
||||
// Even though `x` is never actually moved out of, we don't want borrowck results to be based on
|
||||
// whether MIR lowering reveals which patterns are unreachable.
|
||||
let x = String::new();
|
||||
match true {
|
||||
_ => {},
|
||||
_ => drop(x),
|
||||
}
|
||||
// Borrowck must not know the second arm is never run.
|
||||
drop(x); //~ ERROR use of moved value
|
||||
|
||||
let x = String::new();
|
||||
if let _ = true { //~ WARN irrefutable
|
||||
} else {
|
||||
drop(x)
|
||||
}
|
||||
// Borrowck must not know the else branch is never run.
|
||||
drop(x); //~ ERROR use of moved value
|
||||
|
||||
let x = (String::new(), String::new());
|
||||
match x {
|
||||
(y, _) | (_, y) => (),
|
||||
}
|
||||
&x.0; //~ ERROR borrow of moved value
|
||||
// Borrowck must not know the second pattern never matches.
|
||||
&x.1; //~ ERROR borrow of moved value
|
||||
|
||||
let x = (String::new(), String::new());
|
||||
let ((y, _) | (_, y)) = x;
|
||||
&x.0; //~ ERROR borrow of moved value
|
||||
// Borrowck must not know the second pattern never matches.
|
||||
&x.1; //~ ERROR borrow of moved value
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn guard_always_precedes_arm(y: i32) {
|
||||
let mut x;
|
||||
// x should always be initialized, as the only way to reach the arm is
|
||||
// through the guard.
|
||||
let mut x;
|
||||
match y {
|
||||
0 | 2 if { x = 2; true } => x,
|
||||
_ => 2,
|
||||
};
|
||||
|
||||
let mut x;
|
||||
match y {
|
||||
_ => 2,
|
||||
0 | 2 if { x = 2; true } => x,
|
||||
};
|
||||
|
||||
let mut x;
|
||||
match y {
|
||||
0 | 2 if let Some(()) = { x = 2; Some(()) } => x,
|
||||
@ -19,51 +61,58 @@ fn guard_always_precedes_arm(y: i32) {
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn guard_may_be_skipped(y: i32) {
|
||||
// Even though x *is* always initialized, we don't want to have borrowck results be based on
|
||||
// whether MIR lowering reveals which patterns are exhaustive.
|
||||
let x;
|
||||
match y {
|
||||
_ if { x = 2; true } => {},
|
||||
// Borrowck must not know the guard is always run.
|
||||
_ => drop(x), //~ ERROR used binding `x` is possibly-uninitialized
|
||||
};
|
||||
|
||||
let x;
|
||||
// Even though x *is* always initialized, we don't want to have borrowck
|
||||
// results be based on whether patterns are exhaustive.
|
||||
match y {
|
||||
_ if { x = 2; true } => 1,
|
||||
_ if {
|
||||
x; //~ ERROR E0381
|
||||
false
|
||||
} => 2,
|
||||
// Borrowck must not know the guard is always run.
|
||||
_ if { x; false } => 2, //~ ERROR used binding `x` isn't initialized
|
||||
_ => 3,
|
||||
};
|
||||
|
||||
let x;
|
||||
match y {
|
||||
_ if let Some(()) = { x = 2; Some(()) } => 1,
|
||||
_ if let Some(()) = {
|
||||
x; //~ ERROR E0381
|
||||
None
|
||||
} => 2,
|
||||
_ if let Some(()) = { x; None } => 2, //~ ERROR used binding `x` isn't initialized
|
||||
_ => 3,
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn guard_may_be_taken(y: bool) {
|
||||
let x = String::new();
|
||||
// Even though x *is* never moved before the use, we don't want to have
|
||||
// borrowck results be based on whether patterns are disjoint.
|
||||
let x = String::new();
|
||||
match y {
|
||||
false if { drop(x); true } => 1,
|
||||
true => {
|
||||
x; //~ ERROR use of moved value: `x`
|
||||
2
|
||||
}
|
||||
false => 3,
|
||||
false if { drop(x); true } => {},
|
||||
// Borrowck must not know the guard is not run in the `true` case.
|
||||
true => drop(x), //~ ERROR use of moved value: `x`
|
||||
false => {},
|
||||
};
|
||||
|
||||
// Fine in the other order.
|
||||
let x = String::new();
|
||||
match y {
|
||||
true => drop(x),
|
||||
false if { drop(x); true } => {},
|
||||
false => {},
|
||||
};
|
||||
|
||||
let x = String::new();
|
||||
match y {
|
||||
false if let Some(()) = { drop(x); Some(()) } => 1,
|
||||
true => {
|
||||
x; //~ ERROR use of moved value: `x`
|
||||
2
|
||||
}
|
||||
false => 3,
|
||||
false if let Some(()) = { drop(x); Some(()) } => {},
|
||||
true => drop(x), //~ ERROR use of moved value: `x`
|
||||
false => {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,128 @@
|
||||
error[E0381]: used binding `x` isn't initialized
|
||||
--> $DIR/match-cfg-fake-edges.rs:29:13
|
||||
warning: irrefutable `if let` pattern
|
||||
--> $DIR/match-cfg-fake-edges.rs:19:8
|
||||
|
|
||||
LL | if let _ = true {
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: this pattern will always match, so the `if let` is useless
|
||||
= help: consider replacing the `if let` with a `let`
|
||||
= note: `#[warn(irrefutable_let_patterns)]` on by default
|
||||
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/match-cfg-fake-edges.rs:16:10
|
||||
|
|
||||
LL | let x = String::new();
|
||||
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
|
||||
...
|
||||
LL | _ => drop(x),
|
||||
| - value moved here
|
||||
...
|
||||
LL | drop(x);
|
||||
| ^ value used here after move
|
||||
|
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | _ => drop(x.clone()),
|
||||
| ++++++++
|
||||
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/match-cfg-fake-edges.rs:24:10
|
||||
|
|
||||
LL | let x = String::new();
|
||||
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
|
||||
...
|
||||
LL | drop(x)
|
||||
| - value moved here
|
||||
...
|
||||
LL | drop(x);
|
||||
| ^ value used here after move
|
||||
|
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | drop(x.clone())
|
||||
| ++++++++
|
||||
|
||||
error[E0382]: borrow of moved value: `x.0`
|
||||
--> $DIR/match-cfg-fake-edges.rs:30:5
|
||||
|
|
||||
LL | (y, _) | (_, y) => (),
|
||||
| - value moved here
|
||||
LL | }
|
||||
LL | &x.0;
|
||||
| ^^^^ value borrowed here after move
|
||||
|
|
||||
= note: move occurs because `x.0` has type `String`, which does not implement the `Copy` trait
|
||||
help: borrow this binding in the pattern to avoid moving the value
|
||||
|
|
||||
LL | (ref y, _) | (_, y) => (),
|
||||
| +++
|
||||
|
||||
error[E0382]: borrow of moved value: `x.1`
|
||||
--> $DIR/match-cfg-fake-edges.rs:32:5
|
||||
|
|
||||
LL | (y, _) | (_, y) => (),
|
||||
| - value moved here
|
||||
...
|
||||
LL | &x.1;
|
||||
| ^^^^ value borrowed here after move
|
||||
|
|
||||
= note: move occurs because `x.1` has type `String`, which does not implement the `Copy` trait
|
||||
help: borrow this binding in the pattern to avoid moving the value
|
||||
|
|
||||
LL | (y, _) | (_, ref y) => (),
|
||||
| +++
|
||||
|
||||
error[E0382]: borrow of moved value: `x.0`
|
||||
--> $DIR/match-cfg-fake-edges.rs:36:5
|
||||
|
|
||||
LL | let ((y, _) | (_, y)) = x;
|
||||
| - value moved here
|
||||
LL | &x.0;
|
||||
| ^^^^ value borrowed here after move
|
||||
|
|
||||
= note: move occurs because `x.0` has type `String`, which does not implement the `Copy` trait
|
||||
help: borrow this binding in the pattern to avoid moving the value
|
||||
|
|
||||
LL | let ((ref y, _) | (_, y)) = x;
|
||||
| +++
|
||||
|
||||
error[E0382]: borrow of moved value: `x.1`
|
||||
--> $DIR/match-cfg-fake-edges.rs:38:5
|
||||
|
|
||||
LL | let ((y, _) | (_, y)) = x;
|
||||
| - value moved here
|
||||
...
|
||||
LL | &x.1;
|
||||
| ^^^^ value borrowed here after move
|
||||
|
|
||||
= note: move occurs because `x.1` has type `String`, which does not implement the `Copy` trait
|
||||
help: borrow this binding in the pattern to avoid moving the value
|
||||
|
|
||||
LL | let ((y, _) | (_, ref y)) = x;
|
||||
| +++
|
||||
|
||||
error[E0381]: used binding `x` is possibly-uninitialized
|
||||
--> $DIR/match-cfg-fake-edges.rs:72:19
|
||||
|
|
||||
LL | let x;
|
||||
| - binding declared here but left uninitialized
|
||||
...
|
||||
LL | _ => drop(x),
|
||||
| - ^ `x` used here but it is possibly-uninitialized
|
||||
| |
|
||||
| if this pattern is matched, `x` is not initialized
|
||||
|
||||
error[E0381]: used binding `x` isn't initialized
|
||||
--> $DIR/match-cfg-fake-edges.rs:79:16
|
||||
|
|
||||
LL | let x;
|
||||
| - binding declared here but left uninitialized
|
||||
LL | match y {
|
||||
LL | _ if { x = 2; true } => 1,
|
||||
| ----- binding initialized here in some conditions
|
||||
LL | _ if {
|
||||
LL | x;
|
||||
| ^ `x` used here but it isn't initialized
|
||||
LL | // Borrowck must not know the guard is always run.
|
||||
LL | _ if { x; false } => 2,
|
||||
| ^ `x` used here but it isn't initialized
|
||||
|
|
||||
help: consider assigning a value
|
||||
|
|
||||
@ -16,16 +130,15 @@ LL | let x = 0;
|
||||
| +++
|
||||
|
||||
error[E0381]: used binding `x` isn't initialized
|
||||
--> $DIR/match-cfg-fake-edges.rs:39:13
|
||||
--> $DIR/match-cfg-fake-edges.rs:86:31
|
||||
|
|
||||
LL | let x;
|
||||
| - binding declared here but left uninitialized
|
||||
LL | match y {
|
||||
LL | _ if let Some(()) = { x = 2; Some(()) } => 1,
|
||||
| ----- binding initialized here in some conditions
|
||||
LL | _ if let Some(()) = {
|
||||
LL | x;
|
||||
| ^ `x` used here but it isn't initialized
|
||||
LL | _ if let Some(()) = { x; None } => 2,
|
||||
| ^ `x` used here but it isn't initialized
|
||||
|
|
||||
help: consider assigning a value
|
||||
|
|
||||
@ -33,40 +146,39 @@ LL | let x = 0;
|
||||
| +++
|
||||
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/match-cfg-fake-edges.rs:53:13
|
||||
|
|
||||
LL | let x = String::new();
|
||||
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
|
||||
...
|
||||
LL | false if { drop(x); true } => 1,
|
||||
| - value moved here
|
||||
LL | true => {
|
||||
LL | x;
|
||||
| ^ value used here after move
|
||||
|
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | false if { drop(x.clone()); true } => 1,
|
||||
| ++++++++
|
||||
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/match-cfg-fake-edges.rs:63:13
|
||||
--> $DIR/match-cfg-fake-edges.rs:99:22
|
||||
|
|
||||
LL | let x = String::new();
|
||||
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
|
||||
LL | match y {
|
||||
LL | false if let Some(()) = { drop(x); Some(()) } => 1,
|
||||
| - value moved here
|
||||
LL | true => {
|
||||
LL | x;
|
||||
| ^ value used here after move
|
||||
LL | false if { drop(x); true } => {},
|
||||
| - value moved here
|
||||
LL | // Borrowck must not know the guard is not run in the `true` case.
|
||||
LL | true => drop(x),
|
||||
| ^ value used here after move
|
||||
|
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | false if let Some(()) = { drop(x.clone()); Some(()) } => 1,
|
||||
LL | false if { drop(x.clone()); true } => {},
|
||||
| ++++++++
|
||||
|
||||
error[E0382]: use of moved value: `x`
|
||||
--> $DIR/match-cfg-fake-edges.rs:114:22
|
||||
|
|
||||
LL | let x = String::new();
|
||||
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
|
||||
LL | match y {
|
||||
LL | false if let Some(()) = { drop(x); Some(()) } => {},
|
||||
| - value moved here
|
||||
LL | true => drop(x),
|
||||
| ^ value used here after move
|
||||
|
|
||||
help: consider cloning the value if the performance cost is acceptable
|
||||
|
|
||||
LL | false if let Some(()) = { drop(x.clone()); Some(()) } => {},
|
||||
| ++++++++
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 11 previous errors; 1 warning emitted
|
||||
|
||||
Some errors have detailed explanations: E0381, E0382.
|
||||
For more information about an error, try `rustc --explain E0381`.
|
||||
|
@ -5,13 +5,20 @@ fn all_previous_tests_may_be_done(y: &mut (bool, bool)) {
|
||||
let r = &mut y.1;
|
||||
// We don't actually test y.1 to select the second arm, but we don't want
|
||||
// borrowck results to be based on the order we match patterns.
|
||||
match y { //~ ERROR cannot use `y.1` because it was mutably borrowed
|
||||
(false, true) => 1,
|
||||
(true, _) => {
|
||||
r;
|
||||
2
|
||||
}
|
||||
(false, _) => 3,
|
||||
match y {
|
||||
//~^ ERROR cannot use `y.1` because it was mutably borrowed
|
||||
(false, true) => {}
|
||||
// Borrowck must not know we don't test `y.1` when `y.0` is `true`.
|
||||
(true, _) => drop(r),
|
||||
(false, _) => {}
|
||||
};
|
||||
|
||||
// Fine in the other order.
|
||||
let r = &mut y.1;
|
||||
match y {
|
||||
(true, _) => drop(r),
|
||||
(false, true) => {}
|
||||
(false, _) => {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ LL | let r = &mut y.1;
|
||||
LL | match y {
|
||||
| ^^^^^^^ use of borrowed `y.1`
|
||||
...
|
||||
LL | r;
|
||||
| - borrow later used here
|
||||
LL | (true, _) => drop(r),
|
||||
| - borrow later used here
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user