@@ -943,6 +943,19 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
943
943
}
944
944
945
945
/// The core driver for walking a pattern
946
+ ///
947
+ /// This should mirror how pattern-matching gets lowered to MIR, as
948
+ /// otherwise lowering will ICE when trying to resolve the upvars.
949
+ ///
950
+ /// However, it is okay to approximate it here by doing *more* accesses than
951
+ /// the actual MIR builder will, which is useful when some checks are too
952
+ /// cumbersome to perform here. For example, if after typeck it becomes
953
+ /// clear that only one variant of an enum is inhabited, and therefore a
954
+ /// read of the discriminant is not necessary, `walk_pat` will have
955
+ /// over-approximated the necessary upvar capture granularity.
956
+ ///
957
+ /// Do note that discrepancies like these do still create obscure corners
958
+ /// in the semantics of the language, and should be avoided if possible.
946
959
#[ instrument( skip( self ) , level = "debug" ) ]
947
960
fn walk_pat (
948
961
& self ,
@@ -952,6 +965,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
952
965
) -> Result < ( ) , Cx :: Error > {
953
966
let tcx = self . cx . tcx ( ) ;
954
967
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
+
955
973
match pat. kind {
956
974
PatKind :: Binding ( _, canonical_id, ..) => {
957
975
debug ! ( "walk_pat: binding place={:?} pat={:?}" , place, pat) ;
@@ -974,11 +992,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
974
992
// binding when lowering pattern guards to ensure that the guard does not
975
993
// modify the scrutinee.
976
994
if has_guard {
977
- self . delegate . borrow_mut ( ) . borrow (
978
- place,
979
- discr_place. hir_id ,
980
- BorrowKind :: Immutable ,
981
- ) ;
995
+ read_discriminant ( ) ;
982
996
}
983
997
984
998
// It is also a borrow or copy/move of the value being matched.
@@ -1014,13 +1028,71 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
1014
1028
PatKind :: Never => {
1015
1029
// A `!` pattern always counts as an immutable read of the discriminant,
1016
1030
// even in an irrefutable pattern.
1017
- self . delegate . borrow_mut ( ) . borrow (
1018
- place,
1019
- discr_place. hir_id ,
1020
- BorrowKind :: Immutable ,
1021
- ) ;
1031
+ read_discriminant ( ) ;
1032
+ }
1033
+ PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( qpath) , hir_id, span } ) => {
1034
+ // A `Path` pattern is just a name like `Foo`. This is either a
1035
+ // named constant or else it refers to an ADT variant
1036
+
1037
+ let res = self . cx . typeck_results ( ) . qpath_res ( qpath, * hir_id) ;
1038
+ match res {
1039
+ Res :: Def ( DefKind :: Const , _) | Res :: Def ( DefKind :: AssocConst , _) => {
1040
+ // Named constants have to be equated with the value
1041
+ // being matched, so that's a read of the value being matched.
1042
+ //
1043
+ // FIXME: Does the MIR code skip this read when matching on a ZST?
1044
+ // If so, we can also skip it here.
1045
+ read_discriminant ( ) ;
1046
+ }
1047
+ _ => {
1048
+ // Otherwise, this is a struct/enum variant, and so it's
1049
+ // only a read if we need to read the discriminant.
1050
+ if self . is_multivariant_adt ( place. place . ty ( ) , * span) {
1051
+ read_discriminant ( ) ;
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+ PatKind :: Expr ( _) | PatKind :: Range ( ..) => {
1057
+ // When matching against a literal or range, we need to
1058
+ // borrow the place to compare it against the pattern.
1059
+ //
1060
+ // FIXME: What if the type being matched only has one
1061
+ // possible value?
1062
+ // FIXME: What if the range is the full range of the type
1063
+ // and doesn't actually require a discriminant read?
1064
+ read_discriminant ( ) ;
1065
+ }
1066
+ PatKind :: Struct ( ..) | PatKind :: TupleStruct ( ..) => {
1067
+ if self . is_multivariant_adt ( place. place . ty ( ) , pat. span ) {
1068
+ read_discriminant ( ) ;
1069
+ }
1070
+ }
1071
+ PatKind :: Slice ( lhs, wild, rhs) => {
1072
+ // We don't need to test the length if the pattern is `[..]`
1073
+ if matches ! ( ( lhs, wild, rhs) , ( & [ ] , Some ( _) , & [ ] ) )
1074
+ // Arrays have a statically known size, so
1075
+ // there is no need to read their length
1076
+ || place. place . ty ( ) . peel_refs ( ) . is_array ( )
1077
+ {
1078
+ // No read necessary
1079
+ } else {
1080
+ read_discriminant ( ) ;
1081
+ }
1082
+ }
1083
+ PatKind :: Or ( _)
1084
+ | PatKind :: Box ( _)
1085
+ | PatKind :: Ref ( ..)
1086
+ | PatKind :: Guard ( ..)
1087
+ | PatKind :: Tuple ( ..)
1088
+ | PatKind :: Wild
1089
+ | PatKind :: Missing
1090
+ | PatKind :: Err ( _) => {
1091
+ // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
1092
+ // are made later as these patterns contains subpatterns.
1093
+ // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
1094
+ // the other patterns that are part of the match
1022
1095
}
1023
- _ => { }
1024
1096
}
1025
1097
1026
1098
Ok ( ( ) )
@@ -1888,6 +1960,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
1888
1960
}
1889
1961
}
1890
1962
1963
+ /// Checks whether a type has multiple variants, and therefore, whether a
1964
+ /// read of the discriminant might be necessary. Note that the actual MIR
1965
+ /// builder code does a more specific check, filtering out variants that
1966
+ /// happen to be uninhabited.
1967
+ ///
1968
+ /// Here, we cannot perform such an accurate checks, because querying
1969
+ /// whether a type is inhabited requires that it has been fully inferred,
1970
+ /// which cannot be guaranteed at this point.
1891
1971
fn is_multivariant_adt ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
1892
1972
if let ty:: Adt ( def, _) = self . cx . structurally_resolve_type ( span, ty) . kind ( ) {
1893
1973
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
0 commit comments