]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use rustc_hir as hir; |
e74abb32 | 2 | use rustc_index::vec::Idx; |
ba9703b0 XL |
3 | use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; |
4 | use rustc_middle::mir::Field; | |
5 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
6 | use rustc_session::lint; | |
dfeec247 | 7 | use rustc_span::Span; |
ba9703b0 XL |
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}; | |
e74abb32 XL |
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> { | |
dfeec247 XL |
53 | fn new( |
54 | pat_ctxt: &PatCtxt<'_, 'tcx>, | |
55 | id: hir::HirId, | |
56 | span: Span, | |
57 | infcx: InferCtxt<'a, 'tcx>, | |
58 | ) -> Self { | |
e74abb32 | 59 | ConstToPat { |
dfeec247 XL |
60 | id, |
61 | span, | |
62 | infcx, | |
e74abb32 XL |
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 | ||
dfeec247 XL |
69 | fn tcx(&self) -> TyCtxt<'tcx> { |
70 | self.infcx.tcx | |
71 | } | |
e74abb32 | 72 | |
dfeec247 XL |
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) | |
e74abb32 XL |
78 | } |
79 | ||
80 | fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { | |
dfeec247 | 81 | traits::type_marked_structural(self.id, self.span, &self.infcx, ty) |
e74abb32 XL |
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); | |
dfeec247 XL |
99 | debug!( |
100 | "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", | |
101 | cv.ty, structural | |
102 | ); | |
e74abb32 XL |
103 | if let Some(non_sm_ty) = structural { |
104 | let adt_def = match non_sm_ty { | |
dfeec247 XL |
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 | } | |
e74abb32 XL |
109 | }; |
110 | let path = self.tcx().def_path_str(adt_def.did); | |
74b04a01 XL |
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 | }; | |
e74abb32 XL |
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(); | |
dfeec247 XL |
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 | ); | |
e74abb32 XL |
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). | |
74b04a01 | 148 | self.tcx().sess.span_fatal(self.span, &make_msg()); |
e74abb32 | 149 | } else { |
74b04a01 | 150 | self.tcx().struct_span_lint_hir( |
dfeec247 XL |
151 | lint::builtin::INDIRECT_STRUCTURAL_MATCH, |
152 | self.id, | |
153 | self.span, | |
74b04a01 | 154 | |lint| lint.build(&make_msg()).emit(), |
dfeec247 | 155 | ); |
e74abb32 XL |
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 | ||
dfeec247 XL |
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() | |
e74abb32 | 178 | }; |
e74abb32 XL |
179 | |
180 | let kind = match cv.ty.kind { | |
181 | ty::Float(_) => { | |
74b04a01 | 182 | tcx.struct_span_lint_hir( |
ba9703b0 | 183 | lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, |
e74abb32 XL |
184 | id, |
185 | span, | |
74b04a01 | 186 | |lint| lint.build("floating-point types cannot be used in patterns").emit(), |
e74abb32 | 187 | ); |
dfeec247 | 188 | PatKind::Constant { value: cv } |
e74abb32 XL |
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) => { | |
dfeec247 | 198 | debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty); |
e74abb32 XL |
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)]`", | |
dfeec247 | 203 | path, path, |
e74abb32 XL |
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 | { | |
dfeec247 XL |
213 | let adt_def = |
214 | if let ty::Adt(adt_def, _) = adt_ty.kind { adt_def } else { unreachable!() }; | |
e74abb32 | 215 | |
dfeec247 XL |
216 | debug!( |
217 | "adt_def {:?} has !type_marked_structural for adt_ty: {:?}", | |
218 | adt_def, adt_ty | |
219 | ); | |
e74abb32 XL |
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)]`", | |
dfeec247 | 227 | path, path, |
e74abb32 XL |
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() => { | |
dfeec247 | 234 | let destructured = tcx.destructure_const(param_env.and(cv)); |
e74abb32 XL |
235 | PatKind::Variant { |
236 | adt_def, | |
237 | substs, | |
dfeec247 XL |
238 | variant_index: destructured.variant, |
239 | subpatterns: field_pats(destructured.fields), | |
e74abb32 XL |
240 | } |
241 | } | |
dfeec247 XL |
242 | ty::Adt(_, _) => { |
243 | let destructured = tcx.destructure_const(param_env.and(cv)); | |
244 | PatKind::Leaf { subpatterns: field_pats(destructured.fields) } | |
e74abb32 | 245 | } |
dfeec247 XL |
246 | ty::Tuple(_) => { |
247 | let destructured = tcx.destructure_const(param_env.and(cv)); | |
248 | PatKind::Leaf { subpatterns: field_pats(destructured.fields) } | |
e74abb32 | 249 | } |
dfeec247 XL |
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 }, | |
e74abb32 XL |
261 | }; |
262 | ||
dfeec247 | 263 | Pat { span, ty: cv.ty, kind: Box::new(kind) } |
e74abb32 XL |
264 | } |
265 | } |