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