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