]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir_build/hair/pattern/const_to_pat.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / src / librustc_mir_build / hair / pattern / const_to_pat.rs
CommitLineData
dfeec247 1use rustc_hir as hir;
e74abb32 2use rustc_index::vec::Idx;
ba9703b0
XL
3use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
4use rustc_middle::mir::Field;
5use rustc_middle::ty::{self, Ty, TyCtxt};
6use rustc_session::lint;
dfeec247 7use rustc_span::Span;
ba9703b0
XL
8use rustc_trait_selection::traits::predicate_for_trait_def;
9use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
10use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
e74abb32
XL
11
12use std::cell::Cell;
13
14use super::{FieldPat, Pat, PatCtxt, PatKind};
15
16impl<'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
36struct 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
52impl<'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}