use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::sym; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{ErrorGuaranteed, DUMMY_SP}; use rustc_type_ir::RegionKind as IrRegionKind; pub use rustc_type_ir::RegionVid; use std::ops::Deref; use crate::ty::{self, BoundVar, TyCtxt, TypeFlags}; pub type RegionKind<'tcx> = IrRegionKind>; /// Use this rather than `RegionKind`, whenever possible. #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] pub struct Region<'tcx>(pub Interned<'tcx, RegionKind<'tcx>>); impl<'tcx> rustc_type_ir::inherent::IntoKind for Region<'tcx> { type Kind = RegionKind<'tcx>; fn kind(self) -> RegionKind<'tcx> { *self } } impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> { fn flags(&self) -> TypeFlags { self.type_flags() } fn outer_exclusive_binder(&self) -> ty::DebruijnIndex { match **self { ty::ReBound(debruijn, _) => debruijn.shifted_in(1), _ => ty::INNERMOST, } } } impl<'tcx> Region<'tcx> { #[inline] pub fn new_early_param( tcx: TyCtxt<'tcx>, early_bound_region: ty::EarlyParamRegion, ) -> Region<'tcx> { tcx.intern_region(ty::ReEarlyParam(early_bound_region)) } #[inline] pub fn new_bound( tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, bound_region: ty::BoundRegion, ) -> Region<'tcx> { // Use a pre-interned one when possible. if let ty::BoundRegion { var, kind: ty::BrAnon } = bound_region && let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize()) && let Some(re) = inner.get(var.as_usize()).copied() { re } else { tcx.intern_region(ty::ReBound(debruijn, bound_region)) } } #[inline] pub fn new_late_param( tcx: TyCtxt<'tcx>, scope: DefId, bound_region: ty::BoundRegionKind, ) -> Region<'tcx> { tcx.intern_region(ty::ReLateParam(ty::LateParamRegion { scope, bound_region })) } #[inline] pub 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))) } #[inline] pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Region<'tcx> { tcx.intern_region(ty::RePlaceholder(placeholder)) } /// Constructs a `RegionKind::ReError` region. #[track_caller] pub 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] pub 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] pub fn new_error_with_message>( 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`. pub 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(debruijn, region) => Region::new_bound(tcx, debruijn, region), ty::ReLateParam(ty::LateParamRegion { scope, bound_region }) => { Region::new_late_param(tcx, scope, bound_region) } 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), } } } impl<'tcx> rustc_type_ir::inherent::Region> for Region<'tcx> { fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self { Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon }) } fn new_static(tcx: TyCtxt<'tcx>) -> Self { tcx.lifetimes.re_static } } /// Region utilities impl<'tcx> Region<'tcx> { pub fn kind(self) -> RegionKind<'tcx> { *self.0.0 } pub fn get_name(self) -> Option { if self.has_name() { match *self { ty::ReEarlyParam(ebr) => Some(ebr.name), ty::ReBound(_, br) => br.kind.get_name(), ty::ReLateParam(fr) => fr.bound_region.get_name(), ty::ReStatic => Some(kw::StaticLifetime), ty::RePlaceholder(placeholder) => placeholder.bound.kind.get_name(), _ => None, } } else { None } } pub fn get_name_or_anon(self) -> Symbol { match self.get_name() { Some(name) => name, None => sym::anon, } } /// Is this region named by the user? pub fn has_name(self) -> bool { match *self { ty::ReEarlyParam(ebr) => ebr.has_name(), ty::ReBound(_, br) => br.kind.is_named(), ty::ReLateParam(fr) => fr.bound_region.is_named(), ty::ReStatic => true, ty::ReVar(..) => false, ty::RePlaceholder(placeholder) => placeholder.bound.kind.is_named(), ty::ReErased => false, ty::ReError(_) => false, } } #[inline] pub fn is_error(self) -> bool { matches!(*self, ty::ReError(_)) } #[inline] pub fn is_static(self) -> bool { matches!(*self, ty::ReStatic) } #[inline] pub fn is_erased(self) -> bool { matches!(*self, ty::ReErased) } #[inline] pub fn is_bound(self) -> bool { matches!(*self, ty::ReBound(..)) } #[inline] pub fn is_placeholder(self) -> bool { matches!(*self, ty::RePlaceholder(..)) } #[inline] pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool { match *self { ty::ReBound(debruijn, _) => debruijn >= index, _ => false, } } pub fn type_flags(self) -> TypeFlags { let mut flags = TypeFlags::empty(); match *self { ty::ReVar(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; } ty::RePlaceholder(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; } ty::ReEarlyParam(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PARAM; } ty::ReLateParam { .. } => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; } ty::ReStatic => { flags = flags | TypeFlags::HAS_FREE_REGIONS; } ty::ReBound(..) => { flags = flags | TypeFlags::HAS_RE_BOUND; } ty::ReErased => { flags = flags | TypeFlags::HAS_RE_ERASED; } ty::ReError(_) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_ERROR; } } debug!("type_flags({:?}) = {:?}", self, flags); flags } /// Given an early-bound or free region, returns the `DefId` where it was bound. /// For example, consider the regions in this snippet of code: /// /// ```ignore (illustrative) /// impl<'a> Foo { /// // ^^ -- early bound, declared on an impl /// /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c /// // ^^ ^^ ^ anonymous, late-bound /// // | early-bound, appears in where-clauses /// // late-bound, appears only in fn args /// {..} /// } /// ``` /// /// Here, `free_region_binding_scope('a)` would return the `DefId` /// of the impl, and for all the other highlighted regions, it /// would return the `DefId` of the function. In other cases (not shown), this /// function might return the `DefId` of a closure. pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId { match *self { ty::ReEarlyParam(br) => tcx.parent(br.def_id), ty::ReLateParam(fr) => fr.scope, _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), } } /// True for free regions other than `'static`. pub fn is_param(self) -> bool { matches!(*self, ty::ReEarlyParam(_) | ty::ReLateParam(_)) } /// True for free region in the current context. /// /// This is the case for `'static` and param regions. pub fn is_free(self) -> bool { match *self { ty::ReStatic | ty::ReEarlyParam(..) | ty::ReLateParam(..) => true, ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReBound(..) | ty::ReErased | ty::ReError(..) => false, } } pub fn is_var(self) -> bool { matches!(self.kind(), ty::ReVar(_)) } pub fn as_var(self) -> RegionVid { match self.kind() { ty::ReVar(vid) => vid, _ => bug!("expected region {:?} to be of kind ReVar", self), } } } impl<'tcx> Deref for Region<'tcx> { type Target = RegionKind<'tcx>; #[inline] fn deref(&self) -> &RegionKind<'tcx> { self.0.0 } } #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct EarlyParamRegion { pub def_id: DefId, pub index: u32, pub name: Symbol, } impl std::fmt::Debug for EarlyParamRegion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // FIXME(BoxyUwU): self.def_id goes first because of `erased-regions-in-hidden-ty.rs` being impossible to write // error annotations for otherwise. :). Ideally this would be `self.name, self.index, self.def_id`. write!(f, "{:?}_{}/#{}", self.def_id, self.name, self.index) } } #[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] /// The parameter representation of late-bound function parameters, "some region /// at least as big as the scope `fr.scope`". pub struct LateParamRegion { pub scope: DefId, pub bound_region: BoundRegionKind, } #[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)] #[derive(HashStable)] pub enum BoundRegionKind { /// An anonymous region parameter for a given fn (&T) BrAnon, /// Named region parameters for functions (a in &'a T) /// /// The `DefId` is needed to distinguish free regions in /// the event of shadowing. BrNamed(DefId, Symbol), /// Anonymous region for the implicit env pointer parameter /// to a closure BrEnv, } #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] #[derive(HashStable)] pub struct BoundRegion { pub var: BoundVar, pub kind: BoundRegionKind, } impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { fn var(self) -> BoundVar { self.var } } impl core::fmt::Debug for BoundRegion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.kind { BoundRegionKind::BrAnon => write!(f, "{:?}", self.var), BoundRegionKind::BrEnv => write!(f, "{:?}.Env", self.var), BoundRegionKind::BrNamed(def, symbol) => { write!(f, "{:?}.Named({:?}, {:?})", self.var, def, symbol) } } } } impl BoundRegionKind { pub fn is_named(&self) -> bool { match *self { BoundRegionKind::BrNamed(_, name) => { name != kw::UnderscoreLifetime && name != kw::Empty } _ => false, } } pub fn get_name(&self) -> Option { if self.is_named() { match *self { BoundRegionKind::BrNamed(_, name) => return Some(name), _ => unreachable!(), } } None } pub fn get_id(&self) -> Option { match *self { BoundRegionKind::BrNamed(id, _) => return Some(id), _ => None, } } }