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