1
Fork 0

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>), 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)] #[derive(Debug, HashStable_Generic)]
pub struct ExprField<'hir> { pub struct ExprField<'hir> {
#[stable_hasher(ignore)] #[stable_hasher(ignore)]

View file

@ -285,14 +285,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
self.visit_pat(pat); self.visit_pat(pat);
if let Some(ref g) = guard { if let Some(ref g) = guard {
self.guard_bindings.push(<_>::default()); self.guard_bindings.push(<_>::default());
ArmPatCollector { {
guard_bindings_set: &mut self.guard_bindings_set, ArmPatCollector {
guard_bindings: self interior_visitor: self,
.guard_bindings scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node },
.last_mut() }
.expect("should have pushed at least one earlier"), .visit_pat(pat);
} }
.visit_pat(pat);
match g { match g {
Guard::If(ref e) => { Guard::If(ref e) => {
@ -459,17 +458,31 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
} }
} }
struct ArmPatCollector<'a> { struct ArmPatCollector<'a, 'b, 'tcx> {
guard_bindings_set: &'a mut HirIdSet, interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>,
guard_bindings: &'a mut SmallVec<[HirId; 4]>, 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>) { fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
intravisit::walk_pat(self, pat); intravisit::walk_pat(self, pat);
if let PatKind::Binding(_, id, ..) = pat.kind { if let PatKind::Binding(_, id, ..) = pat.kind {
self.guard_bindings.push(id); self.interior_visitor
self.guard_bindings_set.insert(id); .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::mir::FakeReadCause;
use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt};
use rustc_target::abi::VariantIdx; use rustc_target::abi::VariantIdx;
use ty::BorrowKind::ImmBorrow;
use crate::mem_categorization as mc; use crate::mem_categorization as mc;
@ -621,7 +622,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
FakeReadCause::ForMatchedPlace(closure_def_id), FakeReadCause::ForMatchedPlace(closure_def_id),
discr_place.hir_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 { if let Some(hir::Guard::If(e)) = arm.guard {
self.consume_expr(e) self.consume_expr(e)
@ -645,12 +646,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
FakeReadCause::ForLet(closure_def_id), FakeReadCause::ForLet(closure_def_id),
discr_place.hir_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 /// The core driver for walking a pattern
fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { fn walk_pat(
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, 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 tcx = self.tcx();
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; 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); 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. // 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 // 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 // of the pattern, as this just looks confusing, instead use the span

View file

@ -2,15 +2,6 @@
// edition:2018 // edition:2018
// compile-flags: -Zdrop-tracking // 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)] #![feature(generators)]
fn main() { fn main() {