]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/hair/pattern/const_to_pat.rs
Rename in librustc_mir.
[rust.git] / src / librustc_mir / hair / pattern / const_to_pat.rs
1 use crate::const_eval::const_variant_index;
2
3 use rustc::hir;
4 use rustc::lint;
5 use rustc::mir::Field;
6 use rustc::infer::InferCtxt;
7 use rustc::traits::{ObligationCause, PredicateObligation};
8 use rustc::ty::{self, Ty, TyCtxt};
9
10 use rustc_index::vec::Idx;
11
12 use syntax_pos::Span;
13
14
15 use std::cell::Cell;
16
17 use super::{FieldPat, Pat, PatCtxt, PatKind};
18
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(
24         &self,
25         cv: &'tcx ty::Const<'tcx>,
26         id: hir::HirId,
27         span: Span,
28     ) -> Pat<'tcx> {
29         debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
30         debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
31
32         self.tcx.infer_ctxt().enter(|infcx| {
33             let mut convert = ConstToPat::new(self, id, span, infcx);
34             convert.to_pat(cv)
35         })
36     }
37 }
38
39 struct ConstToPat<'a, 'tcx> {
40     id: hir::HirId,
41     span: Span,
42     param_env: ty::ParamEnv<'tcx>,
43
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
46     // value.
47     saw_const_match_error: Cell<bool>,
48
49     // inference context used for checking `T: Structural` bounds.
50     infcx: InferCtxt<'a, 'tcx>,
51
52     include_lint_checks: bool,
53 }
54
55 impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
56     fn new(pat_ctxt: &PatCtxt<'_, 'tcx>,
57                       id: hir::HirId,
58                       span: Span,
59                       infcx: InferCtxt<'a, 'tcx>) -> Self {
60         ConstToPat {
61             id, span, infcx,
62             param_env: pat_ctxt.param_env,
63             include_lint_checks: pat_ctxt.include_lint_checks,
64             saw_const_match_error: Cell::new(false),
65         }
66     }
67
68     fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx }
69
70     fn search_for_structural_match_violation(&self,
71                                              ty: Ty<'tcx>)
72                                              -> Option<ty::NonStructuralMatchTy<'tcx>>
73     {
74         ty::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty)
75     }
76
77     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
78         ty::type_marked_structural(self.id, self.span, &self.infcx, ty)
79     }
80
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.
85         //
86         // once indirect_structural_match is a full fledged error, this
87         // level of indirection can be eliminated
88
89         let inlined_const_as_pat = self.recur(cv);
90
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`.
94
95             let structural = self.search_for_structural_match_violation(cv.ty);
96             debug!("search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
97                    cv.ty, structural);
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"),
103                 };
104                 let path = self.tcx().def_path_str(adt_def.did);
105                 let msg = format!(
106                     "to use a constant of type `{}` in a pattern, \
107                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
108                     path,
109                     path,
110                 );
111
112                 // double-check there even *is* a semantic `PartialEq` to dispatch to.
113                 //
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.)
117                 //
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(
127                             self.param_env,
128                             ObligationCause::misc(self.span, self.id),
129                             partial_eq_trait_id,
130                             0,
131                             cv.ty,
132                             &[]);
133                     // FIXME: should this call a `predicate_must_hold` variant instead?
134                     self.infcx.predicate_may_hold(&obligation)
135                 };
136
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);
140                 } else {
141                     self.tcx().lint_hir(lint::builtin::INDIRECT_STRUCTURAL_MATCH,
142                                         self.id,
143                                         self.span,
144                                         &msg);
145                 }
146             }
147         }
148
149         inlined_const_as_pat
150     }
151
152     // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
153     fn recur(&self, cv: &'tcx ty::Const<'tcx>) -> Pat<'tcx> {
154         let id = self.id;
155         let span = self.span;
156         let tcx = self.tcx();
157         let param_env = self.param_env;
158
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
163             );
164             self.recur(val)
165         };
166         let adt_subpatterns = |n, variant_opt| {
167             (0..n).map(|i| {
168                 let field = Field::new(i);
169                 FieldPat {
170                     field,
171                     pattern: adt_subpattern(i, variant_opt),
172                 }
173             }).collect::<Vec<_>>()
174         };
175
176
177         let kind = match cv.ty.kind {
178             ty::Float(_) => {
179                 tcx.lint_hir(
180                     ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
181                     id,
182                     span,
183                     "floating-point types cannot be used in patterns",
184                 );
185                 PatKind::Constant {
186                     value: cv,
187                 }
188             }
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");
193                 PatKind::Wild
194             }
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: {:?}",
198                        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,
204                     path,
205                 );
206                 self.saw_const_match_error.set(true);
207                 tcx.sess.span_err(span, &msg);
208                 PatKind::Wild
209             }
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) =>
213             {
214                 let adt_def = if let ty::Adt(adt_def, _) = adt_ty.kind {
215                     adt_def
216                 } else {
217                     unreachable!()
218                 };
219
220                 debug!("adt_def {:?} has !type_marked_structural for adt_ty: {:?}",
221                        adt_def, adt_ty);
222
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);
226                 let msg = format!(
227                     "to use a constant of type `{}` in a pattern, \
228                      `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
229                     path,
230                     path,
231                 );
232                 self.saw_const_match_error.set(true);
233                 tcx.sess.span_err(span, &msg);
234                 PatKind::Wild
235             }
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(),
240                     Some(variant_index),
241                 );
242                 PatKind::Variant {
243                     adt_def,
244                     substs,
245                     variant_index,
246                     subpatterns,
247                 }
248             }
249             ty::Adt(adt_def, _) => {
250                 let struct_var = adt_def.non_enum_variant();
251                 PatKind::Leaf {
252                     subpatterns: adt_subpatterns(struct_var.fields.len(), None),
253                 }
254             }
255             ty::Tuple(fields) => {
256                 PatKind::Leaf {
257                     subpatterns: adt_subpatterns(fields.len(), None),
258                 }
259             }
260             ty::Array(_, n) => {
261                 PatKind::Array {
262                     prefix: (0..n.eval_usize(tcx, self.param_env))
263                         .map(|i| adt_subpattern(i as usize, None))
264                         .collect(),
265                     slice: None,
266                     suffix: Vec::new(),
267                 }
268             }
269             _ => {
270                 PatKind::Constant {
271                     value: cv,
272                 }
273             }
274         };
275
276         Pat {
277             span,
278             ty: cv.ty,
279             kind: Box::new(kind),
280         }
281     }
282 }