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