11use crate :: const_eval:: const_variant_index;
22
33use rustc:: hir;
4- use rustc:: lint ;
4+ use rustc:: hir :: def_id :: DefId ;
55use rustc:: mir:: Field ;
66use rustc:: infer:: InferCtxt ;
77use rustc:: traits:: { ObligationCause , PredicateObligation } ;
@@ -15,23 +15,28 @@ use syntax_pos::Span;
1515use std:: cell:: Cell ;
1616
1717use super :: { FieldPat , Pat , PatCtxt , PatKind } ;
18+ use super :: structural_match:: search_const_rhs_for_structural_match_violation;
1819
1920impl < ' a , ' tcx > PatCtxt < ' a , ' tcx > {
2021 /// Converts an evaluated constant to a pattern (if possible).
2122 /// This means aggregate values (like structs and enums) are converted
2223 /// to a pattern that matches the value (as if you'd compared via structural equality).
24+ ///
25+ /// For literals, pass `None` as the `opt_const_def_id`; for a const
26+ /// identifier, pass its `DefId`.
2327 pub ( super ) fn const_to_pat (
2428 & self ,
2529 cv : & ' tcx ty:: Const < ' tcx > ,
30+ opt_const_def_id : Option < DefId > ,
2631 id : hir:: HirId ,
2732 span : Span ,
2833 ) -> Pat < ' tcx > {
29- debug ! ( "const_to_pat : cv={:#?} id={:?}" , cv, id) ;
30- debug ! ( "const_to_pat : cv.ty={:?} span={:?}" , cv. ty, span) ;
34+ debug ! ( "const_def_to_pat : cv={:#?} const_def_id: {:?} id={:?}" , cv, opt_const_def_id , id) ;
35+ debug ! ( "const_def_to_pat : cv.ty={:?} span={:?}" , cv. ty, span) ;
3136
3237 self . tcx . infer_ctxt ( ) . enter ( |infcx| {
3338 let mut convert = ConstToPat :: new ( self , id, span, infcx) ;
34- convert. to_pat ( cv)
39+ convert. to_pat ( cv, opt_const_def_id )
3540 } )
3641 }
3742}
@@ -67,85 +72,77 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
6772
6873 fn tcx ( & self ) -> TyCtxt < ' tcx > { self . infcx . tcx }
6974
70- fn search_for_structural_match_violation ( & self ,
71- ty : Ty < ' tcx > )
72- -> Option < ty:: NonStructuralMatchTy < ' tcx > >
75+ fn search_const_def_for_structural_match_violation ( & self , const_def_id : DefId )
7376 {
74- ty:: search_for_structural_match_violation ( self . id , self . span , self . tcx ( ) , ty)
77+ assert ! ( const_def_id. is_local( ) ) ;
78+ self . tcx ( ) . infer_ctxt ( ) . enter ( |infcx| {
79+ search_const_rhs_for_structural_match_violation (
80+ & infcx, self . param_env , const_def_id, self . id , self . span ) ;
81+ } ) ;
82+ }
83+
84+ fn search_ty_for_structural_match_violation ( & self , ty : Ty < ' tcx > )
85+ {
86+ let structural = ty:: search_type_for_structural_match_violation (
87+ self . id , self . span , self . tcx ( ) , ty) ;
88+ debug ! ( "search_ty_for_structural_match_violation ty: {:?} returned: {:?}" , ty, structural) ;
89+ if let Some ( non_sm_ty) = structural {
90+
91+ // double-check there even *is* a semantic `PartialEq` to dispatch to.
92+ //
93+ // (If there isn't, then we can safely issue a hard
94+ // error, because that's never worked, due to compiler
95+ // using `PartialEq::eq` in this scenario in the past.)
96+ //
97+ // Note: To fix rust-lang/rust#65466, one could lift this check
98+ // *before* any structural-match checking, and unconditionally error
99+ // if `PartialEq` is not implemented. However, that breaks stable
100+ // code at the moment, because types like `for <'a> fn(&'a ())` do
101+ // not *yet* implement `PartialEq`. So for now we leave this here.
102+ let warn_instead_of_hard_error: bool = {
103+ let partial_eq_trait_id = self . tcx ( ) . lang_items ( ) . eq_trait ( ) . unwrap ( ) ;
104+ let obligation: PredicateObligation < ' _ > =
105+ self . tcx ( ) . predicate_for_trait_def (
106+ self . param_env ,
107+ ObligationCause :: misc ( self . span , self . id ) ,
108+ partial_eq_trait_id,
109+ 0 ,
110+ ty,
111+ & [ ] ) ;
112+ // FIXME: should this call a `predicate_must_hold` variant instead?
113+ self . infcx . predicate_may_hold ( & obligation)
114+ } ;
115+
116+ debug ! ( "call report_structural_match_violation non_sm_ty: {:?} id: {:?} warn: {:?}" ,
117+ non_sm_ty, self . id, warn_instead_of_hard_error) ;
118+ ty:: report_structural_match_violation (
119+ self . tcx ( ) , non_sm_ty, self . id , self . span , warn_instead_of_hard_error) ;
120+ }
75121 }
76122
77123 fn type_marked_structural ( & self , ty : Ty < ' tcx > ) -> bool {
78124 ty:: type_marked_structural ( self . id , self . span , & self . infcx , ty)
79125 }
80126
81- fn to_pat ( & mut self , cv : & ' tcx ty:: Const < ' tcx > ) -> Pat < ' tcx > {
82- // This method is just a wrapper handling a validity check; the heavy lifting is
83- // performed by the recursive `recur` method, which is not meant to be
84- // invoked except by this method.
85- //
86- // once indirect_structural_match is a full fledged error, this
87- // level of indirection can be eliminated
88-
127+ fn to_pat ( & mut self ,
128+ cv : & ' tcx ty:: Const < ' tcx > ,
129+ opt_const_def_id : Option < DefId > )
130+ -> Pat < ' tcx >
131+ {
89132 let inlined_const_as_pat = self . recur ( cv) ;
90133
91134 if self . include_lint_checks && !self . saw_const_match_error . get ( ) {
92135 // If we were able to successfully convert the const to some pat,
93136 // double-check that all types in the const implement `Structural`.
94-
95- let structural = self . search_for_structural_match_violation ( cv. ty ) ;
96- debug ! ( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}" ,
97- cv. ty, structural) ;
98- if let Some ( non_sm_ty) = structural {
99- let adt_def = match non_sm_ty {
100- ty:: NonStructuralMatchTy :: Adt ( adt_def) => adt_def,
101- ty:: NonStructuralMatchTy :: Param =>
102- bug ! ( "use of constant whose type is a parameter inside a pattern" ) ,
103- } ;
104- let path = self . tcx ( ) . def_path_str ( adt_def. did ) ;
105- let msg = format ! (
106- "to use a constant of type `{}` in a pattern, \
107- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
108- path,
109- path,
110- ) ;
111-
112- // double-check there even *is* a semantic `PartialEq` to dispatch to.
113- //
114- // (If there isn't, then we can safely issue a hard
115- // error, because that's never worked, due to compiler
116- // using `PartialEq::eq` in this scenario in the past.)
117- //
118- // Note: To fix rust-lang/rust#65466, one could lift this check
119- // *before* any structural-match checking, and unconditionally error
120- // if `PartialEq` is not implemented. However, that breaks stable
121- // code at the moment, because types like `for <'a> fn(&'a ())` do
122- // not *yet* implement `PartialEq`. So for now we leave this here.
123- let ty_is_partial_eq: bool = {
124- let partial_eq_trait_id = self . tcx ( ) . lang_items ( ) . eq_trait ( ) . unwrap ( ) ;
125- let obligation: PredicateObligation < ' _ > =
126- self . tcx ( ) . predicate_for_trait_def (
127- self . param_env ,
128- ObligationCause :: misc ( self . span , self . id ) ,
129- partial_eq_trait_id,
130- 0 ,
131- cv. ty ,
132- & [ ] ) ;
133- // FIXME: should this call a `predicate_must_hold` variant instead?
134- self . infcx . predicate_may_hold ( & obligation)
135- } ;
136-
137- if !ty_is_partial_eq {
138- // span_fatal avoids ICE from resolution of non-existent method (rare case).
139- self . tcx ( ) . sess . span_fatal ( self . span , & msg) ;
140- } else {
141- self . tcx ( ) . lint_hir ( lint:: builtin:: INDIRECT_STRUCTURAL_MATCH ,
142- self . id ,
143- self . span ,
144- & msg) ;
137+ match opt_const_def_id {
138+ Some ( const_def_id) if const_def_id. is_local ( ) => {
139+ self . search_const_def_for_structural_match_violation ( const_def_id) ;
140+ }
141+ _ => {
142+ self . search_ty_for_structural_match_violation ( cv. ty ) ;
145143 }
146144 }
147145 }
148-
149146 inlined_const_as_pat
150147 }
151148
0 commit comments