]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
implement IsZero for Saturating and Wrapping
[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     ) -> 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     ) -> 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(|_| Pat {
170                 span: self.span,
171                 ty: cv.ty(),
172                 kind: Box::new(PatKind::Constant { value: cv }),
173             });
174
175         if self.include_lint_checks && !self.saw_const_match_error.get() {
176             // If we were able to successfully convert the const to some pat,
177             // double-check that all types in the const implement `Structural`.
178
179             let structural = self.search_for_structural_match_violation(cv.ty());
180             debug!(
181                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
182                 cv.ty(),
183                 structural
184             );
185
186             // This can occur because const qualification treats all associated constants as
187             // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
188             // before it runs.
189             //
190             // FIXME(#73448): Find a way to bring const qualification into parity with
191             // `search_for_structural_match_violation`.
192             if structural.is_none() && mir_structural_match_violation {
193                 warn!("MIR const-checker found novel structural match violation. See #73448.");
194                 return inlined_const_as_pat;
195             }
196
197             if let Some(msg) = structural {
198                 if !self.type_may_have_partial_eq_impl(cv.ty()) {
199                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
200                     self.tcx().sess.span_fatal(self.span, &msg);
201                 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
202                     self.tcx().struct_span_lint_hir(
203                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
204                         self.id,
205                         self.span,
206                         |lint| {
207                             lint.build(&msg).emit();
208                         },
209                     );
210                 } else {
211                     debug!(
212                         "`search_for_structural_match_violation` found one, but `CustomEq` was \
213                           not in the qualifs for that `const`"
214                     );
215                 }
216             }
217         }
218
219         inlined_const_as_pat
220     }
221
222     fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
223         // double-check there even *is* a semantic `PartialEq` to dispatch to.
224         //
225         // (If there isn't, then we can safely issue a hard
226         // error, because that's never worked, due to compiler
227         // using `PartialEq::eq` in this scenario in the past.)
228         let partial_eq_trait_id =
229             self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
230         let obligation: PredicateObligation<'_> = predicate_for_trait_def(
231             self.tcx(),
232             self.param_env,
233             ObligationCause::misc(self.span, self.id),
234             partial_eq_trait_id,
235             0,
236             ty,
237             &[],
238         );
239         // FIXME: should this call a `predicate_must_hold` variant instead?
240
241         let has_impl = self.infcx.predicate_may_hold(&obligation);
242
243         // Note: To fix rust-lang/rust#65466, we could just remove this type
244         // walk hack for function pointers, and unconditionally error
245         // if `PartialEq` is not implemented. However, that breaks stable
246         // code at the moment, because types like `for <'a> fn(&'a ())` do
247         // not *yet* implement `PartialEq`. So for now we leave this here.
248         has_impl
249             || ty.walk().any(|t| match t.unpack() {
250                 ty::subst::GenericArgKind::Lifetime(_) => false,
251                 ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
252                 ty::subst::GenericArgKind::Const(_) => false,
253             })
254     }
255
256     fn field_pats(
257         &self,
258         vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
259     ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
260         vals.enumerate()
261             .map(|(idx, val)| {
262                 let field = Field::new(idx);
263                 Ok(FieldPat { field, pattern: self.recur(val, false)? })
264             })
265             .collect()
266     }
267
268     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
269     #[instrument(skip(self), level = "debug")]
270     fn recur(
271         &self,
272         cv: mir::ConstantKind<'tcx>,
273         mir_structural_match_violation: bool,
274     ) -> Result<Pat<'tcx>, FallbackToConstRef> {
275         let id = self.id;
276         let span = self.span;
277         let tcx = self.tcx();
278         let param_env = self.param_env;
279
280         let kind = match cv.ty().kind() {
281             ty::Float(_) => {
282                 if self.include_lint_checks {
283                     tcx.struct_span_lint_hir(
284                         lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
285                         id,
286                         span,
287                         |lint| {
288                             lint.build("floating-point types cannot be used in patterns").emit();
289                         },
290                     );
291                 }
292                 PatKind::Constant { value: cv }
293             }
294             ty::Adt(adt_def, _) if adt_def.is_union() => {
295                 // Matching on union fields is unsafe, we can't hide it in constants
296                 self.saw_const_match_error.set(true);
297                 let msg = "cannot use unions in constant patterns";
298                 if self.include_lint_checks {
299                     tcx.sess.span_err(span, msg);
300                 } else {
301                     tcx.sess.delay_span_bug(span, msg);
302                 }
303                 PatKind::Wild
304             }
305             ty::Adt(..)
306                 if !self.type_may_have_partial_eq_impl(cv.ty())
307                     // FIXME(#73448): Find a way to bring const qualification into parity with
308                     // `search_for_structural_match_violation` and then remove this condition.
309                     && self.search_for_structural_match_violation(cv.ty()).is_some() =>
310             {
311                 // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
312                 // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
313                 let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
314                 self.saw_const_match_error.set(true);
315                 if self.include_lint_checks {
316                     tcx.sess.span_err(self.span, &msg);
317                 } else {
318                     tcx.sess.delay_span_bug(self.span, &msg);
319                 }
320                 PatKind::Wild
321             }
322             // If the type is not structurally comparable, just emit the constant directly,
323             // causing the pattern match code to treat it opaquely.
324             // FIXME: This code doesn't emit errors itself, the caller emits the errors.
325             // So instead of specific errors, you just get blanket errors about the whole
326             // const type. See
327             // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
328             // details.
329             // Backwards compatibility hack because we can't cause hard errors on these
330             // types, so we compare them via `PartialEq::eq` at runtime.
331             ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
332                 if self.include_lint_checks
333                     && !self.saw_const_match_error.get()
334                     && !self.saw_const_match_lint.get()
335                 {
336                     self.saw_const_match_lint.set(true);
337                     tcx.struct_span_lint_hir(
338                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
339                         id,
340                         span,
341                         |lint| {
342                             let msg = format!(
343                                 "to use a constant of type `{}` in a pattern, \
344                                  `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
345                                 cv.ty(),
346                                 cv.ty(),
347                             );
348                             lint.build(&msg).emit();
349                         },
350                     );
351                 }
352                 // Since we are behind a reference, we can just bubble the error up so we get a
353                 // constant at reference type, making it easy to let the fallback call
354                 // `PartialEq::eq` on it.
355                 return Err(fallback_to_const_ref(self));
356             }
357             ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
358                 debug!(
359                     "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
360                     adt_def,
361                     cv.ty()
362                 );
363                 let path = tcx.def_path_str(adt_def.did());
364                 let msg = format!(
365                     "to use a constant of type `{}` in a pattern, \
366                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
367                     path, path,
368                 );
369                 self.saw_const_match_error.set(true);
370                 if self.include_lint_checks {
371                     tcx.sess.span_err(span, &msg);
372                 } else {
373                     tcx.sess.delay_span_bug(span, &msg);
374                 }
375                 PatKind::Wild
376             }
377             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
378                 let destructured = tcx.destructure_mir_constant(param_env, cv);
379
380                 PatKind::Variant {
381                     adt_def: *adt_def,
382                     substs,
383                     variant_index: destructured
384                         .variant
385                         .expect("destructed const of adt without variant id"),
386                     subpatterns: self.field_pats(destructured.fields.iter().copied())?,
387                 }
388             }
389             ty::Tuple(_) | ty::Adt(_, _) => {
390                 let destructured = tcx.destructure_mir_constant(param_env, cv);
391                 PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
392             }
393             ty::Array(..) => PatKind::Array {
394                 prefix: tcx
395                     .destructure_mir_constant(param_env, cv)
396                     .fields
397                     .iter()
398                     .map(|val| self.recur(*val, false))
399                     .collect::<Result<_, _>>()?,
400                 slice: None,
401                 suffix: Vec::new(),
402             },
403             ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
404                 // These are not allowed and will error elsewhere anyway.
405                 ty::Dynamic(..) => {
406                     self.saw_const_match_error.set(true);
407                     let msg = format!("`{}` cannot be used in patterns", cv.ty());
408                     if self.include_lint_checks {
409                         tcx.sess.span_err(span, &msg);
410                     } else {
411                         tcx.sess.delay_span_bug(span, &msg);
412                     }
413                     PatKind::Wild
414                 }
415                 // `&str` is represented as `ConstValue::Slice`, let's keep using this
416                 // optimization for now.
417                 ty::Str => PatKind::Constant { value: cv },
418                 // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
419                 // matching against references, you can only use byte string literals.
420                 // The typechecker has a special case for byte string literals, by treating them
421                 // as slices. This means we turn `&[T; N]` constants into slice patterns, which
422                 // has no negative effects on pattern matching, even if we're actually matching on
423                 // arrays.
424                 ty::Array(..) if !self.treat_byte_string_as_slice => {
425                     let old = self.behind_reference.replace(true);
426                     let array = tcx.deref_mir_constant(self.param_env.and(cv));
427                     let val = PatKind::Deref {
428                         subpattern: Pat {
429                             kind: Box::new(PatKind::Array {
430                                 prefix: tcx
431                                     .destructure_mir_constant(param_env, array)
432                                     .fields
433                                     .iter()
434                                     .map(|val| self.recur(*val, false))
435                                     .collect::<Result<_, _>>()?,
436                                 slice: None,
437                                 suffix: vec![],
438                             }),
439                             span,
440                             ty: *pointee_ty,
441                         },
442                     };
443                     self.behind_reference.set(old);
444                     val
445                 }
446                 ty::Array(elem_ty, _) |
447                 // Cannot merge this with the catch all branch below, because the `const_deref`
448                 // changes the type from slice to array, we need to keep the original type in the
449                 // pattern.
450                 ty::Slice(elem_ty) => {
451                     let old = self.behind_reference.replace(true);
452                     let array = tcx.deref_mir_constant(self.param_env.and(cv));
453                     let val = PatKind::Deref {
454                         subpattern: Pat {
455                             kind: Box::new(PatKind::Slice {
456                                 prefix: tcx
457                                     .destructure_mir_constant(param_env, array)
458                                     .fields
459                                     .iter()
460                                     .map(|val| self.recur(*val, false))
461                                     .collect::<Result<_, _>>()?,
462                                 slice: None,
463                                 suffix: vec![],
464                             }),
465                             span,
466                             ty: tcx.mk_slice(elem_ty),
467                         },
468                     };
469                     self.behind_reference.set(old);
470                     val
471                 }
472                 // Backwards compatibility hack: support references to non-structural types.
473                 // We'll lower
474                 // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
475                 // reference. This makes the rest of the matching logic simpler as it doesn't have
476                 // to figure out how to get a reference again.
477                 ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
478                     if self.behind_reference.get() {
479                         if self.include_lint_checks
480                             && !self.saw_const_match_error.get()
481                             && !self.saw_const_match_lint.get()
482                         {
483                             self.saw_const_match_lint.set(true);
484                             let msg = self.adt_derive_msg(adt_def);
485                             self.tcx().struct_span_lint_hir(
486                                 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
487                                 self.id,
488                                 self.span,
489                                 |lint| {lint.build(&msg).emit();},
490                             );
491                         }
492                         PatKind::Constant { value: cv }
493                     } else {
494                         if !self.saw_const_match_error.get() {
495                             self.saw_const_match_error.set(true);
496                             let msg = self.adt_derive_msg(adt_def);
497                             if self.include_lint_checks {
498                                 tcx.sess.span_err(span, &msg);
499                             } else {
500                                 tcx.sess.delay_span_bug(span, &msg);
501                             }
502                         }
503                         PatKind::Wild
504                     }
505                 }
506                 // All other references are converted into deref patterns and then recursively
507                 // convert the dereferenced constant to a pattern that is the sub-pattern of the
508                 // deref pattern.
509                 _ => {
510                     if !pointee_ty.is_sized(tcx.at(span), param_env) {
511                         // `tcx.deref_mir_constant()` below will ICE with an unsized type
512                         // (except slices, which are handled in a separate arm above).
513                         let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
514                         if self.include_lint_checks {
515                             tcx.sess.span_err(span, &msg);
516                         } else {
517                             tcx.sess.delay_span_bug(span, &msg);
518                         }
519                         PatKind::Wild
520                     } else {
521                         let old = self.behind_reference.replace(true);
522                         // In case there are structural-match violations somewhere in this subpattern,
523                         // we fall back to a const pattern. If we do not do this, we may end up with
524                         // a !structural-match constant that is not of reference type, which makes it
525                         // very hard to invoke `PartialEq::eq` on it as a fallback.
526                         let val = match self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false) {
527                             Ok(subpattern) => PatKind::Deref { subpattern },
528                             Err(_) => PatKind::Constant { value: cv },
529                         };
530                         self.behind_reference.set(old);
531                         val
532                     }
533                 }
534             },
535             ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
536                 PatKind::Constant { value: cv }
537             }
538             ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
539                 PatKind::Constant { value: cv }
540             }
541             // FIXME: these can have very surprising behaviour where optimization levels or other
542             // compilation choices change the runtime behaviour of the match.
543             // See https://github.com/rust-lang/rust/issues/70861 for examples.
544             ty::FnPtr(..) | ty::RawPtr(..) => {
545                 if self.include_lint_checks
546                     && !self.saw_const_match_error.get()
547                     && !self.saw_const_match_lint.get()
548                 {
549                     self.saw_const_match_lint.set(true);
550                     let msg = "function pointers and unsized pointers in patterns behave \
551                         unpredictably and should not be relied upon. \
552                         See https://github.com/rust-lang/rust/issues/70861 for details.";
553                     tcx.struct_span_lint_hir(
554                         lint::builtin::POINTER_STRUCTURAL_MATCH,
555                         id,
556                         span,
557                         |lint| {
558                             lint.build(msg).emit();
559                         },
560                     );
561                 }
562                 PatKind::Constant { value: cv }
563             }
564             _ => {
565                 self.saw_const_match_error.set(true);
566                 let msg = format!("`{}` cannot be used in patterns", cv.ty());
567                 if self.include_lint_checks {
568                     tcx.sess.span_err(span, &msg);
569                 } else {
570                     tcx.sess.delay_span_bug(span, &msg);
571                 }
572                 PatKind::Wild
573             }
574         };
575
576         if self.include_lint_checks
577             && !self.saw_const_match_error.get()
578             && !self.saw_const_match_lint.get()
579             && mir_structural_match_violation
580             // FIXME(#73448): Find a way to bring const qualification into parity with
581             // `search_for_structural_match_violation` and then remove this condition.
582             && self.search_for_structural_match_violation(cv.ty()).is_some()
583         {
584             self.saw_const_match_lint.set(true);
585             // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
586             // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
587             let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
588                 "in a pattern,",
589                 "in a pattern, the constant's initializer must be trivial or",
590             );
591             tcx.struct_span_lint_hir(
592                 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
593                 id,
594                 span,
595                 |lint| {
596                     lint.build(&msg).emit();
597                 },
598             );
599         }
600
601         Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) })
602     }
603 }