//! Validation of patterns/matches.

mod check_match;
mod const_to_pat;
mod migration;

use std::assert_matches;
use std::cmp::Ordering;
use std::sync::Arc;

use rustc_abi::{FieldIdx, Integer};
use rustc_ast::LitKind;
use rustc_errors::codes::*;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::{self as hir, RangeEnd};
use rustc_index::Idx;
use rustc_middle::thir::{
    Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
};
use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{
    self, CanonicalUserTypeAnnotation, LitToConstInput, Ty, TyCtxt, const_lit_matches_ty,
};
use rustc_middle::{bug, span_bug};
use rustc_span::ErrorGuaranteed;
use tracing::{debug, instrument};

pub(crate) use self::check_match::check_match;
use self::migration::PatMigration;
use crate::errors::*;
use crate::thir::cx::ThirBuildCx;

/// Context for lowering HIR patterns to THIR patterns.
struct PatCtxt<'tcx, 'ptcx> {
    upper: &'ptcx mut ThirBuildCx<'tcx>,
    tcx: TyCtxt<'tcx>,
    typing_env: ty::TypingEnv<'tcx>,
    typeck_results: &'tcx ty::TypeckResults<'tcx>,

    /// Used by the Rust 2024 migration lint.
    rust_2024_migration: Option<PatMigration<'tcx>>,
}

#[instrument(level = "debug", skip(upper, tcx, typing_env, typeck_results), ret)]
pub(super) fn pat_from_hir<'tcx, 'ptcx>(
    upper: &'ptcx mut ThirBuildCx<'tcx>,
    tcx: TyCtxt<'tcx>,
    typing_env: ty::TypingEnv<'tcx>,
    typeck_results: &'tcx ty::TypeckResults<'tcx>,
    pat: &'tcx hir::Pat<'tcx>,
    // Present if `pat` came from a let statement with an explicit type annotation
    let_stmt_type: Option<&hir::Ty<'tcx>>,
) -> Box<Pat<'tcx>> {
    let mut pcx = PatCtxt {
        upper,
        tcx,
        typing_env,
        typeck_results,
        rust_2024_migration: typeck_results
            .rust_2024_migration_desugared_pats()
            .get(pat.hir_id)
            .map(PatMigration::new),
    };

    let mut thir_pat = pcx.lower_pattern(pat);

    // If this pattern came from a let statement with an explicit type annotation
    // (e.g. `let x: Foo = ...`), retain that user type information in the THIR pattern.
    if let Some(let_stmt_type) = let_stmt_type
        && let Some(&user_ty) = typeck_results.user_provided_types().get(let_stmt_type.hir_id)
    {
        debug!(?user_ty);
        let annotation = CanonicalUserTypeAnnotation {
            user_ty: Box::new(user_ty),
            span: let_stmt_type.span,
            inferred_ty: typeck_results.node_type(let_stmt_type.hir_id),
        };
        thir_pat
            .extra
            .get_or_insert_default()
            .ascriptions
            .push(Ascription { annotation, variance: ty::Covariant });
    }

    if let Some(m) = pcx.rust_2024_migration {
        m.emit(tcx, pat.hir_id);
    }

    thir_pat
}

impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> {
    fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
        let adjustments: &[PatAdjustment<'tcx>] =
            self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);

        // Track the default binding mode for the Rust 2024 migration suggestion.
        // Implicitly dereferencing references changes the default binding mode, but implicit deref
        // patterns do not. Only track binding mode changes if a ref type is in the adjustments.
        let mut opt_old_mode_span = None;
        if let Some(s) = &mut self.rust_2024_migration
            && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
        {
            opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments);
        }

        // When implicit dereferences have been inserted in this pattern, the unadjusted lowered
        // pattern has the type that results *after* dereferencing. For example, in this code:
        //
        // ```
        // match &&Some(0i32) {
        //     Some(n) => { ... },
        //     _ => { ... },
        // }
        // ```
        //
        // the type assigned to `Some(n)` in `unadjusted_pat` would be `Option<i32>` (this is
        // determined in rustc_hir_analysis::check::match). The adjustments would be
        //
        // `vec![&&Option<i32>, &Option<i32>]`.
        //
        // Applying the adjustments, we want to instead output `&&Some(n)` (as a THIR pattern). So
        // we wrap the unadjusted pattern in `PatKind::Deref` repeatedly, consuming the
        // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted
        // gets the least-dereferenced type).
        let unadjusted_pat = match pat.kind {
            hir::PatKind::Ref(inner, _, _)
                if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) =>
            {
                self.lower_pattern(inner)
            }
            _ => self.lower_pattern_unadjusted(pat),
        };

        let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| {
            debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust);
            let span = thir_pat.span;
            let kind = match adjust.kind {
                PatAdjust::BuiltinDeref => {
                    PatKind::Deref { pin: hir::Pinnedness::Not, subpattern: thir_pat }
                }
                PatAdjust::OverloadedDeref => {
                    let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat);
                    PatKind::DerefPattern { subpattern: thir_pat, borrow }
                }
                PatAdjust::PinDeref => {
                    PatKind::Deref { pin: hir::Pinnedness::Pinned, subpattern: thir_pat }
                }
            };
            Box::new(Pat { span, ty: adjust.source, kind, extra: None })
        });

        if let Some(s) = &mut self.rust_2024_migration
            && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref)
        {
            s.leave_ref(opt_old_mode_span);
        }

        adjusted_pat
    }

    fn lower_pattern_range_endpoint(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>, // Range pattern containing the endpoint
        expr: Option<&'tcx hir::PatExpr<'tcx>>,
        // Out-parameter collecting extra data to be reapplied by the caller
        ascriptions: &mut Vec<Ascription<'tcx>>,
    ) -> Result<Option<PatRangeBoundary<'tcx>>, ErrorGuaranteed> {
        assert_matches!(pat.kind, hir::PatKind::Range(..));

        // For partly-bounded ranges like `X..` or `..X`, an endpoint will be absent.
        // Return None in that case; the caller will use NegInfinity or PosInfinity instead.
        let Some(expr) = expr else { return Ok(None) };

        // Lower the endpoint into a temporary `thir::Pat` that will then be
        // deconstructed to obtain the constant value and other data.
        let endpoint_pat: Box<Pat<'tcx>> = self.lower_pat_expr(pat, expr);
        let Pat { ref kind, extra, .. } = endpoint_pat;

        // Preserve any ascriptions from endpoint constants.
        if let Some(extra) = extra {
            ascriptions.extend(extra.ascriptions);
        }

        // The unpeeled kind should now be a constant, giving us the endpoint value.
        let PatKind::Constant { value } = kind else {
            let msg =
                format!("found bad range pattern endpoint `{expr:?}` outside of error recovery");
            return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
        };
        Ok(Some(PatRangeBoundary::Finite(value.valtree)))
    }

    /// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
    /// encounter a range pattern like `-130i8..2`: if we believe `eval_bits`, this looks like a
    /// range where the endpoints are in the wrong order. To avoid a confusing error message, we
    /// check for overflow then.
    /// This is only called when the range is already known to be malformed.
    fn error_on_literal_overflow(
        &self,
        expr: Option<&'tcx hir::PatExpr<'tcx>>,
        ty: Ty<'tcx>,
    ) -> Result<(), ErrorGuaranteed> {
        let Some(expr) = expr else {
            return Ok(());
        };
        let span = expr.span;

        // We need to inspect the original expression, because if we only inspect the output of
        // `eval_bits`, an overflowed value has already been wrapped around.
        // We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint.
        let hir::PatExprKind::Lit { lit, negated } = expr.kind else {
            return Ok(());
        };
        let LitKind::Int(lit_val, _) = lit.node else {
            return Ok(());
        };
        let (min, max): (i128, u128) = match ty.kind() {
            ty::Int(ity) => {
                let size = Integer::from_int_ty(&self.tcx, *ity).size();
                (size.signed_int_min(), size.signed_int_max() as u128)
            }
            ty::Uint(uty) => {
                let size = Integer::from_uint_ty(&self.tcx, *uty).size();
                (0, size.unsigned_int_max())
            }
            _ => {
                return Ok(());
            }
        };
        // Detect literal value out of range `[min, max]` inclusive, avoiding use of `-min` to
        // prevent overflow/panic.
        if (negated && lit_val > max + 1) || (!negated && lit_val > max) {
            return Err(self.tcx.dcx().emit_err(LiteralOutOfRange { span, ty, min, max }));
        }
        Ok(())
    }

    fn lower_pattern_range(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>,
        lo_expr: Option<&'tcx hir::PatExpr<'tcx>>,
        hi_expr: Option<&'tcx hir::PatExpr<'tcx>>,
        end: RangeEnd,
    ) -> Result<Box<Pat<'tcx>>, ErrorGuaranteed> {
        let ty = self.typeck_results.node_type(pat.hir_id);
        let span = pat.span;

        if lo_expr.is_none() && hi_expr.is_none() {
            let msg = "found twice-open range pattern (`..`) outside of error recovery";
            self.tcx.dcx().span_bug(span, msg);
        }

        // Collect extra data while lowering the endpoints, to be reapplied later.
        let mut ascriptions = vec![];
        let mut lower_endpoint =
            |expr| self.lower_pattern_range_endpoint(pat, expr, &mut ascriptions);

        let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
        let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);

        let cmp = lo.compare_with(hi, ty, self.tcx);
        let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty }));
        match (end, cmp) {
            // `x..y` where `x < y`.
            (RangeEnd::Excluded, Some(Ordering::Less)) => {}
            // `x..=y` where `x < y`.
            (RangeEnd::Included, Some(Ordering::Less)) => {}
            // `x..=y` where `x == y` and `x` and `y` are finite.
            (RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
                let value = ty::Value { ty, valtree: lo.as_finite().unwrap() };
                kind = PatKind::Constant { value };
            }
            // `..=x` where `x == ty::MIN`.
            (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
            // `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
            // to include `ty::MAX`).
            (RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
            // `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
            _ => {
                // Emit a more appropriate message if there was overflow.
                self.error_on_literal_overflow(lo_expr, ty)?;
                self.error_on_literal_overflow(hi_expr, ty)?;
                let e = match end {
                    RangeEnd::Included => {
                        self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
                            span,
                            teach: self.tcx.sess.teach(E0030),
                        })
                    }
                    RangeEnd::Excluded if lo_expr.is_none() => {
                        self.tcx.dcx().emit_err(UpperRangeBoundCannotBeMin { span })
                    }
                    RangeEnd::Excluded => {
                        self.tcx.dcx().emit_err(LowerRangeBoundMustBeLessThanUpper { span })
                    }
                };
                return Err(e);
            }
        }
        let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None });

        // If we are handling a range with associated constants (e.g.
        // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
        // constants somewhere. Have them on the range pattern.
        thir_pat.extra.get_or_insert_default().ascriptions.extend(ascriptions);
        // IDs of expanded constants from range endpoints used to
        // also be preserved here, but that was only needed for unsafeck of
        // inline `const { .. }` patterns, which were removed by
        // <https://github.com/rust-lang/rust/pull/138492>.

        Ok(thir_pat)
    }

    #[instrument(skip(self), level = "debug")]
    fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
        let ty = self.typeck_results.node_type(pat.hir_id);
        let span = pat.span;

        // Some of these match arms return a `Box<Pat>` early, while others
        // evaluate to a `PatKind` that will become a `Box<Pat>` at the end of
        // this function.
        let kind = match pat.kind {
            hir::PatKind::Missing => PatKind::Missing,

            hir::PatKind::Wild => PatKind::Wild,

            hir::PatKind::Never => PatKind::Never,

            hir::PatKind::Expr(value) => return self.lower_pat_expr(pat, value),

            hir::PatKind::Range(lo_expr, hi_expr, end) => {
                match self.lower_pattern_range(pat, lo_expr, hi_expr, end) {
                    Ok(thir_pat) => return thir_pat,
                    Err(e) => PatKind::Error(e),
                }
            }

            hir::PatKind::Deref(subpattern) => {
                let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern);
                PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow }
            }
            hir::PatKind::Ref(subpattern, pin, _) => {
                // Track the default binding mode for the Rust 2024 migration suggestion.
                let opt_old_mode_span =
                    self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref());
                let subpattern = self.lower_pattern(subpattern);
                if let Some(s) = &mut self.rust_2024_migration {
                    s.leave_ref(opt_old_mode_span);
                }
                PatKind::Deref { pin, subpattern }
            }
            hir::PatKind::Box(subpattern) => PatKind::DerefPattern {
                subpattern: self.lower_pattern(subpattern),
                borrow: DerefPatBorrowMode::Box,
            },

            hir::PatKind::Slice(prefix, slice, suffix) => {
                return self.slice_or_array_pattern(pat, prefix, slice, suffix);
            }

            hir::PatKind::Tuple(pats, ddpos) => {
                let ty::Tuple(tys) = ty.kind() else {
                    span_bug!(pat.span, "unexpected type for tuple pattern: {:?}", ty);
                };
                let subpatterns = self.lower_tuple_subpats(pats, tys.len(), ddpos);
                PatKind::Leaf { subpatterns }
            }

            hir::PatKind::Binding(explicit_ba, id, ident, sub) => {
                let mut thir_pat_span = span;
                if let Some(ident_span) = ident.span.find_ancestor_inside(span) {
                    thir_pat_span = span.with_hi(ident_span.hi());
                }

                let mode = *self
                    .typeck_results
                    .pat_binding_modes()
                    .get(pat.hir_id)
                    .expect("missing binding mode");

                if let Some(s) = &mut self.rust_2024_migration {
                    s.visit_binding(pat.span, mode, explicit_ba, ident);
                }

                // A ref x pattern is the same node used for x, and as such it has
                // x's type, which is &T, where we want T (the type being matched).
                let var_ty = ty;
                let mut thir_pat_ty = ty;
                if let hir::ByRef::Yes(pinnedness, _) = mode.0 {
                    match pinnedness {
                        hir::Pinnedness::Pinned
                            if let Some(pty) = ty.pinned_ty()
                                && let &ty::Ref(_, rty, _) = pty.kind() =>
                        {
                            thir_pat_ty = rty;
                        }
                        hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => {
                            thir_pat_ty = rty;
                        }
                        _ => bug!("`ref {}` has wrong type {}", ident, ty),
                    }
                };

                let kind = PatKind::Binding {
                    mode,
                    name: ident.name,
                    var: LocalVarId(id),
                    ty: var_ty,
                    subpattern: self.lower_opt_pattern(sub),
                    is_primary: id == pat.hir_id,
                    is_shorthand: false,
                };
                // We might have modified the type or span, so use the modified
                // values in the THIR pattern node.
                return Box::new(Pat { ty: thir_pat_ty, span: thir_pat_span, kind, extra: None });
            }

            hir::PatKind::TupleStruct(ref qpath, pats, ddpos) => {
                let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
                let ty::Adt(adt_def, _) = ty.kind() else {
                    span_bug!(pat.span, "tuple struct pattern not applied to an ADT {:?}", ty);
                };
                let variant_def = adt_def.variant_of_res(res);
                let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos);
                return self.lower_variant_or_leaf(pat, None, res, subpatterns);
            }

            hir::PatKind::Struct(ref qpath, fields, _) => {
                let res = self.typeck_results.qpath_res(qpath, pat.hir_id);
                let subpatterns = fields
                    .iter()
                    .map(|field| {
                        let mut pattern = *self.lower_pattern(field.pat);
                        if let PatKind::Binding { ref mut is_shorthand, .. } = pattern.kind {
                            *is_shorthand = field.is_shorthand;
                        }
                        let field = self.typeck_results.field_index(field.hir_id);
                        FieldPat { field, pattern }
                    })
                    .collect();

                return self.lower_variant_or_leaf(pat, None, res, subpatterns);
            }

            hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) },

            hir::PatKind::Guard(pat, condition) => PatKind::Guard {
                subpattern: self.lower_pattern(pat),
                condition: self.upper.mirror_expr(condition),
            },

            hir::PatKind::Err(guar) => PatKind::Error(guar),
        };

        // For pattern kinds that haven't already returned, create a `thir::Pat`
        // with the HIR pattern node's type and span.
        Box::new(Pat { span, ty, kind, extra: None })
    }

    fn lower_tuple_subpats(
        &mut self,
        pats: &'tcx [hir::Pat<'tcx>],
        expected_len: usize,
        gap_pos: hir::DotDotPos,
    ) -> Vec<FieldPat<'tcx>> {
        pats.iter()
            .enumerate_and_adjust(expected_len, gap_pos)
            .map(|(i, subpattern)| FieldPat {
                field: FieldIdx::new(i),
                pattern: *self.lower_pattern(subpattern),
            })
            .collect()
    }

    fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Pat<'tcx>]> {
        pats.iter().map(|p| *self.lower_pattern(p)).collect()
    }

    fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option<Box<Pat<'tcx>>> {
        pat.map(|p| self.lower_pattern(p))
    }

    fn slice_or_array_pattern(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>,
        prefix: &'tcx [hir::Pat<'tcx>],
        slice: Option<&'tcx hir::Pat<'tcx>>,
        suffix: &'tcx [hir::Pat<'tcx>],
    ) -> Box<Pat<'tcx>> {
        let ty = self.typeck_results.node_type(pat.hir_id);
        let span = pat.span;

        let prefix = self.lower_patterns(prefix);
        let slice = self.lower_opt_pattern(slice);
        let suffix = self.lower_patterns(suffix);
        let kind = match ty.kind() {
            // Matching a slice, `[T]`.
            ty::Slice(..) => PatKind::Slice { prefix, slice, suffix },
            // Fixed-length array, `[T; len]`.
            ty::Array(_, len) => {
                let len = len
                    .try_to_target_usize(self.tcx)
                    .expect("expected len of array pat to be definite");
                assert!(len >= prefix.len() as u64 + suffix.len() as u64);
                PatKind::Array { prefix, slice, suffix }
            }
            _ => span_bug!(span, "bad slice pattern type {ty:?}"),
        };
        Box::new(Pat { ty, span, kind, extra: None })
    }

    fn lower_variant_or_leaf(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>,
        expr: Option<&'tcx hir::PatExpr<'tcx>>,
        res: Res,
        subpatterns: Vec<FieldPat<'tcx>>,
    ) -> Box<Pat<'tcx>> {
        // Check whether the caller should have provided an `expr` for this pattern kind.
        assert_matches!(
            (pat.kind, expr),
            (hir::PatKind::Expr(..) | hir::PatKind::Range(..), Some(_))
                | (hir::PatKind::Struct(..) | hir::PatKind::TupleStruct(..), None)
        );

        // Use the id/span of the `hir::PatExpr`, if provided.
        // Otherwise, use the id/span of the `hir::Pat`.
        let (hir_id, span) = match expr {
            Some(expr) => (expr.hir_id, expr.span),
            None => (pat.hir_id, pat.span),
        };
        let ty = self.typeck_results.node_type(hir_id);

        let res = match res {
            Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
                let variant_id = self.tcx.parent(variant_ctor_id);
                Res::Def(DefKind::Variant, variant_id)
            }
            res => res,
        };

        let kind = match res {
            Res::Def(DefKind::Variant, variant_id) => {
                let enum_id = self.tcx.parent(variant_id);
                let adt_def = self.tcx.adt_def(enum_id);
                if adt_def.is_enum() {
                    let args = match ty.kind() {
                        ty::Adt(_, args) | ty::FnDef(_, args) => args,
                        ty::Error(e) => {
                            // Avoid ICE (#50585)
                            return Box::new(Pat {
                                ty,
                                span,
                                kind: PatKind::Error(*e),
                                extra: None,
                            });
                        }
                        _ => bug!("inappropriate type for def: {:?}", ty),
                    };
                    PatKind::Variant {
                        adt_def,
                        args,
                        variant_index: adt_def.variant_index_with_id(variant_id),
                        subpatterns,
                    }
                } else {
                    PatKind::Leaf { subpatterns }
                }
            }

            Res::Def(
                DefKind::Struct
                | DefKind::Ctor(CtorOf::Struct, ..)
                | DefKind::Union
                | DefKind::TyAlias
                | DefKind::AssocTy,
                _,
            )
            | Res::SelfTyParam { .. }
            | Res::SelfTyAlias { .. }
            | Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
            _ => {
                let e = match res {
                    Res::Def(DefKind::ConstParam, def_id) => {
                        let const_span = self.tcx.def_span(def_id);
                        self.tcx.dcx().emit_err(ConstParamInPattern { span, const_span })
                    }
                    Res::Def(DefKind::Static { .. }, def_id) => {
                        let static_span = self.tcx.def_span(def_id);
                        self.tcx.dcx().emit_err(StaticInPattern { span, static_span })
                    }
                    _ => self.tcx.dcx().emit_err(NonConstPath { span }),
                };
                PatKind::Error(e)
            }
        };
        let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None });

        if let Some(user_ty) = self.user_args_applied_to_ty_of_hir_id(hir_id) {
            debug!(?thir_pat, ?user_ty, ?span, "lower_variant_or_leaf: applying ascription");
            let annotation = CanonicalUserTypeAnnotation {
                user_ty: Box::new(user_ty),
                span,
                inferred_ty: self.typeck_results.node_type(hir_id),
            };
            thir_pat
                .extra
                .get_or_insert_default()
                .ascriptions
                .push(Ascription { annotation, variance: ty::Covariant });
        }

        thir_pat
    }

    fn user_args_applied_to_ty_of_hir_id(
        &self,
        hir_id: hir::HirId,
    ) -> Option<ty::CanonicalUserType<'tcx>> {
        crate::thir::util::user_args_applied_to_ty_of_hir_id(self.tcx, self.typeck_results, hir_id)
    }

    /// Takes a HIR Path. If the path is a constant, evaluates it and feeds
    /// it to `const_to_pat`. Any other path (like enum variants without fields)
    /// is converted to the corresponding pattern via `lower_variant_or_leaf`.
    #[instrument(skip(self), level = "debug")]
    fn lower_path(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>, // Pattern that directly contains `expr`
        expr: &'tcx hir::PatExpr<'tcx>,
        qpath: &hir::QPath<'_>,
    ) -> Box<Pat<'tcx>> {
        assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));

        let id = expr.hir_id;
        let span = expr.span;
        let ty = self.typeck_results.node_type(id);
        let res = self.typeck_results.qpath_res(qpath, id);

        let (def_id, user_ty) = match res {
            Res::Def(DefKind::Const { .. }, def_id)
            | Res::Def(DefKind::AssocConst { .. }, def_id) => {
                (def_id, self.typeck_results.user_provided_types().get(id))
            }

            _ => {
                // The path isn't the name of a constant, so it must actually
                // be a unit struct or unit variant (e.g. `Option::None`).
                return self.lower_variant_or_leaf(pat, Some(expr), res, vec![]);
            }
        };

        // Lower the named constant to a THIR pattern.
        let args = self.typeck_results.node_args(id);
        // FIXME(mgca): we will need to special case IACs here to have type system compatible
        // generic args, instead of how we represent them in body expressions.
        let c = ty::Const::new_unevaluated(
            self.tcx,
            ty::UnevaluatedConst::new(
                self.tcx,
                ty::UnevaluatedConstKind::new_from_def_id(self.tcx, def_id),
                args,
            ),
        );
        let mut pattern = self.const_to_pat(c, ty, id, span);

        // If this is an associated constant with an explicit user-written
        // type, add an ascription node (e.g. `<Foo<'a> as MyTrait>::CONST`).
        if let Some(&user_ty) = user_ty {
            let annotation = CanonicalUserTypeAnnotation {
                user_ty: Box::new(user_ty),
                span,
                inferred_ty: self.typeck_results.node_type(id),
            };
            // Note that we use `Contravariant` here. See the
            // `variance` field documentation for details.
            pattern
                .extra
                .get_or_insert_default()
                .ascriptions
                .push(Ascription { annotation, variance: ty::Contravariant });
        }

        pattern
    }

    /// Lowers the kinds of "expression" that can appear in a HIR pattern:
    /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`)
    /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`)
    fn lower_pat_expr(
        &mut self,
        pat: &'tcx hir::Pat<'tcx>, // Pattern that directly contains `expr`
        expr: &'tcx hir::PatExpr<'tcx>,
    ) -> Box<Pat<'tcx>> {
        assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..));
        match &expr.kind {
            hir::PatExprKind::Path(qpath) => self.lower_path(pat, expr, qpath),
            hir::PatExprKind::Lit { lit, negated } => {
                // We handle byte string literal patterns by using the pattern's type instead of the
                // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference,
                // the pattern's type will be `&[u8]` whereas the literal's type is `&[u8; 3]`; using the
                // pattern's type means we'll properly translate it to a slice reference pattern. This works
                // because slices and arrays have the same valtree representation.
                //
                // Under `feature(deref_patterns)`, this adjustment can also convert string literal
                // patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`.

                let pat_ty = self.typeck_results.node_type(pat.hir_id);
                let lit_input = LitToConstInput { lit: lit.node, ty: Some(pat_ty), neg: *negated };
                let constant = const_lit_matches_ty(self.tcx, &lit.node, pat_ty, *negated)
                    .then(|| self.tcx.at(expr.span).lit_to_const(lit_input))
                    .flatten()
                    .map(|v| ty::Const::new_value(self.tcx, v.valtree, pat_ty))
                    .unwrap_or_else(|| {
                        ty::Const::new_error_with_message(
                            self.tcx,
                            expr.span,
                            "literal does not match expected type",
                        )
                    });
                self.const_to_pat(constant, pat_ty, expr.hir_id, lit.span)
            }
        }
    }
}
