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