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