]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir_build/hair/pattern/const_to_pat.rs
Rollup merge of #70038 - DutchGhost:const-forget-tests, r=RalfJung
[rust.git] / src / librustc_mir_build / hair / pattern / const_to_pat.rs
1 use rustc::mir::Field;
2 use rustc::ty::{self, Ty, TyCtxt};
3 use rustc_hir as hir;
4 use rustc_index::vec::Idx;
5 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
6 use rustc_session::lint;
7 use rustc_span::Span;
8 use rustc_trait_selection::traits::predicate_for_trait_def;
9 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
10 use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
11
12 use std::cell::Cell;
13
14 use super::{FieldPat, Pat, PatCtxt, PatKind};
15
16 impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
17     /// Converts an evaluated constant to a pattern (if possible).
18     /// This means aggregate values (like structs and enums) are converted
19     /// to a pattern that matches the value (as if you'd compared via structural equality).
20     pub(super) fn const_to_pat(
21         &self,
22         cv: &'tcx ty::Const<'tcx>,
23         id: hir::HirId,
24         span: Span,
25     ) -> Pat<'tcx> {
26         debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
27         debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
28
29         self.tcx.infer_ctxt().enter(|infcx| {
30             let mut convert = ConstToPat::new(self, id, span, infcx);
31             convert.to_pat(cv)
32         })
33     }
34 }
35
36 struct ConstToPat<'a, 'tcx> {
37     id: hir::HirId,
38     span: Span,
39     param_env: ty::ParamEnv<'tcx>,
40
41     // This tracks if we signal some hard error for a given const value, so that
42     // we will not subsequently issue an irrelevant lint for the same const
43     // value.
44     saw_const_match_error: Cell<bool>,
45
46     // inference context used for checking `T: Structural` bounds.
47     infcx: InferCtxt<'a, 'tcx>,
48
49     include_lint_checks: bool,
50 }
51
52 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
53     fn new(
54         pat_ctxt: &PatCtxt<'_, 'tcx>,
55         id: hir::HirId,
56         span: Span,
57         infcx: InferCtxt<'a, 'tcx>,
58     ) -> Self {
59         ConstToPat {
60             id,
61             span,
62             infcx,
63             param_env: pat_ctxt.param_env,
64             include_lint_checks: pat_ctxt.include_lint_checks,
65             saw_const_match_error: Cell::new(false),
66         }
67     }
68
69     fn tcx(&self) -> TyCtxt<'tcx> {
70         self.infcx.tcx
71     }
72
73     fn search_for_structural_match_violation(
74         &self,
75         ty: Ty<'tcx>,
76     ) -> Option<traits::NonStructuralMatchTy<'tcx>> {
77         traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
78     }
79
80     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
81         traits::type_marked_structural(self.id, self.span, &self.infcx, ty)
82     }
83
84     fn to_pat(&mut self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
85         // This method is just a wrapper handling a validity check; the heavy lifting is
86         // performed by the recursive `recur` method, which is not meant to be
87         // invoked except by this method.
88         //
89         // once indirect_structural_match is a full fledged error, this
90         // level of indirection can be eliminated
91
92         let inlined_const_as_pat = self.recur(cv);
93
94         if self.include_lint_checks && !self.saw_const_match_error.get() {
95             // If we were able to successfully convert the const to some pat,
96             // double-check that all types in the const implement `Structural`.
97
98             let structural = self.search_for_structural_match_violation(cv.ty);
99             debug!(
100                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
101                 cv.ty, structural
102             );
103             if let Some(non_sm_ty) = structural {
104                 let adt_def = match non_sm_ty {
105                     traits::NonStructuralMatchTy::Adt(adt_def) => adt_def,
106                     traits::NonStructuralMatchTy::Param => {
107                         bug!("use of constant whose type is a parameter inside a pattern")
108                     }
109                 };
110                 let path = self.tcx().def_path_str(adt_def.did);
111
112                 let make_msg = || -> String {
113                     format!(
114                         "to use a constant of type `{}` in a pattern, \
115                          `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
116                         path, path,
117                     )
118                 };
119
120                 // double-check there even *is* a semantic `PartialEq` to dispatch to.
121                 //
122                 // (If there isn't, then we can safely issue a hard
123                 // error, because that's never worked, due to compiler
124                 // using `PartialEq::eq` in this scenario in the past.)
125                 //
126                 // Note: To fix rust-lang/rust#65466, one could lift this check
127                 // *before* any structural-match checking, and unconditionally error
128                 // if `PartialEq` is not implemented. However, that breaks stable
129                 // code at the moment, because types like `for <'a> fn(&'a ())` do
130                 // not *yet* implement `PartialEq`. So for now we leave this here.
131                 let ty_is_partial_eq: bool = {
132                     let partial_eq_trait_id = self.tcx().lang_items().eq_trait().unwrap();
133                     let obligation: PredicateObligation<'_> = predicate_for_trait_def(
134                         self.tcx(),
135                         self.param_env,
136                         ObligationCause::misc(self.span, self.id),
137                         partial_eq_trait_id,
138                         0,
139                         cv.ty,
140                         &[],
141                     );
142                     // FIXME: should this call a `predicate_must_hold` variant instead?
143                     self.infcx.predicate_may_hold(&obligation)
144                 };
145
146                 if !ty_is_partial_eq {
147                     // span_fatal avoids ICE from resolution of non-existent method (rare case).
148                     self.tcx().sess.span_fatal(self.span, &make_msg());
149                 } else {
150                     self.tcx().struct_span_lint_hir(
151                         lint::builtin::INDIRECT_STRUCTURAL_MATCH,
152                         self.id,
153                         self.span,
154                         |lint| lint.build(&make_msg()).emit(),
155                     );
156                 }
157             }
158         }
159
160         inlined_const_as_pat
161     }
162
163     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
164     fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
165         let id = self.id;
166         let span = self.span;
167         let tcx = self.tcx();
168         let param_env = self.param_env;
169
170         let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| {
171             vals.iter()
172                 .enumerate()
173                 .map(|(idx, val)| {
174                     let field = Field::new(idx);
175                     FieldPat { field, pattern: self.recur(val) }
176                 })
177                 .collect()
178         };
179
180         let kind = match cv.ty.kind {
181             ty::Float(_) => {
182                 tcx.struct_span_lint_hir(
183                     lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
184                     id,
185                     span,
186                     |lint| lint.build("floating-point types cannot be used in patterns").emit(),
187                 );
188                 PatKind::Constant { value: cv }
189             }
190             ty::Adt(adt_def, _) if adt_def.is_union() => {
191                 // Matching on union fields is unsafe, we can't hide it in constants
192                 self.saw_const_match_error.set(true);
193                 tcx.sess.span_err(span, "cannot use unions in constant patterns");
194                 PatKind::Wild
195             }
196             // keep old code until future-compat upgraded to errors.
197             ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
198                 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
199                 let path = tcx.def_path_str(adt_def.did);
200                 let msg = format!(
201                     "to use a constant of type `{}` in a pattern, \
202                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
203                     path, path,
204                 );
205                 self.saw_const_match_error.set(true);
206                 tcx.sess.span_err(span, &msg);
207                 PatKind::Wild
208             }
209             // keep old code until future-compat upgraded to errors.
210             ty::Ref(_, adt_ty @ ty::TyS { kind: ty::Adt(_, _), .. }, _)
211                 if !self.type_marked_structural(adt_ty) =>
212             {
213                 let adt_def =
214                     if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() };
215
216                 debug!(
217                     "adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
218                     adt_def, adt_ty
219                 );
220
221                 // HACK(estebank): Side-step ICE #53708, but anything other than erroring here
222                 // would be wrong. Returnging `PatKind::Wild` is not technically correct.
223                 let path = tcx.def_path_str(adt_def.did);
224                 let msg = format!(
225                     "to use a constant of type `{}` in a pattern, \
226                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
227                     path, path,
228                 );
229                 self.saw_const_match_error.set(true);
230                 tcx.sess.span_err(span, &msg);
231                 PatKind::Wild
232             }
233             ty::Adt(adt_def, substs) if adt_def.is_enum() => {
234                 let destructured = tcx.destructure_const(param_env.and(cv));
235                 PatKind::Variant {
236                     adt_def,
237                     substs,
238                     variant_index: destructured.variant,
239                     subpatterns: field_pats(destructured.fields),
240                 }
241             }
242             ty::Adt(_, _) => {
243                 let destructured = tcx.destructure_const(param_env.and(cv));
244                 PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
245             }
246             ty::Tuple(_) => {
247                 let destructured = tcx.destructure_const(param_env.and(cv));
248                 PatKind::Leaf { subpatterns: field_pats(destructured.fields) }
249             }
250             ty::Array(..) => PatKind::Array {
251                 prefix: tcx
252                     .destructure_const(param_env.and(cv))
253                     .fields
254                     .iter()
255                     .map(|val| self.recur(val))
256                     .collect(),
257                 slice: None,
258                 suffix: Vec::new(),
259             },
260             _ => PatKind::Constant { value: cv },
261         };
262
263         Pat { span, ty: cv.ty, kind: Box::new(kind) }
264     }
265 }