Skip to content

Commit 7f5721c

Browse files
also record the types of borrows from the pattern locals in match guards
so that it reflects the fact that borrowing these pattern locals is happening before any yield points in match guards
1 parent f9ccd39 commit 7f5721c

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

compiler/rustc_typeck/src/check/generator_interior.rs

+61-9
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ use rustc_hir as hir;
99
use rustc_hir::def::{CtorKind, DefKind, Res};
1010
use rustc_hir::def_id::DefId;
1111
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
12-
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
12+
use rustc_hir::{Expr, ExprKind, Pat, PatKind, Arm, Guard, HirId};
13+
use rustc_hir::hir_id::HirIdSet;
1314
use rustc_middle::middle::region::{self, YieldData};
1415
use rustc_middle::ty::{self, Ty};
1516
use rustc_span::Span;
17+
use smallvec::SmallVec;
1618

1719
struct InteriorVisitor<'a, 'tcx> {
1820
fcx: &'a FnCtxt<'a, 'tcx>,
@@ -21,6 +23,14 @@ struct InteriorVisitor<'a, 'tcx> {
2123
expr_count: usize,
2224
kind: hir::GeneratorKind,
2325
prev_unresolved_span: Option<Span>,
26+
/// Match arm guards have temporary borrows from the pattern bindings.
27+
/// In case there is a yield point in a guard with a reference to such bindings,
28+
/// such borrows can span across this yield point.
29+
/// As such, we need to track these borrows and record them despite of the fact
30+
/// that they may succeed the said yield point in the post-order.
31+
nested_scope_of_guards: SmallVec<[SmallVec<[HirId; 4]>; 4]>,
32+
current_scope_of_guards: HirIdSet,
33+
arm_has_guard: bool,
2434
}
2535

2636
impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
@@ -30,6 +40,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
3040
scope: Option<region::Scope>,
3141
expr: Option<&'tcx Expr<'tcx>>,
3242
source_span: Span,
43+
guard_borrowing_from_pattern: bool,
3344
) {
3445
use rustc_span::DUMMY_SP;
3546

@@ -53,7 +64,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
5364
yield_data.expr_and_pat_count, self.expr_count, source_span
5465
);
5566

56-
if yield_data.expr_and_pat_count >= self.expr_count {
67+
if guard_borrowing_from_pattern || yield_data.expr_and_pat_count >= self.expr_count {
5768
Some(yield_data)
5869
} else {
5970
None
@@ -134,6 +145,9 @@ pub fn resolve_interior<'a, 'tcx>(
134145
expr_count: 0,
135146
kind,
136147
prev_unresolved_span: None,
148+
nested_scope_of_guards: <_>::default(),
149+
current_scope_of_guards: <_>::default(),
150+
arm_has_guard: false,
137151
};
138152
intravisit::walk_body(&mut visitor, body);
139153

@@ -210,19 +224,46 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
210224
NestedVisitorMap::None
211225
}
212226

227+
fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) {
228+
if arm.guard.is_some() {
229+
self.nested_scope_of_guards.push(<_>::default());
230+
self.arm_has_guard = true;
231+
}
232+
self.visit_pat(&arm.pat);
233+
if let Some(ref g) = arm.guard {
234+
match g {
235+
Guard::If(ref e) => {
236+
self.visit_expr(e);
237+
}
238+
}
239+
let mut scope_var_ids =
240+
self.nested_scope_of_guards.pop().expect("should have pushed at least one earlier");
241+
for var_id in scope_var_ids.drain(..) {
242+
assert!(self.current_scope_of_guards.remove(&var_id), "variable should be placed in scope earlier");
243+
}
244+
self.arm_has_guard = false;
245+
}
246+
self.visit_expr(&arm.body);
247+
}
248+
213249
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
214250
intravisit::walk_pat(self, pat);
215251

216252
self.expr_count += 1;
217253

218-
if let PatKind::Binding(..) = pat.kind {
254+
if let PatKind::Binding(_, id, ..) = pat.kind {
219255
let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
220256
let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
221-
self.record(ty, Some(scope), None, pat.span);
257+
self.record(ty, Some(scope), None, pat.span, false);
258+
if self.arm_has_guard {
259+
self.nested_scope_of_guards.as_mut_slice().last_mut().unwrap().push(id);
260+
self.current_scope_of_guards.insert(id);
261+
}
222262
}
223263
}
224264

225265
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
266+
let mut guard_borrowing_from_pattern = false;
226267
match &expr.kind {
227268
ExprKind::Call(callee, args) => match &callee.kind {
228269
ExprKind::Path(qpath) => {
@@ -248,7 +289,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
248289
}
249290
}
250291
_ => intravisit::walk_expr(self, expr),
251-
},
292+
}
293+
ExprKind::Path(qpath) => {
294+
intravisit::walk_expr(self, expr);
295+
let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id);
296+
match res {
297+
Res::Local(id) if self.current_scope_of_guards.contains(&id) => {
298+
debug!("a borrow in guard from pattern local is detected");
299+
guard_borrowing_from_pattern = true;
300+
}
301+
_ => {}
302+
}
303+
}
252304
_ => intravisit::walk_expr(self, expr),
253305
}
254306

@@ -259,18 +311,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
259311
// If there are adjustments, then record the final type --
260312
// this is the actual value that is being produced.
261313
if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
262-
self.record(adjusted_ty, scope, Some(expr), expr.span);
314+
self.record(adjusted_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
263315
}
264316

265317
// Also record the unadjusted type (which is the only type if
266318
// there are no adjustments). The reason for this is that the
267319
// unadjusted value is sometimes a "temporary" that would wind
268320
// up in a MIR temporary.
269321
//
270-
// As an example, consider an expression like `vec![].push()`.
322+
// As an example, consider an expression like `vec![].push(x)`.
271323
// Here, the `vec![]` would wind up MIR stored into a
272324
// temporary variable `t` which we can borrow to invoke
273-
// `<Vec<_>>::push(&mut t)`.
325+
// `<Vec<_>>::push(&mut t, x)`.
274326
//
275327
// Note that an expression can have many adjustments, and we
276328
// are just ignoring those intermediate types. This is because
@@ -287,7 +339,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
287339
// The type table might not have information for this expression
288340
// if it is in a malformed scope. (#66387)
289341
if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) {
290-
self.record(ty, scope, Some(expr), expr.span);
342+
self.record(ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
291343
} else {
292344
self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
293345
}

0 commit comments

Comments
 (0)