Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/consts.rs')
| -rw-r--r-- | crates/hir-ty/src/next_solver/consts.rs | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs new file mode 100644 index 0000000000..926dbdc03d --- /dev/null +++ b/crates/hir-ty/src/next_solver/consts.rs @@ -0,0 +1,446 @@ +//! Things related to consts in the next-trait-solver. + +use std::hash::Hash; + +use hir_def::ConstParamId; +use macros::{TypeFoldable, TypeVisitable}; +use rustc_ast_ir::visit::VisitorResult; +use rustc_type_ir::{ + BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, InferConst, + TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + WithCachedTypeInfo, + inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike}, + relate::Relate, +}; + +use crate::{ + MemoryMap, + next_solver::{ClauseKind, ParamEnv, interner::InternedWrapperNoDebug}, +}; + +use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; + +pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>; +pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>; + +#[salsa::interned(constructor = new_)] +pub struct Const<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>, +} + +impl<'db> Const<'db> { + pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + let flags = FlagComputation::for_const_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Const::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> { + crate::with_attached_db(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Const::new(interner, ConstKind::Error(ErrorGuaranteed)) + } + + pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self { + Const::new(interner, ConstKind::Param(param)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self { + Const::new(interner, ConstKind::Placeholder(placeholder)) + } + + pub fn new_bound(interner: DbInterner<'db>, index: DebruijnIndex, bound: BoundConst) -> Self { + Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound)) + } + + pub fn new_valtree( + interner: DbInterner<'db>, + ty: Ty<'db>, + memory: Box<[u8]>, + memory_map: MemoryMap<'db>, + ) -> Self { + Const::new( + interner, + ConstKind::Value(ValueConst { + ty, + value: Valtree::new(ConstBytes { memory, memory_map }), + }), + ) + } + + pub fn is_ct_infer(&self) -> bool { + matches!(self.kind(), ConstKind::Infer(_)) + } + + pub fn is_error(&self) -> bool { + matches!(self.kind(), ConstKind::Error(_)) + } + + pub fn is_trivially_wf(self) -> bool { + match self.kind() { + ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true, + ConstKind::Infer(_) + | ConstKind::Unevaluated(..) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for Const<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +pub type PlaceholderConst = Placeholder<BoundConst>; + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct ParamConst { + // FIXME: See `ParamTy`. + pub id: ConstParamId, + pub index: u32, +} + +impl std::fmt::Debug for ParamConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +impl ParamConst { + pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable)] +pub struct ValueConst<'db> { + pub ty: Ty<'db>, + // FIXME: Should we ignore this for TypeVisitable, TypeFoldable? + #[type_visitable(ignore)] + #[type_foldable(identity)] + pub value: Valtree<'db>, +} + +impl<'db> ValueConst<'db> { + pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self { + let value = Valtree::new(bytes); + ValueConst { ty, value } + } +} + +impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> { + fn ty(self) -> Ty<'db> { + self.ty + } + + fn valtree(self) -> Valtree<'db> { + self.value + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstBytes<'db> { + pub memory: Box<[u8]>, + pub memory_map: MemoryMap<'db>, +} + +impl Hash for ConstBytes<'_> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.memory.hash(state) + } +} + +#[salsa::interned(constructor = new_, debug)] +pub struct Valtree<'db> { + #[returns(ref)] + bytes_: ConstBytes<'db>, +} + +impl<'db> Valtree<'db> { + pub fn new(bytes: ConstBytes<'db>) -> Self { + crate::with_attached_db(|db| unsafe { + // SAFETY: ¯\_(ツ)_/¯ + std::mem::transmute(Valtree::new_(db, bytes)) + }) + } + + pub fn inner(&self) -> &ConstBytes<'db> { + crate::with_attached_db(|db| { + let inner = self.bytes_(db); + // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub struct ExprConst; + +impl rustc_type_ir::inherent::ParamLike for ParamConst { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> IntoKind for Const<'db> { + type Kind = ConstKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_const(*self) + } +} + +impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> { + fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self.kind() { + ConstKind::Unevaluated(uv) => uv.visit_with(visitor), + ConstKind::Value(v) => v.visit_with(visitor), + ConstKind::Expr(e) => e.visit_with(visitor), + ConstKind::Error(e) => e.visit_with(visitor), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + folder.try_fold_const(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_const(self) + } +} + +impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> { + fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), + ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), + ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return Ok(self), + }; + if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) } + } + fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), + ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return self, + }; + if kind != self.kind() { Const::new(folder.cx(), kind) } else { self } + } +} + +impl<'db> Relate<DbInterner<'db>> for Const<'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.consts(a, b) + } +} + +impl<'db> Flags for Const<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> { + fn new_infer(interner: DbInterner<'db>, var: InferConst) -> Self { + Const::new(interner, ConstKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: ConstVid) -> Self { + Const::new(interner, ConstKind::Infer(InferConst::Var(var))) + } + + fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst) -> Self { + Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var)) + } + + fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self { + Const::new( + interner, + ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst { var }), + ) + } + + fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self { + Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst { var })) + } + + fn new_placeholder( + interner: DbInterner<'db>, + param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst, + ) -> Self { + Const::new(interner, ConstKind::Placeholder(param)) + } + + fn new_unevaluated( + interner: DbInterner<'db>, + uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>, + ) -> Self { + Const::new(interner, ConstKind::Unevaluated(uv)) + } + + fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self { + Const::new(interner, ConstKind::Expr(expr)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Const::new(interner, ConstKind::Error(guar)) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct BoundConst { + pub var: BoundVar, +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundConst { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + var.expect_const() + } +} + +impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst { + type Bound = BoundConst; + + 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, var: BoundConst) -> Self { + Placeholder { universe: ui, bound: var } + } + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundConst { var } } + } +} + +impl<'db> Relate<DbInterner<'db>> for ExprConst { + 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> { + // Ensure we get back to this when we fill in the fields + let ExprConst = b; + Ok(a) + } +} + +impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst { + fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs { + // Ensure we get back to this when we fill in the fields + let ExprConst = self; + GenericArgs::default() + } +} |