]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
New upstream version 1.48.0+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / thir / pattern / const_to_pat.rs
CommitLineData
1b1a35ee
XL
1use rustc_hir as hir;
2use rustc_index::vec::Idx;
3use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
4use rustc_middle::mir::Field;
5use rustc_middle::ty::print::with_no_trimmed_paths;
6use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
7use rustc_session::lint;
8use rustc_span::Span;
9use rustc_trait_selection::traits::predicate_for_trait_def;
10use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
11use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
12
13use std::cell::Cell;
14
15use super::{FieldPat, Pat, PatCtxt, PatKind};
16
17impl<'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 mir_structural_match_violation: bool,
27 ) -> Pat<'tcx> {
28 debug!("const_to_pat: cv={:#?} id={:?}", cv, id);
29 debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span);
30
31 let pat = self.tcx.infer_ctxt().enter(|infcx| {
32 let mut convert = ConstToPat::new(self, id, span, infcx);
33 convert.to_pat(cv, mir_structural_match_violation)
34 });
35
36 debug!("const_to_pat: pat={:?}", pat);
37 pat
38 }
39}
40
41struct ConstToPat<'a, 'tcx> {
42 id: hir::HirId,
43 span: Span,
44 param_env: ty::ParamEnv<'tcx>,
45
46 // This tracks if we emitted some hard error for a given const value, so that
47 // we will not subsequently issue an irrelevant lint for the same const
48 // value.
49 saw_const_match_error: Cell<bool>,
50
51 // This tracks if we emitted some diagnostic for a given const value, so that
52 // we will not subsequently issue an irrelevant lint for the same const
53 // value.
54 saw_const_match_lint: Cell<bool>,
55
56 // For backcompat we need to keep allowing non-structurally-eq types behind references.
57 // See also all the `cant-hide-behind` tests.
58 behind_reference: Cell<bool>,
59
60 // inference context used for checking `T: Structural` bounds.
61 infcx: InferCtxt<'a, 'tcx>,
62
63 include_lint_checks: bool,
64}
65
66mod fallback_to_const_ref {
67 #[derive(Debug)]
68 /// This error type signals that we encountered a non-struct-eq situation behind a reference.
69 /// We bubble this up in order to get back to the reference destructuring and make that emit
70 /// a const pattern instead of a deref pattern. This allows us to simply call `PartialEq::eq`
71 /// on such patterns (since that function takes a reference) and not have to jump through any
72 /// hoops to get a reference to the value.
73 pub(super) struct FallbackToConstRef(());
74
75 pub(super) fn fallback_to_const_ref<'a, 'tcx>(
76 c2p: &super::ConstToPat<'a, 'tcx>,
77 ) -> FallbackToConstRef {
78 assert!(c2p.behind_reference.get());
79 FallbackToConstRef(())
80 }
81}
82use fallback_to_const_ref::{fallback_to_const_ref, FallbackToConstRef};
83
84impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
85 fn new(
86 pat_ctxt: &PatCtxt<'_, 'tcx>,
87 id: hir::HirId,
88 span: Span,
89 infcx: InferCtxt<'a, 'tcx>,
90 ) -> Self {
91 ConstToPat {
92 id,
93 span,
94 infcx,
95 param_env: pat_ctxt.param_env,
96 include_lint_checks: pat_ctxt.include_lint_checks,
97 saw_const_match_error: Cell::new(false),
98 saw_const_match_lint: Cell::new(false),
99 behind_reference: Cell::new(false),
100 }
101 }
102
103 fn tcx(&self) -> TyCtxt<'tcx> {
104 self.infcx.tcx
105 }
106
107 fn adt_derive_msg(&self, adt_def: &AdtDef) -> String {
108 let path = self.tcx().def_path_str(adt_def.did);
109 format!(
110 "to use a constant of type `{}` in a pattern, \
111 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
112 path, path,
113 )
114 }
115
116 fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
117 traits::search_for_structural_match_violation(self.id, self.span, self.tcx(), ty).map(
118 |non_sm_ty| {
119 with_no_trimmed_paths(|| match non_sm_ty {
120 traits::NonStructuralMatchTy::Adt(adt) => self.adt_derive_msg(adt),
121 traits::NonStructuralMatchTy::Dynamic => {
122 "trait objects cannot be used in patterns".to_string()
123 }
124 traits::NonStructuralMatchTy::Opaque => {
125 "opaque types cannot be used in patterns".to_string()
126 }
127 traits::NonStructuralMatchTy::Generator => {
128 "generators cannot be used in patterns".to_string()
129 }
130 traits::NonStructuralMatchTy::Closure => {
131 "closures cannot be used in patterns".to_string()
132 }
133 traits::NonStructuralMatchTy::Param => {
134 bug!("use of a constant whose type is a parameter inside a pattern")
135 }
136 traits::NonStructuralMatchTy::Projection => {
137 bug!("use of a constant whose type is a projection inside a pattern")
138 }
139 traits::NonStructuralMatchTy::Foreign => {
140 bug!("use of a value of a foreign type inside a pattern")
141 }
142 })
143 },
144 )
145 }
146
147 fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
148 ty.is_structural_eq_shallow(self.infcx.tcx)
149 }
150
151 fn to_pat(
152 &mut self,
153 cv: &'tcx ty::Const<'tcx>,
154 mir_structural_match_violation: bool,
155 ) -> Pat<'tcx> {
156 // This method is just a wrapper handling a validity check; the heavy lifting is
157 // performed by the recursive `recur` method, which is not meant to be
158 // invoked except by this method.
159 //
160 // once indirect_structural_match is a full fledged error, this
161 // level of indirection can be eliminated
162
163 let inlined_const_as_pat = self.recur(cv, mir_structural_match_violation).unwrap();
164
165 if self.include_lint_checks && !self.saw_const_match_error.get() {
166 // If we were able to successfully convert the const to some pat,
167 // double-check that all types in the const implement `Structural`.
168
169 let structural = self.search_for_structural_match_violation(cv.ty);
170 debug!(
171 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
172 cv.ty, structural
173 );
174
175 // This can occur because const qualification treats all associated constants as
176 // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them
177 // before it runs.
178 //
179 // FIXME(#73448): Find a way to bring const qualification into parity with
180 // `search_for_structural_match_violation`.
181 if structural.is_none() && mir_structural_match_violation {
182 warn!("MIR const-checker found novel structural match violation. See #73448.");
183 return inlined_const_as_pat;
184 }
185
186 if let Some(msg) = structural {
187 if !self.type_may_have_partial_eq_impl(cv.ty) {
188 // span_fatal avoids ICE from resolution of non-existent method (rare case).
189 self.tcx().sess.span_fatal(self.span, &msg);
190 } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
191 self.tcx().struct_span_lint_hir(
192 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
193 self.id,
194 self.span,
195 |lint| lint.build(&msg).emit(),
196 );
197 } else {
198 debug!(
199 "`search_for_structural_match_violation` found one, but `CustomEq` was \
200 not in the qualifs for that `const`"
201 );
202 }
203 }
204 }
205
206 inlined_const_as_pat
207 }
208
209 fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
210 // double-check there even *is* a semantic `PartialEq` to dispatch to.
211 //
212 // (If there isn't, then we can safely issue a hard
213 // error, because that's never worked, due to compiler
214 // using `PartialEq::eq` in this scenario in the past.)
215 let partial_eq_trait_id =
216 self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
217 let obligation: PredicateObligation<'_> = predicate_for_trait_def(
218 self.tcx(),
219 self.param_env,
220 ObligationCause::misc(self.span, self.id),
221 partial_eq_trait_id,
222 0,
223 ty,
224 &[],
225 );
226 // FIXME: should this call a `predicate_must_hold` variant instead?
227
228 let has_impl = self.infcx.predicate_may_hold(&obligation);
229
230 // Note: To fix rust-lang/rust#65466, we could just remove this type
231 // walk hack for function pointers, and unconditionally error
232 // if `PartialEq` is not implemented. However, that breaks stable
233 // code at the moment, because types like `for <'a> fn(&'a ())` do
234 // not *yet* implement `PartialEq`. So for now we leave this here.
235 has_impl
236 || ty.walk().any(|t| match t.unpack() {
237 ty::subst::GenericArgKind::Lifetime(_) => false,
238 ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
239 ty::subst::GenericArgKind::Const(_) => false,
240 })
241 }
242
243 // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
244 fn recur(
245 &self,
246 cv: &'tcx ty::Const<'tcx>,
247 mir_structural_match_violation: bool,
248 ) -> Result<Pat<'tcx>, FallbackToConstRef> {
249 let id = self.id;
250 let span = self.span;
251 let tcx = self.tcx();
252 let param_env = self.param_env;
253
254 let field_pats = |vals: &[&'tcx ty::Const<'tcx>]| -> Result<_, _> {
255 vals.iter()
256 .enumerate()
257 .map(|(idx, val)| {
258 let field = Field::new(idx);
259 Ok(FieldPat { field, pattern: self.recur(val, false)? })
260 })
261 .collect()
262 };
263
264 let kind = match cv.ty.kind() {
265 ty::Float(_) => {
266 tcx.struct_span_lint_hir(
267 lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
268 id,
269 span,
270 |lint| lint.build("floating-point types cannot be used in patterns").emit(),
271 );
272 PatKind::Constant { value: cv }
273 }
274 ty::Adt(adt_def, _) if adt_def.is_union() => {
275 // Matching on union fields is unsafe, we can't hide it in constants
276 self.saw_const_match_error.set(true);
277 let msg = "cannot use unions in constant patterns";
278 if self.include_lint_checks {
279 tcx.sess.span_err(span, msg);
280 } else {
281 tcx.sess.delay_span_bug(span, msg)
282 }
283 PatKind::Wild
284 }
285 ty::Adt(..)
286 if !self.type_may_have_partial_eq_impl(cv.ty)
287 // FIXME(#73448): Find a way to bring const qualification into parity with
288 // `search_for_structural_match_violation` and then remove this condition.
289 && self.search_for_structural_match_violation(cv.ty).is_some() =>
290 {
291 // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
292 // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
293 let msg = self.search_for_structural_match_violation(cv.ty).unwrap();
294 self.saw_const_match_error.set(true);
295 if self.include_lint_checks {
296 tcx.sess.span_err(self.span, &msg);
297 } else {
298 tcx.sess.delay_span_bug(self.span, &msg)
299 }
300 PatKind::Wild
301 }
302 // If the type is not structurally comparable, just emit the constant directly,
303 // causing the pattern match code to treat it opaquely.
304 // FIXME: This code doesn't emit errors itself, the caller emits the errors.
305 // So instead of specific errors, you just get blanket errors about the whole
306 // const type. See
307 // https://github.com/rust-lang/rust/pull/70743#discussion_r404701963 for
308 // details.
309 // Backwards compatibility hack because we can't cause hard errors on these
310 // types, so we compare them via `PartialEq::eq` at runtime.
311 ty::Adt(..) if !self.type_marked_structural(cv.ty) && self.behind_reference.get() => {
312 if self.include_lint_checks
313 && !self.saw_const_match_error.get()
314 && !self.saw_const_match_lint.get()
315 {
316 self.saw_const_match_lint.set(true);
317 let msg = format!(
318 "to use a constant of type `{}` in a pattern, \
319 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
320 cv.ty, cv.ty,
321 );
322 tcx.struct_span_lint_hir(
323 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
324 id,
325 span,
326 |lint| lint.build(&msg).emit(),
327 );
328 }
329 // Since we are behind a reference, we can just bubble the error up so we get a
330 // constant at reference type, making it easy to let the fallback call
331 // `PartialEq::eq` on it.
332 return Err(fallback_to_const_ref(self));
333 }
334 ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
335 debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
336 let path = tcx.def_path_str(adt_def.did);
337 let msg = format!(
338 "to use a constant of type `{}` in a pattern, \
339 `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
340 path, path,
341 );
342 self.saw_const_match_error.set(true);
343 if self.include_lint_checks {
344 tcx.sess.span_err(span, &msg);
345 } else {
346 tcx.sess.delay_span_bug(span, &msg)
347 }
348 PatKind::Wild
349 }
350 ty::Adt(adt_def, substs) if adt_def.is_enum() => {
351 let destructured = tcx.destructure_const(param_env.and(cv));
352 PatKind::Variant {
353 adt_def,
354 substs,
355 variant_index: destructured
356 .variant
357 .expect("destructed const of adt without variant id"),
358 subpatterns: field_pats(destructured.fields)?,
359 }
360 }
361 ty::Tuple(_) | ty::Adt(_, _) => {
362 let destructured = tcx.destructure_const(param_env.and(cv));
363 PatKind::Leaf { subpatterns: field_pats(destructured.fields)? }
364 }
365 ty::Array(..) => PatKind::Array {
366 prefix: tcx
367 .destructure_const(param_env.and(cv))
368 .fields
369 .iter()
370 .map(|val| self.recur(val, false))
371 .collect::<Result<_, _>>()?,
372 slice: None,
373 suffix: Vec::new(),
374 },
375 ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
376 // These are not allowed and will error elsewhere anyway.
377 ty::Dynamic(..) => {
378 self.saw_const_match_error.set(true);
379 let msg = format!("`{}` cannot be used in patterns", cv.ty);
380 if self.include_lint_checks {
381 tcx.sess.span_err(span, &msg);
382 } else {
383 tcx.sess.delay_span_bug(span, &msg)
384 }
385 PatKind::Wild
386 }
387 // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
388 // optimization for now.
389 ty::Str => PatKind::Constant { value: cv },
390 ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
391 // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
392 // matching against references, you can only use byte string literals.
393 // FIXME: clean this up, likely by permitting array patterns when matching on slices
394 ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
395 // Cannot merge this with the catch all branch below, because the `const_deref`
396 // changes the type from slice to array, and slice patterns behave differently from
397 // array patterns.
398 ty::Slice(..) => {
399 let old = self.behind_reference.replace(true);
400 let array = tcx.deref_const(self.param_env.and(cv));
401 let val = PatKind::Deref {
402 subpattern: Pat {
403 kind: Box::new(PatKind::Slice {
404 prefix: tcx
405 .destructure_const(param_env.and(array))
406 .fields
407 .iter()
408 .map(|val| self.recur(val, false))
409 .collect::<Result<_, _>>()?,
410 slice: None,
411 suffix: vec![],
412 }),
413 span,
414 ty: pointee_ty,
415 },
416 };
417 self.behind_reference.set(old);
418 val
419 }
420 // Backwards compatibility hack: support references to non-structural types.
421 // We'll lower
422 // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
423 // reference. This makes the rest of the matching logic simpler as it doesn't have
424 // to figure out how to get a reference again.
425 ty::Adt(adt_def, _) if !self.type_marked_structural(pointee_ty) => {
426 if self.behind_reference.get() {
427 if self.include_lint_checks
428 && !self.saw_const_match_error.get()
429 && !self.saw_const_match_lint.get()
430 {
431 self.saw_const_match_lint.set(true);
432 let msg = self.adt_derive_msg(adt_def);
433 self.tcx().struct_span_lint_hir(
434 lint::builtin::INDIRECT_STRUCTURAL_MATCH,
435 self.id,
436 self.span,
437 |lint| lint.build(&msg).emit(),
438 );
439 }
440 PatKind::Constant { value: cv }
441 } else {
442 if !self.saw_const_match_error.get() {
443 self.saw_const_match_error.set(true);
444 let msg = self.adt_derive_msg(adt_def);
445 if self.include_lint_checks {
446 tcx.sess.span_err(span, &msg);
447 } else {
448 tcx.sess.delay_span_bug(span, &msg)
449 }
450 }
451 PatKind::Wild
452 }
453 }
454 // All other references are converted into deref patterns and then recursively
455 // convert the dereferenced constant to a pattern that is the sub-pattern of the
456 // deref pattern.
457 _ => {
458 let old = self.behind_reference.replace(true);
459 // In case there are structural-match violations somewhere in this subpattern,
460 // we fall back to a const pattern. If we do not do this, we may end up with
461 // a !structural-match constant that is not of reference type, which makes it
462 // very hard to invoke `PartialEq::eq` on it as a fallback.
463 let val = match self.recur(tcx.deref_const(self.param_env.and(cv)), false) {
464 Ok(subpattern) => PatKind::Deref { subpattern },
465 Err(_) => PatKind::Constant { value: cv },
466 };
467 self.behind_reference.set(old);
468 val
469 }
470 },
471 ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
472 PatKind::Constant { value: cv }
473 }
474 ty::RawPtr(pointee) if pointee.ty.is_sized(tcx.at(span), param_env) => {
475 PatKind::Constant { value: cv }
476 }
477 // FIXME: these can have very suprising behaviour where optimization levels or other
478 // compilation choices change the runtime behaviour of the match.
479 // See https://github.com/rust-lang/rust/issues/70861 for examples.
480 ty::FnPtr(..) | ty::RawPtr(..) => {
481 if self.include_lint_checks
482 && !self.saw_const_match_error.get()
483 && !self.saw_const_match_lint.get()
484 {
485 self.saw_const_match_lint.set(true);
486 let msg = "function pointers and unsized pointers in patterns behave \
487 unpredictably and should not be relied upon. \
488 See https://github.com/rust-lang/rust/issues/70861 for details.";
489 tcx.struct_span_lint_hir(
490 lint::builtin::POINTER_STRUCTURAL_MATCH,
491 id,
492 span,
493 |lint| lint.build(&msg).emit(),
494 );
495 }
496 PatKind::Constant { value: cv }
497 }
498 _ => {
499 self.saw_const_match_error.set(true);
500 let msg = format!("`{}` cannot be used in patterns", cv.ty);
501 if self.include_lint_checks {
502 tcx.sess.span_err(span, &msg);
503 } else {
504 tcx.sess.delay_span_bug(span, &msg)
505 }
506 PatKind::Wild
507 }
508 };
509
510 if self.include_lint_checks
511 && !self.saw_const_match_error.get()
512 && !self.saw_const_match_lint.get()
513 && mir_structural_match_violation
514 // FIXME(#73448): Find a way to bring const qualification into parity with
515 // `search_for_structural_match_violation` and then remove this condition.
516 && self.search_for_structural_match_violation(cv.ty).is_some()
517 {
518 self.saw_const_match_lint.set(true);
519 // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
520 // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
521 let msg = self.search_for_structural_match_violation(cv.ty).unwrap().replace(
522 "in a pattern,",
523 "in a pattern, the constant's initializer must be trivial or",
524 );
525 tcx.struct_span_lint_hir(
526 lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
527 id,
528 span,
529 |lint| lint.build(&msg).emit(),
530 );
531 }
532
533 Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) })
534 }
535}