frontend 0.4.0

rustc's frontend with no LLVM and no std: parsing through MIR, as a library
// `#![no_std]`: these arrive with the standard prelude and name no path, so a `std::`
// search cannot see them - and a `#[derive]` can use them without the name appearing
// in this file at all, which is why they are not trimmed by inspection.
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;

use crate::rustc_errors::MultiSpan;
use crate::rustc_hir::def_id::DefId;
use rustc_macros::{StableHash, TyDecodable, TyEncodable, extension};
use crate::rustc_span::{DUMMY_SP, ErrorGuaranteed, Symbol, kw, sym};
pub use crate::rustc_type_ir::RegionVid;
use crate::rustc_type_ir::{
    LateParamRegion as IrLateParamRegion, Region as IrRegion, RegionKind as IrRegionKind,
};

use crate::rustc_middle::ty::{self, BoundVar, TyCtxt};

pub type Region<'tcx> = IrRegion<TyCtxt<'tcx>>;
pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
pub type LateParamRegion<'tcx> = IrLateParamRegion<TyCtxt<'tcx>>;

#[extension(pub trait RegionExt<'tcx>)]
impl<'tcx> Region<'tcx> {
    #[inline]
    fn new_early_param(
        tcx: TyCtxt<'tcx>,
        early_bound_region: ty::EarlyParamRegion,
    ) -> Region<'tcx> {
        tcx.intern_region(ty::ReEarlyParam(early_bound_region))
    }

    #[inline]
    fn new_late_param(tcx: TyCtxt<'tcx>, scope: DefId, kind: LateParamRegionKind) -> Region<'tcx> {
        let data = LateParamRegion { scope, kind };
        tcx.intern_region(ty::ReLateParam(data))
    }

    #[inline]
    fn new_var(tcx: TyCtxt<'tcx>, v: ty::RegionVid) -> Region<'tcx> {
        // Use a pre-interned one when possible.
        tcx.lifetimes
            .re_vars
            .get(v.as_usize())
            .copied()
            .unwrap_or_else(|| tcx.intern_region(ty::ReVar(v)))
    }

    /// Constructs a `RegionKind::ReError` region.
    #[track_caller]
    fn new_error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Region<'tcx> {
        tcx.intern_region(ty::ReError(guar))
    }

    /// Constructs a `RegionKind::ReError` region and registers a delayed bug to ensure it gets
    /// used.
    #[track_caller]
    fn new_error_misc(tcx: TyCtxt<'tcx>) -> Region<'tcx> {
        Region::new_error_with_message(
            tcx,
            DUMMY_SP,
            "RegionKind::ReError constructed but no error reported",
        )
    }

    /// Constructs a `RegionKind::ReError` region and registers a delayed bug with the given `msg`
    /// to ensure it gets used.
    #[track_caller]
    fn new_error_with_message<S: Into<MultiSpan>>(
        tcx: TyCtxt<'tcx>,
        span: S,
        msg: &'static str,
    ) -> Region<'tcx> {
        let reported = tcx.dcx().span_delayed_bug(span, msg);
        Region::new_error(tcx, reported)
    }

    /// Avoid this in favour of more specific `new_*` methods, where possible,
    /// to avoid the cost of the `match`.
    fn new_from_kind(tcx: TyCtxt<'tcx>, kind: RegionKind<'tcx>) -> Region<'tcx> {
        match kind {
            ty::ReEarlyParam(region) => Region::new_early_param(tcx, region),
            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), region) => {
                Region::new_bound(tcx, debruijn, region)
            }
            ty::ReBound(ty::BoundVarIndexKind::Canonical, region) => {
                Region::new_canonical_bound(tcx, region.var)
            }
            ty::ReLateParam(ty::LateParamRegion { scope, kind }) => {
                Region::new_late_param(tcx, scope, kind)
            }
            ty::ReStatic => tcx.lifetimes.re_static,
            ty::ReVar(vid) => Region::new_var(tcx, vid),
            ty::RePlaceholder(region) => Region::new_placeholder(tcx, region),
            ty::ReErased => tcx.lifetimes.re_erased,
            ty::ReError(reported) => Region::new_error(tcx, reported),
        }
    }

    fn get_name(self, tcx: TyCtxt<'tcx>) -> Option<Symbol> {
        match self.kind() {
            ty::ReEarlyParam(ebr) => ebr.is_named().then_some(ebr.name),
            ty::ReBound(_, br) => br.kind.get_name(tcx),
            ty::ReLateParam(fr) => fr.kind.get_name(tcx),
            ty::ReStatic => Some(kw::StaticLifetime),
            ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(tcx),
            _ => None,
        }
    }

    fn get_name_or_anon(self, tcx: TyCtxt<'tcx>) -> Symbol {
        match self.get_name(tcx) {
            Some(name) => name,
            None => sym::anon,
        }
    }

    /// Is this region named by the user?
    fn is_named(self, tcx: TyCtxt<'tcx>) -> bool {
        match self.kind() {
            ty::ReEarlyParam(ebr) => ebr.is_named(),
            ty::ReBound(_, br) => br.kind.is_named(tcx),
            ty::ReLateParam(fr) => fr.kind.is_named(tcx),
            ty::ReStatic => true,
            ty::ReVar(..) => false,
            ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(tcx),
            ty::ReErased => false,
            ty::ReError(_) => false,
        }
    }

    #[inline]
    fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool {
        match self.kind() {
            ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), _) => debruijn >= index,
            _ => false,
        }
    }

    /// Given some item `binding_item`, check if this region is a generic parameter introduced by it
    /// or one of the parent generics. Returns the `DefId` of the parameter definition if so.
    fn opt_param_def_id(self, tcx: TyCtxt<'tcx>, binding_item: DefId) -> Option<DefId> {
        match self.kind() {
            ty::ReEarlyParam(ebr) => {
                Some(tcx.generics_of(binding_item).region_param(ebr, tcx).def_id)
            }
            ty::ReLateParam(ty::LateParamRegion {
                kind: ty::LateParamRegionKind::Named(def_id),
                ..
            }) => Some(def_id),
            _ => None,
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(StableHash)]
pub struct EarlyParamRegion {
    pub index: u32,
    pub name: Symbol,
}

impl EarlyParamRegion {
    /// Does this early bound region have a name? Early bound regions normally
    /// always have names except when using anonymous lifetimes (`'_`).
    pub fn is_named(&self) -> bool {
        self.name != kw::UnderscoreLifetime
    }
}

impl crate::rustc_type_ir::inherent::ParamLike for EarlyParamRegion {
    fn index(self) -> u32 {
        self.index
    }
}

impl core::fmt::Debug for EarlyParamRegion {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}/#{}", self.name, self.index)
    }
}

