@@ -10,9 +10,11 @@ use crate::const_eval::const_variant_index;
10
10
use crate :: hair:: util:: UserAnnotatedTyHelpers ;
11
11
use crate :: hair:: constant:: * ;
12
12
13
+ use rustc:: lint;
13
14
use rustc:: mir:: { Field , BorrowKind , Mutability } ;
14
15
use rustc:: mir:: { UserTypeProjection } ;
15
16
use rustc:: mir:: interpret:: { GlobalId , ConstValue , sign_extend, AllocId , Pointer } ;
17
+ use rustc:: traits:: { ObligationCause , PredicateObligation } ;
16
18
use rustc:: ty:: { self , Region , TyCtxt , AdtDef , Ty , UserType , DefIdTree } ;
17
19
use rustc:: ty:: { CanonicalUserType , CanonicalUserTypeAnnotation , CanonicalUserTypeAnnotations } ;
18
20
use rustc:: ty:: subst:: { SubstsRef , Kind } ;
@@ -23,6 +25,7 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
23
25
use rustc:: hir:: ptr:: P ;
24
26
25
27
use rustc_data_structures:: indexed_vec:: Idx ;
28
+ use rustc_data_structures:: fx:: FxHashSet ;
26
29
27
30
use std:: cmp:: Ordering ;
28
31
use std:: fmt;
@@ -332,6 +335,7 @@ pub struct PatternContext<'a, 'tcx> {
332
335
pub tables : & ' a ty:: TypeckTables < ' tcx > ,
333
336
pub substs : SubstsRef < ' tcx > ,
334
337
pub errors : Vec < PatternError > ,
338
+ include_lint_checks : bool ,
335
339
}
336
340
337
341
impl < ' a , ' tcx > Pattern < ' tcx > {
@@ -363,10 +367,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
363
367
param_env : param_env_and_substs. param_env ,
364
368
tables,
365
369
substs : param_env_and_substs. value ,
366
- errors : vec ! [ ]
370
+ errors : vec ! [ ] ,
371
+ include_lint_checks : false ,
367
372
}
368
373
}
369
374
375
+ pub fn include_lint_checks ( & mut self ) -> & mut Self {
376
+ self . include_lint_checks = true ;
377
+ self
378
+ }
379
+
370
380
pub fn lower_pattern ( & mut self , pat : & ' tcx hir:: Pat ) -> Pattern < ' tcx > {
371
381
// When implicit dereferences have been inserted in this pattern, the unadjusted lowered
372
382
// pattern has the type that results *after* dereferencing. For example, in this code:
@@ -942,23 +952,94 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
942
952
943
953
/// Converts an evaluated constant to a pattern (if possible).
944
954
/// This means aggregate values (like structs and enums) are converted
945
- /// to a pattern that matches the value (as if you'd compared via equality).
955
+ /// to a pattern that matches the value (as if you'd compared via structural equality).
946
956
fn const_to_pat (
947
957
& self ,
948
958
instance : ty:: Instance < ' tcx > ,
949
959
cv : & ' tcx ty:: Const < ' tcx > ,
950
960
id : hir:: HirId ,
951
961
span : Span ,
952
962
) -> Pattern < ' tcx > {
963
+ // This method is just a warpper handling a validity check; the heavy lifting is
964
+ // performed by the recursive const_to_pat_inner method, which is not meant to be
965
+ // invoked except by this method.
966
+ //
967
+ // once indirect_structural_match is a full fledged error, this
968
+ // level of indirection can be eliminated
969
+
953
970
debug ! ( "const_to_pat: cv={:#?} id={:?}" , cv, id) ;
954
- let adt_subpattern = |i, variant_opt| {
971
+ debug ! ( "const_to_pat: cv.ty={:?} span={:?}" , cv. ty, span) ;
972
+
973
+ let mut saw_error = false ;
974
+ let inlined_const_as_pat = self . const_to_pat_inner ( instance, cv, id, span, & mut saw_error) ;
975
+
976
+ if self . include_lint_checks && !saw_error {
977
+ // If we were able to successfully convert the const to some pat, double-check
978
+ // that the type of the const obeys `#[structural_match]` constraint.
979
+ if let Some ( adt_def) = search_for_adt_without_structural_match ( self . tcx , cv. ty ) {
980
+
981
+ let path = self . tcx . def_path_str ( adt_def. did ) ;
982
+ let msg = format ! (
983
+ "to use a constant of type `{}` in a pattern, \
984
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
985
+ path,
986
+ path,
987
+ ) ;
988
+
989
+ // before issuing lint, double-check there even *is* a
990
+ // semantic PartialEq for us to dispatch to.
991
+ //
992
+ // (If there isn't, then we can safely issue a hard
993
+ // error, because that's never worked, due to compiler
994
+ // using PartialEq::eq in this scenario in the past.)
995
+
996
+ let ty_is_partial_eq: bool = {
997
+ let partial_eq_trait_id = self . tcx . lang_items ( ) . eq_trait ( ) . unwrap ( ) ;
998
+ let obligation: PredicateObligation < ' _ > =
999
+ self . tcx . predicate_for_trait_def ( self . param_env ,
1000
+ ObligationCause :: misc ( span, id) ,
1001
+ partial_eq_trait_id,
1002
+ 0 ,
1003
+ cv. ty ,
1004
+ & [ ] ) ;
1005
+ self . tcx
1006
+ . infer_ctxt ( )
1007
+ . enter ( |infcx| infcx. predicate_may_hold ( & obligation) )
1008
+ } ;
1009
+
1010
+ if !ty_is_partial_eq {
1011
+ // span_fatal avoids ICE from resolution of non-existent method (rare case).
1012
+ self . tcx . sess . span_fatal ( span, & msg) ;
1013
+ } else {
1014
+ self . tcx . lint_hir ( lint:: builtin:: INDIRECT_STRUCTURAL_MATCH , id, span, & msg) ;
1015
+ }
1016
+ }
1017
+ }
1018
+
1019
+ inlined_const_as_pat
1020
+ }
1021
+
1022
+ /// Recursive helper for `const_to_pat`; invoke that (instead of calling this directly).
1023
+ fn const_to_pat_inner (
1024
+ & self ,
1025
+ instance : ty:: Instance < ' tcx > ,
1026
+ cv : & ' tcx ty:: Const < ' tcx > ,
1027
+ id : hir:: HirId ,
1028
+ span : Span ,
1029
+ // This tracks if we signal some hard error for a given const
1030
+ // value, so that we will not subsequently issue an irrelevant
1031
+ // lint for the same const value.
1032
+ saw_const_match_error : & mut bool ,
1033
+ ) -> Pattern < ' tcx > {
1034
+
1035
+ let mut adt_subpattern = |i, variant_opt| {
955
1036
let field = Field :: new ( i) ;
956
1037
let val = crate :: const_eval:: const_field (
957
1038
self . tcx , self . param_env , variant_opt, field, cv
958
1039
) ;
959
- self . const_to_pat ( instance, val, id, span)
1040
+ self . const_to_pat_inner ( instance, val, id, span, saw_const_match_error )
960
1041
} ;
961
- let adt_subpatterns = |n, variant_opt| {
1042
+ let mut adt_subpatterns = |n, variant_opt| {
962
1043
( 0 ..n) . map ( |i| {
963
1044
let field = Field :: new ( i) ;
964
1045
FieldPattern {
@@ -967,7 +1048,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
967
1048
}
968
1049
} ) . collect :: < Vec < _ > > ( )
969
1050
} ;
970
- debug ! ( "const_to_pat: cv.ty={:?} span={:?}" , cv. ty, span) ;
1051
+
1052
+
971
1053
let kind = match cv. ty . sty {
972
1054
ty:: Float ( _) => {
973
1055
self . tcx . lint_hir (
@@ -982,9 +1064,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
982
1064
}
983
1065
ty:: Adt ( adt_def, _) if adt_def. is_union ( ) => {
984
1066
// Matching on union fields is unsafe, we can't hide it in constants
1067
+ * saw_const_match_error = true ;
985
1068
self . tcx . sess . span_err ( span, "cannot use unions in constant patterns" ) ;
986
1069
PatternKind :: Wild
987
1070
}
1071
+ // keep old code until future-compat upgraded to errors.
988
1072
ty:: Adt ( adt_def, _) if !self . tcx . has_attr ( adt_def. did , sym:: structural_match) => {
989
1073
let path = self . tcx . def_path_str ( adt_def. did ) ;
990
1074
let msg = format ! (
@@ -993,9 +1077,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
993
1077
path,
994
1078
path,
995
1079
) ;
1080
+ * saw_const_match_error = true ;
996
1081
self . tcx . sess . span_err ( span, & msg) ;
997
1082
PatternKind :: Wild
998
1083
}
1084
+ // keep old code until future-compat upgraded to errors.
999
1085
ty:: Ref ( _, ty:: TyS { sty : ty:: Adt ( adt_def, _) , .. } , _)
1000
1086
if !self . tcx . has_attr ( adt_def. did , sym:: structural_match) => {
1001
1087
// HACK(estebank): Side-step ICE #53708, but anything other than erroring here
@@ -1007,6 +1093,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
1007
1093
path,
1008
1094
path,
1009
1095
) ;
1096
+ * saw_const_match_error = true ;
1010
1097
self . tcx . sess . span_err ( span, & msg) ;
1011
1098
PatternKind :: Wild
1012
1099
}
@@ -1058,6 +1145,120 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
1058
1145
}
1059
1146
}
1060
1147
1148
+ /// This method traverses the structure of `ty`, trying to find an
1149
+ /// instance of an ADT (i.e. struct or enum) that was declared without
1150
+ /// the `#[structural_match]` attribute.
1151
+ ///
1152
+ /// The "structure of a type" includes all components that would be
1153
+ /// considered when doing a pattern match on a constant of that
1154
+ /// type.
1155
+ ///
1156
+ /// * This means this method descends into fields of structs/enums,
1157
+ /// and also descends into the inner type `T` of `&T` and `&mut T`
1158
+ ///
1159
+ /// * The traversal doesn't dereference unsafe pointers (`*const T`,
1160
+ /// `*mut T`), and it does not visit the type arguments of an
1161
+ /// instantiated generic like `PhantomData<T>`.
1162
+ ///
1163
+ /// The reason we do this search is Rust currently require all ADT's
1164
+ /// reachable from a constant's type to be annotated with
1165
+ /// `#[structural_match]`, an attribute which essentially says that
1166
+ /// the implementation of `PartialEq::eq` behaves *equivalently* to a
1167
+ /// comparison against the unfolded structure.
1168
+ ///
1169
+ /// For more background on why Rust has this requirement, and issues
1170
+ /// that arose when the requirement was not enforced completely, see
1171
+ /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
1172
+ fn search_for_adt_without_structural_match < ' tcx > ( tcx : TyCtxt < ' tcx > ,
1173
+ ty : Ty < ' tcx > )
1174
+ -> Option < & ' tcx AdtDef >
1175
+ {
1176
+ // Import here (not mod level), because `TypeFoldable::fold_with`
1177
+ // conflicts with `PatternFoldable::fold_with`
1178
+ use crate :: rustc:: ty:: fold:: TypeVisitor ;
1179
+ use crate :: rustc:: ty:: TypeFoldable ;
1180
+
1181
+ let mut search = Search { tcx, found : None , seen : FxHashSet :: default ( ) } ;
1182
+ ty. visit_with ( & mut search) ;
1183
+ return search. found ;
1184
+
1185
+ struct Search < ' tcx > {
1186
+ tcx : TyCtxt < ' tcx > ,
1187
+
1188
+ // records the first ADT we find without `#[structural_match`
1189
+ found : Option < & ' tcx AdtDef > ,
1190
+
1191
+ // tracks ADT's previously encountered during search, so that
1192
+ // we will not recur on them again.
1193
+ seen : FxHashSet < & ' tcx AdtDef > ,
1194
+ }
1195
+
1196
+ impl < ' tcx > TypeVisitor < ' tcx > for Search < ' tcx > {
1197
+ fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> bool {
1198
+ debug ! ( "Search visiting ty: {:?}" , ty) ;
1199
+
1200
+ let ( adt_def, substs) = match ty. sty {
1201
+ ty:: Adt ( adt_def, substs) => ( adt_def, substs) ,
1202
+ ty:: RawPtr ( ..) => {
1203
+ // `#[structural_match]` ignores substructure of
1204
+ // `*const _`/`*mut _`, so skip super_visit_with
1205
+
1206
+ // (But still tell caller to continue search.)
1207
+ return false ;
1208
+ }
1209
+ ty:: Array ( _, n) if n. assert_usize ( self . tcx ) == Some ( 0 ) => {
1210
+ // rust-lang/rust#62336: ignore type of contents
1211
+ // for empty array.
1212
+ return false ;
1213
+ }
1214
+ _ => {
1215
+ ty. super_visit_with ( self ) ;
1216
+ return false ;
1217
+ }
1218
+ } ;
1219
+
1220
+ if !self . tcx . has_attr ( adt_def. did , sym:: structural_match) {
1221
+ self . found = Some ( & adt_def) ;
1222
+ debug ! ( "Search found adt_def: {:?}" , adt_def) ;
1223
+ return true // Halt visiting!
1224
+ }
1225
+
1226
+ if self . seen . contains ( adt_def) {
1227
+ debug ! ( "Search already seen adt_def: {:?}" , adt_def) ;
1228
+ // let caller continue its search
1229
+ return false ;
1230
+ }
1231
+
1232
+ self . seen . insert ( adt_def) ;
1233
+
1234
+ // `#[structural_match]` does not care about the
1235
+ // instantiation of the generics in an ADT (it
1236
+ // instead looks directly at its fields outside
1237
+ // this match), so we skip super_visit_with.
1238
+ //
1239
+ // (Must not recur on substs for `PhantomData<T>` cf
1240
+ // rust-lang/rust#55028 and rust-lang/rust#55837; but also
1241
+ // want to skip substs when only uses of generic are
1242
+ // behind unsafe pointers `*const T`/`*mut T`.)
1243
+
1244
+ // even though we skip super_visit_with, we must recur on
1245
+ // fields of ADT.
1246
+ let tcx = self . tcx ;
1247
+ for field_ty in adt_def. all_fields ( ) . map ( |field| field. ty ( tcx, substs) ) {
1248
+ if field_ty. visit_with ( self ) {
1249
+ // found an ADT without `#[structural_match]`; halt visiting!
1250
+ assert ! ( self . found. is_some( ) ) ;
1251
+ return true ;
1252
+ }
1253
+ }
1254
+
1255
+ // Even though we do not want to recur on substs, we do
1256
+ // want our caller to continue its own search.
1257
+ false
1258
+ }
1259
+ }
1260
+ }
1261
+
1061
1262
impl UserAnnotatedTyHelpers < ' tcx > for PatternContext < ' _ , ' tcx > {
1062
1263
fn tcx ( & self ) -> TyCtxt < ' tcx > {
1063
1264
self . tcx
0 commit comments