about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <ericholk@microsoft.com>2022-05-17 15:36:39 -0700
committerEric Holk <ericholk@microsoft.com>2022-05-19 16:23:28 -0700
commitd08efdec1c0e3c8135a547b2853af49a7f107f7e (patch)
tree5bdeda55400cdd98c6efbb6c5385f7a3fe7393bb
parent7db4c0277de996531c2e794ba16bb6746af65fef (diff)
downloadrust-d08efdec1c0e3c8135a547b2853af49a7f107f7e.tar.gz
rust-d08efdec1c0e3c8135a547b2853af49a7f107f7e.zip
Borrow guard patterns for the body of the guard
-rw-r--r--compiler/rustc_hir/src/hir.rs14
-rw-r--r--compiler/rustc_typeck/src/check/generator_interior.rs39
-rw-r--r--compiler/rustc_typeck/src/expr_use_visitor.rs21
-rw-r--r--src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs9
4 files changed, 57 insertions, 26 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 81d544c7b96..5bd6632640f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -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)]
diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs
index 92a2584a6de..d530504489a 100644
--- a/compiler/rustc_typeck/src/check/generator_interior.rs
+++ b/compiler/rustc_typeck/src/check/generator_interior.rs
@@ -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);
         }
     }
 }
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 6de6b6ee479..ad44adb68c6 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -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
diff --git a/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs b/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs
index c818963e466..646365e4359 100644
--- a/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs
+++ b/src/test/ui/generator/drop-tracking-yielding-in-match-guards.rs
@@ -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() {