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