@@ -242,7 +242,7 @@ use rustc_hir::{HirId, RangeEnd};
242242use rustc_middle:: mir:: interpret:: { truncate, AllocId , ConstValue , Pointer , Scalar } ;
243243use rustc_middle:: mir:: Field ;
244244use rustc_middle:: ty:: layout:: IntegerExt ;
245- use rustc_middle:: ty:: { self , Const , Ty , TyCtxt , TypeFoldable , VariantDef } ;
245+ use rustc_middle:: ty:: { self , Const , Ty , TyCtxt , TypeFoldable } ;
246246use rustc_session:: lint;
247247use rustc_span:: { Span , DUMMY_SP } ;
248248use rustc_target:: abi:: { Integer , Size , VariantIdx } ;
@@ -591,7 +591,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
591591 }
592592 }
593593
594- // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
594+ /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
595595 crate fn is_foreign_non_exhaustive_enum ( & self , ty : Ty < ' tcx > ) -> bool {
596596 match ty. kind {
597597 ty:: Adt ( def, ..) => {
@@ -600,15 +600,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
600600 _ => false ,
601601 }
602602 }
603-
604- // Returns whether the given variant is from another crate and has its fields declared
605- // `#[non_exhaustive]`.
606- fn is_foreign_non_exhaustive_variant ( & self , ty : Ty < ' tcx > , variant : & VariantDef ) -> bool {
607- match ty. kind {
608- ty:: Adt ( def, ..) => variant. is_field_list_non_exhaustive ( ) && !def. did . is_local ( ) ,
609- _ => false ,
610- }
611- }
612603}
613604
614605#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -876,7 +867,7 @@ impl<'tcx> Constructor<'tcx> {
876867 ty : Ty < ' tcx > ,
877868 fields : Fields < ' p , ' tcx > ,
878869 ) -> Pat < ' tcx > {
879- let mut subpatterns = fields. into_iter ( ) . cloned ( ) ;
870+ let mut subpatterns = fields. all_patterns ( ) ;
880871
881872 let pat = match self {
882873 Single | Variant ( _) => match ty. kind {
@@ -945,12 +936,45 @@ impl<'tcx> Constructor<'tcx> {
945936 }
946937}
947938
939+ #[ derive( Debug , Copy , Clone ) ]
940+ enum FilteredField < ' p , ' tcx > {
941+ Kept ( & ' p Pat < ' tcx > ) ,
942+ Hidden ( Ty < ' tcx > ) ,
943+ }
944+
945+ impl < ' p , ' tcx > FilteredField < ' p , ' tcx > {
946+ fn kept ( self ) -> Option < & ' p Pat < ' tcx > > {
947+ match self {
948+ FilteredField :: Kept ( p) => Some ( p) ,
949+ FilteredField :: Hidden ( _) => None ,
950+ }
951+ }
952+
953+ fn to_pattern ( self ) -> Pat < ' tcx > {
954+ match self {
955+ FilteredField :: Kept ( p) => p. clone ( ) ,
956+ FilteredField :: Hidden ( ty) => Pat :: wildcard_from_ty ( ty) ,
957+ }
958+ }
959+ }
960+
948961/// A value can be decomposed into a constructor applied to some fields. This struct represents
949962/// those fields, generalized to allow patterns in each field. See also `Constructor`.
963+ ///
964+ /// If a private or `non_exhaustive` field is uninhabited, the code mustn't observe that it is
965+ /// uninhabited. For that, we filter these fields out of the matrix. This is subtle because we
966+ /// still need to have those fields back when going to/from a `Pat`. Mot of this is handled
967+ /// automatically in `Fields`, but when constructing or deconstructing fields you need to use the
968+ /// correct method. As a rule, when going to/from the matrix, use the filtered field list; when
969+ /// going to/from `Pat`, use the full field list.
970+ /// This filtering is uncommon in practice, because uninhabited fields are rarely used.
950971#[ derive( Debug , Clone ) ]
951972enum Fields < ' p , ' tcx > {
973+ /// Lists of patterns that don't contain any filtered fields.
952974 Slice ( & ' p [ Pat < ' tcx > ] ) ,
953975 Vec ( SmallVec < [ & ' p Pat < ' tcx > ; 2 ] > ) ,
976+ /// Patterns where some of the fields need to be hidden.
977+ Filtered ( SmallVec < [ FilteredField < ' p , ' tcx > ; 2 ] > ) ,
954978}
955979
956980impl < ' p , ' tcx > Fields < ' p , ' tcx > {
@@ -964,7 +988,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
964988 Fields :: Slice ( std:: slice:: from_ref ( pat) )
965989 }
966990
967- fn from_vec ( pats : SmallVec < [ & ' p Pat < ' tcx > ; 2 ] > ) -> Self {
991+ /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't
992+ /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`.
993+ fn from_vec_unfiltered ( pats : SmallVec < [ & ' p Pat < ' tcx > ; 2 ] > ) -> Self {
968994 Fields :: Vec ( pats)
969995 }
970996
@@ -999,26 +1025,40 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
9991025 Fields :: from_single_pattern ( wildcard_from_ty ( substs. type_at ( 0 ) ) )
10001026 } else {
10011027 let variant = & adt. variants [ constructor. variant_index_for_adt ( cx, adt) ] ;
1002- let is_non_exhaustive = cx. is_foreign_non_exhaustive_variant ( ty, variant) ;
1003- Fields :: wildcards_from_tys (
1004- cx,
1005- variant. fields . iter ( ) . map ( |field| {
1006- let ty = field. ty ( cx. tcx , substs) ;
1007- let is_visible = adt. is_enum ( )
1008- || field. vis . is_accessible_from ( cx. module , cx. tcx ) ;
1009- let is_inhabited = !cx. is_uninhabited ( ty) ;
1010- // Treat all uninhabited non-visible fields as `TyErr`. They can't
1011- // appear in any other pattern from this match (because they are
1012- // private), so their type does not matter - but we don't want
1013- // to know they are uninhabited.
1014- // Also treat all uninhabited types in non-exhaustive variants as
1015- // `TyErr`.
1016- let allowed_to_inspect =
1017- is_inhabited || ( is_visible && !is_non_exhaustive) ;
1018-
1019- if allowed_to_inspect { ty } else { cx. tcx . types . err }
1020- } ) ,
1021- )
1028+ // Whether we must not match the fields of this variant exhaustively.
1029+ let is_non_exhaustive =
1030+ variant. is_field_list_non_exhaustive ( ) && !adt. did . is_local ( ) ;
1031+ let field_tys = variant. fields . iter ( ) . map ( |field| field. ty ( cx. tcx , substs) ) ;
1032+ // In the following cases, we don't need to filter out any fields. This is
1033+ // the vast majority of real cases, since uninhabited fields are uncommon.
1034+ let has_no_hidden_fields = ( adt. is_enum ( ) && !is_non_exhaustive)
1035+ || !field_tys. clone ( ) . any ( |ty| cx. is_uninhabited ( ty) ) ;
1036+
1037+ if has_no_hidden_fields {
1038+ Fields :: wildcards_from_tys ( cx, field_tys)
1039+ } else {
1040+ let fields = variant
1041+ . fields
1042+ . iter ( )
1043+ . map ( |field| {
1044+ let ty = field. ty ( cx. tcx , substs) ;
1045+ let is_visible = adt. is_enum ( )
1046+ || field. vis . is_accessible_from ( cx. module , cx. tcx ) ;
1047+ let is_uninhabited = cx. is_uninhabited ( ty) ;
1048+
1049+ // In the cases of either a `#[non_exhaustive]` field list
1050+ // or a non-public field, we hide uninhabited fields in
1051+ // order not to reveal the uninhabitedness of the whole
1052+ // variant.
1053+ if is_uninhabited && ( !is_visible || is_non_exhaustive) {
1054+ FilteredField :: Hidden ( ty)
1055+ } else {
1056+ FilteredField :: Kept ( wildcard_from_ty ( ty) )
1057+ }
1058+ } )
1059+ . collect ( ) ;
1060+ Fields :: Filtered ( fields)
1061+ }
10221062 }
10231063 }
10241064 _ => Fields :: empty ( ) ,
@@ -1038,29 +1078,30 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10381078 match self {
10391079 Fields :: Slice ( pats) => pats. len ( ) ,
10401080 Fields :: Vec ( pats) => pats. len ( ) ,
1081+ Fields :: Filtered ( fields) => fields. iter ( ) . filter ( |p| p. kept ( ) . is_some ( ) ) . count ( ) ,
10411082 }
10421083 }
10431084
1044- fn into_iter ( self ) -> impl Iterator < Item = & ' p Pat < ' tcx > > {
1045- let pats: SmallVec < _ > = match self {
1046- Fields :: Slice ( pats) => pats. iter ( ) . collect ( ) ,
1047- Fields :: Vec ( pats) => pats,
1085+ /// Returns the complete list of patterns, including hidden fields.
1086+ fn all_patterns ( self ) -> impl Iterator < Item = Pat < ' tcx > > {
1087+ let pats: SmallVec < [ _ ; 2 ] > = match self {
1088+ Fields :: Slice ( pats) => pats. iter ( ) . cloned ( ) . collect ( ) ,
1089+ Fields :: Vec ( pats) => pats. into_iter ( ) . cloned ( ) . collect ( ) ,
1090+ Fields :: Filtered ( fields) => {
1091+ // We don't skip any fields here.
1092+ fields. into_iter ( ) . map ( |p| p. to_pattern ( ) ) . collect ( )
1093+ }
10481094 } ;
10491095 pats. into_iter ( )
10501096 }
10511097
10521098 /// Overrides some of the fields with the provided patterns.
10531099 fn replace_with_fieldpats (
10541100 & self ,
1055- cx : & MatchCheckCtxt < ' p , ' tcx > ,
10561101 new_pats : impl IntoIterator < Item = & ' p FieldPat < ' tcx > > ,
1057- is_non_exhaustive : bool ,
10581102 ) -> Self {
10591103 self . replace_fields_indexed (
1060- new_pats
1061- . into_iter ( )
1062- . map ( |pat| ( pat. field . index ( ) , & pat. pattern ) )
1063- . filter ( |( _, pat) | !( is_non_exhaustive && cx. is_uninhabited ( pat. ty ) ) ) ,
1104+ new_pats. into_iter ( ) . map ( |pat| ( pat. field . index ( ) , & pat. pattern ) ) ,
10641105 )
10651106 }
10661107
@@ -1080,6 +1121,13 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10801121 pats[ i] = pat
10811122 }
10821123 }
1124+ Fields :: Filtered ( fields) => {
1125+ for ( i, pat) in new_pats {
1126+ if let FilteredField :: Kept ( p) = & mut fields[ i] {
1127+ * p = pat
1128+ }
1129+ }
1130+ }
10831131 Fields :: Slice ( _) => unreachable ! ( ) ,
10841132 }
10851133 fields
@@ -1093,7 +1141,21 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
10931141 pats : impl IntoIterator < Item = Pat < ' tcx > > ,
10941142 ) -> Self {
10951143 let pats: & [ _ ] = cx. pattern_arena . alloc_from_iter ( pats) ;
1096- Fields :: Slice ( pats)
1144+
1145+ match self {
1146+ Fields :: Filtered ( fields) => {
1147+ let mut pats = pats. iter ( ) ;
1148+ let mut fields = fields. clone ( ) ;
1149+ for f in & mut fields {
1150+ if let FilteredField :: Kept ( p) = f {
1151+ // We take one input pattern for each `Kept` field, in order.
1152+ * p = pats. next ( ) . unwrap ( ) ;
1153+ }
1154+ }
1155+ Fields :: Filtered ( fields)
1156+ }
1157+ _ => Fields :: Slice ( pats) ,
1158+ }
10971159 }
10981160
10991161 fn push_on_patstack ( self , stack : & [ & ' p Pat < ' tcx > ] ) -> PatStack < ' p , ' tcx > {
@@ -1103,6 +1165,10 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11031165 pats. extend_from_slice ( stack) ;
11041166 pats
11051167 }
1168+ Fields :: Filtered ( fields) => {
1169+ // We skip hidden fields here
1170+ fields. into_iter ( ) . filter_map ( |p| p. kept ( ) ) . chain ( stack. iter ( ) . copied ( ) ) . collect ( )
1171+ }
11061172 } ;
11071173 PatStack :: from_vec ( pats)
11081174 }
@@ -2411,12 +2477,11 @@ fn specialize_one_pattern<'p, 'tcx>(
24112477 if constructor != & Variant ( variant. def_id ) {
24122478 return None ;
24132479 }
2414- let is_non_exhaustive = cx. is_foreign_non_exhaustive_variant ( pat. ty , variant) ;
2415- Some ( ctor_wild_subpatterns. replace_with_fieldpats ( cx, subpatterns, is_non_exhaustive) )
2480+ Some ( ctor_wild_subpatterns. replace_with_fieldpats ( subpatterns) )
24162481 }
24172482
24182483 PatKind :: Leaf { ref subpatterns } => {
2419- Some ( ctor_wild_subpatterns. replace_with_fieldpats ( cx , subpatterns, false ) )
2484+ Some ( ctor_wild_subpatterns. replace_with_fieldpats ( subpatterns) )
24202485 }
24212486
24222487 PatKind :: Deref { ref subpattern } => Some ( Fields :: from_single_pattern ( subpattern) ) ,
@@ -2485,7 +2550,7 @@ fn specialize_one_pattern<'p, 'tcx>(
24852550 Some ( & * cx. pattern_arena . alloc ( pattern) )
24862551 } )
24872552 . collect :: < Option < _ > > ( ) ?;
2488- Some ( Fields :: from_vec ( pats) )
2553+ Some ( Fields :: from_vec_unfiltered ( pats) )
24892554 }
24902555
24912556 PatKind :: Constant { .. } | PatKind :: Range { .. } => {
0 commit comments