@@ -43,7 +43,7 @@ struct ConstToPat<'a, 'tcx> {
43
43
span : Span ,
44
44
param_env : ty:: ParamEnv < ' tcx > ,
45
45
46
- // This tracks if we signal some hard error for a given const value, so that
46
+ // This tracks if we saw some error or lint for a given const value, so that
47
47
// we will not subsequently issue an irrelevant lint for the same const
48
48
// value.
49
49
saw_const_match_error : Cell < bool > ,
@@ -103,7 +103,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
103
103
// once indirect_structural_match is a full fledged error, this
104
104
// level of indirection can be eliminated
105
105
106
- let inlined_const_as_pat = self . recur ( cv) ;
106
+ let inlined_const_as_pat = self . recur ( cv, mir_structural_match_violation ) ;
107
107
108
108
if self . include_lint_checks && !self . saw_const_match_error . get ( ) {
109
109
// If we were able to successfully convert the const to some pat,
@@ -216,7 +216,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
216
216
}
217
217
218
218
// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
219
- fn recur ( & self , cv : & ' tcx ty:: Const < ' tcx > ) -> Pat < ' tcx > {
219
+ fn recur ( & self , cv : & ' tcx ty:: Const < ' tcx > , mir_structural_match_violation : bool ) -> Pat < ' tcx > {
220
220
let id = self . id ;
221
221
let span = self . span ;
222
222
let tcx = self . tcx ( ) ;
@@ -227,7 +227,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
227
227
. enumerate ( )
228
228
. map ( |( idx, val) | {
229
229
let field = Field :: new ( idx) ;
230
- FieldPat { field, pattern : self . recur ( val) }
230
+ FieldPat { field, pattern : self . recur ( val, false ) }
231
231
} )
232
232
. collect ( )
233
233
} ;
@@ -248,6 +248,21 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
248
248
tcx. sess . span_err ( span, "cannot use unions in constant patterns" ) ;
249
249
PatKind :: Wild
250
250
}
251
+ ty:: Adt ( ..)
252
+ if !self . type_has_partial_eq_impl ( cv. ty )
253
+ // FIXME(#73448): Find a way to bring const qualification into parity with
254
+ // `search_for_structural_match_violation` and then remove this condition.
255
+ && self . search_for_structural_match_violation ( cv. ty ) . is_some ( ) =>
256
+ {
257
+ let msg = format ! (
258
+ "to use a constant of type `{}` in a pattern, \
259
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
260
+ cv. ty, cv. ty,
261
+ ) ;
262
+ self . saw_const_match_error . set ( true ) ;
263
+ self . tcx ( ) . sess . span_err ( self . span , & msg) ;
264
+ PatKind :: Wild
265
+ }
251
266
// If the type is not structurally comparable, just emit the constant directly,
252
267
// causing the pattern match code to treat it opaquely.
253
268
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
@@ -258,6 +273,20 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
258
273
// Backwards compatibility hack because we can't cause hard errors on these
259
274
// types, so we compare them via `PartialEq::eq` at runtime.
260
275
ty:: Adt ( ..) if !self . type_marked_structural ( cv. ty ) && self . behind_reference . get ( ) => {
276
+ if self . include_lint_checks && !self . saw_const_match_error . get ( ) {
277
+ self . saw_const_match_error . set ( true ) ;
278
+ let msg = format ! (
279
+ "to use a constant of type `{}` in a pattern, \
280
+ `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
281
+ cv. ty, cv. ty,
282
+ ) ;
283
+ tcx. struct_span_lint_hir (
284
+ lint:: builtin:: INDIRECT_STRUCTURAL_MATCH ,
285
+ id,
286
+ span,
287
+ |lint| lint. build ( & msg) . emit ( ) ,
288
+ ) ;
289
+ }
261
290
PatKind :: Constant { value : cv }
262
291
}
263
292
ty:: Adt ( adt_def, _) if !self . type_marked_structural ( cv. ty ) => {
@@ -292,14 +321,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
292
321
. destructure_const ( param_env. and ( cv) )
293
322
. fields
294
323
. iter ( )
295
- . map ( |val| self . recur ( val) )
324
+ . map ( |val| self . recur ( val, false ) )
296
325
. collect ( ) ,
297
326
slice : None ,
298
327
suffix : Vec :: new ( ) ,
299
328
} ,
300
329
ty:: Ref ( _, pointee_ty, ..) => match * pointee_ty. kind ( ) {
301
330
// These are not allowed and will error elsewhere anyway.
302
- ty:: Dynamic ( ..) => PatKind :: Constant { value : cv } ,
331
+ ty:: Dynamic ( ..) => {
332
+ self . saw_const_match_error . set ( true ) ;
333
+ tcx. sess . span_err ( span, & format ! ( "`{}` cannot be used in patterns" , cv. ty) ) ;
334
+ PatKind :: Wild
335
+ }
303
336
// `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
304
337
// optimization for now.
305
338
ty:: Str => PatKind :: Constant { value : cv } ,
@@ -321,7 +354,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
321
354
. destructure_const ( param_env. and ( array) )
322
355
. fields
323
356
. iter ( )
324
- . map ( |val| self . recur ( val) )
357
+ . map ( |val| self . recur ( val, false ) )
325
358
. collect ( ) ,
326
359
slice : None ,
327
360
suffix : vec ! [ ] ,
@@ -333,16 +366,21 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
333
366
self . behind_reference . set ( old) ;
334
367
val
335
368
}
336
- // Backwards compatibility hack. Don't take away the reference, since
337
- // `PartialEq::eq` takes a reference, this makes the rest of the matching logic
338
- // simpler.
369
+ // Backwards compatibility hack: support references to non-structural types.
370
+ // We'll lower
371
+ // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
372
+ // reference. This makes the rest of the matching logic simpler as it doesn't have
373
+ // to figure out how to get a reference again.
339
374
ty:: Adt ( ..) if !self . type_marked_structural ( pointee_ty) => {
340
375
PatKind :: Constant { value : cv }
341
376
}
377
+ // All other references are converted into deref patterns and then recursively
378
+ // convert the dereferenced constant to a pattern that is the sub-pattern of the
379
+ // deref pattern.
342
380
_ => {
343
381
let old = self . behind_reference . replace ( true ) ;
344
382
let val = PatKind :: Deref {
345
- subpattern : self . recur ( tcx. deref_const ( self . param_env . and ( cv) ) ) ,
383
+ subpattern : self . recur ( tcx. deref_const ( self . param_env . and ( cv) ) , false ) ,
346
384
} ;
347
385
self . behind_reference . set ( old) ;
348
386
val
@@ -373,11 +411,34 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
373
411
PatKind :: Constant { value : cv }
374
412
}
375
413
_ => {
376
- tcx. sess . delay_span_bug ( span, & format ! ( "cannot make a pattern out of {}" , cv. ty) ) ;
414
+ self . saw_const_match_error . set ( true ) ;
415
+ tcx. sess . span_err ( span, & format ! ( "`{}` cannot be used in patterns" , cv. ty) ) ;
377
416
PatKind :: Wild
378
417
}
379
418
} ;
380
419
420
+ if self . include_lint_checks
421
+ && !self . saw_const_match_error . get ( )
422
+ && mir_structural_match_violation
423
+ // FIXME(#73448): Find a way to bring const qualification into parity with
424
+ // `search_for_structural_match_violation` and then remove this condition.
425
+ && self . search_for_structural_match_violation ( cv. ty ) . is_some ( )
426
+ {
427
+ self . saw_const_match_error . set ( true ) ;
428
+ let msg = format ! (
429
+ "to use a constant of type `{}` in a pattern, \
430
+ the constant's initializer must be trivial or all types \
431
+ in the constant must be annotated with `#[derive(PartialEq, Eq)]`",
432
+ cv. ty,
433
+ ) ;
434
+ tcx. struct_span_lint_hir (
435
+ lint:: builtin:: NONTRIVIAL_STRUCTURAL_MATCH ,
436
+ id,
437
+ span,
438
+ |lint| lint. build ( & msg) . emit ( ) ,
439
+ ) ;
440
+ }
441
+
381
442
Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
382
443
}
383
444
}
0 commit comments