@@ -43,7 +43,7 @@ struct ConstToPat<'a, 'tcx> {
4343 span : Span ,
4444 param_env : ty:: ParamEnv < ' tcx > ,
4545
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
4747 // we will not subsequently issue an irrelevant lint for the same const
4848 // value.
4949 saw_const_match_error : Cell < bool > ,
@@ -103,7 +103,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
103103 // once indirect_structural_match is a full fledged error, this
104104 // level of indirection can be eliminated
105105
106- let inlined_const_as_pat = self . recur ( cv) ;
106+ let inlined_const_as_pat = self . recur ( cv, mir_structural_match_violation ) ;
107107
108108 if self . include_lint_checks && !self . saw_const_match_error . get ( ) {
109109 // If we were able to successfully convert the const to some pat,
@@ -216,7 +216,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
216216 }
217217
218218 // 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 > {
220220 let id = self . id ;
221221 let span = self . span ;
222222 let tcx = self . tcx ( ) ;
@@ -227,7 +227,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
227227 . enumerate ( )
228228 . map ( |( idx, val) | {
229229 let field = Field :: new ( idx) ;
230- FieldPat { field, pattern : self . recur ( val) }
230+ FieldPat { field, pattern : self . recur ( val, false ) }
231231 } )
232232 . collect ( )
233233 } ;
@@ -248,6 +248,21 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
248248 tcx. sess . span_err ( span, "cannot use unions in constant patterns" ) ;
249249 PatKind :: Wild
250250 }
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+ }
251266 // If the type is not structurally comparable, just emit the constant directly,
252267 // causing the pattern match code to treat it opaquely.
253268 // FIXME: This code doesn't emit errors itself, the caller emits the errors.
@@ -258,6 +273,20 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
258273 // Backwards compatibility hack because we can't cause hard errors on these
259274 // types, so we compare them via `PartialEq::eq` at runtime.
260275 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+ }
261290 PatKind :: Constant { value : cv }
262291 }
263292 ty:: Adt ( adt_def, _) if !self . type_marked_structural ( cv. ty ) => {
@@ -292,14 +321,18 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
292321 . destructure_const ( param_env. and ( cv) )
293322 . fields
294323 . iter ( )
295- . map ( |val| self . recur ( val) )
324+ . map ( |val| self . recur ( val, false ) )
296325 . collect ( ) ,
297326 slice : None ,
298327 suffix : Vec :: new ( ) ,
299328 } ,
300329 ty:: Ref ( _, pointee_ty, ..) => match * pointee_ty. kind ( ) {
301330 // 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+ }
303336 // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
304337 // optimization for now.
305338 ty:: Str => PatKind :: Constant { value : cv } ,
@@ -321,7 +354,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
321354 . destructure_const ( param_env. and ( array) )
322355 . fields
323356 . iter ( )
324- . map ( |val| self . recur ( val) )
357+ . map ( |val| self . recur ( val, false ) )
325358 . collect ( ) ,
326359 slice : None ,
327360 suffix : vec ! [ ] ,
@@ -333,16 +366,21 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
333366 self . behind_reference . set ( old) ;
334367 val
335368 }
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.
339374 ty:: Adt ( ..) if !self . type_marked_structural ( pointee_ty) => {
340375 PatKind :: Constant { value : cv }
341376 }
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.
342380 _ => {
343381 let old = self . behind_reference . replace ( true ) ;
344382 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 ) ,
346384 } ;
347385 self . behind_reference . set ( old) ;
348386 val
@@ -373,11 +411,34 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
373411 PatKind :: Constant { value : cv }
374412 }
375413 _ => {
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) ) ;
377416 PatKind :: Wild
378417 }
379418 } ;
380419
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+
381442 Pat { span, ty : cv. ty , kind : Box :: new ( kind) }
382443 }
383444}
0 commit comments