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;
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};
15 use super::{FieldPat, Pat, PatCtxt, PatKind};
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(
23 cv: &'tcx ty::Const<'tcx>,
26 mir_structural_match_violation: bool,
28 debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
29 debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
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)
36 debug!("const_to_pat: pat={:?}", pat);
41 struct ConstToPat<'a, 'tcx> {
44 param_env: ty::ParamEnv<'tcx>,
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
49 saw_const_match_error: Cell<bool>,
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
54 saw_const_match_lint: Cell<bool>,
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>,
60 // inference context used for checking `T: Structural` bounds.
61 infcx: InferCtxt<'a, 'tcx>,
63 include_lint_checks: bool,
67 struct FallbackToConstRef;
69 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
71 pat_ctxt: &PatCtxt<'_, 'tcx>,
74 infcx: InferCtxt<'a, 'tcx>,
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),
88 fn tcx(&self) -> TyCtxt<'tcx> {
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(
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);
99 "to use a constant of type `{}` in a pattern, \
100 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
104 traits::NonStructuralMatchTy::Dynamic => {
105 "trait objects cannot be used in patterns".to_string()
107 traits::NonStructuralMatchTy::Opaque => {
108 "opaque types cannot be used in patterns".to_string()
110 traits::NonStructuralMatchTy::Generator => {
111 "generators cannot be used in patterns".to_string()
113 traits::NonStructuralMatchTy::Closure => {
114 "closures cannot be used in patterns".to_string()
116 traits::NonStructuralMatchTy::Param => {
117 bug!("use of a constant whose type is a parameter inside a pattern")
119 traits::NonStructuralMatchTy::Projection => {
120 bug!("use of a constant whose type is a projection inside a pattern")
122 traits::NonStructuralMatchTy::Foreign => {
123 bug!("use of a value of a foreign type inside a pattern")
130 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
131 ty.is_structural_eq_shallow(self.infcx.tcx)
136 cv: &'tcx ty::Const<'tcx>,
137 mir_structural_match_violation: bool,
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.
143 // once indirect_structural_match is a full fledged error, this
144 // level of indirection can be eliminated
146 let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
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`.
152 let structural = self.search_for_structural_match_violation(cv.ty);
154 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
158 // This can occur because const qualification treats all associated constants as
159 // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
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;
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,
178 |lint| lint.build(&msg).emit(),
182 "`search_for_structural_match_violation` found one, but `CustomEq` was \
183 not in the qualifs for that `const`"
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.
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(
203 ObligationCause::misc(self.span, self.id),
209 // FIXME: should this call a `predicate_must_hold` variant instead?
211 let has_impl = self.infcx.predicate_may_hold(&obligation);
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.
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,
226 // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
229 cv: &'tcx ty::Const<'tcx>,
230 mir_structural_match_violation: bool,
231 ) -> Result<Pat<'tcx>, FallbackToConstRef> {
233 let span = self.span;
234 let tcx = self.tcx();
235 let param_env = self.param_env;
237 let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| -> Result<_, _> {
241 let field = Field::new(idx);
242 Ok(FieldPat { field, pattern: self.recur(val, false)? })
247 let kind = match cv.ty.kind() {
249 tcx.struct_span_lint_hir(
250 lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
253 |lint| lint.build("floating-point types cannot be used in patterns").emit(),
255 PatKind::Constant { value: cv }
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);
264 tcx.sess.delay_span_bug(span, msg)
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() =>
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);
281 tcx.sess.delay_span_bug(self.span, &msg)
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
290 // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
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()
299 self.saw_const_match_lint.set(true);
301 "to use a constant of type `{}` in a pattern, \
302 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
305 tcx.struct_span_lint_hir(
306 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
309 |lint| lint.build(&msg).emit(),
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);
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);
321 "to use a constant of type `{}` in a pattern, \
322 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
325 self.saw_const_match_error.set(true);
326 if self.include_lint_checks {
327 tcx.sess.span_err(span, &msg);
329 tcx.sess.delay_span_bug(span, &msg)
333 ty::Adt(adt_def, substs) if adt_def.is_enum() => {
334 let destructured = tcx.destructure_const(param_env.and(cv));
338 variant_index: destructured
340 .expect("destructed const of adt without variant id"),
341 subpatterns: field_pats(destructured.fields)?,
344 ty::Tuple(_) | ty::Adt(_, _) => {
345 let destructured = tcx.destructure_const(param_env.and(cv));
346 PatKind::Leaf { subpatterns: field_pats(destructured.fields)? }
348 ty::Array(..) => PatKind::Array {
350 .destructure_const(param_env.and(cv))
353 .map(|val| self.recur(val, false))
354 .collect::<Result<_, _>>()?,
358 ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
359 // These are not allowed and will error elsewhere anyway.
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);
366 tcx.sess.delay_span_bug(span, &msg)
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
382 let old = self.behind_reference.replace(true);
383 let array = tcx.deref_const(self.param_env.and(cv));
384 let val = PatKind::Deref {
386 kind: Box::new(PatKind::Slice {
388 .destructure_const(param_env.and(array))
391 .map(|val| self.recur(val, false))
392 .collect::<Result<_, _>>()?,
400 self.behind_reference.set(old);
403 // Backwards compatibility hack: support references to non-structural types.
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()
414 self.saw_const_match_lint.set(true);
415 let path = self.tcx().def_path_str(adt_def.did);
417 "to use a constant of type `{}` in a pattern, \
418 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
421 self.tcx().struct_span_lint_hir(
422 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
425 |lint| lint.build(&msg).emit(),
428 PatKind::Constant { value: cv }
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);
434 "to use a constant of type `{}` in a pattern, \
435 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
438 if self.include_lint_checks {
439 tcx.sess.span_err(span, &msg);
441 tcx.sess.delay_span_bug(span, &msg)
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
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 },
460 self.behind_reference.set(old);
464 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
465 PatKind::Constant { value: cv }
467 ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
468 PatKind::Constant { value: cv }
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()
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,
486 |lint| lint.build(&msg).emit(),
489 PatKind::Constant { value: cv }
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);
497 tcx.sess.delay_span_bug(span, &msg)
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()
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(
516 "in a pattern, the constant's initializer must be trivial or",
518 tcx.struct_span_lint_hir(
519 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
522 |lint| lint.build(&msg).emit(),
526 Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) })