Skip to content

Commit 647e8bd

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 2759284 commit 647e8bd

11 files changed

+155
-263
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+23-148
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};
@@ -312,7 +310,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
312310

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

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

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

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

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

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

692-
if needs_to_be_read {
693-
self.borrow_expr(discr, BorrowKind::Immutable)?;
606+
let cause = if refutable {
607+
FakeReadCause::ForMatchedPlace(closure_def_id)
694608
} else {
695-
let closure_def_id = match discr_place.place.base {
696-
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
697-
_ => None,
698-
};
609+
FakeReadCause::ForLet(closure_def_id)
610+
};
699611

700-
self.delegate.borrow_mut().fake_read(
701-
&discr_place,
702-
FakeReadCause::ForMatchedPlace(closure_def_id),
703-
discr_place.hir_id,
704-
);
612+
self.delegate.borrow_mut().fake_read(discr_place, cause, discr_place.hir_id);
705613

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

@@ -723,12 +627,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
723627
self.walk_expr(expr)?;
724628
let expr_place = self.cat_expr(expr)?;
725629
f()?;
630+
self.fake_read_scrutinee(&expr_place, els.is_some())?;
631+
self.walk_pat(&expr_place, pat, false)?;
726632
if let Some(els) = els {
727-
// borrowing because we need to test the discriminant
728-
self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
729633
self.walk_block(els)?;
730634
}
731-
self.walk_irrefutable_pat(&expr_place, pat)?;
732635
Ok(())
733636
}
734637

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

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

923-
/// Walks a pat that occurs in isolation (i.e., top-level of fn argument or
924-
/// let binding, and *not* a match arm or nested pat.)
925-
fn walk_irrefutable_pat(
926-
&self,
927-
discr_place: &PlaceWithHirId<'tcx>,
928-
pat: &hir::Pat<'_>,
929-
) -> Result<(), Cx::Error> {
930-
let closure_def_id = match discr_place.place.base {
931-
PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
932-
_ => None,
933-
};
934-
935-
self.delegate.borrow_mut().fake_read(
936-
discr_place,
937-
FakeReadCause::ForLet(closure_def_id),
938-
discr_place.hir_id,
939-
);
940-
self.walk_pat(discr_place, pat, false)?;
941-
Ok(())
942-
}
943-
944816
/// The core driver for walking a pattern
945817
///
946818
/// This should mirror how pattern-matching gets lowered to MIR, as
@@ -1960,8 +1832,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
19601832
/// Here, we cannot perform such an accurate checks, because querying
19611833
/// whether a type is inhabited requires that it has been fully inferred,
19621834
/// which cannot be guaranteed at this point.
1835+
#[instrument(skip(self, span), level = "debug")]
19631836
fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
1964-
if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
1837+
let ty = self.cx.try_structurally_resolve_type(span, ty);
1838+
debug!(?ty, "is_multivariant_adt: resolved");
1839+
if let ty::Adt(def, _) = ty.kind() {
19651840
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
19661841
// to assume that more cases will be added to the variant in the future. This mean
19671842
// that we should handle non-exhaustive SingleVariant the same way we would handle
File renamed without changes.

tests/crashes/119786-2.rs

+15
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

+15
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

+12-17
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

+10-14
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

0 commit comments

Comments
 (0)