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;
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};
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(
28 mir_structural_match_violation: bool,
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)
40 struct ConstToPat<'a, 'tcx> {
43 param_env: ty::ParamEnv<'tcx>,
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
48 saw_const_match_error: Cell<bool>,
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
53 saw_const_match_lint: Cell<bool>,
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>,
59 // inference context used for checking `T: Structural` bounds.
60 infcx: InferCtxt<'a, 'tcx>,
62 include_lint_checks: bool,
64 treat_byte_string_as_slice: bool,
67 mod fallback_to_const_ref {
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(());
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(())
83 use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
85 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
87 pat_ctxt: &PatCtxt<'_, 'tcx>,
90 infcx: InferCtxt<'a, 'tcx>,
92 trace!(?pat_ctxt.typeck_results.hir_owner);
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
104 .treat_byte_string_as_slice
105 .contains(&id.local_id),
109 fn tcx(&self) -> TyCtxt<'tcx> {
113 fn adt_derive_msg(&self, adt_def: &AdtDef) -> String {
114 let path = self.tcx().def_path_str(adt_def.did);
116 "to use a constant of type `{}` in a pattern, \
117 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
122 fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
123 traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
124 with_no_trimmed_paths(|| match non_sm_ty {
125 traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt),
126 traits::NonStructuralMatchTy::Dynamic => {
127 "trait objects cannot be used in patterns".to_string()
129 traits::NonStructuralMatchTy::Opaque => {
130 "opaque types cannot be used in patterns".to_string()
132 traits::NonStructuralMatchTy::Closure => {
133 "closures cannot be used in patterns".to_string()
135 traits::NonStructuralMatchTy::Generator => {
136 "generators cannot be used in patterns".to_string()
138 traits::NonStructuralMatchTy::Param => {
139 bug!("use of a constant whose type is a parameter inside a pattern")
141 traits::NonStructuralMatchTy::Projection => {
142 bug!("use of a constant whose type is a projection inside a pattern")
144 traits::NonStructuralMatchTy::Foreign => {
145 bug!("use of a value of a foreign type inside a pattern")
151 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
152 ty.is_structural_eq_shallow(self.infcx.tcx)
155 fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> {
156 trace!(self.treat_byte_string_as_slice);
157 // This method is just a wrapper handling a validity check; the heavy lifting is
158 // performed by the recursive `recur` method, which is not meant to be
159 // invoked except by this method.
161 // once indirect_structural_match is a full fledged error, this
162 // level of indirection can be eliminated
164 let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
166 if self.include_lint_checks && !self.saw_const_match_error.get() {
167 // If we were able to successfully convert the const to some pat,
168 // double-check that all types in the const implement `Structural`.
170 let structural = self.search_for_structural_match_violation(cv.ty());
172 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
177 // This can occur because const qualification treats all associated constants as
178 // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
181 // FIXME(#73448): Find a way to bring const qualification into parity with
182 // `search_for_structural_match_violation`.
183 if structural.is_none() && mir_structural_match_violation {
184 warn!("MIR const-checker found novel structural match violation. See #73448.");
185 return inlined_const_as_pat;
188 if let Some(msg) = structural {
189 if !self.type_may_have_partial_eq_impl(cv.ty()) {
190 // span_fatal avoids ICE from resolution of non-existent method (rare case).
191 self.tcx().sess.span_fatal(self.span, &msg);
192 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
193 self.tcx().struct_span_lint_hir(
194 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
197 |lint| lint.build(&msg).emit(),
201 "`search_for_structural_match_violation` found one, but `CustomEq` was \
202 not in the qualifs for that `const`"
211 fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
212 // double-check there even *is* a semantic `PartialEq` to dispatch to.
214 // (If there isn't, then we can safely issue a hard
215 // error, because that's never worked, due to compiler
216 // using `PartialEq::eq` in this scenario in the past.)
217 let partial_eq_trait_id =
218 self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
219 let obligation: PredicateObligation<'_> = predicate_for_trait_def(
222 ObligationCause::misc(self.span, self.id),
228 // FIXME: should this call a `predicate_must_hold` variant instead?
230 let has_impl = self.infcx.predicate_may_hold(&obligation);
232 // Note: To fix rust-lang/rust#65466, we could just remove this type
233 // walk hack for function pointers, and unconditionally error
234 // if `PartialEq` is not implemented. However, that breaks stable
235 // code at the moment, because types like `for <'a> fn(&'a ())` do
236 // not *yet* implement `PartialEq`. So for now we leave this here.
238 || ty.walk().any(|t| match t.unpack() {
239 ty::subst::GenericArgKind::Lifetime(_) => false,
240 ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
241 ty::subst::GenericArgKind::Const(_) => false,
247 vals: impl Iterator<Item = ty::Const<'tcx>>,
248 ) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
251 let field = Field::new(idx);
252 Ok(FieldPat { field, pattern: self.recur(val, false)? })
257 // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
261 mir_structural_match_violation: bool,
262 ) -> Result<Pat<'tcx>, FallbackToConstRef> {
264 let span = self.span;
265 let tcx = self.tcx();
266 let param_env = self.param_env;
268 let kind = match cv.ty().kind() {
270 if self.include_lint_checks {
271 tcx.struct_span_lint_hir(
272 lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
275 |lint| lint.build("floating-point types cannot be used in patterns").emit(),
278 PatKind::Constant { value: cv }
280 ty::Adt(adt_def, _) if adt_def.is_union() => {
281 // Matching on union fields is unsafe, we can't hide it in constants
282 self.saw_const_match_error.set(true);
283 let msg = "cannot use unions in constant patterns";
284 if self.include_lint_checks {
285 tcx.sess.span_err(span, msg);
287 tcx.sess.delay_span_bug(span, msg)
292 if !self.type_may_have_partial_eq_impl(cv.ty())
293 // FIXME(#73448): Find a way to bring const qualification into parity with
294 // `search_for_structural_match_violation` and then remove this condition.
295 && self.search_for_structural_match_violation(cv.ty()).is_some() =>
297 // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
298 // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
299 let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
300 self.saw_const_match_error.set(true);
301 if self.include_lint_checks {
302 tcx.sess.span_err(self.span, &msg);
304 tcx.sess.delay_span_bug(self.span, &msg)
308 // If the type is not structurally comparable, just emit the constant directly,
309 // causing the pattern match code to treat it opaquely.
310 // FIXME: This code doesn't emit errors itself, the caller emits the errors.
311 // So instead of specific errors, you just get blanket errors about the whole
313 // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
315 // Backwards compatibility hack because we can't cause hard errors on these
316 // types, so we compare them via `PartialEq::eq` at runtime.
317 ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
318 if self.include_lint_checks
319 && !self.saw_const_match_error.get()
320 && !self.saw_const_match_lint.get()
322 self.saw_const_match_lint.set(true);
323 tcx.struct_span_lint_hir(
324 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
329 "to use a constant of type `{}` in a pattern, \
330 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
334 lint.build(&msg).emit()
338 // Since we are behind a reference, we can just bubble the error up so we get a
339 // constant at reference type, making it easy to let the fallback call
340 // `PartialEq::eq` on it.
341 return Err(fallback_to_const_ref(self));
343 ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
345 "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
349 let path = tcx.def_path_str(adt_def.did);
351 "to use a constant of type `{}` in a pattern, \
352 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
355 self.saw_const_match_error.set(true);
356 if self.include_lint_checks {
357 tcx.sess.span_err(span, &msg);
359 tcx.sess.delay_span_bug(span, &msg)
363 ty::Adt(adt_def, substs) if adt_def.is_enum() => {
364 let destructured = tcx.destructure_const(param_env.and(cv));
368 variant_index: destructured
370 .expect("destructed const of adt without variant id"),
371 subpatterns: self.field_pats(destructured.fields.iter().copied())?,
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())? }
378 ty::Array(..) => PatKind::Array {
380 .destructure_const(param_env.and(cv))
383 .map(|val| self.recur(*val, false))
384 .collect::<Result<_, _>>()?,
388 ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
389 // These are not allowed and will error elsewhere anyway.
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);
396 tcx.sess.delay_span_bug(span, &msg)
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
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 {
414 kind: Box::new(PatKind::Array {
416 .destructure_const(param_env.and(array))
419 .map(|val| self.recur(*val, false))
420 .collect::<Result<_, _>>()?,
428 self.behind_reference.set(old);
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
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 {
440 kind: Box::new(PatKind::Slice {
442 .destructure_const(param_env.and(array))
445 .map(|val| self.recur(*val, false))
446 .collect::<Result<_, _>>()?,
451 ty: tcx.mk_slice(elem_ty),
454 self.behind_reference.set(old);
457 // Backwards compatibility hack: support references to non-structural types.
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()
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,
474 |lint| lint.build(&msg).emit(),
477 PatKind::Constant { value: cv }
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);
485 tcx.sess.delay_span_bug(span, &msg)
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
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);
502 tcx.sess.delay_span_bug(span, &msg);
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 },
515 self.behind_reference.set(old);
520 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
521 PatKind::Constant { value: cv }
523 ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
524 PatKind::Constant { value: cv }
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()
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,
542 |lint| lint.build(&msg).emit(),
545 PatKind::Constant { value: cv }
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);
553 tcx.sess.delay_span_bug(span, &msg)
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()
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(
572 "in a pattern, the constant's initializer must be trivial or",
574 tcx.struct_span_lint_hir(
575 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
578 |lint| lint.build(&msg).emit(),
582 Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) })