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