From 52711bac382123fcb948b67ca02d263f51dc9e34 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 20 Sep 2023 11:57:00 +0000 Subject: [PATCH] Capture scrutinee of if let guards correctly Previously we were always capturing by value. --- .../rustc_hir_typeck/src/expr_use_visitor.rs | 10 ++-- .../match/if-let-guards-errors.e2018.stderr | 33 +++++++++++ .../match/if-let-guards-errors.e2021.stderr | 33 +++++++++++ .../match/if-let-guards-errors.rs | 37 +++++++++++++ .../match/if-let-guards.rs | 55 +++++++++++++++++++ 5 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2018.stderr create mode 100644 tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2021.stderr create mode 100644 tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.rs create mode 100644 tests/ui/closures/2229_closure_analysis/match/if-let-guards.rs diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 840910732d89f..8bc66ac5509be 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -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); diff --git a/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2018.stderr b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2018.stderr new file mode 100644 index 0000000000000..394629c000136 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2018.stderr @@ -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) { + | ----- move occurs because `value` has type `Box`, 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`. diff --git a/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2021.stderr b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2021.stderr new file mode 100644 index 0000000000000..5672845019b8f --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.e2021.stderr @@ -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) { + | ----- move occurs because `value` has type `Box`, 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`. diff --git a/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.rs b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.rs new file mode 100644 index 0000000000000..17e38c033b168 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/match/if-let-guards-errors.rs @@ -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) { + 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) { + 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() {} diff --git a/tests/ui/closures/2229_closure_analysis/match/if-let-guards.rs b/tests/ui/closures/2229_closure_analysis/match/if-let-guards.rs new file mode 100644 index 0000000000000..88963ed43c750 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/match/if-let-guards.rs @@ -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) { + |x: &E| { + match &x { + E::Number(_) if let _ = *value => { } + _ => {} + } + }; + let x = value; +} + +fn if_let_copy(value: Box) { + |x: &E| { + match &x { + E::Number(_) if let E::Number(n) = *value => { } + _ => {} + } + }; + let x = value; +} + +fn if_let_ref(value: Box) { + |x: &E| { + match &x { + E::Number(_) if let E::Number(ref n) = *value => { } + _ => {} + } + }; + let x = value; +} + +fn if_let_ref_mut(mut value: Box) { + |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() {}