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,22 +877,84 @@ 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 ) -> Option < Ty < ' tcx > > {
899
+ self . patterns . get ( 0 ) . map ( |p| p. 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
- if column. is_empty ( ) {
952
+ let Some ( ty ) = column. head_ty ( ) else {
888
953
return Vec :: new ( ) ;
889
- }
890
- let ty = column[ 0 ] . ty ( ) ;
954
+ } ;
891
955
let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
892
956
893
- let set = ConstructorSet :: for_ty ( pcx . cx , pcx . ty ) . split ( pcx , column. iter ( ) . map ( |p| p . ctor ( ) ) ) ;
957
+ let set = column. analyze_ctors ( pcx ) ;
894
958
if set. present . is_empty ( ) {
895
959
// We can't consistently handle the case where no constructors are present (since this would
896
960
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -911,35 +975,11 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
911
975
912
976
// Recurse into the fields.
913
977
for ctor in set. present {
914
- let arity = ctor. arity ( pcx) ;
915
- if arity == 0 {
916
- continue ;
917
- }
918
-
919
- // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
920
- // columns may have different lengths in the presence of or-patterns (this is why we can't
921
- // reuse `Matrix`).
922
- let mut specialized_columns: Vec < Vec < _ > > = ( 0 ..arity) . map ( |_| Vec :: new ( ) ) . collect ( ) ;
923
- let relevant_patterns = column. iter ( ) . filter ( |pat| ctor. is_covered_by ( pcx, pat. ctor ( ) ) ) ;
924
- for pat in relevant_patterns {
925
- let specialized = pat. specialize ( pcx, & ctor) ;
926
- for ( subpat, sub_column) in specialized. iter ( ) . zip ( & mut specialized_columns) {
927
- if subpat. is_or_pat ( ) {
928
- sub_column. extend ( subpat. iter_fields ( ) )
929
- } else {
930
- sub_column. push ( subpat)
931
- }
932
- }
933
- }
934
- debug_assert ! (
935
- !specialized_columns[ 0 ] . is_empty( ) ,
936
- "ctor {ctor:?} was listed as present but isn't"
937
- ) ;
938
-
978
+ let specialized_columns = column. specialize ( pcx, & ctor) ;
939
979
let wild_pat = WitnessPat :: wild_from_ctor ( pcx, ctor) ;
940
980
for ( i, col_i) in specialized_columns. iter ( ) . enumerate ( ) {
941
981
// Compute witnesses for each column.
942
- let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i. as_slice ( ) ) ;
982
+ let wits_for_col_i = collect_nonexhaustive_missing_variants ( cx, col_i) ;
943
983
// For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
944
984
// adding enough wildcards to match `arity`.
945
985
for wit in wits_for_col_i {
@@ -1032,6 +1072,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1032
1072
)
1033
1073
{
1034
1074
let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1075
+ let pat_column = PatternColumn :: new ( pat_column) ;
1035
1076
let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1036
1077
1037
1078
if !witnesses. is_empty ( ) {
0 commit comments