hir_ty/next_solver/
consts.rs

1//! Things related to consts in the next-trait-solver.
2
3use std::hash::Hash;
4
5use hir_def::{ConstParamId, TypeOrConstParamId};
6use intern::{Interned, Symbol};
7use rustc_ast_ir::try_visit;
8use rustc_ast_ir::visit::VisitorResult;
9use rustc_type_ir::{
10    BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
11    TypeVisitable, WithCachedTypeInfo,
12    inherent::{IntoKind, PlaceholderLike},
13    relate::Relate,
14};
15
16use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug};
17
18use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty};
19
20pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
21pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
22
23#[salsa::interned(constructor = new_, debug)]
24pub struct Const<'db> {
25    #[returns(ref)]
26    kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>,
27}
28
29impl<'db> Const<'db> {
30    pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self {
31        let flags = FlagComputation::for_const_kind(&kind);
32        let cached = WithCachedTypeInfo {
33            internee: kind,
34            flags: flags.flags,
35            outer_exclusive_binder: flags.outer_exclusive_binder,
36        };
37        Const::new_(interner.db(), InternedWrapperNoDebug(cached))
38    }
39
40    pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> {
41        salsa::with_attached_database(|db| {
42            let inner = &self.kind_(db).0;
43            // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will
44            // make sure that our returned value is valid for the lifetime `'db`.
45            unsafe { std::mem::transmute(inner) }
46        })
47        .unwrap()
48    }
49
50    pub fn error(interner: DbInterner<'db>) -> Self {
51        Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
52    }
53
54    pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self {
55        Const::new(interner, rustc_type_ir::ConstKind::Param(param))
56    }
57
58    pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self {
59        Const::new(interner, ConstKind::Placeholder(placeholder))
60    }
61
62    pub fn is_ct_infer(&self) -> bool {
63        matches!(&self.inner().internee, ConstKind::Infer(_))
64    }
65
66    pub fn is_trivially_wf(self) -> bool {
67        match self.kind() {
68            ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true,
69            ConstKind::Infer(_)
70            | ConstKind::Unevaluated(..)
71            | ConstKind::Value(_)
72            | ConstKind::Error(_)
73            | ConstKind::Expr(_) => false,
74        }
75    }
76}
77
78impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        self.0.internee.fmt(f)
81    }
82}
83
84pub type PlaceholderConst = Placeholder<rustc_type_ir::BoundVar>;
85
86#[derive(Copy, Clone, Hash, Eq, PartialEq)]
87pub struct ParamConst {
88    // FIXME: See `ParamTy`.
89    pub id: ConstParamId,
90    pub index: u32,
91}
92
93impl std::fmt::Debug for ParamConst {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        write!(f, "#{}", self.index)
96    }
97}
98
99/// A type-level constant value.
100///
101/// Represents a typed, fully evaluated constant.
102#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
103pub struct ValueConst<'db> {
104    pub(crate) ty: Ty<'db>,
105    pub(crate) value: Valtree<'db>,
106}
107
108impl<'db> ValueConst<'db> {
109    pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self {
110        let value = Valtree::new(bytes);
111        ValueConst { ty, value }
112    }
113}
114
115impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
116    fn ty(self) -> Ty<'db> {
117        self.ty
118    }
119
120    fn valtree(self) -> Valtree<'db> {
121        self.value
122    }
123}
124
125impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ValueConst<'db> {
126    fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
127        &self,
128        visitor: &mut V,
129    ) -> V::Result {
130        self.ty.visit_with(visitor)
131    }
132}
133
134impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ValueConst<'db> {
135    fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
136        ValueConst { ty: self.ty.fold_with(folder), value: self.value }
137    }
138    fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
139        self,
140        folder: &mut F,
141    ) -> Result<Self, F::Error> {
142        Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value })
143    }
144}
145
146#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct ConstBytes<'db>(pub Box<[u8]>, pub MemoryMap<'db>);
148
149impl Hash for ConstBytes<'_> {
150    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
151        self.0.hash(state)
152    }
153}
154
155#[salsa::interned(constructor = new_, debug)]
156pub struct Valtree<'db> {
157    #[returns(ref)]
158    bytes_: ConstBytes<'db>,
159}
160
161impl<'db> Valtree<'db> {
162    pub fn new(bytes: ConstBytes<'db>) -> Self {
163        salsa::with_attached_database(|db| unsafe {
164            // SAFETY: ¯\_(ツ)_/¯
165            std::mem::transmute(Valtree::new_(db, bytes))
166        })
167        .unwrap()
168    }
169
170    pub fn inner(&self) -> &ConstBytes<'db> {
171        salsa::with_attached_database(|db| {
172            let inner = self.bytes_(db);
173            // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will
174            // make sure that our returned value is valid for the lifetime `'db`.
175            unsafe { std::mem::transmute(inner) }
176        })
177        .unwrap()
178    }
179}
180
181#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
182pub struct ExprConst;
183
184impl rustc_type_ir::inherent::ParamLike for ParamConst {
185    fn index(self) -> u32 {
186        self.index
187    }
188}
189
190impl<'db> IntoKind for Const<'db> {
191    type Kind = ConstKind<'db>;
192
193    fn kind(self) -> Self::Kind {
194        self.inner().internee
195    }
196}
197
198impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> {
199    fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
200        &self,
201        visitor: &mut V,
202    ) -> V::Result {
203        visitor.visit_const(*self)
204    }
205}
206
207impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> {
208    fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
209        &self,
210        visitor: &mut V,
211    ) -> V::Result {
212        match self.kind() {
213            ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
214            ConstKind::Value(v) => v.visit_with(visitor),
215            ConstKind::Expr(e) => e.visit_with(visitor),
216            ConstKind::Error(e) => e.visit_with(visitor),
217
218            ConstKind::Param(_)
219            | ConstKind::Infer(_)
220            | ConstKind::Bound(..)
221            | ConstKind::Placeholder(_) => V::Result::output(),
222        }
223    }
224}
225
226impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> {
227    fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
228        self,
229        folder: &mut F,
230    ) -> Result<Self, F::Error> {
231        folder.try_fold_const(self)
232    }
233    fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
234        folder.fold_const(self)
235    }
236}
237
238impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> {
239    fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
240        self,
241        folder: &mut F,
242    ) -> Result<Self, F::Error> {
243        let kind = match self.kind() {
244            ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
245            ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
246            ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
247
248            ConstKind::Param(_)
249            | ConstKind::Infer(_)
250            | ConstKind::Bound(..)
251            | ConstKind::Placeholder(_)
252            | ConstKind::Error(_) => return Ok(self),
253        };
254        if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) }
255    }
256    fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
257        self,
258        folder: &mut F,
259    ) -> Self {
260        let kind = match self.kind() {
261            ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)),
262            ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)),
263            ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)),
264
265            ConstKind::Param(_)
266            | ConstKind::Infer(_)
267            | ConstKind::Bound(..)
268            | ConstKind::Placeholder(_)
269            | ConstKind::Error(_) => return self,
270        };
271        if kind != self.kind() { Const::new(folder.cx(), kind) } else { self }
272    }
273}
274
275impl<'db> Relate<DbInterner<'db>> for Const<'db> {
276    fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
277        relation: &mut R,
278        a: Self,
279        b: Self,
280    ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
281        relation.consts(a, b)
282    }
283}
284
285impl<'db> Flags for Const<'db> {
286    fn flags(&self) -> rustc_type_ir::TypeFlags {
287        self.inner().flags
288    }
289
290    fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
291        self.inner().outer_exclusive_binder
292    }
293}
294
295impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
296    fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self {
297        Const::new(interner, ConstKind::Infer(var))
298    }
299
300    fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self {
301        Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var)))
302    }
303
304    fn new_bound(
305        interner: DbInterner<'db>,
306        debruijn: rustc_type_ir::DebruijnIndex,
307        var: BoundVar,
308    ) -> Self {
309        Const::new(interner, ConstKind::Bound(debruijn, var))
310    }
311
312    fn new_anon_bound(
313        interner: DbInterner<'db>,
314        debruijn: rustc_type_ir::DebruijnIndex,
315        var: rustc_type_ir::BoundVar,
316    ) -> Self {
317        Const::new(interner, ConstKind::Bound(debruijn, var))
318    }
319
320    fn new_unevaluated(
321        interner: DbInterner<'db>,
322        uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>,
323    ) -> Self {
324        Const::new(interner, ConstKind::Unevaluated(uv))
325    }
326
327    fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self {
328        Const::new(interner, ConstKind::Expr(expr))
329    }
330
331    fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
332        Const::new(interner, ConstKind::Error(guar))
333    }
334
335    fn new_placeholder(
336        interner: DbInterner<'db>,
337        param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst,
338    ) -> Self {
339        Const::new(interner, ConstKind::Placeholder(param))
340    }
341}
342
343impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst {
344    type Bound = rustc_type_ir::BoundVar;
345
346    fn universe(self) -> rustc_type_ir::UniverseIndex {
347        self.universe
348    }
349
350    fn var(self) -> rustc_type_ir::BoundVar {
351        self.bound
352    }
353
354    fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
355        Placeholder { universe: ui, bound: self.bound }
356    }
357
358    fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
359        Placeholder { universe: ui, bound: var }
360    }
361    fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
362        Placeholder { universe: ui, bound: var }
363    }
364}
365
366impl<'db> TypeVisitable<DbInterner<'db>> for ExprConst {
367    fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
368        &self,
369        visitor: &mut V,
370    ) -> V::Result {
371        // Ensure we get back to this when we fill in the fields
372        let ExprConst = &self;
373        V::Result::output()
374    }
375}
376
377impl<'db> TypeFoldable<DbInterner<'db>> for ExprConst {
378    fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
379        self,
380        folder: &mut F,
381    ) -> Result<Self, F::Error> {
382        Ok(ExprConst)
383    }
384    fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
385        ExprConst
386    }
387}
388
389impl<'db> Relate<DbInterner<'db>> for ExprConst {
390    fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
391        relation: &mut R,
392        a: Self,
393        b: Self,
394    ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
395        // Ensure we get back to this when we fill in the fields
396        let ExprConst = b;
397        Ok(a)
398    }
399}
400
401impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst {
402    fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
403        // Ensure we get back to this when we fill in the fields
404        let ExprConst = self;
405        GenericArgs::default()
406    }
407}