@@ -948,6 +948,21 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
948
948
}
949
949
950
950
/// 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.
951
966
fn walk_pat (
952
967
& self ,
953
968
discr_place : & PlaceWithHirId < ' tcx > ,
@@ -958,6 +973,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
958
973
959
974
let tcx = self . cx . tcx ( ) ;
960
975
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
+
961
981
match pat. kind {
962
982
PatKind :: Binding ( _, canonical_id, ..) => {
963
983
debug ! ( "walk_pat: binding place={:?} pat={:?}" , place, pat) ;
@@ -980,11 +1000,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
980
1000
// binding when lowering pattern guards to ensure that the guard does not
981
1001
// modify the scrutinee.
982
1002
if has_guard {
983
- self . delegate . borrow_mut ( ) . borrow (
984
- place,
985
- discr_place. hir_id ,
986
- BorrowKind :: Immutable ,
987
- ) ;
1003
+ read_discriminant ( ) ;
988
1004
}
989
1005
990
1006
// 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
1016
1032
PatKind :: Never => {
1017
1033
// A `!` pattern always counts as an immutable read of the discriminant,
1018
1034
// 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
1024
1098
}
1025
- _ => { }
1026
1099
}
1027
1100
1028
1101
Ok ( ( ) )
@@ -1858,6 +1931,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
1858
1931
Ok ( ( ) )
1859
1932
}
1860
1933
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.
1861
1942
fn is_multivariant_adt ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
1862
1943
if let ty:: Adt ( def, _) = self . cx . try_structurally_resolve_type ( span, ty) . kind ( ) {
1863
1944
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
0 commit comments