@@ -97,82 +97,67 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
97
97
98
98
let inlined_const_as_pat = self . recur ( cv) ;
99
99
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
+ }
103
103
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
+ } ;
109
112
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
+ }
113
116
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, \
120
138
`{}` 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
+ )
174
141
}
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
+ ) ;
176
161
}
177
162
178
163
inlined_const_as_pat
@@ -280,4 +265,81 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
280
265
281
266
Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
282
267
}
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 } )
283
345
}
0 commit comments