Skip to content

Commit 656d393

Browse files
committed
ExprUseVisitor: remove maybe_read_scrutinee
The split between walk_pat and maybe_read_scrutinee has now become redundant. Due to this change, one testcase within the testsuite has become similar enough to a known ICE to also break. I am leaving this as future work, as it requires feature(type_alias_impl_trait)
1 parent e52177f commit 656d393

11 files changed

+152
-262
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

Lines changed: 20 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
88
use std::cell::{Ref, RefCell};
99
use std::ops::Deref;
10-
use std::slice::from_ref;
1110

12-
use hir::Expr;
1311
use hir::def::DefKind;
1412
use hir::pat_util::EnumerateAndAdjustIterator as _;
1513
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
@@ -313,7 +311,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
313311

314312
let param_place = self.cat_rvalue(param.hir_id, param_ty);
315313

316-
self.walk_irrefutable_pat(&param_place, param.pat)?;
314+
self.fake_read_scrutinee(&param_place, false)?;
315+
self.walk_pat(&param_place, param.pat, false)?;
317316
}
318317

319318
self.consume_expr(body.value)?;
@@ -455,13 +454,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
455454

456455
hir::ExprKind::Match(discr, arms, _) => {
457456
let discr_place = self.cat_expr(discr)?;
458-
self.maybe_read_scrutinee(
459-
discr,
460-
discr_place.clone(),
461-
arms.iter().map(|arm| arm.pat),
462-
)?;
457+
self.fake_read_scrutinee(&discr_place, true)?;
458+
self.walk_expr(discr)?;
463459

464-
// treatment of the discriminant is handled while walking the arms.
465460
for arm in arms {
466461
self.walk_arm(&discr_place, arm)?;
467462
}
@@ -598,116 +593,25 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
598593
Ok(())
599594
}
600595

601-
fn maybe_read_scrutinee<'t>(
596+
#[instrument(skip(self), level = "debug")]
597+
fn fake_read_scrutinee(
602598
&self,
603-
discr: &Expr<'_>,
604-
discr_place: PlaceWithHirId<'tcx>,
605-
pats: impl Iterator<Item = &'t hir::Pat<'t>>,
599+
discr_place: &PlaceWithHirId<'tcx>,
600+
refutable: bool,
606601
) -> Result<(), Cx::Error> {
607-
// Matching should not always be considered a use of the place, hence
608-
// discr does not necessarily need to be borrowed.
609-
// We only want to borrow discr if the pattern contain something other
610-
// than wildcards.
611-
let mut needs_to_be_read = false;
612-
for pat in pats {
613-
self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
614-
match &pat.kind {
615-
PatKind::Missing => unreachable!(),
616-
PatKind::Binding(.., opt_sub_pat) => {
617-
// If the opt_sub_pat is None, then the binding does not count as
618-
// a wildcard for the purpose of borrowing discr.
619-
if opt_sub_pat.is_none() {
620-
needs_to_be_read = true;
621-
}
622-
}
623-
PatKind::Never => {
624-
// A never pattern reads the value.
625-
// FIXME(never_patterns): does this do what I expect?
626-
needs_to_be_read = true;
627-
}
628-
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
629-
// A `Path` pattern is just a name like `Foo`. This is either a
630-
// named constant or else it refers to an ADT variant
631-
632-
let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
633-
match res {
634-
Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
635-
// Named constants have to be equated with the value
636-
// being matched, so that's a read of the value being matched.
637-
//
638-
// FIXME: We don't actually reads for ZSTs.
639-
needs_to_be_read = true;
640-
}
641-
_ => {
642-
// Otherwise, this is a struct/enum variant, and so it's
643-
// only a read if we need to read the discriminant.
644-
needs_to_be_read |=
645-
self.is_multivariant_adt(place.place.ty(), *span);
646-
}
647-
}
648-
}
649-
PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
650-
// For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching
651-
// against a multivariant enum or struct. In that case, we have to read
652-
// the discriminant. Otherwise this kind of pattern doesn't actually
653-
// read anything (we'll get invoked for the `...`, which may indeed
654-
// perform some reads).
655-
656-
let place_ty = place.place.ty();
657-
needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
658-
}
659-
PatKind::Expr(_) | PatKind::Range(..) => {
660-
// If the PatKind is a Lit or a Range then we want
661-
// to borrow discr.
662-
needs_to_be_read = true;
663-
}
664-
PatKind::Slice(lhs, wild, rhs) => {
665-
// We don't need to test the length if the pattern is `[..]`
666-
if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
667-
// Arrays have a statically known size, so
668-
// there is no need to read their length
669-
|| place.place.ty().peel_refs().is_array()
670-
{
671-
} else {
672-
needs_to_be_read = true;
673-
}
674-
}
675-
PatKind::Or(_)
676-
| PatKind::Box(_)
677-
| PatKind::Deref(_)
678-
| PatKind::Ref(..)
679-
| PatKind::Guard(..)
680-
| PatKind::Wild
681-
| PatKind::Err(_) => {
682-
// If the PatKind is Or, Box, or Ref, the decision is made later
683-
// as these patterns contains subpatterns
684-
// If the PatKind is Wild or Err, the decision is made based on the other patterns
685-
// being examined
686-
}
687-
}
688-
689-
Ok(())
690-
})?
691-
}
602+
let closure_def_id = match discr_place.place.base {
603+
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
604+
_ => None,
605+
};
692606

