| //! Things related to regions. |
| |
| use hir_def::LifetimeParamId; |
| use intern::{Interned, Symbol}; |
| use rustc_type_ir::{ |
| BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, |
| inherent::{IntoKind, PlaceholderLike, SliceLike}, |
| relate::Relate, |
| }; |
| |
| use crate::next_solver::{GenericArg, OutlivesPredicate}; |
| |
| use super::{ |
| ErrorGuaranteed, SolverDefId, interned_vec_db, |
| interner::{BoundVarKind, DbInterner, Placeholder}, |
| }; |
| |
| pub type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>; |
| |
| #[salsa::interned(constructor = new_, debug)] |
| pub struct Region<'db> { |
| #[returns(ref)] |
| kind_: RegionKind<'db>, |
| } |
| |
| impl<'db> Region<'db> { |
| pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { |
| Region::new_(interner.db(), kind) |
| } |
| |
| pub fn inner(&self) -> &RegionKind<'db> { |
| salsa::with_attached_database(|db| { |
| let inner = self.kind_(db); |
| // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will |
| // make sure that our returned value is valid for the lifetime `'db`. |
| unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) } |
| }) |
| .unwrap() |
| } |
| |
| pub fn new_early_param( |
| interner: DbInterner<'db>, |
| early_bound_region: EarlyParamRegion, |
| ) -> Self { |
| Region::new(interner, RegionKind::ReEarlyParam(early_bound_region)) |
| } |
| |
| pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self { |
| Region::new(interner, RegionKind::RePlaceholder(placeholder)) |
| } |
| |
| pub fn new_var(interner: DbInterner<'db>, v: RegionVid) -> Region<'db> { |
| Region::new(interner, RegionKind::ReVar(v)) |
| } |
| |
| pub fn new_erased(interner: DbInterner<'db>) -> Region<'db> { |
| Region::new(interner, RegionKind::ReErased) |
| } |
| |
| pub fn is_placeholder(&self) -> bool { |
| matches!(self.inner(), RegionKind::RePlaceholder(..)) |
| } |
| |
| pub fn is_static(&self) -> bool { |
| matches!(self.inner(), RegionKind::ReStatic) |
| } |
| |
| pub fn is_var(&self) -> bool { |
| matches!(self.inner(), RegionKind::ReVar(_)) |
| } |
| |
| pub fn error(interner: DbInterner<'db>) -> Self { |
| Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) |
| } |
| |
| pub fn type_flags(&self) -> TypeFlags { |
| let mut flags = TypeFlags::empty(); |
| |
| match &self.inner() { |
| RegionKind::ReVar(..) => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; |
| flags |= TypeFlags::HAS_RE_INFER; |
| } |
| RegionKind::RePlaceholder(..) => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; |
| flags |= TypeFlags::HAS_RE_PLACEHOLDER; |
| } |
| RegionKind::ReEarlyParam(..) => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; |
| flags |= TypeFlags::HAS_RE_PARAM; |
| } |
| RegionKind::ReLateParam(..) => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; |
| } |
| RegionKind::ReStatic => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| } |
| RegionKind::ReBound(..) => { |
| flags |= TypeFlags::HAS_RE_BOUND; |
| } |
| RegionKind::ReErased => { |
| flags |= TypeFlags::HAS_RE_ERASED; |
| } |
| RegionKind::ReError(..) => { |
| flags |= TypeFlags::HAS_FREE_REGIONS; |
| flags |= TypeFlags::HAS_ERROR; |
| } |
| } |
| |
| flags |
| } |
| } |
| |
| pub type PlaceholderRegion = Placeholder<BoundRegion>; |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
| pub struct EarlyParamRegion { |
| // FIXME: See `ParamTy`. |
| pub id: LifetimeParamId, |
| pub index: u32, |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
| /// The parameter representation of late-bound function parameters, "some region |
| /// at least as big as the scope `fr.scope`". |
| /// |
| /// Similar to a placeholder region as we create `LateParam` regions when entering a binder |
| /// except they are always in the root universe and instead of using a boundvar to distinguish |
| /// between others we use the `DefId` of the parameter. For this reason the `bound_region` field |
| /// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling |
| /// different parameters apart. |
| pub struct LateParamRegion { |
| pub scope: SolverDefId, |
| pub bound_region: BoundRegionKind, |
| } |
| |
| impl std::fmt::Debug for LateParamRegion { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) |
| } |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
| pub enum BoundRegionKind { |
| /// An anonymous region parameter for a given fn (&T) |
| Anon, |
| |
| /// Named region parameters for functions (a in &'a T) |
| /// |
| /// The `DefId` is needed to distinguish free regions in |
| /// the event of shadowing. |
| Named(SolverDefId), |
| |
| /// Anonymous region for the implicit env pointer parameter |
| /// to a closure |
| ClosureEnv, |
| } |
| |
| impl std::fmt::Debug for BoundRegionKind { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| match *self { |
| BoundRegionKind::Anon => write!(f, "BrAnon"), |
| BoundRegionKind::Named(did) => { |
| write!(f, "BrNamed({did:?})") |
| } |
| BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), |
| } |
| } |
| } |
| |
| #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
| pub struct BoundRegion { |
| pub var: BoundVar, |
| pub kind: BoundRegionKind, |
| } |
| |
| impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { |
| fn index(self) -> u32 { |
| self.index |
| } |
| } |
| |
| impl std::fmt::Debug for EarlyParamRegion { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| write!(f, "#{}", self.index) |
| // write!(f, "{}/#{}", self.name, self.index) |
| } |
| } |
| |
| impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundRegion { |
| fn var(self) -> BoundVar { |
| self.var |
| } |
| |
| fn assert_eq(self, var: BoundVarKind) { |
| assert_eq!(self.kind, var.expect_region()) |
| } |
| } |
| |
| impl core::fmt::Debug for BoundRegion { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| match &self.kind { |
| BoundRegionKind::Anon => write!(f, "{:?}", self.var), |
| BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), |
| BoundRegionKind::Named(def) => { |
| write!(f, "{:?}.Named({:?})", self.var, def) |
| } |
| } |
| } |
| } |
| |
| impl BoundRegionKind { |
| pub fn is_named(&self) -> bool { |
| matches!(self, BoundRegionKind::Named(_)) |
| } |
| |
| pub fn get_name(&self) -> Option<Symbol> { |
| None |
| } |
| |
| pub fn get_id(&self) -> Option<SolverDefId> { |
| match self { |
| BoundRegionKind::Named(id) => Some(*id), |
| _ => None, |
| } |
| } |
| } |
| |
| impl<'db> IntoKind for Region<'db> { |
| type Kind = RegionKind<'db>; |
| |
| fn kind(self) -> Self::Kind { |
| *self.inner() |
| } |
| } |
| |
| impl<'db> TypeVisitable<DbInterner<'db>> for Region<'db> { |
| fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( |
| &self, |
| visitor: &mut V, |
| ) -> V::Result { |
| visitor.visit_region(*self) |
| } |
| } |
| |
| impl<'db> TypeFoldable<DbInterner<'db>> for Region<'db> { |
| fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( |
| self, |
| folder: &mut F, |
| ) -> Result<Self, F::Error> { |
| folder.try_fold_region(self) |
| } |
| fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { |
| folder.fold_region(self) |
| } |
| } |
| |
| impl<'db> Relate<DbInterner<'db>> for Region<'db> { |
| fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( |
| relation: &mut R, |
| a: Self, |
| b: Self, |
| ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { |
| relation.regions(a, b) |
| } |
| } |
| |
| impl<'db> Flags for Region<'db> { |
| fn flags(&self) -> rustc_type_ir::TypeFlags { |
| self.type_flags() |
| } |
| |
| fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { |
| match &self.inner() { |
| RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1), |
| _ => INNERMOST, |
| } |
| } |
| } |
| |
| impl<'db> rustc_type_ir::inherent::Region<DbInterner<'db>> for Region<'db> { |
| fn new_bound( |
| interner: DbInterner<'db>, |
| debruijn: rustc_type_ir::DebruijnIndex, |
| var: BoundRegion, |
| ) -> Self { |
| Region::new(interner, RegionKind::ReBound(debruijn, var)) |
| } |
| |
| fn new_anon_bound( |
| interner: DbInterner<'db>, |
| debruijn: rustc_type_ir::DebruijnIndex, |
| var: rustc_type_ir::BoundVar, |
| ) -> Self { |
| Region::new( |
| interner, |
| RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }), |
| ) |
| } |
| |
| fn new_static(interner: DbInterner<'db>) -> Self { |
| Region::new(interner, RegionKind::ReStatic) |
| } |
| |
| fn new_placeholder( |
| interner: DbInterner<'db>, |
| var: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderRegion, |
| ) -> Self { |
| Region::new(interner, RegionKind::RePlaceholder(var)) |
| } |
| } |
| |
| impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderRegion { |
| type Bound = BoundRegion; |
| |
| fn universe(self) -> rustc_type_ir::UniverseIndex { |
| self.universe |
| } |
| |
| fn var(self) -> rustc_type_ir::BoundVar { |
| self.bound.var |
| } |
| |
| fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { |
| Placeholder { universe: ui, bound: self.bound } |
| } |
| |
| fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self { |
| Placeholder { universe: ui, bound } |
| } |
| |
| fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { |
| Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } |
| } |
| } |
| |
| type GenericArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>; |
| |
| interned_vec_db!(RegionAssumptions, GenericArgOutlivesPredicate); |