@@ -941,6 +941,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
941941 }
942942
943943 /// The core driver for walking a pattern
944+ ///
945+ /// This should mirror how pattern-matching gets lowered to MIR, as
946+ /// otherwise lowering will ICE when trying to resolve the upvars.
947+ ///
948+ /// However, it is okay to approximate it here by doing *more* accesses
949+ /// than the actual MIR builder will, which is useful when some checks
950+ /// are too cumbersome to perform here. For example, if only after type
951+ /// inference it becomes clear that only one variant of an enum is
952+ /// inhabited, and therefore a read of the discriminant is not necessary,
953+ /// `walk_pat` will have over-approximated the necessary upvar capture
954+ /// granularity. (Or, at least, that's what the code seems to be saying.
955+ /// I didn't bother trying to craft an example where this actually happens).
956+ ///
957+ /// Do note that discrepancies like these do still create weird language
958+ /// semantics, and should be avoided if possible.
944959 #[ instrument( skip( self ) , level = "debug" ) ]
945960 fn walk_pat (
946961 & self ,
@@ -950,6 +965,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
950965 ) -> Result < ( ) , Cx :: Error > {
951966 let tcx = self . cx . tcx ( ) ;
952967 self . cat_pattern ( discr_place. clone ( ) , pat, & mut |place, pat| {
968+ debug ! ( "walk_pat: pat.kind={:?}" , pat. kind) ;
969+ let read_discriminant = || {
970+ self . delegate . borrow_mut ( ) . borrow ( place, discr_place. hir_id , BorrowKind :: Immutable ) ;
971+ } ;
972+
953973 match pat. kind {
954974 PatKind :: Binding ( _, canonical_id, ..) => {
955975 debug ! ( "walk_pat: binding place={:?} pat={:?}" , place, pat) ;
@@ -972,11 +992,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
972992 // binding when lowering pattern guards to ensure that the guard does not
973993 // modify the scrutinee.
974994 if has_guard {
975- self . delegate . borrow_mut ( ) . borrow (
976- place,
977- discr_place. hir_id ,
978- BorrowKind :: Immutable ,
979- ) ;
995+ read_discriminant ( ) ;
980996 }
981997
982998 // It is also a borrow or copy/move of the value being matched.
@@ -1008,13 +1024,70 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
10081024 PatKind :: Never => {
10091025 // A `!` pattern always counts as an immutable read of the discriminant,
10101026 // even in an irrefutable pattern.
1011- self . delegate . borrow_mut ( ) . borrow (
1012- place,
1013- discr_place. hir_id ,
1014- BorrowKind :: Immutable ,
1015- ) ;
1027+ read_discriminant ( ) ;
1028+ }
1029+ PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( qpath) , hir_id, span } ) => {
1030+ // A `Path` pattern is just a name like `Foo`. This is either a
1031+ // named constant or else it refers to an ADT variant
1032+
1033+ let res = self . cx . typeck_results ( ) . qpath_res ( qpath, * hir_id) ;
1034+ match res {
1035+ Res :: Def ( DefKind :: Const , _) | Res :: Def ( DefKind :: AssocConst , _) => {
1036+ // Named constants have to be equated with the value
1037+ // being matched, so that's a read of the value being matched.
1038+ //
1039+ // FIXME: Does the MIR code skip this read when matching on a ZST?
1040+ // If so, we can also skip it here.
1041+ read_discriminant ( ) ;
1042+ }
1043+ _ => {
1044+ // Otherwise, this is a struct/enum variant, and so it's
1045+ // only a read if we need to read the discriminant.
1046+ if self . is_multivariant_adt ( place. place . ty ( ) , * span) {
1047+ read_discriminant ( ) ;
1048+ }
1049+ }
1050+ }
1051+ }
1052+ PatKind :: Expr ( _) | PatKind :: Range ( ..) => {
1053+ // When matching against a literal or range, we need to
1054+ // borrow the place to compare it against the pattern.
1055+ //
1056+ // FIXME: What if the type being matched only has one
1057+ // possible value?
1058+ // FIXME: What if the range is the full range of the type
1059+ // and doesn't actually require a discriminant read?
1060+ read_discriminant ( ) ;
1061+ }
1062+ PatKind :: Struct ( ..) | PatKind :: TupleStruct ( ..) => {
1063+ if self . is_multivariant_adt ( place. place . ty ( ) , pat. span ) {
1064+ read_discriminant ( ) ;
1065+ }
1066+ }
1067+ PatKind :: Slice ( lhs, wild, rhs) => {
1068+ // We don't need to test the length if the pattern is `[..]`
1069+ if matches ! ( ( lhs, wild, rhs) , ( & [ ] , Some ( _) , & [ ] ) )
1070+ // Arrays have a statically known size, so
1071+ // there is no need to read their length
1072+ || place. place . ty ( ) . peel_refs ( ) . is_array ( )
1073+ {
1074+ // No read necessary
1075+ } else {
1076+ read_discriminant ( ) ;
1077+ }
1078+ }
1079+ PatKind :: Or ( _)
1080+ | PatKind :: Box ( _)
1081+ | PatKind :: Ref ( ..)
1082+ | PatKind :: Guard ( ..)
1083+ | PatKind :: Tuple ( ..)
1084+ | PatKind :: Wild
1085+ | PatKind :: Err ( _) => {
1086+ // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
1087+ // are made later as these patterns contains subpatterns.
1088+ // If the PatKind is Wild or Err, they are made when processing the other patterns
1089+ // being examined
10161090 }
1017- _ => { }
10181091 }
10191092
10201093 Ok ( ( ) )
@@ -1849,6 +1922,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18491922 Ok ( ( ) )
18501923 }
18511924
1925+ /// Checks whether a type has multiple variants, and therefore, whether a
1926+ /// read of the discriminant might be necessary. Note that the actual MIR
1927+ /// builder code does a more specific check, filtering out variants that
1928+ /// happen to be uninhabited.
1929+ ///
1930+ /// Here, we cannot perform such an accurate checks, because querying
1931+ /// whether a type is inhabited requires that it has been fully inferred,
1932+ /// which cannot be guaranteed at this point.
18521933 fn is_multivariant_adt ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
18531934 if let ty:: Adt ( def, _) = self . cx . try_structurally_resolve_type ( span, ty) . kind ( ) {
18541935 // Note that if a non-exhaustive SingleVariant is defined in another crate, we need
0 commit comments