2 use rustc_hir::lang_items::EqTraitLangItem;
3 use rustc_index::vec::Idx;
4 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
5 use rustc_middle::mir::Field;
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 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)
38 struct ConstToPat<'a, 'tcx> {
41 param_env: ty::ParamEnv<'tcx>,
43 // This tracks if we signal some hard error for a given const value, so that
44 // we will not subsequently issue an irrelevant lint for the same const
46 saw_const_match_error: Cell<bool>,
48 // inference context used for checking `T: Structural` bounds.
49 infcx: InferCtxt<'a, 'tcx>,
51 include_lint_checks: bool,
54 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
56 pat_ctxt: &PatCtxt<'_, 'tcx>,
59 infcx: InferCtxt<'a, 'tcx>,
65 param_env: pat_ctxt.param_env,
66 include_lint_checks: pat_ctxt.include_lint_checks,
67 saw_const_match_error: Cell::new(false),
71 fn tcx(&self) -> TyCtxt<'tcx> {
75 fn search_for_structural_match_violation(
78 ) -> Option<traits::NonStructuralMatchTy<'tcx>> {
79 traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
82 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
83 ty.is_structural_eq_shallow(self.infcx.tcx)
88 cv: &'tcx ty::Const<'tcx>,
89 mir_structural_match_violation: bool,
91 // This method is just a wrapper handling a validity check; the heavy lifting is
92 // performed by the recursive `recur` method, which is not meant to be
93 // invoked except by this method.
95 // once indirect_structural_match is a full fledged error, this
96 // level of indirection can be eliminated
98 let inlined_const_as_pat = self.recur(cv);
100 if self.include_lint_checks && !self.saw_const_match_error.get() {
101 // If we were able to successfully convert the const to some pat,
102 // double-check that all types in the const implement `Structural`.
104 let structural = self.search_for_structural_match_violation(cv.ty);
106 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
110 if structural.is_none() && mir_structural_match_violation {
111 bug!("MIR const-checker found novel structural match violation");
114 if let Some(non_sm_ty) = structural {
115 let msg = match non_sm_ty {
116 traits::NonStructuralMatchTy::Adt(adt_def) => {
117 let path = self.tcx().def_path_str(adt_def.did);
119 "to use a constant of type `{}` in a pattern, \
120 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
124 traits::NonStructuralMatchTy::Dynamic => {
125 "trait objects cannot be used in patterns".to_string()
127 traits::NonStructuralMatchTy::Opaque => {
128 "opaque types cannot be used in patterns".to_string()
130 traits::NonStructuralMatchTy::Generator => {
131 "generators cannot be used in patterns".to_string()
133 traits::NonStructuralMatchTy::Closure => {
134 "closures cannot be used in patterns".to_string()
136 traits::NonStructuralMatchTy::Param => {
137 bug!("use of a constant whose type is a parameter inside a pattern")
139 traits::NonStructuralMatchTy::Projection => {
140 bug!("use of a constant whose type is a projection inside a pattern")
142 traits::NonStructuralMatchTy::Foreign => {
143 bug!("use of a value of a foreign type inside a pattern")
147 // double-check there even *is* a semantic `PartialEq` to dispatch to.
149 // (If there isn't, then we can safely issue a hard
150 // error, because that's never worked, due to compiler
151 // using `PartialEq::eq` in this scenario in the past.)
153 // Note: To fix rust-lang/rust#65466, one could lift this check
154 // *before* any structural-match checking, and unconditionally error
155 // if `PartialEq` is not implemented. However, that breaks stable
156 // code at the moment, because types like `for <'a> fn(&'a ())` do
157 // not *yet* implement `PartialEq`. So for now we leave this here.
158 let ty_is_partial_eq: bool = {
159 let partial_eq_trait_id =
160 self.tcx().require_lang_item(EqTraitLangItem, Some(self.span));
161 let obligation: PredicateObligation<'_> = predicate_for_trait_def(
164 ObligationCause::misc(self.span, self.id),
170 // FIXME: should this call a `predicate_must_hold` variant instead?
171 self.infcx.predicate_may_hold(&obligation)
174 if !ty_is_partial_eq {
175 // span_fatal avoids ICE from resolution of non-existent method (rare case).
176 self.tcx().sess.span_fatal(self.span, &msg);
177 } else if mir_structural_match_violation {
178 self.tcx().struct_span_lint_hir(
179 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
182 |lint| lint.build(&msg).emit(),
186 "`search_for_structural_match_violation` found one, but `CustomEq` was \
187 not in the qualifs for that `const`"
196 // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
197 fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
199 let span = self.span;
200 let tcx = self.tcx();
201 let param_env = self.param_env;
203 let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| {
207 let field = Field::new(idx);
208 FieldPat { field, pattern: self.recur(val) }
213 let kind = match cv.ty.kind {
215 tcx.struct_span_lint_hir(
216 lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
219 |lint| lint.build("floating-point types cannot be used in patterns").emit(),
221 PatKind::Constant { value: cv }
223 ty::Adt(adt_def, _) if adt_def.is_union() => {
224 // Matching on union fields is unsafe, we can't hide it in constants
225 self.saw_const_match_error.set(true);
226 tcx.sess.span_err(span, "cannot use unions in constant patterns");
229 // keep old code until future-compat upgraded to errors.
230 ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
231 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
232 let path = tcx.def_path_str(adt_def.did);
234 "to use a constant of type `{}` in a pattern, \
235 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
238 self.saw_const_match_error.set(true);
239 tcx.sess.span_err(span, &msg);
242 // keep old code until future-compat upgraded to errors.
243 ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
244 if !self.type_marked_structural(adt_ty) =>
247 if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() };
250 "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
254 // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
255 // would be wrong. Returnging `PatKind::Wild` is not technically correct.
256 let path = tcx.def_path_str(adt_def.did);
258 "to use a constant of type `{}` in a pattern, \
259 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
262 self.saw_const_match_error.set(true);
263 tcx.sess.span_err(span, &msg);
266 ty::Adt(adt_def, substs) if adt_def.is_enum() => {
267 let destructured = tcx.destructure_const(param_env.and(cv));
271 variant_index: destructured.variant,
272 subpatterns: field_pats(destructured.fields),
276 let destructured = tcx.destructure_const(param_env.and(cv));
277 PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
280 let destructured = tcx.destructure_const(param_env.and(cv));
281 PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
283 ty::Array(..) => PatKind::Array {
285 .destructure_const(param_env.and(cv))
288 .map(|val| self.recur(val))
293 _ => PatKind::Constant { value: cv },
296 Pat { span, ty: cv.ty, kind: Box::new(kind) }