]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Rollup merge of #101995 - scottmcm:carrying-mul-example, r=Mark-Simulacrum
[rust.git] / compiler / rustc_mir_build / src / thir / pattern / const_to_pat.rs
1 use rustc_hir as hir;
2 use rustc_index::vec::Idx;
3 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
4 use rustc_middle::mir::{self, Field};
5 use rustc_middle::thir::{FieldPat, Pat, PatKind};
6 use rustc_middle::ty::print::with_no_trimmed_paths;
7 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
8 use rustc_session::lint;
9 use rustc_span::Span;
10 use rustc_trait_selection::traits::predicate_for_trait_def;
11 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
12 use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
13
14 use std::cell::Cell;
15
16 use super::PatCtxt;
17
18 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
19     /// Converts an evaluated constant to a pattern (if possible).
20     /// This means aggregate values (like structs and enums) are converted
21     /// to a pattern that matches the value (as if you'd compared via structural equality).
22     #[instrument(level = "debug", skip(self), ret)]
23     pub(super) fn const_to_pat(
24         &self,
25         cv: mir::ConstantKind<'tcx>,
26         id: hir::HirId,
27         span: Span,
28         mir_structural_match_violation: bool,
29     ) -> Box<Pat<'tcx>> {
30         self.tcx.infer_ctxt().enter(|infcx| {
31             let mut convert = ConstToPat::new(self, id, span, infcx);
32             convert.to_pat(cv, mir_structural_match_violation)
33         })
34     }
35 }
36
37 struct ConstToPat<'a, 'tcx> {
38     id: hir::HirId,
39     span: Span,
40     param_env: ty::ParamEnv<'tcx>,
41
42     // This tracks if we emitted some hard error for a given const value, so that
43     // we will not subsequently issue an irrelevant lint for the same const
44     // value.
45     saw_const_match_error: Cell<bool>,
46
47     // This tracks if we emitted some diagnostic for a given const value, so that
48     // we will not subsequently issue an irrelevant lint for the same const
49     // value.
50     saw_const_match_lint: Cell<bool>,
51
52     // For backcompat we need to keep allowing non-structurally-eq types behind references.
53     // See also all the `cant-hide-behind` tests.
54     behind_reference: Cell<bool>,
55
56     // inference context used for checking `T: Structural` bounds.
57     infcx: InferCtxt<'a, 'tcx>,
58
59     include_lint_checks: bool,
60
61     treat_byte_string_as_slice: bool,
62 }
63
64 mod fallback_to_const_ref {
65     #[derive(Debug)]
66     /// This error type signals that we encountered a non-struct-eq situation behind a reference.
67     /// We bubble this up in order to get back to the reference destructuring and make that emit
68     /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
69     /// on such patterns (since that function takes a reference) and not have to jump through any
70     /// hoops to get a reference to the value.
71     pub(super) struct FallbackToConstRef(());
72
73     pub(super) fn fallback_to_const_ref<'a, 'tcx>(
74         c2p: &super::ConstToPat<'a, 'tcx>,
75     ) -> FallbackToConstRef {
76         assert!(c2p.behind_reference.get());
77         FallbackToConstRef(())
78     }
79 }
80 use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
81
82 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
83     fn new(
84         pat_ctxt: &PatCtxt<'_, 'tcx>,
85         id: hir::HirId,
86         span: Span,
87         infcx: InferCtxt<'a, 'tcx>,
88     ) -> Self {
89         trace!(?pat_ctxt.typeck_results.hir_owner);
90         ConstToPat {
91             id,
92             span,
93             infcx,
94             param_env: pat_ctxt.param_env,
95             include_lint_checks: pat_ctxt.include_lint_checks,
96             saw_const_match_error: Cell::new(false),
97             saw_const_match_lint: Cell::new(false),
98             behind_reference: Cell::new(false),
99             treat_byte_string_as_slice: pat_ctxt
100                 .typeck_results
101                 .treat_byte_string_as_slice
102                 .contains(&id.local_id),
103         }
104     }
105
106     fn tcx(&self) -> TyCtxt<'tcx> {
107         self.infcx.tcx
108     }
109
110     fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String {
111         let path = self.tcx().def_path_str(adt_def.did());
112         format!(
113             "to use a constant of type `{}` in a pattern, \
114             `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
115             path, path,
116         )
117     }
118
119     fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
120         traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
121             with_no_trimmed_paths!(match non_sm_ty.kind() {
122                 ty::Adt(adt, _) => self.adt_derive_msg(*adt),
123                 ty::Dynamic(..) => {
124                     "trait objects cannot be used in patterns".to_string()
125                 }
126                 ty::Opaque(..) => {
127                     "opaque types cannot be used in patterns".to_string()
128                 }
129                 ty::Closure(..) => {
130                     "closures cannot be used in patterns".to_string()
131                 }
132                 ty::Generator(..) | ty::GeneratorWitness(..) => {
133                     "generators cannot be used in patterns".to_string()
134                 }
135                 ty::Float(..) => {
136                     "floating-point numbers cannot be used in patterns".to_string()
137                 }
138                 ty::FnPtr(..) => {
139                     "function pointers cannot be used in patterns".to_string()
140                 }
141                 ty::RawPtr(..) => {
142                     "raw pointers cannot be used in patterns".to_string()
143                 }
144                 _ => {
145                     bug!("use of a value of `{non_sm_ty}` inside a pattern")
146                 }
147             })
148         })
149     }
150
151     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
152         ty.is_structural_eq_shallow(self.infcx.tcx)
153     }
154
155     fn to_pat(
156         &mut self,
157         cv: mir::ConstantKind<'tcx>,
158         mir_structural_match_violation: bool,
159     ) -> Box<Pat<'tcx>> {
160         trace!(self.treat_byte_string_as_slice);
161         // This method is just a wrapper handling a validity check; the heavy lifting is
162         // performed by the recursive `recur` method, which is not meant to be
163         // invoked except by this method.
164         //
165         // once indirect_structural_match is a full fledged error, this
166         // level of indirection can be eliminated
167
168         let inlined_const_as_pat =
169             self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| {
170                 Box::new(Pat {
171                     span: self.span,
172                     ty: cv.ty(),
173                     kind: PatKind::Constant { value: cv },
174                 })
175             });
176
177         if self.include_lint_checks && !self.saw_const_match_error.get() {
178             // If we were able to successfully convert the const to some pat,
179             // double-check that all types in the const implement `Structural`.
180
181             let structural = self.search_for_structural_match_violation(cv.ty());
182             debug!(
183                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
184                 cv.ty(),
185                 structural
186             );
187
188             // This can occur because const qualification treats all associated constants as
189             // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
190             // before it runs.
191             //
192             // FIXME(#73448): Find a way to bring const qualification into parity with
193             // `search_for_structural_match_violation`.
194             if structural.is_none() && mir_structural_match_violation {
195                 warn!("MIR const-checker found novel structural match violation. See #73448.");
196                 return inlined_const_as_pat;
197             }
198
199             if let Some(msg) = structural {
200                 if !self.type_may_have_partial_eq_impl(cv.ty()) {
201                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
202                     self.tcx().sess.span_fatal(self.span, &msg);
203                 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
204                     self.tcx().struct_span_lint_hir(
205                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
206                         self.id,
207                         self.span,
208                         |lint| {
209                             lint.build(&msg).emit();
210                         },
211                     );
212                 } else {
213                     debug!(
214                         "`search_for_structural_match_violation` found one, but `CustomEq` was \
215                           not in the qualifs for that `const`"
216                     );
217                 }
218             }
219         }
220
221         inlined_const_as_pat
222     }
223
224     fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
225         // double-check there even *is* a semantic `PartialEq` to dispatch to.
226         //
227         // (If there isn't, then we can safely issue a hard
228         // error, because that's never worked, due to compiler
229         // using `PartialEq::eq` in this scenario in the past.)
230         let partial_eq_trait_id =
231             self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
232         let obligation: PredicateObligation<'_> = predicate_for_trait_def(
233             self.tcx(),
234             self.param_env,
235             ObligationCause::misc(self.span, self.id),
236             partial_eq_trait_id,
237             0,
238             ty,
239             &[],
240         );
241         // FIXME: should this call a `predicate_must_hold` variant instead?
242
243         let has_impl = self.infcx.predicate_may_hold(&obligation);
244
245         // Note: To fix rust-lang/rust#65466, we could just remove this type
246         // walk hack for function pointers, and unconditionally error
247         // if `PartialEq` is not implemented. However, that breaks stable
248         // code at the moment, because types like `for <'a> fn(&'a ())` do
249         // not *yet* implement `PartialEq`. So for now we leave this here.
250         has_impl
251             || ty.walk().any(|t| match t.unpack() {
252                 ty::subst::GenericArgKind::Lifetime(_) => false,
253                 ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
254                 ty::subst::GenericArgKind::Const(_) => false,
255             })
256     }
257
258     fn field_pats(
259         &self,
260         vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
261     ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
262         vals.enumerate()
263             .map(|(idx, val)| {
264                 let field = Field::new(idx);
265                 Ok(FieldPat { field, pattern: self.recur(val, false)? })
266             })
267             .collect()
268     }
269
270     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
271     #[instrument(skip(self), level = "debug")]
272     fn recur(
273         &self,
274         cv: mir::ConstantKind<'tcx>,
275         mir_structural_match_violation: bool,
276     ) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> {
277         let id = self.id;
278         let span = self.span;
279         let tcx = self.tcx();
280         let param_env = self.param_env;
281
282         let kind = match cv.ty().kind() {
283             ty::Float(_) => {
284                 if self.include_lint_checks {
285                     tcx.struct_span_lint_hir(
286                         lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
287                         id,
288                         span,
289                         |lint| {
290                             lint.build("floating-point types cannot be used in patterns").emit();
291                         },
292                     );
293                 }
294                 PatKind::Constant { value: cv }
295             }
296             ty::Adt(adt_def, _) if adt_def.is_union() => {
297                 // Matching on union fields is unsafe, we can't hide it in constants
298                 self.saw_const_match_error.set(true);
299                 let msg = "cannot use unions in constant patterns";
300                 if self.include_lint_checks {
301                     tcx.sess.span_err(span, msg);
302                 } else {
303                     tcx.sess.delay_span_bug(span, msg);
304                 }
305                 PatKind::Wild
306             }
307             ty::Adt(..)
308                 if !self.type_may_have_partial_eq_impl(cv.ty())
309                     // FIXME(#73448): Find a way to bring const qualification into parity with
310                     // `search_for_structural_match_violation` and then remove this condition.
311                     && self.search_for_structural_match_violation(cv.ty()).is_some() =>
312             {
313                 // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
314                 // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
315                 let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
316                 self.saw_const_match_error.set(true);
317                 if self.include_lint_checks {
318                     tcx.sess.span_err(self.span, &msg);
319                 } else {
320                     tcx.sess.delay_span_bug(self.span, &msg);
321                 }
322                 PatKind::Wild
323             }
324             // If the type is not structurally comparable, just emit the constant directly,
325             // causing the pattern match code to treat it opaquely.
326             // FIXME: This code doesn't emit errors itself, the caller emits the errors.
327             // So instead of specific errors, you just get blanket errors about the whole
328             // const type. See
329             // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
330             // details.
331             // Backwards compatibility hack because we can't cause hard errors on these
332             // types, so we compare them via `PartialEq::eq` at runtime.
333             ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
334                 if self.include_lint_checks
335                     && !self.saw_const_match_error.get()
336                     && !self.saw_const_match_lint.get()
337                 {
338                     self.saw_const_match_lint.set(true);
339                     tcx.struct_span_lint_hir(
340                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
341                         id,
342                         span,
343                         |lint| {
344                             let msg = format!(
345                                 "to use a constant of type `{}` in a pattern, \
346                                  `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
347                                 cv.ty(),
348                                 cv.ty(),
349                             );
350                             lint.build(&msg).emit();
351                         },
352                     );
353                 }
354                 // Since we are behind a reference, we can just bubble the error up so we get a
355                 // constant at reference type, making it easy to let the fallback call
356                 // `PartialEq::eq` on it.
357                 return Err(fallback_to_const_ref(self));
358             }
359             ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
360                 debug!(
361                     "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
362                     adt_def,
363                     cv.ty()
364                 );
365                 let path = tcx.def_path_str(adt_def.did());
366                 let msg = format!(
367                     "to use a constant of type `{}` in a pattern, \
368                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
369                     path, path,
370                 );
371                 self.saw_const_match_error.set(true);
372                 if self.include_lint_checks {
373                     tcx.sess.span_err(span, &msg);
374                 } else {
375                     tcx.sess.delay_span_bug(span, &msg);
376                 }
377                 PatKind::Wild
378             }
379             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
380                 let destructured = tcx.destructure_mir_constant(param_env, cv);
381
382                 PatKind::Variant {
383                     adt_def: *adt_def,
384                     substs,
385                     variant_index: destructured
386                         .variant
387                         .expect("destructed const of adt without variant id"),
388                     subpatterns: self.field_pats(destructured.fields.iter().copied())?,
389                 }
390             }
391             ty::Tuple(_) | ty::Adt(_, _) => {
392                 let destructured = tcx.destructure_mir_constant(param_env, cv);
393                 PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
394             }
395             ty::Array(..) => PatKind::Array {
396                 prefix: tcx
397                     .destructure_mir_constant(param_env, cv)
398                     .fields
399                     .iter()
400                     .map(|val| self.recur(*val, false))
401                     .collect::<Result<_, _>>()?,
402                 slice: None,
403                 suffix: Box::new([]),
404             },
405             ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
406                 // These are not allowed and will error elsewhere anyway.
407                 ty::Dynamic(..) => {
408                     self.saw_const_match_error.set(true);
409                     let msg = format!("`{}` cannot be used in patterns", cv.ty());
410                     if self.include_lint_checks {
411                         tcx.sess.span_err(span, &msg);
412                     } else {
413                         tcx.sess.delay_span_bug(span, &msg);
414                     }
415                     PatKind::Wild
416                 }
417                 // `&str` is represented as `ConstValue::Slice`, let's keep using this
418                 // optimization for now.
419                 ty::Str => PatKind::Constant { value: cv },
420                 // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
421                 // matching against references, you can only use byte string literals.
422                 // The typechecker has a special case for byte string literals, by treating them
423                 // as slices. This means we turn `&[T; N]` constants into slice patterns, which
424                 // has no negative effects on pattern matching, even if we're actually matching on
425                 // arrays.
426                 ty::Array(..) if !self.treat_byte_string_as_slice => {
427                     let old = self.behind_reference.replace(true);
428                     let array = tcx.deref_mir_constant(self.param_env.and(cv));
429                     let val = PatKind::Deref {
430                         subpattern: Box::new(Pat {
431                             kind: PatKind::Array {
432                                 prefix: tcx
433                                     .destructure_mir_constant(param_env, array)
434                                     .fields
435                                     .iter()
436                                     .map(|val| self.recur(*val, false))
437                                     .collect::<Result<_, _>>()?,
438                                 slice: None,
439                                 suffix: Box::new([]),
440                             },
441                             span,
442                             ty: *pointee_ty,
443                         }),
444                     };
445                     self.behind_reference.set(old);
446                     val
447                 }
448                 ty::Array(elem_ty, _) |
449                 // Cannot merge this with the catch all branch below, because the `const_deref`
450                 // changes the type from slice to array, we need to keep the original type in the
451                 // pattern.
452                 ty::Slice(elem_ty) => {
453                     let old = self.behind_reference.replace(true);
454                     let array = tcx.deref_mir_constant(self.param_env.and(cv));
455                     let val = PatKind::Deref {
456                         subpattern: Box::new(Pat {
457                             kind: PatKind::Slice {
458                                 prefix: tcx
459                                     .destructure_mir_constant(param_env, array)
460                                     .fields
461                                     .iter()
462                                     .map(|val| self.recur(*val, false))
463                                     .collect::<Result<_, _>>()?,
464                                 slice: None,
465                                 suffix: Box::new([]),
466                             },
467                             span,
468                             ty: tcx.mk_slice(elem_ty),
469                         }),
470                     };
471                     self.behind_reference.set(old);
472                     val
473                 }
474                 // Backwards compatibility hack: support references to non-structural types.
475                 // We'll lower
476                 // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
477                 // reference. This makes the rest of the matching logic simpler as it doesn't have
478                 // to figure out how to get a reference again.
479                 ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
480                     if self.behind_reference.get() {
481                         if self.include_lint_checks
482                             && !self.saw_const_match_error.get()
483                             && !self.saw_const_match_lint.get()
484                         {
485                             self.saw_const_match_lint.set(true);
486                             let msg = self.adt_derive_msg(adt_def);
487                             self.tcx().struct_span_lint_hir(
488                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
489                                 self.id,
490                                 self.span,
491                                 |lint| {lint.build(&msg).emit();},
492                             );
493                         }
494                         PatKind::Constant { value: cv }
495                     } else {
496                         if !self.saw_const_match_error.get() {
497                             self.saw_const_match_error.set(true);
498                             let msg = self.adt_derive_msg(adt_def);
499                             if self.include_lint_checks {
500                                 tcx.sess.span_err(span, &msg);
501                             } else {
502                                 tcx.sess.delay_span_bug(span, &msg);
503                             }
504                         }
505                         PatKind::Wild
506                     }
507                 }
508                 // All other references are converted into deref patterns and then recursively
509                 // convert the dereferenced constant to a pattern that is the sub-pattern of the
510                 // deref pattern.
511                 _ => {
512                     if !pointee_ty.is_sized(tcx.at(span), param_env) {
513                         // `tcx.deref_mir_constant()` below will ICE with an unsized type
514                         // (except slices, which are handled in a separate arm above).
515                         let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
516                         if self.include_lint_checks {
517                             tcx.sess.span_err(span, &msg);
518                         } else {
519                             tcx.sess.delay_span_bug(span, &msg);
520                         }
521                         PatKind::Wild
522                     } else {
523                         let old = self.behind_reference.replace(true);
524                         // In case there are structural-match violations somewhere in this subpattern,
525                         // we fall back to a const pattern. If we do not do this, we may end up with
526                         // a !structural-match constant that is not of reference type, which makes it
527                         // very hard to invoke `PartialEq::eq` on it as a fallback.
528                         let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) {
529                             Ok(subpattern) => PatKind::Deref { subpattern },
530                             Err(_) => PatKind::Constant { value: cv },
531                         };
532                         self.behind_reference.set(old);
533                         val
534                     }
535                 }
536             },
537             ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
538                 PatKind::Constant { value: cv }
539             }
540             ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
541                 PatKind::Constant { value: cv }
542             }
543             // FIXME: these can have very surprising behaviour where optimization levels or other
544             // compilation choices change the runtime behaviour of the match.
545             // See https://github.com/rust-lang/rust/issues/70861 for examples.
546             ty::FnPtr(..) | ty::RawPtr(..) => {
547                 if self.include_lint_checks
548                     && !self.saw_const_match_error.get()
549                     && !self.saw_const_match_lint.get()
550                 {
551                     self.saw_const_match_lint.set(true);
552                     let msg = "function pointers and unsized pointers in patterns behave \
553                         unpredictably and should not be relied upon. \
554                         See https://github.com/rust-lang/rust/issues/70861 for details.";
555                     tcx.struct_span_lint_hir(
556                         lint::builtin::POINTER_STRUCTURAL_MATCH,
557                         id,
558                         span,
559                         |lint| {
560                             lint.build(msg).emit();
561                         },
562                     );
563                 }
564                 PatKind::Constant { value: cv }
565             }
566             _ => {
567                 self.saw_const_match_error.set(true);
568                 let msg = format!("`{}` cannot be used in patterns", cv.ty());
569                 if self.include_lint_checks {
570                     tcx.sess.span_err(span, &msg);
571                 } else {
572                     tcx.sess.delay_span_bug(span, &msg);
573                 }
574                 PatKind::Wild
575             }
576         };
577
578         if self.include_lint_checks
579             && !self.saw_const_match_error.get()
580             && !self.saw_const_match_lint.get()
581             && mir_structural_match_violation
582             // FIXME(#73448): Find a way to bring const qualification into parity with
583             // `search_for_structural_match_violation` and then remove this condition.
584             && self.search_for_structural_match_violation(cv.ty()).is_some()
585         {
586             self.saw_const_match_lint.set(true);
587             // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
588             // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
589             let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
590                 "in a pattern,",
591                 "in a pattern, the constant's initializer must be trivial or",
592             );
593             tcx.struct_span_lint_hir(
594                 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
595                 id,
596                 span,
597                 |lint| {
598                     lint.build(&msg).emit();
599                 },
600             );
601         }
602
603         Ok(Box::new(Pat { span, ty: cv.ty(), kind }))
604     }
605 }