307
307
308
308
use self :: ArmType :: * ;
309
309
use self :: Usefulness :: * ;
310
- use super :: deconstruct_pat:: { Constructor , ConstructorSet , DeconstructedPat , WitnessPat } ;
310
+ use super :: deconstruct_pat:: {
311
+ Constructor , ConstructorSet , DeconstructedPat , SplitConstructorSet , WitnessPat ,
312
+ } ;
311
313
use crate :: errors:: { NonExhaustiveOmittedPattern , Uncovered } ;
312
314
313
315
use rustc_data_structures:: captures:: Captures ;
@@ -875,19 +877,85 @@ fn is_useful<'p, 'tcx>(
875
877
ret
876
878
}
877
879
880
+ /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
881
+ /// inspect the same subvalue".
882
+ /// This is used to traverse patterns column-by-column for lints. Despite similarities with
883
+ /// `is_useful`, this is a different traversal. Notably this is linear in the depth of patterns,
884
+ /// whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
885
+ #[ derive( Debug ) ]
886
+ struct PatternColumn < ' p , ' tcx > {
887
+ patterns : Vec < & ' p DeconstructedPat < ' p , ' tcx > > ,
888
+ }
889
+
890
+ impl < ' p , ' tcx > PatternColumn < ' p , ' tcx > {
891
+ fn new ( patterns : Vec < & ' p DeconstructedPat < ' p , ' tcx > > ) -> Self {
892
+ Self { patterns }
893
+ }
894
+
895
+ fn is_empty ( & self ) -> bool {
896
+ self . patterns . is_empty ( )
897
+ }
898
+ fn head_ty ( & self ) -> Ty < ' tcx > {
899
+ self . patterns [ 0 ] . ty ( )
900
+ }
901
+
902
+ fn analyze_ctors ( & self , pcx : & PatCtxt < ' _ , ' p , ' tcx > ) -> SplitConstructorSet < ' tcx > {
903
+ let column_ctors = self . patterns . iter ( ) . map ( |p| p. ctor ( ) ) ;
904
+ ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column_ctors)
905
+ }
906
+
907
+ /// Does specialization: given a constructor, this takes the patterns from the column that match
908
+ /// the constructor, and outputs their fields.
909
+ /// This returns one column per field of the constructor. The normally all have the same length
910
+ /// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
911
+ /// which may change the lengths.
912
+ fn specialize ( & self , pcx : & PatCtxt < ' _ , ' p , ' tcx > , ctor : & Constructor < ' tcx > ) -> Vec < Self > {
913
+ let arity = ctor. arity ( pcx) ;
914
+ if arity == 0 {
915
+ return Vec :: new ( ) ;
916
+ }
917
+
918
+ // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
919
+ // columns may have different lengths in the presence of or-patterns (this is why we can't
920
+ // reuse `Matrix`).
921
+ let mut specialized_columns: Vec < _ > =
922
+ ( 0 ..arity) . map ( |_| Self { patterns : Vec :: new ( ) } ) . collect ( ) ;
923
+ let relevant_patterns =
924
+ self . patterns . iter ( ) . filter ( |pat| ctor. is_covered_by ( pcx, pat. ctor ( ) ) ) ;
925
+ for pat in relevant_patterns {
926
+ let specialized = pat. specialize ( pcx, & ctor) ;
927
+ for ( subpat, column) in specialized. iter ( ) . zip ( & mut specialized_columns) {
928
+ if subpat. is_or_pat ( ) {
929
+ column. patterns . extend ( subpat. iter_fields ( ) )
930
+ } else {
931
+ column. patterns . push ( subpat)
932
+ }
933
+ }
934
+ }
935
+
936
+ assert ! (
937
+ !specialized_columns[ 0 ] . is_empty( ) ,
938
+ "ctor {ctor:?} was listed as present but isn't;
939
+ there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
940
+ ) ;
941
+ specialized_columns
942
+ }
943
+ }
944
+
878
945
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
879
- /// in a given column. This traverses patterns column-by-column, where a column is the intuitive
880
- /// notion of "subpatterns that inspect the same subvalue".
881
- /// Despite similarities with `is_useful`, this traversal is different. Notably this is linear in the
882
- /// depth of patterns, whereas `is_useful` is worst-case exponential (exhaustiveness is NP-complete).
946
+ /// in a given column.
947
+ #[ instrument( level = "debug" , skip( cx) , ret) ]
883
948
fn collect_nonexhaustive_missing_variants < ' p , ' tcx > (
884
949
cx : & MatchCheckCtxt < ' p , ' tcx > ,
885
- column : & [ & DeconstructedPat < ' p , ' tcx > ] ,
950
+ column : & PatternColumn < ' p , ' tcx > ,
886
951
) -> Vec < WitnessPat < ' tcx > > {
887
- let ty = column[ 0 ] . ty ( ) ;
952
+ if column. is_empty ( ) {
953
+ return Vec :: new ( ) ;
954
+ }
955
+ let ty = column. head_ty ( ) ;
888
956
let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
889
957
890
- let set = ConstructorSet :: for_ty ( pcx . cx , pcx . ty ) . split ( pcx , column. iter ( ) . map ( |p| p . ctor ( ) ) ) ;
958
+ let set = column. analyze_ctors ( pcx ) ;
891
959
if set. present . is_empty ( ) {
892
960
// We can't consistently handle the case where no constructors are present (since this would
893
961
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -908,35 +976,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
908
976
909
977
// Recurse into the fields.
910
978
for ctor in set. present {
911
- let arity = ctor. arity ( pcx) ;
912
- if arity == 0 {
913
- continue ;
914
- }
915
-
916
- // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
917
- // columns may have different lengths in the presence of or-patterns (this is why we can't
918
- // reuse `Matrix`).
919
- let mut specialized_columns: Vec < Vec < _ > > = ( 0 ..arity) . map ( |_| Vec :: new ( ) ) . collect ( ) ;
920
- let relevant_patterns = column. iter ( ) . filter ( |pat| ctor. is_covered_by ( pcx, pat. ctor ( ) ) ) ;
921
- for pat in relevant_patterns {
922
- let specialized = pat. specialize ( pcx, & ctor) ;
923
- for ( subpat, sub_column) in specialized. iter ( ) . zip ( & mut specialized_columns) {
924
- if subpat. is_or_pat ( ) {
925
- sub_column. extend ( subpat. iter_fields ( ) )
926
- } else {
927
- sub_column. push ( subpat)
928
- }
929
- }
930
- }
931
- debug_assert ! (
932
- !specialized_columns[ 0 ] . is_empty( ) ,
933
- "ctor {ctor:?} was listed as present but isn't"
934
- ) ;
935
-
979
+ let specialized_columns = column. specialize ( pcx, & ctor) ;
936
980
let wild_pat = WitnessPat :: wild_from_ctor ( pcx, ctor) ;
937
981
for ( i, col_i) in specialized_columns. iter ( ) . enumerate ( ) {
938
982
// Compute witnesses for each column.
939
- let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i. as_slice ( ) ) ;
983
+ let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i) ;
940
984
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
941
985
// adding enough wildcards to match `arity`.
942
986
for wit in wits_for_col_i {
@@ -1029,6 +1073,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1029
1073
)
1030
1074
{
1031
1075
let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1076
+ let pat_column = PatternColumn :: new ( pat_column) ;
1032
1077
let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1033
1078
1034
1079
if !witnesses. is_empty ( ) {
0 commit comments