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