Skip to main content

hir_ty/next_solver/
consts.rs

1//! Things related to consts in the next-trait-solver.
2
3mod valtree;
4
5use std::hash::Hash;
6
7use hir_def::ConstParamId;
8use intern::{Interned, InternedRef, impl_internable};
9use macros::{GenericTypeVisitable, TypeFoldable, TypeVisitable};
10use rustc_ast_ir::visit::VisitorResult;
11use rustc_type_ir::{
12    BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags,
13    GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
14    TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
15};
16
17use crate::{
18    ParamEnvAndCrate,
19    next_solver::{
20        AllocationData, ClauseKind, ParamEnv, impl_foldable_for_interned_slice,
21        impl_stored_interned, interned_slice,
22    },
23};
24
25use super::{DbInterner, ErrorGuaranteed, GenericArgs, Ty};
26
27pub use self::valtree::*;
28
29pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
30pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
31
32#[derive(Clone, Copy, PartialEq, Eq, Hash)]
33pub struct Const<'db> {
34    pub(super) interned: InternedRef<'db, ConstInterned>,
35}
36
37#[derive(PartialEq, Eq, Hash, GenericTypeVisitable)]
38#[repr(align(4))] // Required for `GenericArg` bit-tagging.
39pub(super) struct ConstInterned(pub(super) WithCachedTypeInfo<ConstKind<'static>>);
40
41impl_internable!(gc; ConstInterned);
42impl_stored_interned!(ConstInterned, Const, StoredConst);
43
44const _: () = {
45    const fn is_copy<T: Copy>() {}
46    is_copy::<Const<'static>>();
47};
48
49impl<'db> Const<'db> {
50    pub fn new(_interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self {
51        let kind = unsafe { std::mem::transmute::<ConstKind<'db>, ConstKind<'static>>(kind) };
52        let flags = FlagComputation::for_const_kind(&kind);
53        let cached = WithCachedTypeInfo {
54            internee: kind,
55            flags: flags.flags,
56            outer_exclusive_binder: flags.outer_exclusive_binder,
57        };
58        Self { interned: Interned::new_gc(ConstInterned(cached)) }
59    }
60
61    pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> {
62        let inner = &self.interned.0;
63        unsafe {
64            std::mem::transmute::<
65                &WithCachedTypeInfo<ConstKind<'static>>,
66                &WithCachedTypeInfo<ConstKind<'db>>,
67            >(inner)
68        }
69    }
70
71    pub fn error(interner: DbInterner<'db>) -> Self {
72        interner.default_types().consts.error
73    }
74
75    pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self {
76        Const::new(interner, ConstKind::Param(param))
77    }
78
79    pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst<'db>) -> Self {
80        Const::new(interner, ConstKind::Placeholder(placeholder))
81    }
82
83    pub fn new_bound(
84        interner: DbInterner<'db>,
85        index: DebruijnIndex,
86        bound: BoundConst<'db>,
87    ) -> Self {
88        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound))
89    }
90
91    pub fn new_valtree(interner: DbInterner<'db>, ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
92        Const::new(interner, ConstKind::Value(ValueConst { ty, value: ValTree::new(kind) }))
93    }
94
95    pub fn new_from_allocation(
96        interner: DbInterner<'db>,
97        allocation: &AllocationData<'db>,
98        param_env: ParamEnvAndCrate<'db>,
99    ) -> Self {
100        allocation_to_const(
101            interner,
102            allocation.ty,
103            &allocation.memory,
104            &allocation.memory_map,
105            param_env,
106        )
107    }
108
109    pub fn is_ct_infer(&self) -> bool {
110        matches!(self.kind(), ConstKind::Infer(_))
111    }
112
113    pub fn is_error(&self) -> bool {
114        matches!(self.kind(), ConstKind::Error(_))
115    }
116
117    pub fn is_trivially_wf(self) -> bool {
118        match self.kind() {
119            ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true,
120            ConstKind::Infer(_)
121            | ConstKind::Unevaluated(..)
122            | ConstKind::Value(_)
123            | ConstKind::Error(_)
124            | ConstKind::Expr(_) => false,
125        }
126    }
127}
128
129impl<'db> std::fmt::Debug for Const<'db> {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        self.inner().internee.fmt(f)
132    }
133}
134
135pub type PlaceholderConst<'db> = rustc_type_ir::PlaceholderConst<DbInterner<'db>>;
136
137#[derive(Copy, Clone, Hash, Eq, PartialEq)]
138pub struct ParamConst {
139    // FIXME: See `ParamTy`.
140    pub id: ConstParamId,
141    pub index: u32,
142}
143
144impl std::fmt::Debug for ParamConst {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "#{}", self.index)
147    }
148}
149
150impl ParamConst {
151    pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> {
152        let mut candidates = env.clauses.iter().filter_map(|clause| {
153            // `ConstArgHasType` are never desugared to be higher ranked.
154            match clause.kind().skip_binder() {
155                ClauseKind::ConstArgHasType(param_ct, ty) => {
156                    assert!(!(param_ct, ty).has_escaping_bound_vars());
157
158                    match param_ct.kind() {
159                        ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty),
160                        _ => None,
161                    }
162                }
163                _ => None,
164            }
165        });
166
167        // N.B. it may be tempting to fix ICEs by making this function return
168        // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally
169        // considered to be a bandaid solution, since it hides more important
170        // underlying issues with how we construct generics and predicates of
171        // items. It's advised to fix the underlying issue rather than trying
172        // to modify this function.
173        let ty = candidates.next().unwrap_or_else(|| {
174            panic!("cannot find `{self:?}` in param-env: {env:#?}");
175        });
176        assert!(
177            candidates.next().is_none(),
178            "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
179        );
180        ty
181    }
182}
183
184#[derive(
185    Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable,
186)]
187pub struct ExprConst;
188
189impl rustc_type_ir::inherent::ParamLike for ParamConst {
190    fn index(self) -> u32 {
191        self.index
192    }
193}
194
195impl<'db> IntoKind for Const<'db> {
196    type Kind = ConstKind<'db>;
197
198    fn kind(self) -> Self::Kind {
199        self.inner().internee
200    }
201}
202
203impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Const<'db> {
204    fn generic_visit_with(&self, visitor: &mut V) {
205        if visitor.on_interned(self.interned).is_continue() {
206            self.kind().generic_visit_with(visitor);
207        }
208    }
209}
210
211impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> {
212    fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
213        &self,
214        visitor: &mut V,
215    ) -> V::Result {
216        visitor.visit_const(*self)
217    }
218}
219
220impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> {
221    fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
222        &self,
223        visitor: &mut V,
224    ) -> V::Result {
225        match self.kind() {
226            ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
227            ConstKind::Value(v) => v.visit_with(visitor),
228            ConstKind::Expr(e) => e.visit_with(visitor),
229            ConstKind::Error(e) => e.visit_with(visitor),
230
231            ConstKind::Param(_)
232            | ConstKind::Infer(_)
233            | ConstKind::Bound(..)
234            | ConstKind::Placeholder(_) => V::Result::output(),
235        }
236    }
237}
238
239impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> {
240    fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
241        self,
242        folder: &mut F,
243    ) -> Result<Self, F::Error> {
244        folder.try_fold_const(self)
245    }
246    fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
247        folder.fold_const(self)
248    }
249}
250
251impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> {
252    fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
253        self,
254        folder: &mut F,
255    ) -> Result<Self, F::Error> {
256        let kind = match self.kind() {
257            ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
258            ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
259            ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
260
261            ConstKind::Param(_)
262            | ConstKind::Infer(_)
263            | ConstKind::Bound(..)
264            | ConstKind::Placeholder(_)
265            | ConstKind::Error(_) => return Ok(self),
266        };
267        if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) }
268    }
269    fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
270        self,
271        folder: &mut F,
272    ) -> Self {
273        let kind = match self.kind() {
274            ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)),
275            ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)),
276            ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)),
277
278            ConstKind::Param(_)
279            | ConstKind::Infer(_)
280            | ConstKind::Bound(..)
281            | ConstKind::Placeholder(_)
282            | ConstKind::Error(_) => return self,
283        };
284        if kind != self.kind() { Const::new(folder.cx(), kind) } else { self }
285    }
286}
287
288impl<'db> Relate<DbInterner<'db>> for Const<'db> {
289    fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
290        relation: &mut R,
291        a: Self,
292        b: Self,
293    ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
294        relation.consts(a, b)
295    }
296}
297
298impl<'db> Flags for Const<'db> {
299    fn flags(&self) -> rustc_type_ir::TypeFlags {
300        self.inner().flags
301    }
302
303    fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
304        self.inner().outer_exclusive_binder
305    }
306}
307
308impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
309    fn new_infer(interner: DbInterner<'db>, var: InferConst) -> Self {
310        Const::new(interner, ConstKind::Infer(var))
311    }
312
313    fn new_var(interner: DbInterner<'db>, var: ConstVid) -> Self {
314        Const::new(interner, ConstKind::Infer(InferConst::Var(var)))
315    }
316
317    fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst<'db>) -> Self {
318        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
319    }
320
321    fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self {
322        Const::new(
323            interner,
324            ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst::new(var)),
325        )
326    }
327
328    fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self {
329        Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst::new(var)))
330    }
331
332    fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderConst<'db>) -> Self {
333        Const::new(interner, ConstKind::Placeholder(param))
334    }
335
336    fn new_unevaluated(
337        interner: DbInterner<'db>,
338        uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>,
339    ) -> Self {
340        Const::new(interner, ConstKind::Unevaluated(uv))
341    }
342
343    fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self {
344        Const::new(interner, ConstKind::Expr(expr))
345    }
346
347    fn new_error(interner: DbInterner<'db>, _guar: ErrorGuaranteed) -> Self {
348        Const::error(interner)
349    }
350}
351
352pub type BoundConst<'db> = rustc_type_ir::BoundConst<DbInterner<'db>>;
353
354impl<'db> Relate<DbInterner<'db>> for ExprConst {
355    fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
356        _relation: &mut R,
357        a: Self,
358        b: Self,
359    ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
360        // Ensure we get back to this when we fill in the fields
361        let ExprConst = b;
362        Ok(a)
363    }
364}
365
366impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst {
367    fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
368        // Ensure we get back to this when we fill in the fields
369        let ExprConst = self;
370        GenericArgs::default()
371    }
372}
373
374interned_slice!(ConstsStorage, Consts, StoredConsts, consts, Const<'db>, Const<'static>);
375impl_foldable_for_interned_slice!(Consts);