/// When liberating bound regions, we map their [`ty::BoundRegionKind`]
/// to this as we need to track the index of anonymous regions. We
/// otherwise end up liberating multiple bound regions to the same
/// late-bound region.
#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)]
#[derive(StableHash)]
pub enum LateParamRegionKind {
    /// An anonymous region parameter for a given fn (&T)
    ///
    /// Unlike [`ty::BoundRegionKind::Anon`], this tracks the index of the
    /// liberated bound region.
    ///
    /// We should ideally never liberate anonymous regions, but do so for the
    /// sake of diagnostics in `FnCtxt::sig_of_closure_with_expectation`.
    Anon(u32),

    /// An anonymous region parameter with a `Symbol` name.
    ///
    /// Used to give late-bound regions names for things like pretty printing.
    NamedAnon(u32, Symbol),

    /// Late-bound regions that appear in the AST.
    Named(DefId),

    /// Anonymous region for the implicit env pointer parameter
    /// to a closure
    ClosureEnv,
}

impl LateParamRegionKind {
    pub fn from_bound(var: BoundVar, br: ty::BoundRegionKind<'_>) -> LateParamRegionKind {
        match br {
            ty::BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
            ty::BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
            ty::BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
            ty::BoundRegionKind::NamedForPrinting(name) => {
                LateParamRegionKind::NamedAnon(var.as_u32(), name)
            }
        }
    }

    pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool {
        self.get_name(tcx).is_some()
    }

    pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
        match *self {
            LateParamRegionKind::Named(def_id) => {
                let name = tcx.item_name(def_id);
                if name != kw::UnderscoreLifetime { Some(name) } else { None }
            }
            LateParamRegionKind::NamedAnon(_, name) => Some(name),
            _ => None,
        }
    }

    pub fn get_id(&self) -> Option<DefId> {
        match *self {
            LateParamRegionKind::Named(id) => Some(id),
            _ => None,
        }
    }
}

// Some types are used a lot. Make sure they don't unintentionally get bigger.
#[cfg(target_pointer_width = "64")]
mod size_asserts {
    use crate::static_assert_size;

    use super::*;
    // tidy-alphabetical-start
    static_assert_size!(RegionKind<'_>, 24);
    static_assert_size!(ty::WithCachedTypeInfo<RegionKind<'_>>, 32);
    // tidy-alphabetical-end
}