693-
if needs_to_be_read {
694-
self.borrow_expr(discr, BorrowKind::Immutable)?;
607+
let cause = if refutable {
608+
FakeReadCause::ForMatchedPlace(closure_def_id)
695609
} else {
696-
let closure_def_id = match discr_place.place.base {
697-
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
698-
_ => None,
699-
};
610+
FakeReadCause::ForLet(closure_def_id)
611+
};
700612

701-
self.delegate.borrow_mut().fake_read(
702-
&discr_place,
703-
FakeReadCause::ForMatchedPlace(closure_def_id),
704-
discr_place.hir_id,
705-
);
613+
self.delegate.borrow_mut().fake_read(discr_place, cause, discr_place.hir_id);
706614

707-
// We always want to walk the discriminant. We want to make sure, for instance,
708-
// that the discriminant has been initialized.
709-
self.walk_expr(discr)?;
710-
}
711615
Ok(())
712616
}
713617

@@ -724,12 +628,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
724628
self.walk_expr(expr)?;
725629
let expr_place = self.cat_expr(expr)?;
726630
f()?;
631+
self.fake_read_scrutinee(&expr_place, els.is_some())?;
632+
self.walk_pat(&expr_place, pat, false)?;
727633
if let Some(els) = els {
728-
// borrowing because we need to test the discriminant
729-
self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
730634
self.walk_block(els)?;
731635
}
732-
self.walk_irrefutable_pat(&expr_place, pat)?;
733636
Ok(())
734637
}
735638

