@@ -28,10 +28,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
28
28
debug ! ( "const_to_pat: cv={:#?} id={:?}" , cv, id) ;
29
29
debug ! ( "const_to_pat: cv.ty={:?} span={:?}" , cv. ty, span) ;
30
30
31
- self . tcx . infer_ctxt ( ) . enter ( |infcx| {
31
+ let pat = self . tcx . infer_ctxt ( ) . enter ( |infcx| {
32
32
let mut convert = ConstToPat :: new ( self , id, span, infcx) ;
33
33
convert. to_pat ( cv, mir_structural_match_violation)
34
- } )
34
+ } ) ;
35
+
36
+ debug ! ( "const_to_pat: pat={:?}" , pat) ;
37
+ pat
35
38
}
36
39
}
37
40
@@ -45,6 +48,10 @@ struct ConstToPat<'a, 'tcx> {
45
48
// value.
46
49
saw_const_match_error : Cell < bool > ,
47
50
51
+ // For backcompat we need to keep allowing non-structurally-eq types behind references.
52
+ // See also all the `cant-hide-behind` tests.
53
+ behind_reference : Cell < bool > ,
54
+
48
55
// inference context used for checking `T: Structural` bounds.
49
56
infcx : InferCtxt < ' a , ' tcx > ,
50
57
@@ -65,6 +72,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
65
72
param_env : pat_ctxt. param_env ,
66
73
include_lint_checks : pat_ctxt. include_lint_checks ,
67
74
saw_const_match_error : Cell :: new ( false ) ,
75
+ behind_reference : Cell :: new ( false ) ,
68
76
}
69
77
}
70
78
@@ -233,7 +241,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
233
241
tcx. sess . span_err ( span, "cannot use unions in constant patterns" ) ;
234
242
PatKind :: Wild
235
243
}
236
- // keep old code until future-compat upgraded to errors.
244
+ // If the type is not structurally comparable, just emit the constant directly,
245
+ // causing the pattern match code to treat it opaquely.
246
+ // FIXME: This code doesn't emit errors itself, the caller emits the errors.
247
+ // So instead of specific errors, you just get blanket errors about the whole
248
+ // const type. See
249
+ // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
250
+ // details.
251
+ // Backwards compatibility hack because we can't cause hard errors on these
252
+ // types, so we compare them via `PartialEq::eq` at runtime.
253
+ ty:: Adt ( ..) if !self . type_marked_structural ( cv. ty ) && self . behind_reference . get ( ) => {
254
+ PatKind :: Constant { value : cv }
255
+ }
237
256
ty:: Adt ( adt_def, _) if !self . type_marked_structural ( cv. ty ) => {
238
257
debug ! ( "adt_def {:?} has !type_marked_structural for cv.ty: {:?}" , adt_def, cv. ty) ;
239
258
let path = tcx. def_path_str ( adt_def. did ) ;
@@ -246,28 +265,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
246
265
tcx. sess . span_err ( span, & msg) ;
247
266
PatKind :: Wild
248
267
}
249
- // keep old code until future-compat upgraded to errors.
250
- ty:: Ref ( _, adt_ty, _) if adt_ty. is_adt ( ) && !self . type_marked_structural ( adt_ty) => {
251
- let adt_def =
252
- if let ty:: Adt ( adt_def, _) = adt_ty. kind ( ) { adt_def } else { unreachable ! ( ) } ;
253
-
254
- debug ! (
255
- "adt_def {:?} has !type_marked_structural for adt_ty: {:?}" ,
256
- adt_def, adt_ty
257
- ) ;
258
-
259
- // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
260
- // would be wrong. Returnging `PatKind::Wild` is not technically correct.
261
- let path = tcx. def_path_str ( adt_def. did ) ;
262
- let msg = format ! (
263
- "to use a constant of type `{}` in a pattern, \
264
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
265
- path, path,
266
- ) ;
267
- self . saw_const_match_error . set ( true ) ;
268
- tcx. sess . span_err ( span, & msg) ;
269
- PatKind :: Wild
270
- }
271
268
ty:: Adt ( adt_def, substs) if adt_def. is_enum ( ) => {
272
269
let destructured = tcx. destructure_const ( param_env. and ( cv) ) ;
273
270
PatKind :: Variant {
@@ -293,7 +290,68 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
293
290
slice : None ,
294
291
suffix : Vec :: new ( ) ,
295
292
} ,
296
- _ => PatKind :: Constant { value : cv } ,
293
+ ty:: Ref ( _, pointee_ty, ..) => match * pointee_ty. kind ( ) {
294
+ // These are not allowed and will error elsewhere anyway.
295
+ ty:: Dynamic ( ..) => PatKind :: Constant { value : cv } ,
296
+ // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
297
+ // optimization for now.
298
+ ty:: Str => PatKind :: Constant { value : cv } ,
299
+ ty:: Slice ( elem_ty) if elem_ty == tcx. types . u8 => PatKind :: Constant { value : cv } ,
300
+ // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
301
+ // matching against references, you can only use byte string literals.
302
+ // FIXME: clean this up, likely by permitting array patterns when matching on slices
303
+ ty:: Array ( elem_ty, _) if elem_ty == tcx. types . u8 => PatKind :: Constant { value : cv } ,
304
+ // Cannot merge this with the catch all branch below, because the `const_deref`
305
+ // changes the type from slice to array, and slice patterns behave differently from
306
+ // array patterns.
307
+ ty:: Slice ( ..) => {
308
+ let old = self . behind_reference . replace ( true ) ;
309
+ let array = tcx. deref_const ( self . param_env . and ( cv) ) ;
310
+ let val = PatKind :: Deref {
311
+ subpattern : Pat {
312
+ kind : Box :: new ( PatKind :: Slice {
313
+ prefix : tcx
314
+ . destructure_const ( param_env. and ( array) )
315
+ . fields
316
+ . iter ( )
317
+ . map ( |val| self . recur ( val) )
318
+ . collect ( ) ,
319
+ slice : None ,
320
+ suffix : vec ! [ ] ,
321
+ } ) ,
322
+ span,
323
+ ty : pointee_ty,
324
+ } ,
325
+ } ;
326
+ self . behind_reference . set ( old) ;
327
+ val
328
+ }
329
+ // Backwards compatibility hack. Don't take away the reference, since
330
+ // `PartialEq::eq` takes a reference, this makes the rest of the matching logic
331
+ // simpler.
332
+ ty:: Adt ( ..) if !self . type_marked_structural ( pointee_ty) => {
333
+ PatKind :: Constant { value : cv }
334
+ }
335
+ _ => {
336
+ let old = self . behind_reference . replace ( true ) ;
337
+ let val = PatKind :: Deref {
338
+ subpattern : self . recur ( tcx. deref_const ( self . param_env . and ( cv) ) ) ,
339
+ } ;
340
+ self . behind_reference . set ( old) ;
341
+ val
342
+ }
343
+ } ,
344
+ ty:: Bool | ty:: Char | ty:: Int ( _) | ty:: Uint ( _) | ty:: FnDef ( ..) => {
345
+ PatKind :: Constant { value : cv }
346
+ }
347
+ // FIXME: these can have very suprising behaviour where optimization levels or other
348
+ // compilation choices change the runtime behaviour of the match.
349
+ // See https://github.com/rust-lang/rust/issues/70861 for examples.
350
+ ty:: FnPtr ( ..) | ty:: RawPtr ( ..) => PatKind :: Constant { value : cv } ,
351
+ _ => {
352
+ tcx. sess . delay_span_bug ( span, & format ! ( "cannot make a pattern out of {}" , cv. ty) ) ;
353
+ PatKind :: Wild
354
+ }
297
355
} ;
298
356
299
357
Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
0 commit comments