@@ -28,10 +28,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
2828 debug ! ( "const_to_pat: cv={:#?} id={:?}" , cv, id) ;
2929 debug ! ( "const_to_pat: cv.ty={:?} span={:?}" , cv. ty, span) ;
3030
31- self . tcx . infer_ctxt ( ) . enter ( |infcx| {
31+ let pat = self . tcx . infer_ctxt ( ) . enter ( |infcx| {
3232 let mut convert = ConstToPat :: new ( self , id, span, infcx) ;
3333 convert. to_pat ( cv, mir_structural_match_violation)
34- } )
34+ } ) ;
35+
36+ debug ! ( "const_to_pat: pat={:?}" , pat) ;
37+ pat
3538 }
3639}
3740
@@ -45,6 +48,10 @@ struct ConstToPat<'a, 'tcx> {
4548 // value.
4649 saw_const_match_error : Cell < bool > ,
4750
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+
4855 // inference context used for checking `T: Structural` bounds.
4956 infcx : InferCtxt < ' a , ' tcx > ,
5057
@@ -65,6 +72,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
6572 param_env : pat_ctxt. param_env ,
6673 include_lint_checks : pat_ctxt. include_lint_checks ,
6774 saw_const_match_error : Cell :: new ( false ) ,
75+ behind_reference : Cell :: new ( false ) ,
6876 }
6977 }
7078
@@ -233,7 +241,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
233241 tcx. sess . span_err ( span, "cannot use unions in constant patterns" ) ;
234242 PatKind :: Wild
235243 }
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+ }
237256 ty:: Adt ( adt_def, _) if !self . type_marked_structural ( cv. ty ) => {
238257 debug ! ( "adt_def {:?} has !type_marked_structural for cv.ty: {:?}" , adt_def, cv. ty) ;
239258 let path = tcx. def_path_str ( adt_def. did ) ;
@@ -246,28 +265,6 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
246265 tcx. sess . span_err ( span, & msg) ;
247266 PatKind :: Wild
248267 }
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- }
271268 ty:: Adt ( adt_def, substs) if adt_def. is_enum ( ) => {
272269 let destructured = tcx. destructure_const ( param_env. and ( cv) ) ;
273270 PatKind :: Variant {
@@ -293,7 +290,68 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
293290 slice : None ,
294291 suffix : Vec :: new ( ) ,
295292 } ,
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+ }
297355 } ;
298356
299357 Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
0 commit comments