@@ -10,9 +10,11 @@ use crate::const_eval::const_variant_index;
1010use crate :: hair:: util:: UserAnnotatedTyHelpers ;
1111use crate :: hair:: constant:: * ;
1212
13+ use rustc:: lint;
1314use rustc:: mir:: { Field , BorrowKind , Mutability } ;
1415use rustc:: mir:: { UserTypeProjection } ;
1516use rustc:: mir:: interpret:: { GlobalId , ConstValue , sign_extend, AllocId , Pointer } ;
17+ use rustc:: traits:: { ObligationCause , PredicateObligation } ;
1618use rustc:: ty:: { self , Region , TyCtxt , AdtDef , Ty , UserType , DefIdTree } ;
1719use rustc:: ty:: { CanonicalUserType , CanonicalUserTypeAnnotation , CanonicalUserTypeAnnotations } ;
1820use rustc:: ty:: subst:: { SubstsRef , Kind } ;
@@ -23,6 +25,7 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
2325use rustc:: hir:: ptr:: P ;
2426
2527use rustc_data_structures:: indexed_vec:: Idx ;
28+ use rustc_data_structures:: fx:: FxHashSet ;
2629
2730use std:: cmp:: Ordering ;
2831use std:: fmt;
@@ -332,6 +335,7 @@ pub struct PatternContext<'a, 'tcx> {
332335 pub tables : & ' a ty:: TypeckTables < ' tcx > ,
333336 pub substs : SubstsRef < ' tcx > ,
334337 pub errors : Vec < PatternError > ,
338+ include_lint_checks : bool ,
335339}
336340
337341impl < ' a , ' tcx > Pattern < ' tcx > {
@@ -363,10 +367,16 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
363367 param_env : param_env_and_substs. param_env ,
364368 tables,
365369 substs : param_env_and_substs. value ,
366- errors : vec ! [ ]
370+ errors : vec ! [ ] ,
371+ include_lint_checks : false ,
367372 }
368373 }
369374
375+ pub fn include_lint_checks ( & mut self ) -> & mut Self {
376+ self . include_lint_checks = true ;
377+ self
378+ }
379+
370380 pub fn lower_pattern ( & mut self , pat : & ' tcx hir:: Pat ) -> Pattern < ' tcx > {
371381 // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
372382 // pattern has the type that results *after* dereferencing. For example, in this code:
@@ -942,23 +952,94 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
942952
943953 /// Converts an evaluated constant to a pattern (if possible).
944954 /// 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).
946956 fn const_to_pat (
947957 & self ,
948958 instance : ty:: Instance < ' tcx > ,
949959 cv : & ' tcx ty:: Const < ' tcx > ,
950960 id : hir:: HirId ,
951961 span : Span ,
952962 ) -> 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+
953970 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| {
9551036 let field = Field :: new ( i) ;
9561037 let val = crate :: const_eval:: const_field (
9571038 self . tcx , self . param_env , variant_opt, field, cv
9581039 ) ;
959- self . const_to_pat ( instance, val, id, span)
1040+ self . const_to_pat_inner ( instance, val, id, span, saw_const_match_error )
9601041 } ;
961- let adt_subpatterns = |n, variant_opt| {
1042+ let mut adt_subpatterns = |n, variant_opt| {
9621043 ( 0 ..n) . map ( |i| {
9631044 let field = Field :: new ( i) ;
9641045 FieldPattern {
@@ -967,7 +1048,8 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9671048 }
9681049 } ) . collect :: < Vec < _ > > ( )
9691050 } ;
970- debug ! ( "const_to_pat: cv.ty={:?} span={:?}" , cv. ty, span) ;
1051+
1052+
9711053 let kind = match cv. ty . sty {
9721054 ty:: Float ( _) => {
9731055 self . tcx . lint_hir (
@@ -982,9 +1064,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9821064 }
9831065 ty:: Adt ( adt_def, _) if adt_def. is_union ( ) => {
9841066 // Matching on union fields is unsafe, we can't hide it in constants
1067+ * saw_const_match_error = true ;
9851068 self . tcx . sess . span_err ( span, "cannot use unions in constant patterns" ) ;
9861069 PatternKind :: Wild
9871070 }
1071+ // keep old code until future-compat upgraded to errors.
9881072 ty:: Adt ( adt_def, _) if !self . tcx . has_attr ( adt_def. did , sym:: structural_match) => {
9891073 let path = self . tcx . def_path_str ( adt_def. did ) ;
9901074 let msg = format ! (
@@ -993,9 +1077,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
9931077 path,
9941078 path,
9951079 ) ;
1080+ * saw_const_match_error = true ;
9961081 self . tcx . sess . span_err ( span, & msg) ;
9971082 PatternKind :: Wild
9981083 }
1084+ // keep old code until future-compat upgraded to errors.
9991085 ty:: Ref ( _, ty:: TyS { sty : ty:: Adt ( adt_def, _) , .. } , _)
10001086 if !self . tcx . has_attr ( adt_def. did , sym:: structural_match) => {
10011087 // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
@@ -1007,6 +1093,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
10071093 path,
10081094 path,
10091095 ) ;
1096+ * saw_const_match_error = true ;
10101097 self . tcx . sess . span_err ( span, & msg) ;
10111098 PatternKind :: Wild
10121099 }
@@ -1058,6 +1145,120 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
10581145 }
10591146}
10601147
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+
10611262impl UserAnnotatedTyHelpers < ' tcx > for PatternContext < ' _ , ' tcx > {
10621263 fn tcx ( & self ) -> TyCtxt < ' tcx > {
10631264 self . tcx
0 commit comments