1#![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
87pub 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 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
125pub 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
220pub(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 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}