Skip to content

Commit

Permalink
Capture scrutinee of if let guards correctly
Browse files Browse the repository at this point in the history
Previously we were always capturing by value.
  • Loading branch information
matthewjasper committed Sep 20, 2023
1 parent 62d9034 commit 52711ba
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 4 deletions.
10 changes: 6 additions & 4 deletions compiler/rustc_hir_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,10 +664,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
);
self.walk_pat(discr_place, arm.pat, arm.guard.is_some());

if let Some(hir::Guard::If(e)) = arm.guard {
self.consume_expr(e)
} else if let Some(hir::Guard::IfLet(ref l)) = arm.guard {
self.consume_expr(l.init)
match arm.guard {
Some(hir::Guard::If(ref e)) => self.consume_expr(e),
Some(hir::Guard::IfLet(ref l)) => {
self.walk_local(l.init, l.pat, None, |t| t.borrow_expr(l.init, ty::ImmBorrow))
}
None => {}
}

self.consume_expr(arm.body);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0505]: cannot move out of `value` because it is borrowed
--> $DIR/if-let-guards-errors.rs:16:13
|
LL | let f = |x: &E| {
| ------- borrow of `value` occurs here
LL | match &x {
LL | E::Number(_) if let E::Number(ref mut n) = *value => { }
| ------ borrow occurs due to use in closure
...
LL | let x = value;
| ^^^^^ move out of `value` occurs here
LL |
LL | drop(f);
| - borrow later used here

error[E0382]: use of moved value: `value`
--> $DIR/if-let-guards-errors.rs:28:13
|
LL | fn if_let_move(value: Box<E>) {
| ----- move occurs because `value` has type `Box<E>`, which does not implement the `Copy` trait
LL | let f = |x: &E| {
| ------- value moved into closure here
LL | match &x {
LL | E::Number(_) if let E::String(s) = *value => { }
| ------ variable moved due to use in closure
...
LL | let x = value;
| ^^^^^ value used here after move

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0382, E0505.
For more information about an error, try `rustc --explain E0382`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
error[E0505]: cannot move out of `value` because it is borrowed
--> $DIR/if-let-guards-errors.rs:16:13
|
LL | let f = |x: &E| {
| ------- borrow of `*value` occurs here
LL | match &x {
LL | E::Number(_) if let E::Number(ref mut n) = *value => { }
| ------ borrow occurs due to use in closure
...
LL | let x = value;
| ^^^^^ move out of `value` occurs here
LL |
LL | drop(f);
| - borrow later used here

error[E0382]: use of moved value: `value`
--> $DIR/if-let-guards-errors.rs:28:13
|
LL | fn if_let_move(value: Box<E>) {
| ----- move occurs because `value` has type `Box<E>`, which does not implement the `Copy` trait
LL | let f = |x: &E| {
| ------- value moved into closure here
LL | match &x {
LL | E::Number(_) if let E::String(s) = *value => { }
| ------ variable moved due to use in closure
...
LL | let x = value;
| ^^^^^ value used here after move

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0382, E0505.
For more information about an error, try `rustc --explain E0382`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Check the if let guards don't force capture by value
// revisions: e2018 e2021
//[e2018] edition:2018
//[e2021] edition:2021

#![feature(if_let_guard)]
#![allow(irrefutable_let_patterns)]

fn if_let_ref_mut(mut value: Box<E>) {
let f = |x: &E| {
match &x {
E::Number(_) if let E::Number(ref mut n) = *value => { }
_ => {}
}
};
let x = value;
//~^ ERROR cannot move out of `value` because it is borrowed
drop(f);
}

fn if_let_move(value: Box<E>) {
let f = |x: &E| {
match &x {
E::Number(_) if let E::String(s) = *value => { }
_ => {}
}
};
let x = value;
//~^ ERROR use of moved value: `value`
}

enum E {
String(String),
Number(i32),
}

fn main() {}
55 changes: 55 additions & 0 deletions tests/ui/closures/2229_closure_analysis/match/if-let-guards.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Check the if let guards don't force capture by value
// revisions: e2018 e2021
// build-pass
//[e2018] edition:2018
//[e2021] edition:2021

#![feature(if_let_guard)]
#![allow(irrefutable_let_patterns)]

fn if_let_underscore(value: Box<E>) {
|x: &E| {
match &x {
E::Number(_) if let _ = *value => { }
_ => {}
}
};
let x = value;
}

fn if_let_copy(value: Box<E>) {
|x: &E| {
match &x {
E::Number(_) if let E::Number(n) = *value => { }
_ => {}
}
};
let x = value;
}

fn if_let_ref(value: Box<E>) {
|x: &E| {
match &x {
E::Number(_) if let E::Number(ref n) = *value => { }
_ => {}
}
};
let x = value;
}

fn if_let_ref_mut(mut value: Box<E>) {
|x: &E| {
match &x {
E::Number(_) if let E::Number(ref mut n) = *value => { }
_ => {}
}
};
let x = value;
}

enum E {
String(String),
Number(i32),
}

fn main() {}

0 comments on commit 52711ba

Please sign in to comment.