@@ -97,82 +97,67 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
9797
9898 let inlined_const_as_pat = self . recur ( cv) ;
9999
100- if self . include_lint_checks && ! self . saw_const_match_error . get ( ) {
101- // If we were able to successfully convert the const to some pat,
102- // double-check that all types in the const implement `Structural`.
100+ if self . saw_const_match_error . get ( ) {
101+ return inlined_const_as_pat ;
102+ }
103103
104- let structural = self . search_for_structural_match_violation ( cv. ty ) ;
105- debug ! (
106- "search_for_structural_match_violation cv.ty: {:?} returned: {:?}" ,
107- cv. ty, structural
108- ) ;
104+ // We eventually lower to a call to `PartialEq::eq` for this type, so ensure that this
105+ // method actually exists.
106+ if !self . ty_has_partial_eq_impl ( cv. ty ) {
107+ let msg = if cv. ty . is_trait ( ) {
108+ "trait objects cannot be used in patterns" . to_string ( )
109+ } else {
110+ format ! ( "`{:?}` must implement `PartialEq` to be used in a pattern" , cv. ty)
111+ } ;
109112
110- if structural . is_none ( ) && mir_structural_match_violation {
111- bug ! ( "MIR const-checker found novel structural match violation" ) ;
112- }
113+ // Codegen will ICE if we continue compilation, so abort here.
114+ self . tcx ( ) . sess . span_fatal ( self . span , & msg ) ;
115+ }
113116
114- if let Some ( non_sm_ty) = structural {
115- let msg = match non_sm_ty {
116- traits:: NonStructuralMatchTy :: Adt ( adt_def) => {
117- let path = self . tcx ( ) . def_path_str ( adt_def. did ) ;
118- format ! (
119- "to use a constant of type `{}` in a pattern, \
117+ if !self . include_lint_checks {
118+ return inlined_const_as_pat;
119+ }
120+
121+ // If we were able to successfully convert the const to some pat,
122+ // double-check that all types in the const implement `Structural`.
123+
124+ let ty_violation = self . search_for_structural_match_violation ( cv. ty ) ;
125+ debug ! (
126+ "search_for_structural_match_violation cv.ty: {:?} returned: {:?}" ,
127+ cv. ty, ty_violation,
128+ ) ;
129+
130+ if mir_structural_match_violation {
131+ let non_sm_ty =
132+ ty_violation. expect ( "MIR const-checker found novel structural match violation" ) ;
133+ let msg = match non_sm_ty {
134+ traits:: NonStructuralMatchTy :: Adt ( adt_def) => {
135+ let path = self . tcx ( ) . def_path_str ( adt_def. did ) ;
136+ format ! (
137+ "to use a constant of type `{}` in a pattern, \
120138 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
121- path, path,
122- )
123- }
124- traits:: NonStructuralMatchTy :: Dynamic => {
125- "trait objects cannot be used in patterns" . to_string ( )
126- }
127- traits:: NonStructuralMatchTy :: Param => {
128- bug ! ( "use of constant whose type is a parameter inside a pattern" )
129- }
130- } ;
131-
132- // double-check there even *is* a semantic `PartialEq` to dispatch to.
133- //
134- // (If there isn't, then we can safely issue a hard
135- // error, because that's never worked, due to compiler
136- // using `PartialEq::eq` in this scenario in the past.)
137- //
138- // Note: To fix rust-lang/rust#65466, one could lift this check
139- // *before* any structural-match checking, and unconditionally error
140- // if `PartialEq` is not implemented. However, that breaks stable
141- // code at the moment, because types like `for <'a> fn(&'a ())` do
142- // not *yet* implement `PartialEq`. So for now we leave this here.
143- let ty_is_partial_eq: bool = {
144- let partial_eq_trait_id =
145- self . tcx ( ) . require_lang_item ( EqTraitLangItem , Some ( self . span ) ) ;
146- let obligation: PredicateObligation < ' _ > = predicate_for_trait_def (
147- self . tcx ( ) ,
148- self . param_env ,
149- ObligationCause :: misc ( self . span , self . id ) ,
150- partial_eq_trait_id,
151- 0 ,
152- cv. ty ,
153- & [ ] ,
154- ) ;
155- // FIXME: should this call a `predicate_must_hold` variant instead?
156- self . infcx . predicate_may_hold ( & obligation)
157- } ;
158-
159- if !ty_is_partial_eq {
160- // span_fatal avoids ICE from resolution of non-existent method (rare case).
161- self . tcx ( ) . sess . span_fatal ( self . span , & msg) ;
162- } else if mir_structural_match_violation {
163- self . tcx ( ) . struct_span_lint_hir (
164- lint:: builtin:: INDIRECT_STRUCTURAL_MATCH ,
165- self . id ,
166- self . span ,
167- |lint| lint. build ( & msg) . emit ( ) ,
168- ) ;
169- } else {
170- debug ! (
171- "`search_for_structural_match_violation` found one, but `CustomEq` was \
172- not in the qualifs for that `const`"
173- ) ;
139+ path, path,
140+ )
174141 }
175- }
142+ traits:: NonStructuralMatchTy :: Dynamic => {
143+ "trait objects cannot be used in patterns" . to_string ( )
144+ }
145+ traits:: NonStructuralMatchTy :: Param => {
146+ bug ! ( "use of constant whose type is a parameter inside a pattern" )
147+ }
148+ } ;
149+
150+ self . tcx ( ) . struct_span_lint_hir (
151+ lint:: builtin:: INDIRECT_STRUCTURAL_MATCH ,
152+ self . id ,
153+ self . span ,
154+ |lint| lint. build ( & msg) . emit ( ) ,
155+ ) ;
156+ } else if ty_violation. is_some ( ) {
157+ debug ! (
158+ "`search_for_structural_match_violation` found one, but `CustomEq` was \
159+ not in the qualifs for that `const`"
160+ ) ;
176161 }
177162
178163 inlined_const_as_pat
@@ -280,4 +265,81 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
280265
281266 Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
282267 }
268+
269+ fn ty_has_partial_eq_impl ( & self , ty : Ty < ' tcx > ) -> bool {
270+ let tcx = self . tcx ( ) ;
271+
272+ let is_partial_eq = |ty| {
273+ let partial_eq_trait_id = tcx. require_lang_item ( EqTraitLangItem , Some ( self . span ) ) ;
274+ let obligation: PredicateObligation < ' _ > = predicate_for_trait_def (
275+ tcx,
276+ self . param_env ,
277+ ObligationCause :: misc ( self . span , self . id ) ,
278+ partial_eq_trait_id,
279+ 0 ,
280+ ty,
281+ & [ ] ,
282+ ) ;
283+
284+ // FIXME: should this call a `predicate_must_hold` variant instead?
285+ self . infcx . predicate_may_hold ( & obligation)
286+ } ;
287+
288+ // Higher-ranked function pointers, such as `for<'r> fn(&'r i32)` are allowed in patterns
289+ // but do not satisfy `Self: PartialEq` due to shortcomings in the trait solver.
290+ // Check for bare function pointers first since it is cheap to do so.
291+ if let ty:: FnPtr ( _) = ty. kind {
292+ return true ;
293+ }
294+
295+ // In general, types that appear in patterns need to implement `PartialEq`.
296+ if is_partial_eq ( ty) {
297+ return true ;
298+ }
299+
300+ // HACK: The check for bare function pointers will miss generic types that are instantiated
301+ // with a higher-ranked type (`for<'r> fn(&'r i32)`) as a parameter. To preserve backwards
302+ // compatibility in this case, we must continue to allow types such as `Option<fn(&i32)>`.
303+ //
304+ //
305+ // We accomplish this by replacing *all* late-bound lifetimes in the type with concrete
306+ // ones. This leverages the fact that function pointers with no late-bound lifetimes do
307+ // satisfy `PartialEq`. In other words, we transform `Option<for<'r> fn(&'r i32)>` to
308+ // `Option<fn(&'erased i32)>` and again check whether `PartialEq` is satisfied.
309+ // Obviously this is too permissive, but it is better than the old behavior, which
310+ // allowed *all* types to reach codegen and caused issues like #65466.
311+ let erased_ty = erase_all_late_bound_regions ( tcx, ty) ;
312+ if is_partial_eq ( erased_ty) {
313+ warn ! ( "Non-function pointer only satisfied `PartialEq` after regions were erased" ) ;
314+ return true ;
315+ }
316+
317+ false
318+ }
319+ }
320+
321+ /// Erase *all* late bound regions, ignoring their debruijn index.
322+ ///
323+ /// This is a terrible hack. Do not use it elsewhere.
324+ fn erase_all_late_bound_regions < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
325+ use ty:: fold:: TypeFoldable ;
326+
327+ struct Eraser < ' tcx > {
328+ tcx : TyCtxt < ' tcx > ,
329+ }
330+
331+ impl < ' tcx > ty:: fold:: TypeFolder < ' tcx > for Eraser < ' tcx > {
332+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
333+ self . tcx
334+ }
335+
336+ fn fold_region ( & mut self , r : ty:: Region < ' tcx > ) -> ty:: Region < ' tcx > {
337+ match r {
338+ ty:: ReLateBound ( _, _) => & ty:: ReErased ,
339+ r => r. super_fold_with ( self ) ,
340+ }
341+ }
342+ }
343+
344+ ty. fold_with ( & mut Eraser { tcx } )
283345}
0 commit comments