Skip to content

Commit f24ef2e

Browse files
committed
Auto merge of rust-lang#97029 - eholk:drop-tracking-yielding-in-match-guard, r=nikomatsakis
generator_interior: Count match pattern bindings as borrowed for the whole guard expression The test case `yielding-in-match-guard.rs` was failing with `-Zdrop-tracking` enabled. The reason is that the copy of a local (`y`) was not counted as a borrow in typeck, while MIR did consider this as borrowed. The correct thing to do here is to count pattern bindings are borrowed for the whole guard. Instead, what we were doing is to record the type at the use site of the variable and check if the variable comes from a borrowed pattern. Due to the fix for rust-lang#57017, we were considering too small of a scope for this variable, which meant it was not counted as borrowed. Because we now unconditionally record the borrow, rather than only for bindings that are used, this PR is also able to remove a lot of the logic around match bindings that was there before. r? `@nikomatsakis`
2 parents 4d6992b + 7d1dbdf commit f24ef2e

File tree

4 files changed

+87
-94
lines changed

4 files changed

+87
-94
lines changed

compiler/rustc_hir/src/hir.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1323,6 +1323,20 @@ pub enum Guard<'hir> {
13231323
IfLet(&'hir Let<'hir>),
13241324
}
13251325

1326+
impl<'hir> Guard<'hir> {
1327+
/// Returns the body of the guard
1328+
///
1329+
/// In other words, returns the e in either of the following:
1330+
///
1331+
/// - `if e`
1332+
/// - `if let x = e`
1333+
pub fn body(&self) -> &'hir Expr<'hir> {
1334+
match self {
1335+
Guard::If(e) | Guard::IfLet(Let { init: e, .. }) => e,
1336+
}
1337+
}
1338+
}
1339+
13261340
#[derive(Debug, HashStable_Generic)]
13271341
pub struct ExprField<'hir> {
13281342
#[stable_hasher(ignore)]

compiler/rustc_typeck/src/check/generator_interior.rs

+44-90
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
1717
use rustc_middle::ty::{self, Ty, TyCtxt};
1818
use rustc_span::symbol::sym;
1919
use rustc_span::Span;
20-
use smallvec::SmallVec;
2120
use tracing::debug;
2221

2322
mod drop_ranges;
@@ -29,13 +28,6 @@ struct InteriorVisitor<'a, 'tcx> {
2928
expr_count: usize,
3029
kind: hir::GeneratorKind,
3130
prev_unresolved_span: Option<Span>,
32-
/// Match arm guards have temporary borrows from the pattern bindings.
33-
/// In case there is a yield point in a guard with a reference to such bindings,
34-
/// such borrows can span across this yield point.
35-
/// As such, we need to track these borrows and record them despite of the fact
36-
/// that they may succeed the said yield point in the post-order.
37-
guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>,
38-
guard_bindings_set: HirIdSet,
3931
linted_values: HirIdSet,
4032
drop_ranges: DropRanges,
4133
}
@@ -48,7 +40,6 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
4840
scope: Option<region::Scope>,
4941
expr: Option<&'tcx Expr<'tcx>>,
5042
source_span: Span,
51-
guard_borrowing_from_pattern: bool,
5243
) {
5344
use rustc_span::DUMMY_SP;
5445

@@ -89,8 +80,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
8980
// If it is a borrowing happening in the guard,
9081
// it needs to be recorded regardless because they
9182
// do live across this yield point.
92-
guard_borrowing_from_pattern
93-
|| yield_data.expr_and_pat_count >= self.expr_count
83+
yield_data.expr_and_pat_count >= self.expr_count
9484
})
9585
.cloned()
9686
})
@@ -196,8 +186,6 @@ pub fn resolve_interior<'a, 'tcx>(
196186
expr_count: 0,
197187
kind,
198188
prev_unresolved_span: None,
199-
guard_bindings: <_>::default(),
200-
guard_bindings_set: <_>::default(),
201189
linted_values: <_>::default(),
202190
drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body),
203191
};
@@ -284,15 +272,47 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
284272
let Arm { guard, pat, body, .. } = arm;
285273
self.visit_pat(pat);
286274
if let Some(ref g) = guard {
287-
self.guard_bindings.push(<_>::default());
288-
ArmPatCollector {
289-
guard_bindings_set: &mut self.guard_bindings_set,
290-
guard_bindings: self
291-
.guard_bindings
292-
.last_mut()
293-
.expect("should have pushed at least one earlier"),
275+
{
276+
// If there is a guard, we need to count all variables bound in the pattern as
277+
// borrowed for the entire guard body, regardless of whether they are accessed.
278+
// We do this by walking the pattern bindings and recording `&T` for any `x: T`
279+
// that is bound.
280+
281+
struct ArmPatCollector<'a, 'b, 'tcx> {
282+
interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>,
283+
scope: Scope,
284+
}
285+
286+
impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> {
287+
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
288+
intravisit::walk_pat(self, pat);
289+
if let PatKind::Binding(_, id, ident, ..) = pat.kind {
290+
let ty =
291+
self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
292+
let tcx = self.interior_visitor.fcx.tcx;
293+
let ty = tcx.mk_ref(
294+
// Use `ReErased` as `resolve_interior` is going to replace all the
295+
// regions anyway.
296+
tcx.mk_region(ty::ReErased),
297+
ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
298+
);
299+
self.interior_visitor.record(
300+
ty,
301+
id,
302+
Some(self.scope),
303+
None,
304+
ident.span,
305+
);
306+
}
307+
}
308+
}
309+
310+
ArmPatCollector {
311+
interior_visitor: self,
312+
scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node },
313+
}
314+
.visit_pat(pat);
294315
}
295-
.visit_pat(pat);
296316