@@ -901,16 +804,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
901804
discr_place: &PlaceWithHirId<'tcx>,
902805
arm: &hir::Arm<'_>,
903806
) -> Result<(), Cx::Error> {
904-
let closure_def_id = match discr_place.place.base {
905-
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
906-
_ => None,
907-
};
908-
909-
self.delegate.borrow_mut().fake_read(
910-
discr_place,
911-
FakeReadCause::ForMatchedPlace(closure_def_id),
912-
discr_place.hir_id,
913-
);
914807
self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
915808

916809
if let Some(ref e) = arm.guard {
@@ -921,27 +814,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
921814
Ok(())
922815
}
923816

924-
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
925-
/// let binding, and *not* a match arm or nested pat.)
926-
fn walk_irrefutable_pat(
927-
&self,
928-
discr_place: &PlaceWithHirId<'tcx>,
929-
pat: &hir::Pat<'_>,
930-
) -> Result<(), Cx::Error> {
931-
let closure_def_id = match discr_place.place.base {
932-
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
933-
_ => None,
934-
};
935-
936-
self.delegate.borrow_mut().fake_read(
937-
discr_place,
938-
FakeReadCause::ForLet(closure_def_id),
939-
discr_place.hir_id,
940-
);
941-
self.walk_pat(discr_place, pat, false)?;
942-
Ok(())
943-
}
944-
945817
/// The core driver for walking a pattern
946818
///
947819
/// This should mirror how pattern-matching gets lowered to MIR, as
@@ -1968,6 +1840,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
19681840
/// Here, we cannot perform such an accurate checks, because querying
19691841
/// whether a type is inhabited requires that it has been fully inferred,
19701842
/// which cannot be guaranteed at this point.
1843+
#[instrument(skip(self, span), level = "debug")]
19711844
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
19721845
if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
19731846
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
File renamed without changes.

tests/crashes/119786-2.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ known-bug: #119786
2+
//@ edition:2021
3+
4+
fn enum_upvar() {
5+
type T = impl Copy;
6+
let foo: T = Some((1u32, 2u32));
7+
let x = move || {
8+
match foo {
9+
None => (),
10+
Some(_) => (),
11+
}
12+
};
13+
}
14+
15+
pub fn main() {}

tests/crashes/119786-3.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ known-bug: #119786
2+
//@ edition:2021
3+
4+
fn enum_upvar() {
5+
type T = impl Copy;
6+
let foo: T = Some((1u32, 2u32));
7+
let x = move || {
8+
match foo {
9+
None => (),
10+
Some((a, b)) => (),
11+
}
12+
};
13+
}
14+
15+
pub fn main() {}

tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ fn foo::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_fake
44
yields ()
55
{
66
debug _task_context => _2;
7-
debug f => (*(_1.0: &&Foo));
7+
debug f => (*(_1.0: &Foo));
88
let mut _0: ();
99
let mut _3: &Foo;
1010
let mut _4: &&Foo;
11-
let mut _5: &&&Foo;
12-
let mut _6: isize;
13-
let mut _7: bool;
11+
let mut _5: isize;
12+
let mut _6: bool;
1413

1514
bb0: {
16-
PlaceMention((*(_1.0: &&Foo)));
17-
_6 = discriminant((*(*(_1.0: &&Foo))));
18-
switchInt(move _6) -> [0: bb2, otherwise: bb1];
15+
_5 = discriminant((*(_1.0: &Foo)));
16+
switchInt(move _5) -> [0: bb2, otherwise: bb1];
1917
}
2018

2119
bb1: {
@@ -32,28 +30,25 @@ yields ()
3230
}
3331

3432
bb4: {
35-
FakeRead(ForMatchedPlace(None), (*(_1.0: &&Foo)));
3633
unreachable;
3734
}
3835

3936
bb5: {
40-
_3 = &fake shallow (*(*(_1.0: &&Foo)));
41-
_4 = &fake shallow (*(_1.0: &&Foo));
42-
_5 = &fake shallow (_1.0: &&Foo);
43-
StorageLive(_7);
44-
_7 = const true;
45-
switchInt(move _7) -> [0: bb8, otherwise: bb7];
37+
_3 = &fake shallow (*(_1.0: &Foo));
38+
_4 = &fake shallow (_1.0: &Foo);
39+
StorageLive(_6);
40+
_6 = const true;
41+
switchInt(move _6) -> [0: bb8, otherwise: bb7];
4642
}
4743

4844
bb6: {
4945
falseEdge -> [real: bb3, imaginary: bb1];
5046
}
5147

5248
bb7: {
53-
StorageDead(_7);
49+
StorageDead(_6);
5450
FakeRead(ForMatchGuard, _3);
5551
FakeRead(ForMatchGuard, _4);
56-
FakeRead(ForMatchGuard, _5);
5752
_0 = const ();
5853
goto -> bb10;
5954
}
@@ -63,7 +58,7 @@ yields ()
6358
}
6459

6560
bb9: {
66-
StorageDead(_7);
61+
StorageDead(_6);
6762
goto -> bb6;
6863
}
6964

tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ fn foo::{closure#0}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_fa
44
yields ()
55
{
66
debug _task_context => _2;
7-
debug f => (_1.0: &Foo);
7+
debug f => (*(_1.0: &Foo));
88
let mut _0: ();
99
let mut _3: &Foo;
1010
let mut _4: &&Foo;
11-
let mut _5: &&&Foo;
12-
let mut _6: isize;
13-
let mut _7: bool;
11+
let mut _5: isize;
12+
let mut _6: bool;
1413

1514
bb0: {
16-
PlaceMention((_1.0: &Foo));
17-
_6 = discriminant((*(_1.0: &Foo)));
18-
switchInt(move _6) -> [0: bb2, otherwise: bb1];
15+
_5 = discriminant((*(_1.0: &Foo)));
16+
switchInt(move _5) -> [0: bb2, otherwise: bb1];
1917
}
2018

2119
bb1: {
@@ -29,24 +27,22 @@ yields ()
2927

3028
bb3: {
3129
_3 = &fake shallow (*(_1.0: &Foo));
32-
_4 = &fake shallow (_1.0: &Foo);
3330
nop;
34-
StorageLive(_7);
35-
_7 = const true;
36-
switchInt(move _7) -> [0: bb5, otherwise: bb4];
31+
StorageLive(_6);
32+
_6 = const true;
33+
switchInt(move _6) -> [0: bb5, otherwise: bb4];
3734
}
3835

3936
bb4: {
40-
StorageDead(_7);
37+
StorageDead(_6);
4138
FakeRead(ForMatchGuard, _3);
4239
FakeRead(ForMatchGuard, _4);
43-
FakeRead(ForMatchGuard, _5);
4440
_0 = const ();
4541
goto -> bb6;
4642
}
4743

4844
bb5: {
49-
StorageDead(_7);
45+
StorageDead(_6);
5046
falseEdge -> [real: bb1, imaginary: bb1];
5147
}
5248

tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0505]: cannot move out of `ts` because it is borrowed
44
LL | let _b = || { match ts {
55
| -- -- borrow occurs due to use in closure
66
| |
7-
| borrow of `ts` occurs here
7+
| borrow of `ts.x` occurs here
88
...
99
LL | let mut mut_ts = ts;
1010
| ^^ move out of `ts` occurs here

0 commit comments

Comments
 (0)