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