Borrow guard patterns for the body of the guard

This commit is contained in:
Eric Holk 2022-05-17 15:36:39 -07:00
parent 7db4c0277d
commit d08efdec1c
4 changed files with 57 additions and 26 deletions

View File

@ -1323,6 +1323,20 @@ pub enum Guard<'hir> {
IfLet(&'hir Let<'hir>),
}
impl<'hir> Guard<'hir> {
/// Returns the body of the guard
///
/// In other words, returns the e in either of the following:
///
/// - `if e`
/// - `if let x = e`
pub fn body(&self) -> &'hir Expr<'hir> {
match self {
Guard::If(e) | Guard::IfLet(_, e) => e,
}
}
}
#[derive(Debug, HashStable_Generic)]
pub struct ExprField<'hir> {
#[stable_hasher(ignore)]

View File

@ -285,14 +285,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
self.visit_pat(pat);
if let Some(ref g) = guard {
self.guard_bindings.push(<_>::default());
ArmPatCollector {
guard_bindings_set: &mut self.guard_bindings_set,
guard_bindings: self
.guard_bindings
.last_mut()
.expect("should have pushed at least one earlier"),
{
ArmPatCollector {
interior_visitor: self,
scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node },
}
.visit_pat(pat);
}
.visit_pat(pat);
match g {
Guard::If(ref e) => {
@ -459,17 +458,31 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
}
}
struct ArmPatCollector<'a> {
guard_bindings_set: &'a mut HirIdSet,
guard_bindings: &'a mut SmallVec<[HirId; 4]>,
struct ArmPatCollector<'a, 'b, 'tcx> {
interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>,
scope: Scope,
}
impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> {
impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> {
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
intravisit::walk_pat(self, pat);
if let PatKind::Binding(_, id, ..) = pat.kind {
self.guard_bindings.push(id);
self.guard_bindings_set.insert(id);
self.interior_visitor
.guard_bindings
.last_mut()
.expect("should have pushed at least one earlier")
.push(id);
self.interior_visitor.guard_bindings_set.insert(id);
let ty = self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
let ty = self.interior_visitor.fcx.tcx.mk_ref(
// Use `ReErased` as `resolve_interior` is going to replace all the regions anyway.
self.interior_visitor.fcx.tcx.mk_region(ty::ReErased),
ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
);
// FIXME: use the right span
let span = rustc_span::DUMMY_SP;
self.interior_visitor.record(ty, id, Some(self.scope), None, span, true);
}
}
}

View File

@ -17,6 +17,7 @@ use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
use ty::BorrowKind::ImmBorrow;
use crate::mem_categorization as mc;
@ -621,7 +622,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, arm.pat);
self.walk_pat(discr_place, arm.pat, arm.guard.is_some());
if let Some(hir::Guard::If(e)) = arm.guard {
self.consume_expr(e)
@ -645,12 +646,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
FakeReadCause::ForLet(closure_def_id),
discr_place.hir_id,
);
self.walk_pat(discr_place, pat);
self.walk_pat(discr_place, pat, false);
}
/// The core driver for walking a pattern
fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
fn walk_pat(
&mut self,
discr_place: &PlaceWithHirId<'tcx>,
pat: &hir::Pat<'_>,
has_guard: bool,
) {
debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard);
let tcx = self.tcx();
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
@ -671,6 +677,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
delegate.bind(binding_place, binding_place.hir_id);
}
// Subtle: MIR desugaring introduces immutable borrows for each pattern
// binding when lowering pattern guards to ensure that the guard does not
// modify the scrutinee.
if has_guard {
delegate.borrow(place, discr_place.hir_id, ImmBorrow);
}
// It is also a borrow or copy/move of the value being matched.
// In a cases of pattern like `let pat = upvar`, don't use the span
// of the pattern, as this just looks confusing, instead use the span

View File

@ -2,15 +2,6 @@
// edition:2018
// compile-flags: -Zdrop-tracking
// This test is derived from
// https://github.com/rust-lang/rust/issues/72651#issuecomment-668720468
// This test demonstrates that, in `async fn g()`,
// indeed a temporary borrow `y` from `x` is live
// while `f().await` is being evaluated.
// Thus, `&'_ u8` should be included in type signature
// of the underlying generator.
#![feature(generators)]
fn main() {