hir_ty/
consteval_nextsolver.rs

1//! Constant evaluation details
2// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir
3#![allow(unused)]
4
5use base_db::Crate;
6use hir_def::{
7    EnumVariantId, GeneralConstId,
8    expr_store::{Body, HygieneId, path::Path},
9    hir::{Expr, ExprId},
10    resolver::{Resolver, ValueNs},
11    type_ref::LiteralConstRef,
12};
13use hir_expand::Lookup;
14use rustc_type_ir::{
15    UnevaluatedConst,
16    inherent::{IntoKind, SliceLike},
17};
18use stdx::never;
19use triomphe::Arc;
20
21use crate::{
22    ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment,
23    consteval::ConstEvalError,
24    db::HirDatabase,
25    generics::Generics,
26    infer::InferenceContext,
27    next_solver::{
28        Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
29        ParamConst, SolverDefId, Ty, ValueConst,
30        mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder},
31    },
32};
33
34use super::mir::{interpret_mir, lower_to_mir, pad16};
35
36pub(crate) fn path_to_const<'a, 'g>(
37    db: &'a dyn HirDatabase,
38    resolver: &Resolver<'a>,
39    path: &Path,
40    args: impl FnOnce() -> &'g Generics,
41    expected_ty: Ty<'a>,
42) -> Option<Const<'a>> {
43    let interner = DbInterner::new_with(db, Some(resolver.krate()), None);
44    match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) {
45        Some(ValueNs::GenericParam(p)) => {
46            let args = args();
47            match args
48                .type_or_const_param(p.into())
49                .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone())))
50            {
51                Some((idx, _param)) => {
52                    Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p }))
53                }
54                None => {
55                    never!(
56                        "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
57                        args,
58                        path,
59                        p
60                    );
61                    None
62                }
63            }
64        }
65        Some(ValueNs::ConstId(c)) => {
66            let args = GenericArgs::new_from_iter(interner, []);
67            Some(Const::new(
68                interner,
69                rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(
70                    SolverDefId::ConstId(c),
71                    args,
72                )),
73            ))
74        }
75        _ => None,
76    }
77}
78
79pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> {
80    Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
81}
82
83pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> {
84    unknown_const(ty).into()
85}
86
87/// Interns a constant scalar with the given type
88pub fn intern_const_ref<'a>(
89    db: &'a dyn HirDatabase,
90    value: &LiteralConstRef,
91    ty: Ty<'a>,
92    krate: Crate,
93) -> Const<'a> {
94    let interner = DbInterner::new_with(db, Some(krate), None);
95    let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate));
96    let kind = match value {
97        LiteralConstRef::Int(i) => {
98            // FIXME: We should handle failure of layout better.
99            let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
100            rustc_type_ir::ConstKind::Value(ValueConst::new(
101                ty,
102                ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()),
103            ))
104        }
105        LiteralConstRef::UInt(i) => {
106            let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
107            rustc_type_ir::ConstKind::Value(ValueConst::new(
108                ty,
109                ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()),
110            ))
111        }
112        LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new(
113            ty,
114            ConstBytes(Box::new([*b as u8]), MemoryMap::default()),
115        )),
116        LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new(
117            ty,
118            ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()),
119        )),
120        LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
121    };
122    Const::new(interner, kind)
123}
124
125/// Interns a possibly-unknown target usize
126pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
127    intern_const_ref(
128        db,
129        &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
130        Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize),
131        krate,
132    )
133}
134
135pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
136    let interner = DbInterner::new_with(db, None, None);
137    match c.kind() {
138        ConstKind::Param(_) => None,
139        ConstKind::Infer(_) => None,
140        ConstKind::Bound(_, _) => None,
141        ConstKind::Placeholder(_) => None,
142        ConstKind::Unevaluated(unevaluated_const) => {
143            let c = match unevaluated_const.def {
144                SolverDefId::ConstId(id) => GeneralConstId::ConstId(id),
145                SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
146                _ => unreachable!(),
147            };
148            let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
149            let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
150            try_const_usize(db, ec)
151        }
152        ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))),
153        ConstKind::Error(_) => None,
154        ConstKind::Expr(_) => None,
155    }
156}
157
158pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
159    let interner = DbInterner::new_with(db, None, None);
160    match (*c).kind() {
161        ConstKind::Param(_) => None,
162        ConstKind::Infer(_) => None,
163        ConstKind::Bound(_, _) => None,
164        ConstKind::Placeholder(_) => None,
165        ConstKind::Unevaluated(unevaluated_const) => {
166            let c = match unevaluated_const.def {
167                SolverDefId::ConstId(id) => GeneralConstId::ConstId(id),
168                SolverDefId::StaticId(id) => GeneralConstId::StaticId(id),
169                _ => unreachable!(),
170            };
171            let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice());
172            let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner);
173            try_const_isize(db, &ec)
174        }
175        ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))),
176        ConstKind::Error(_) => None,
177        ConstKind::Expr(_) => None,
178    }
179}
180
181pub(crate) fn const_eval_discriminant_variant(
182    db: &dyn HirDatabase,
183    variant_id: EnumVariantId,
184) -> Result<i128, ConstEvalError> {
185    let interner = DbInterner::new_with(db, None, None);
186    let def = variant_id.into();
187    let body = db.body(def);
188    let loc = variant_id.lookup(db);
189    if matches!(body[body.body_expr], Expr::Missing) {
190        let prev_idx = loc.index.checked_sub(1);
191        let value = match prev_idx {
192            Some(prev_idx) => {
193                1 + db.const_eval_discriminant(
194                    loc.parent.enum_variants(db).variants[prev_idx as usize].0,
195                )?
196            }
197            _ => 0,
198        };
199        return Ok(value);
200    }
201
202    let repr = db.enum_signature(loc.parent).repr;
203    let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
204
205    let mir_body = db.monomorphized_mir_body(
206        def,
207        Substitution::empty(Interner),
208        db.trait_environment_for_body(def),
209    )?;
210    let c = interpret_mir(db, mir_body, false, None)?.0?;
211    let c = c.to_nextsolver(interner);
212    let c = if is_signed {
213        try_const_isize(db, &c).unwrap()
214    } else {
215        try_const_usize(db, c).unwrap() as i128
216    };
217    Ok(c)
218}
219
220// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
221// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
222// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
223pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> {
224    let interner = DbInterner::new_with(ctx.db, None, None);
225    let infer = ctx.clone().resolve_all();
226    fn has_closure(body: &Body, expr: ExprId) -> bool {
227        if matches!(body[expr], Expr::Closure { .. }) {
228            return true;
229        }
230        let mut r = false;
231        body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
232        r
233    }
234    if has_closure(ctx.body, expr) {
235        // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
236        return unknown_const(infer[expr].clone().to_nextsolver(interner));
237    }
238    if let Expr::Path(p) = &ctx.body[expr] {
239        let resolver = &ctx.resolver;
240        if let Some(c) = path_to_const(
241            ctx.db,
242            resolver,
243            p,
244            || ctx.generics(),
245            infer[expr].to_nextsolver(interner),
246        ) {
247            return c;
248        }
249    }
250    if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr)
251        && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None)
252    {
253        return result.to_nextsolver(interner);
254    }
255    unknown_const(infer[expr].to_nextsolver(interner))
256}