297317
match g {
298318
Guard::If(ref e) => {
@@ -302,12 +322,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
302322
self.visit_let_expr(l);
303323
}
304324
}
305-
306-
let mut scope_var_ids =
307-
self.guard_bindings.pop().expect("should have pushed at least one earlier");
308-
for var_id in scope_var_ids.drain(..) {
309-
self.guard_bindings_set.remove(&var_id);
310-
}
311325
}
312326
self.visit_expr(body);
313327
}
@@ -320,13 +334,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
320334
if let PatKind::Binding(..) = pat.kind {
321335
let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
322336
let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
323-
self.record(ty, pat.hir_id, Some(scope), None, pat.span, false);
337+
self.record(ty, pat.hir_id, Some(scope), None, pat.span);
324338
}
325339
}
326340

327341
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
328-
let mut guard_borrowing_from_pattern = false;
329-
330342
match &expr.kind {
331343
ExprKind::Call(callee, args) => match &callee.kind {
332344
ExprKind::Path(qpath) => {
@@ -353,16 +365,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
353365
}
354366
_ => intravisit::walk_expr(self, expr),
355367
},
356-
ExprKind::Path(qpath) => {
357-
intravisit::walk_expr(self, expr);
358-
let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id);
359-
match res {
360-
Res::Local(id) if self.guard_bindings_set.contains(&id) => {
361-
guard_borrowing_from_pattern = true;
362-
}
363-
_ => {}
364-
}
365-
}
366368
_ => intravisit::walk_expr(self, expr),
367369
}
368370

@@ -391,14 +393,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
391393
// If there are adjustments, then record the final type --
392394
// this is the actual value that is being produced.
393395
if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
394-
self.record(
395-
adjusted_ty,
396-
expr.hir_id,
397-
scope,
398-
Some(expr),
399-
expr.span,
400-
guard_borrowing_from_pattern,
401-
);
396+
self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span);
402397
}
403398

