rustc_mir_build/thir/pattern/
const_to_pat.rs

1use core::ops::ControlFlow;
2
3use rustc_abi::{FieldIdx, VariantIdx};
4use rustc_apfloat::Float;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::Diag;
7use rustc_hir as hir;
8use rustc_index::Idx;
9use rustc_infer::infer::TyCtxtInferExt;
10use rustc_infer::traits::Obligation;
11use rustc_middle::mir::interpret::ErrorHandled;
12use rustc_middle::thir::{FieldPat, Pat, PatKind};
13use rustc_middle::ty::{
14    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree,
15};
16use rustc_middle::{mir, span_bug};
17use rustc_span::def_id::DefId;
18use rustc_span::{Span, sym};
19use rustc_trait_selection::traits::ObligationCause;
20use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
21use tracing::{debug, instrument, trace};
22
23use super::PatCtxt;
24use crate::errors::{
25    ConstPatternDependsOnGenericParameter, CouldNotEvalConstPattern, InvalidPattern, NaNPattern,
26    PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern,
27};
28
29impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
30    /// Converts a constant to a pattern (if possible).
31    /// This means aggregate values (like structs and enums) are converted
32    /// to a pattern that matches the value (as if you'd compared via structural equality).
33    ///
34    /// Only type system constants are supported, as we are using valtrees
35    /// as an intermediate step. Unfortunately those don't carry a type
36    /// so we have to carry one ourselves.
37    #[instrument(level = "debug", skip(self), ret)]
38    pub(super) fn const_to_pat(
39        &self,
40        c: ty::Const<'tcx>,
41        ty: Ty<'tcx>,
42        id: hir::HirId,
43        span: Span,
44    ) -> Box<Pat<'tcx>> {
45        let mut convert = ConstToPat::new(self, id, span, c);
46
47        match c.kind() {
48            ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
49            ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
50            _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
51        }
52    }
53}
54
55struct ConstToPat<'tcx> {
56    tcx: TyCtxt<'tcx>,
57    typing_env: ty::TypingEnv<'tcx>,
58    span: Span,
59    id: hir::HirId,
60
61    treat_byte_string_as_slice: bool,
62
63    c: ty::Const<'tcx>,
64}
65
66impl<'tcx> ConstToPat<'tcx> {
67    fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self {
68        trace!(?pat_ctxt.typeck_results.hir_owner);
69        ConstToPat {
70            tcx: pat_ctxt.tcx,
71            typing_env: pat_ctxt.typing_env,
72            span,
73            id,
74            treat_byte_string_as_slice: pat_ctxt
75                .typeck_results
76                .treat_byte_string_as_slice
77                .contains(&id.local_id),
78            c,
79        }
80    }
81
82    fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
83        ty.is_structural_eq_shallow(self.tcx)
84    }
85
86    /// We errored. Signal that in the pattern, so that follow up errors can be silenced.
87    fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
88        if let ty::ConstKind::Unevaluated(uv) = self.c.kind() {
89            let def_kind = self.tcx.def_kind(uv.def);
90            if let hir::def::DefKind::AssocConst = def_kind
91                && let Some(def_id) = uv.def.as_local()
92            {
93                // Include the container item in the output.
94                err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), "");
95            }
96            if let hir::def::DefKind::Const | hir::def::DefKind::AssocConst = def_kind {
97                err.span_label(
98                    self.tcx.def_span(uv.def),
99                    crate::fluent_generated::mir_build_const_defined_here,
100                );
101            }
102        }
103        Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) })
104    }
105
106    fn unevaluated_to_pat(
107        &mut self,
108        uv: ty::UnevaluatedConst<'tcx>,
109        ty: Ty<'tcx>,
110    ) -> Box<Pat<'tcx>> {
111        trace!(self.treat_byte_string_as_slice);
112
113        // It's not *technically* correct to be revealing opaque types here as borrowcheck has
114        // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even
115        // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
116        // As a result we always use a revealed env when resolving the instance to evaluate.
117        //
118        // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
119        // instead of having this logic here
120        let typing_env =
121            self.tcx.erase_regions(self.typing_env).with_post_analysis_normalized(self.tcx);
122        let uv = self.tcx.erase_regions(uv);
123
124        // try to resolve e.g. associated constants to their definition on an impl, and then
125        // evaluate the const.
126        let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) {
127            Ok(Ok(c)) => c,
128            Err(ErrorHandled::Reported(_, _)) => {
129                // Let's tell the use where this failing const occurs.
130                let mut err =
131                    self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span });
132                // We've emitted an error on the original const, it would be redundant to complain
133                // on its use as well.
134                if let ty::ConstKind::Unevaluated(uv) = self.c.kind()
135                    && let hir::def::DefKind::Const | hir::def::DefKind::AssocConst =
136                        self.tcx.def_kind(uv.def)
137                {
138                    err.downgrade_to_delayed_bug();
139                }
140                return self.mk_err(err, ty);
141            }
142            Err(ErrorHandled::TooGeneric(_)) => {
143                let mut e = self
144                    .tcx
145                    .dcx()
146                    .create_err(ConstPatternDependsOnGenericParameter { span: self.span });
147                for arg in uv.args {
148                    if let ty::GenericArgKind::Type(ty) = arg.unpack()
149                        && let ty::Param(param_ty) = ty.kind()
150                    {
151                        let def_id = self.tcx.hir().enclosing_body_owner(self.id);
152                        let generics = self.tcx.generics_of(def_id);
153                        let param = generics.type_param(*param_ty, self.tcx);
154                        let span = self.tcx.def_span(param.def_id);
155                        e.span_label(span, "constant depends on this generic parameter");
156                        if let Some(ident) = self.tcx.def_ident_span(def_id)
157                            && self.tcx.sess.source_map().is_multiline(ident.between(span))
158                        {
159                            // Display the `fn` name as well in the diagnostic, as the generic isn't
160                            // in the same line and it could be confusing otherwise.
161                            e.span_label(ident, "");
162                        }
163                    }
164                }
165                return self.mk_err(e, ty);
166            }
167            Ok(Err(bad_ty)) => {
168                // The pattern cannot be turned into a valtree.
169                let e = match bad_ty.kind() {
170                    ty::Adt(def, ..) => {
171                        assert!(def.is_union());
172                        self.tcx.dcx().create_err(UnionPattern { span: self.span })
173                    }
174                    ty::FnPtr(..) | ty::RawPtr(..) => {
175                        self.tcx.dcx().create_err(PointerPattern { span: self.span })
176                    }
177                    _ => self.tcx.dcx().create_err(InvalidPattern {
178                        span: self.span,
179                        non_sm_ty: bad_ty,
180                        prefix: bad_ty.prefix_string(self.tcx).to_string(),
181                    }),
182                };
183                return self.mk_err(e, ty);
184            }
185        };
186
187        // Convert the valtree to a const.
188        let inlined_const_as_pat = self.valtree_to_pat(valtree, ty);
189
190        if !inlined_const_as_pat.references_error() {
191            // Always check for `PartialEq` if we had no other errors yet.
192            if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl {
193                let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty });
194                extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err);
195                return self.mk_err(err, ty);
196            }
197        }
198
199        inlined_const_as_pat
200    }
201
202    fn field_pats(
203        &self,
204        vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
205    ) -> Vec<FieldPat<'tcx>> {
206        vals.enumerate()
207            .map(|(idx, (val, ty))| {
208                let field = FieldIdx::new(idx);
209                // Patterns can only use monomorphic types.
210                let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
211                FieldPat { field, pattern: *self.valtree_to_pat(val, ty) }
212            })
213            .collect()
214    }
215
216    // Recursive helper for `to_pat`; invoke that (instead of calling this directly).
217    // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
218    #[instrument(skip(self), level = "debug")]
219    fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
220        let span = self.span;
221        let tcx = self.tcx;
222        let kind = match ty.kind() {
223            ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
224                // Extremely important check for all ADTs! Make sure they opted-in to be used in
225                // patterns.
226                debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty);
227                let PartialEqImplStatus {
228                    is_derived, structural_partial_eq, non_blanket_impl, ..
229                } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
230                let (manual_partialeq_impl_span, manual_partialeq_impl_note) =
231                    match (structural_partial_eq, non_blanket_impl) {
232                        (true, _) => (None, false),
233                        (_, Some(def_id)) if def_id.is_local() && !is_derived => {
234                            (Some(tcx.def_span(def_id)), false)
235                        }
236                        _ => (None, true),
237                    };
238                let ty_def_span = tcx.def_span(adt_def.did());
239                let err = TypeNotStructural {
240                    span,
241                    ty,
242                    ty_def_span,
243                    manual_partialeq_impl_span,
244                    manual_partialeq_impl_note,
245                };
246                return self.mk_err(tcx.dcx().create_err(err), ty);
247            }
248            ty::Adt(adt_def, args) if adt_def.is_enum() => {
249                let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
250                let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32());
251                PatKind::Variant {
252                    adt_def: *adt_def,
253                    args,
254                    variant_index,
255                    subpatterns: self.field_pats(
256                        fields.iter().copied().zip(
257                            adt_def.variants()[variant_index]
258                                .fields
259                                .iter()
260                                .map(|field| field.ty(tcx, args)),
261                        ),
262                    ),
263                }
264            }
265            ty::Adt(def, args) => {
266                assert!(!def.is_union()); // Valtree construction would never succeed for unions.
267                PatKind::Leaf {
268                    subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
269                        def.non_enum_variant().fields.iter().map(|field| field.ty(tcx, args)),
270                    )),
271                }
272            }
273            ty::Tuple(fields) => PatKind::Leaf {
274                subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter())),
275            },
276            ty::Slice(elem_ty) => PatKind::Slice {
277                prefix: cv
278                    .unwrap_branch()
279                    .iter()
280                    .map(|val| *self.valtree_to_pat(*val, *elem_ty))
281                    .collect(),
282                slice: None,
283                suffix: Box::new([]),
284            },
285            ty::Array(elem_ty, _) => PatKind::Array {
286                prefix: cv
287                    .unwrap_branch()
288                    .iter()
289                    .map(|val| *self.valtree_to_pat(*val, *elem_ty))
290                    .collect(),
291                slice: None,
292                suffix: Box::new([]),
293            },
294            ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
295                // `&str` is represented as a valtree, let's keep using this
296                // optimization for now.
297                ty::Str => PatKind::Constant {
298                    value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
299                },
300                // All other references are converted into deref patterns and then recursively
301                // convert the dereferenced constant to a pattern that is the sub-pattern of the
302                // deref pattern.
303                _ => {
304                    if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
305                        return self.mk_err(
306                            tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
307                            ty,
308                        );
309                    } else {
310                        // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
311                        // matching against references, you can only use byte string literals.
312                        // The typechecker has a special case for byte string literals, by treating them
313                        // as slices. This means we turn `&[T; N]` constants into slice patterns, which
314                        // has no negative effects on pattern matching, even if we're actually matching on
315                        // arrays.
316                        let pointee_ty = match *pointee_ty.kind() {
317                            ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
318                                Ty::new_slice(tcx, elem_ty)
319                            }
320                            _ => *pointee_ty,
321                        };
322                        // References have the same valtree representation as their pointee.
323                        let subpattern = self.valtree_to_pat(cv, pointee_ty);
324                        PatKind::Deref { subpattern }
325                    }
326                }
327            },
328            ty::Float(flt) => {
329                let v = cv.unwrap_leaf();
330                let is_nan = match flt {
331                    ty::FloatTy::F16 => v.to_f16().is_nan(),
332                    ty::FloatTy::F32 => v.to_f32().is_nan(),
333                    ty::FloatTy::F64 => v.to_f64().is_nan(),
334                    ty::FloatTy::F128 => v.to_f128().is_nan(),
335                };
336                if is_nan {
337                    // NaNs are not ever equal to anything so they make no sense as patterns.
338                    // Also see <https://github.com/rust-lang/rfcs/pull/3535>.
339                    return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
340                } else {
341                    PatKind::Constant {
342                        value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
343                    }
344                }
345            }
346            ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
347                // The raw pointers we see here have been "vetted" by valtree construction to be
348                // just integers, so we simply allow them.
349                PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) }
350            }
351            ty::FnPtr(..) => {
352                unreachable!(
353                    "Valtree construction would never succeed for FnPtr, so this is unreachable."
354                )
355            }
356            _ => {
357                let err = InvalidPattern {
358                    span,
359                    non_sm_ty: ty,
360                    prefix: ty.prefix_string(tcx).to_string(),
361                };
362                return self.mk_err(tcx.dcx().create_err(err), ty);
363            }
364        };
365
366        Box::new(Pat { span, ty, kind })
367    }
368}
369
370/// Given a type with type parameters, visit every ADT looking for types that need to
371/// `#[derive(PartialEq)]` for it to be a structural type.
372fn extend_type_not_partial_eq<'tcx>(
373    tcx: TyCtxt<'tcx>,
374    typing_env: ty::TypingEnv<'tcx>,
375    ty: Ty<'tcx>,
376    err: &mut Diag<'_>,
377) {
378    /// Collect all types that need to be `StructuralPartialEq`.
379    struct UsedParamsNeedInstantiationVisitor<'tcx> {
380        tcx: TyCtxt<'tcx>,
381        typing_env: ty::TypingEnv<'tcx>,
382        /// The user has written `impl PartialEq for Ty` which means it's non-structual.
383        adts_with_manual_partialeq: FxHashSet<Span>,
384        /// The type has no `PartialEq` implementation, neither manual or derived.
385        adts_without_partialeq: FxHashSet<Span>,
386        /// The user has written `impl PartialEq for Ty` which means it's non-structual,
387        /// but we don't have a span to point at, so we'll just add them as a `note`.
388        manual: FxHashSet<Ty<'tcx>>,
389        /// The type has no `PartialEq` implementation, neither manual or derived, but
390        /// we don't have a span to point at, so we'll just add them as a `note`.
391        without: FxHashSet<Ty<'tcx>>,
392    }
393
394    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UsedParamsNeedInstantiationVisitor<'tcx> {
395        type Result = ControlFlow<()>;
396        fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
397            match ty.kind() {
398                ty::Dynamic(..) => return ControlFlow::Break(()),
399                ty::FnPtr(..) => return ControlFlow::Continue(()),
400                ty::Adt(def, _args) => {
401                    let ty_def_id = def.did();
402                    let ty_def_span = self.tcx.def_span(ty_def_id);
403                    let PartialEqImplStatus {
404                        has_impl,
405                        is_derived,
406                        structural_partial_eq,
407                        non_blanket_impl,
408                    } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty);
409                    match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) {
410                        (_, _, true, _) => {}
411                        (true, false, _, Some(def_id)) if def_id.is_local() => {
412                            self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id));
413                        }
414                        (true, false, _, _) if ty_def_id.is_local() => {
415                            self.adts_with_manual_partialeq.insert(ty_def_span);
416                        }
417                        (false, _, _, _) if ty_def_id.is_local() => {
418                            self.adts_without_partialeq.insert(ty_def_span);
419                        }
420                        (true, false, _, _) => {
421                            self.manual.insert(ty);
422                        }
423                        (false, _, _, _) => {
424                            self.without.insert(ty);
425                        }
426                        _ => {}
427                    };
428                    ty.super_visit_with(self)
429                }
430                _ => ty.super_visit_with(self),
431            }
432        }
433    }
434    let mut v = UsedParamsNeedInstantiationVisitor {
435        tcx,
436        typing_env,
437        adts_with_manual_partialeq: FxHashSet::default(),
438        adts_without_partialeq: FxHashSet::default(),
439        manual: FxHashSet::default(),
440        without: FxHashSet::default(),
441    };
442    if v.visit_ty(ty).is_break() {
443        return;
444    }
445    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
446    for span in v.adts_with_manual_partialeq {
447        err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details");
448    }
449    #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering
450    for span in v.adts_without_partialeq {
451        err.span_label(
452            span,
453            "must be annotated with `#[derive(PartialEq)]` to be usable in patterns",
454        );
455    }
456    #[allow(rustc::potential_query_instability)]
457    let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect();
458    manual.sort();
459    for ty in manual {
460        err.note(format!(
461            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"
462        ));
463    }
464    #[allow(rustc::potential_query_instability)]
465    let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect();
466    without.sort();
467    for ty in without {
468        err.note(format!(
469            "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns"
470        ));
471    }
472}
473
474#[derive(Debug)]
475struct PartialEqImplStatus {
476    has_impl: bool,
477    is_derived: bool,
478    structural_partial_eq: bool,
479    non_blanket_impl: Option<DefId>,
480}
481
482#[instrument(level = "trace", skip(tcx), ret)]
483fn type_has_partial_eq_impl<'tcx>(
484    tcx: TyCtxt<'tcx>,
485    typing_env: ty::TypingEnv<'tcx>,
486    ty: Ty<'tcx>,
487) -> PartialEqImplStatus {
488    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
489    // double-check there even *is* a semantic `PartialEq` to dispatch to.
490    //
491    // (If there isn't, then we can safely issue a hard
492    // error, because that's never worked, due to compiler
493    // using `PartialEq::eq` in this scenario in the past.)
494    let partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::PartialEq, None);
495    let structural_partial_eq_trait_id = tcx.require_lang_item(hir::LangItem::StructuralPeq, None);
496
497    let partial_eq_obligation = Obligation::new(
498        tcx,
499        ObligationCause::dummy(),
500        param_env,
501        ty::TraitRef::new(tcx, partial_eq_trait_id, [ty, ty]),
502    );
503
504    let mut automatically_derived = false;
505    let mut structural_peq = false;
506    let mut impl_def_id = None;
507    for def_id in tcx.non_blanket_impls_for_ty(partial_eq_trait_id, ty) {
508        automatically_derived = tcx.has_attr(def_id, sym::automatically_derived);
509        impl_def_id = Some(def_id);
510    }
511    for _ in tcx.non_blanket_impls_for_ty(structural_partial_eq_trait_id, ty) {
512        structural_peq = true;
513    }
514    // This *could* accept a type that isn't actually `PartialEq`, because region bounds get
515    // ignored. However that should be pretty much impossible since consts that do not depend on
516    // generics can only mention the `'static` lifetime, and how would one have a type that's
517    // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem
518    // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck
519    // can ensure that the type really implements `PartialEq`.
520    // We also do *not* require `const PartialEq`, not even in `const fn`. This violates the model
521    // that patterns can only do things that the code could also do without patterns, but it is
522    // needed for backwards compatibility. The actual pattern matching compares primitive values,
523    // `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code.
524    PartialEqImplStatus {
525        has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation),
526        is_derived: automatically_derived,
527        structural_partial_eq: structural_peq,
528        non_blanket_impl: impl_def_id,
529    }
530}