@@ -948,6 +948,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
948948 }
949949
950950 /// The core driver for walking a pattern
951+ ///
952+ /// This should mirror how pattern-matching gets lowered to MIR, as
953+ /// otherwise lowering will ICE when trying to resolve the upvars.
954+ ///
955+ /// However, it is okay to approximate it here by doing *more* accesses
956+ /// than the actual MIR builder will, which is useful when some checks
957+ /// are too cumbersome to perform here. For example, if only after type
958+ /// inference it becomes clear that only one variant of an enum is
959+ /// inhabited, and therefore a read of the discriminant is not necessary,
960+ /// `walk_pat` will have over-approximated the necessary upvar capture
961+ /// granularity. (Or, at least, that's what the code seems to be saying.
962+ /// I didn't bother trying to craft an example where this actually happens).
963+ ///
964+ /// Do note that discrepancies like these do still create weird language
965+ /// semantics, and should be avoided if possible.
951966 fn walk_pat (
952967 & self ,
953968 discr_place : & PlaceWithHirId < ' tcx > ,
@@ -958,6 +973,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
958973
959974 let tcx = self . cx . tcx ( ) ;
960975 self . cat_pattern ( discr_place. clone ( ) , pat, & mut |place, pat| {
976+ debug ! ( "walk_pat: pat.kind={:?}" , pat. kind) ;
977+ let read_discriminant = || {
978+ self . delegate . borrow_mut ( ) . borrow ( place, discr_place. hir_id , BorrowKind :: Immutable ) ;
979+ } ;
980+
961981 match pat. kind {
962982 PatKind :: Binding ( _, canonical_id, ..) => {
963983 debug ! ( "walk_pat: binding place={:?} pat={:?}" , place, pat) ;
@@ -980,11 +1000,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
9801000 // binding when lowering pattern guards to ensure that the guard does not
9811001 // modify the scrutinee.
9821002 if has_guard {
983- self . delegate . borrow_mut ( ) . borrow (
984- place,
985- discr_place. hir_id ,
986- BorrowKind :: Immutable ,
987- ) ;
1003+ read_discriminant ( ) ;
9881004 }
9891005
9901006 // It is also a borrow or copy/move of the value being matched.
@@ -1016,13 +1032,70 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10161032 PatKind :: Never => {
10171033 // A `!` pattern always counts as an immutable read of the discriminant,
10181034 // even in an irrefutable pattern.
1019- self . delegate . borrow_mut ( ) . borrow (
1020- place,
1021- discr_place. hir_id ,
1022- BorrowKind :: Immutable ,
1023- ) ;
1035+ read_discriminant ( ) ;
1036+ }
1037+ PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( qpath) , hir_id, span } ) => {
1038+ // A `Path` pattern is just a name like `Foo`. This is either a
1039+ // named constant or else it refers to an ADT variant
1040+
1041+ let res = self . cx . typeck_results ( ) . qpath_res ( qpath, * hir_id) ;
1042+ match res {
1043+ Res :: Def ( DefKind :: Const , _) | Res :: Def ( DefKind :: AssocConst , _) => {
1044+ // Named constants have to be equated with the value
1045+ // being matched, so that's a read of the value being matched.
1046+ //
1047+ // FIXME: Does the MIR code skip this read when matching on a ZST?
1048+ // If so, we can also skip it here.
1049+ read_discriminant ( ) ;
1050+ }
1051+ _ => {
1052+ // Otherwise, this is a struct/enum variant, and so it's
1053+ // only a read if we need to read the discriminant.
1054+ if self . is_multivariant_adt ( place. place . ty ( ) , * span) {
1055+ read_discriminant ( ) ;
1056+ }
1057+ }
1058+ }
1059+ }
1060+ PatKind :: Expr ( _) | PatKind :: Range ( ..) => {
1061+ // When matching against a literal or range, we need to
1062+ // borrow the place to compare it against the pattern.
1063+ //
1064+ // FIXME: What if the type being matched only has one
1065+ // possible value?
1066+ // FIXME: What if the range is the full range of the type
1067+ // and doesn't actually require a discriminant read?
1068+ read_discriminant ( ) ;
1069+ }
1070+ PatKind :: Struct ( ..) | PatKind :: TupleStruct ( ..) => {
1071+ if self . is_multivariant_adt ( place. place . ty ( ) , pat. span ) {
1072+ read_discriminant ( ) ;
1073+ }
1074+ }
1075+ PatKind :: Slice ( lhs, wild, rhs) => {
1076+ // We don't need to test the length if the pattern is `[..]`
1077+ if matches ! ( ( lhs, wild, rhs) , ( & [ ] , Some ( _) , & [ ] ) )
1078+ // Arrays have a statically known size, so
1079+ // there is no need to read their length
1080+ || place. place . ty ( ) . peel_refs ( ) . is_array ( )
1081+ {
1082+ // No read necessary
1083+ } else {
1084+ read_discriminant ( ) ;
1085+ }
1086+ }
1087+ PatKind :: Or ( _)
1088+ | PatKind :: Box ( _)
1089+ | PatKind :: Ref ( ..)
1090+ | PatKind :: Guard ( ..)
1091+ | PatKind :: Tuple ( ..)
1092+ | PatKind :: Wild
1093+ | PatKind :: Err ( _) => {
1094+ // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
1095+ // are made later as these patterns contains subpatterns.
1096+ // If the PatKind is Wild or Err, they are made when processing the other patterns
1097+ // being examined
10241098 }
1025- _ => { }
10261099 }
10271100
10281101 Ok ( ( ) )
@@ -1858,6 +1931,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18581931 Ok ( ( ) )
18591932 }
18601933
1934+ /// Checks whether a type has multiple variants, and therefore, whether a
1935+ /// read of the discriminant might be necessary. Note that the actual MIR
1936+ /// builder code does a more specific check, filtering out variants that
1937+ /// happen to be uninhabited.
1938+ ///
1939+ /// Here, we cannot perform such an accurate checks, because querying
1940+ /// whether a type is inhabited requires that it has been fully inferred,
1941+ /// which cannot be guaranteed at this point.
18611942 fn is_multivariant_adt ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
18621943 if let ty:: Adt ( def, _) = self . cx . try_structurally_resolve_type ( span, ty) . kind ( ) {
18631944 // Note that if a non-exhaustive SingleVariant is defined in another crate, we need
0 commit comments