404399
// Also record the unadjusted type (which is the only type if
@@ -426,54 +421,13 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
426421
// The type table might not have information for this expression
427422
// if it is in a malformed scope. (#66387)
428423
if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
429-
if guard_borrowing_from_pattern {
430-
// Match guards create references to all the bindings in the pattern that are used
431-
// in the guard, e.g. `y if is_even(y) => ...` becomes `is_even(*r_y)` where `r_y`
432-
// is a reference to `y`, so we must record a reference to the type of the binding.
433-
let tcx = self.fcx.tcx;
434-
let ref_ty = tcx.mk_ref(
435-
// Use `ReErased` as `resolve_interior` is going to replace all the regions anyway.
436-
tcx.mk_region(ty::ReErased),
437-
ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
438-
);
439-
self.record(
440-
ref_ty,
441-
expr.hir_id,
442-
scope,
443-
Some(expr),
444-
expr.span,
445-
guard_borrowing_from_pattern,
446-
);
447-
}
448-
self.record(
449-
ty,
450-
expr.hir_id,
451-
scope,
452-
Some(expr),
453-
expr.span,
454-
guard_borrowing_from_pattern,
455-
);
424+
self.record(ty, expr.hir_id, scope, Some(expr), expr.span);
456425
} else {
457426
self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
458427
}
459428
}
460429
}
461430

462-
struct ArmPatCollector<'a> {
463-
guard_bindings_set: &'a mut HirIdSet,
464-
guard_bindings: &'a mut SmallVec<[HirId; 4]>,
465-
}
466-
467-
impl<'a, 'tcx> Visitor<'tcx> for ArmPatCollector<'a> {
468-
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
469-
intravisit::walk_pat(self, pat);
470-
if let PatKind::Binding(_, id, ..) = pat.kind {
471-
self.guard_bindings.push(id);
472-
self.guard_bindings_set.insert(id);
473-
}
474-
}
475-
}
476-
477431
#[derive(Default)]
478432
pub struct SuspendCheckData<'a, 'tcx> {
479433
expr: Option<&'tcx Expr<'tcx>>,

compiler/rustc_typeck/src/expr_use_visitor.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_middle::hir::place::ProjectionKind;
1717
use rustc_middle::mir::FakeReadCause;
1818
use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt};
1919
use rustc_target::abi::VariantIdx;
20+
use ty::BorrowKind::ImmBorrow;
2021

2122
use crate::mem_categorization as mc;
2223

@@ -621,7 +622,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
621622
FakeReadCause::ForMatchedPlace(closure_def_id),
622623
discr_place.hir_id,
623624
);
624-
self.walk_pat(discr_place, arm.pat);
625+
self.walk_pat(discr_place, arm.pat, arm.guard.is_some());
625626

626627
if let Some(hir::Guard::If(e)) = arm.guard {
627628
self.consume_expr(e)
@@ -645,12 +646,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
645646
FakeReadCause::ForLet(closure_def_id),
646647
discr_place.hir_id,
647648
);
648-
self.walk_pat(discr_place, pat);
649+
self.walk_pat(discr_place, pat, false);
649650
}
650651

651652
/// The core driver for walking a pattern
652-
fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
653-
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
653+
fn walk_pat(
654+
&mut self,
655+
discr_place: &PlaceWithHirId<'tcx>,
656+
pat: &hir::Pat<'_>,
657+
has_guard: bool,
658+
) {
659+
debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard);
654660

655661
let tcx = self.tcx();
656662
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
@@ -671,6 +677,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
671677
delegate.bind(binding_place, binding_place.hir_id);
672678
}
673679

680+
// Subtle: MIR desugaring introduces immutable borrows for each pattern
681+
// binding when lowering pattern guards to ensure that the guard does not
682+
// modify the scrutinee.
683+
if has_guard {
684+
delegate.borrow(place, discr_place.hir_id, ImmBorrow);
685+
}
686+
674687
// It is also a borrow or copy/move of the value being matched.
675688
// In a cases of pattern like `let pat = upvar`, don't use the span
676689
// of the pattern, as this just looks confusing, instead use the span
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// build-pass
2+
// edition:2018
3+
// compile-flags: -Zdrop-tracking
4+
5+
#![feature(generators)]
6+
7+
fn main() {
8+
let _ = static |x: u8| match x {
9+
y if { yield } == y + 1 => (),
10+
_ => (),
11+
};
12+
}

0 commit comments

Comments
 (0)