1 use crate::const_eval::const_variant_index;
6 use rustc::infer::InferCtxt;
7 use rustc::traits::{ObligationCause, PredicateObligation};
8 use rustc::ty::{self, Ty, TyCtxt};
10 use rustc_index::vec::Idx;
17 use super::{FieldPat, Pat, PatCtxt, PatKind};
19 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
20 /// Converts an evaluated constant to a pattern (if possible).
21 /// This means aggregate values (like structs and enums) are converted
22 /// to a pattern that matches the value (as if you'd compared via structural equality).
23 pub(super) fn const_to_pat(
25 cv: &'tcx ty::Const<'tcx>,
29 debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
30 debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
32 self.tcx.infer_ctxt().enter(|infcx| {
33 let mut convert = ConstToPat::new(self, id, span, infcx);
39 struct ConstToPat<'a, 'tcx> {
42 param_env: ty::ParamEnv<'tcx>,
44 // This tracks if we signal some hard error for a given const value, so that
45 // we will not subsequently issue an irrelevant lint for the same const
47 saw_const_match_error: Cell<bool>,
49 // inference context used for checking `T: Structural` bounds.
50 infcx: InferCtxt<'a, 'tcx>,
52 include_lint_checks: bool,
55 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
56 fn new(pat_ctxt: &PatCtxt<'_, 'tcx>,
59 infcx: InferCtxt<'a, 'tcx>) -> Self {
62 param_env: pat_ctxt.param_env,
63 include_lint_checks: pat_ctxt.include_lint_checks,
64 saw_const_match_error: Cell::new(false),
68 fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx }
70 fn search_for_structural_match_violation(&self,
72 -> Option<ty::NonStructuralMatchTy<'tcx>>
74 ty::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
77 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
78 ty::type_marked_structural(self.id, self.span, &self.infcx, ty)
81 fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
82 // This method is just a wrapper handling a validity check; the heavy lifting is
83 // performed by the recursive `recur` method, which is not meant to be
84 // invoked except by this method.
86 // once indirect_structural_match is a full fledged error, this
87 // level of indirection can be eliminated
89 let inlined_const_as_pat = self.recur(cv);
91 if self.include_lint_checks && !self.saw_const_match_error.get() {
92 // If we were able to successfully convert the const to some pat,
93 // double-check that all types in the const implement `Structural`.
95 let structural = self.search_for_structural_match_violation(cv.ty);
96 debug!("search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
98 if let Some(non_sm_ty) = structural {
99 let adt_def = match non_sm_ty {
100 ty::NonStructuralMatchTy::Adt(adt_def) => adt_def,
101 ty::NonStructuralMatchTy::Param =>
102 bug!("use of constant whose type is a parameter inside a pattern"),
104 let path = self.tcx().def_path_str(adt_def.did);
106 "to use a constant of type `{}` in a pattern, \
107 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
112 // double-check there even *is* a semantic `PartialEq` to dispatch to.
114 // (If there isn't, then we can safely issue a hard
115 // error, because that's never worked, due to compiler
116 // using `PartialEq::eq` in this scenario in the past.)
118 // Note: To fix rust-lang/rust#65466, one could lift this check
119 // *before* any structural-match checking, and unconditionally error
120 // if `PartialEq` is not implemented. However, that breaks stable
121 // code at the moment, because types like `for <'a> fn(&'a ())` do
122 // not *yet* implement `PartialEq`. So for now we leave this here.
123 let ty_is_partial_eq: bool = {
124 let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap();
125 let obligation: PredicateObligation<'_> =
126 self.tcx().predicate_for_trait_def(
128 ObligationCause::misc(self.span, self.id),
133 // FIXME: should this call a `predicate_must_hold` variant instead?
134 self.infcx.predicate_may_hold(&obligation)
137 if !ty_is_partial_eq {
138 // span_fatal avoids ICE from resolution of non-existent method (rare case).
139 self.tcx().sess.span_fatal(self.span, &msg);
141 self.tcx().lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH,
152 // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
153 fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
155 let span = self.span;
156 let tcx = self.tcx();
157 let param_env = self.param_env;
159 let adt_subpattern = |i, variant_opt| {
160 let field = Field::new(i);
161 let val = crate::const_eval::const_field(
162 tcx, param_env, variant_opt, field, cv
166 let adt_subpatterns = |n, variant_opt| {
168 let field = Field::new(i);
171 pattern: adt_subpattern(i, variant_opt),
173 }).collect::<Vec<_>>()
177 let kind = match cv.ty.kind {
180 ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
183 "floating-point types cannot be used in patterns",
189 ty::Adt(adt_def, _) if adt_def.is_union() => {
190 // Matching on union fields is unsafe, we can't hide it in constants
191 self.saw_const_match_error.set(true);
192 tcx.sess.span_err(span, "cannot use unions in constant patterns");
195 // keep old code until future-compat upgraded to errors.
196 ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
197 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
199 let path = tcx.def_path_str(adt_def.did);
201 "to use a constant of type `{}` in a pattern, \
202 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
206 self.saw_const_match_error.set(true);
207 tcx.sess.span_err(span, &msg);
210 // keep old code until future-compat upgraded to errors.
211 ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
212 if !self.type_marked_structural(adt_ty) =>
214 let adt_def = if let ty::Adt(adt_def, _) = adt_ty.kind {
220 debug!("adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
223 // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
224 // would be wrong. Returnging `PatKind::Wild` is not technically correct.
225 let path = tcx.def_path_str(adt_def.did);
227 "to use a constant of type `{}` in a pattern, \
228 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
232 self.saw_const_match_error.set(true);
233 tcx.sess.span_err(span, &msg);
236 ty::Adt(adt_def, substs) if adt_def.is_enum() => {
237 let variant_index = const_variant_index(tcx, self.param_env, cv);
238 let subpatterns = adt_subpatterns(
239 adt_def.variants[variant_index].fields.len(),
249 ty::Adt(adt_def, _) => {
250 let struct_var = adt_def.non_enum_variant();
252 subpatterns: adt_subpatterns(struct_var.fields.len(), None),
255 ty::Tuple(fields) => {
257 subpatterns: adt_subpatterns(fields.len(), None),
262 prefix: (0..n.eval_usize(tcx, self.param_env))
263 .map(|i| adt_subpattern(i as usize, None))
279 kind: Box::new(kind),