1#[cfg(test)]
4mod tests;
5
6use base_db::Crate;
7use hir_def::{
8 ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule,
9 StaticId,
10 attrs::AttrFlags,
11 builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
12 expr_store::{Body, ExpressionStore},
13 hir::{Expr, ExprId, Literal},
14};
15use hir_expand::Lookup;
16use rustc_type_ir::inherent::IntoKind;
17use triomphe::Arc;
18
19use crate::{
20 LifetimeElisionKind, MemoryMap, ParamEnvAndCrate, TyLoweringContext,
21 db::HirDatabase,
22 display::DisplayTarget,
23 infer::InferenceContext,
24 mir::{MirEvalError, MirLowerError},
25 next_solver::{
26 Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
27 StoredConst, StoredGenericArgs, Ty, ValueConst,
28 },
29 traits::StoredParamEnvAndCrate,
30};
31
32use super::mir::{interpret_mir, lower_body_to_mir, pad16};
33
34pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> {
35 Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
36}
37
38pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> {
39 unknown_const(ty).into()
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum ConstEvalError {
44 MirLowerError(MirLowerError),
45 MirEvalError(MirEvalError),
46}
47
48impl ConstEvalError {
49 pub fn pretty_print(
50 &self,
51 f: &mut String,
52 db: &dyn HirDatabase,
53 span_formatter: impl Fn(span::FileId, span::TextRange) -> String,
54 display_target: DisplayTarget,
55 ) -> std::result::Result<(), std::fmt::Error> {
56 match self {
57 ConstEvalError::MirLowerError(e) => {
58 e.pretty_print(f, db, span_formatter, display_target)
59 }
60 ConstEvalError::MirEvalError(e) => {
61 e.pretty_print(f, db, span_formatter, display_target)
62 }
63 }
64 }
65}
66
67impl From<MirLowerError> for ConstEvalError {
68 fn from(value: MirLowerError) -> Self {
69 match value {
70 MirLowerError::ConstEvalError(_, e) => *e,
71 _ => ConstEvalError::MirLowerError(value),
72 }
73 }
74}
75
76impl From<MirEvalError> for ConstEvalError {
77 fn from(value: MirEvalError) -> Self {
78 ConstEvalError::MirEvalError(value)
79 }
80}
81
82pub fn intern_const_ref<'a>(
84 db: &'a dyn HirDatabase,
85 value: &Literal,
86 ty: Ty<'a>,
87 _krate: Crate,
88) -> Const<'a> {
89 let interner = DbInterner::new_no_crate(db);
90 let kind = match value {
91 &Literal::Uint(i, builtin_ty)
92 if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) =>
93 {
94 let memory = match ty.as_builtin() {
95 Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
96 BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
97 BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
98 BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
99 BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
100 BuiltinUint::U128 => Box::new((i).to_le_bytes()),
101 BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
102 },
103 _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
104 };
105 rustc_type_ir::ConstKind::Value(ValueConst::new(
106 ty,
107 ConstBytes { memory, memory_map: MemoryMap::default() },
108 ))
109 }
110 &Literal::Int(i, None)
111 if ty
112 .as_builtin()
113 .is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) =>
114 {
115 let memory = match ty.as_builtin() {
116 Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
117 BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
118 BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
119 BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
120 BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
121 BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()),
122 BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
123 },
124 _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
125 };
126 rustc_type_ir::ConstKind::Value(ValueConst::new(
127 ty,
128 ConstBytes { memory, memory_map: MemoryMap::default() },
129 ))
130 }
131 &Literal::Int(i, builtin_ty)
132 if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) =>
133 {
134 let memory = match ty.as_builtin() {
135 Some(BuiltinType::Int(builtin_int)) => match builtin_int {
136 BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>,
137 BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()),
138 BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()),
139 BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()),
140 BuiltinInt::I128 => Box::new((i).to_le_bytes()),
141 BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()),
142 },
143 _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
144 };
145 rustc_type_ir::ConstKind::Value(ValueConst::new(
146 ty,
147 ConstBytes { memory, memory_map: MemoryMap::default() },
148 ))
149 }
150 Literal::Float(float_type_wrapper, builtin_float)
151 if builtin_float.is_none()
152 || ty.as_builtin() == builtin_float.map(BuiltinType::Float) =>
153 {
154 let memory = match ty.as_builtin().unwrap() {
155 BuiltinType::Float(builtin_float) => match builtin_float {
156 hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>,
158 hir_def::builtin_type::BuiltinFloat::F32 => {
159 Box::new(float_type_wrapper.to_f32().to_le_bytes())
160 }
161 hir_def::builtin_type::BuiltinFloat::F64 => {
162 Box::new(float_type_wrapper.to_f64().to_le_bytes())
163 }
164 hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]),
166 },
167 _ => unreachable!(),
168 };
169 rustc_type_ir::ConstKind::Value(ValueConst::new(
170 ty,
171 ConstBytes { memory, memory_map: MemoryMap::default() },
172 ))
173 }
174 Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new(
175 ty,
176 ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
177 )),
178 Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new(
179 ty,
180 ConstBytes {
181 memory: (*c as u32).to_le_bytes().into(),
182 memory_map: MemoryMap::default(),
183 },
184 )),
185 Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new(
186 ty,
187 ConstBytes {
188 memory: symbol.as_str().as_bytes().into(),
189 memory_map: MemoryMap::default(),
190 },
191 )),
192 Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => {
193 rustc_type_ir::ConstKind::Value(ValueConst::new(
194 ty,
195 ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() },
196 ))
197 }
198 Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
200 _ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
201 };
202 Const::new(interner, kind)
203}
204
205pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
207 intern_const_ref(
208 db,
209 &match value {
210 Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)),
211 None => {
212 return Const::new(
213 DbInterner::new_no_crate(db),
214 rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
215 );
216 }
217 },
218 Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize),
219 krate,
220 )
221}
222
223pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
224 match c.kind() {
225 ConstKind::Param(_) => None,
226 ConstKind::Infer(_) => None,
227 ConstKind::Bound(_, _) => None,
228 ConstKind::Placeholder(_) => None,
229 ConstKind::Unevaluated(unevaluated_const) => match unevaluated_const.def.0 {
230 GeneralConstId::ConstId(id) => {
231 let subst = unevaluated_const.args;
232 let ec = db.const_eval(id, subst, None).ok()?;
233 try_const_usize(db, ec)
234 }
235 GeneralConstId::StaticId(id) => {
236 let ec = db.const_eval_static(id).ok()?;
237 try_const_usize(db, ec)
238 }
239 GeneralConstId::AnonConstId(_) => None,
240 },
241 ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))),
242 ConstKind::Error(_) => None,
243 ConstKind::Expr(_) => None,
244 }
245}
246
247pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
248 match (*c).kind() {
249 ConstKind::Param(_) => None,
250 ConstKind::Infer(_) => None,
251 ConstKind::Bound(_, _) => None,
252 ConstKind::Placeholder(_) => None,
253 ConstKind::Unevaluated(unevaluated_const) => match unevaluated_const.def.0 {
254 GeneralConstId::ConstId(id) => {
255 let subst = unevaluated_const.args;
256 let ec = db.const_eval(id, subst, None).ok()?;
257 try_const_isize(db, &ec)
258 }
259 GeneralConstId::StaticId(id) => {
260 let ec = db.const_eval_static(id).ok()?;
261 try_const_isize(db, &ec)
262 }
263 GeneralConstId::AnonConstId(_) => None,
264 },
265 ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))),
266 ConstKind::Error(_) => None,
267 ConstKind::Expr(_) => None,
268 }
269}
270
271pub(crate) fn const_eval_discriminant_variant(
272 db: &dyn HirDatabase,
273 variant_id: EnumVariantId,
274) -> Result<i128, ConstEvalError> {
275 let interner = DbInterner::new_no_crate(db);
276 let def = variant_id.into();
277 let body = Body::of(db, def);
278 let loc = variant_id.lookup(db);
279 if matches!(body[body.root_expr()], Expr::Missing) {
280 let prev_idx = loc.index.checked_sub(1);
281 let value = match prev_idx {
282 Some(prev_idx) => {
283 1 + db.const_eval_discriminant(
284 loc.parent.enum_variants(db).variants[prev_idx as usize].0,
285 )?
286 }
287 _ => 0,
288 };
289 return Ok(value);
290 }
291
292 let repr = AttrFlags::repr(db, loc.parent.into());
293 let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
294
295 let mir_body = db.monomorphized_mir_body(
296 def,
297 GenericArgs::empty(interner).store(),
298 ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) }
299 .store(),
300 )?;
301 let c = interpret_mir(db, mir_body, false, None)?.0?;
302 let c = if is_signed {
303 try_const_isize(db, &c).unwrap()
304 } else {
305 try_const_usize(db, c).unwrap() as i128
306 };
307 Ok(c)
308}
309
310pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> {
314 let infer = ctx.fixme_resolve_all_clone();
315 fn has_closure(store: &ExpressionStore, expr: ExprId) -> bool {
316 if matches!(store[expr], Expr::Closure { .. }) {
317 return true;
318 }
319 let mut r = false;
320 store.walk_child_exprs(expr, |idx| r |= has_closure(store, idx));
321 r
322 }
323 if has_closure(ctx.store, expr) {
324 return Const::error(ctx.interner());
326 }
327 if let Expr::Path(p) = &ctx.store[expr] {
328 let mut ctx = TyLoweringContext::new(
329 ctx.db,
330 &ctx.resolver,
331 ctx.store,
332 ctx.generic_def,
333 LifetimeElisionKind::Infer,
334 );
335 if let Some(c) = ctx.path_to_const(p) {
336 return c;
337 }
338 }
339 if let Some(body_owner) = ctx.owner.as_def_with_body()
340 && let Ok(mir_body) =
341 lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr)
342 && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None)
343 {
344 return result;
345 }
346 Const::error(ctx.interner())
347}
348
349pub(crate) fn const_eval_discriminant_cycle_result(
350 _: &dyn HirDatabase,
351 _: salsa::Id,
352 _: EnumVariantId,
353) -> Result<i128, ConstEvalError> {
354 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
355}
356
357pub(crate) fn const_eval<'db>(
358 db: &'db dyn HirDatabase,
359 def: ConstId,
360 subst: GenericArgs<'db>,
361 trait_env: Option<ParamEnvAndCrate<'db>>,
362) -> Result<Const<'db>, ConstEvalError> {
363 return match const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
364 Ok(konst) => Ok(konst.as_ref()),
365 Err(err) => Err(err.clone()),
366 };
367
368 #[salsa::tracked(returns(ref), cycle_result = const_eval_cycle_result)]
369 pub(crate) fn const_eval_query<'db>(
370 db: &'db dyn HirDatabase,
371 def: ConstId,
372 subst: StoredGenericArgs,
373 trait_env: Option<StoredParamEnvAndCrate>,
374 ) -> Result<StoredConst, ConstEvalError> {
375 let body = db.monomorphized_mir_body(
376 def.into(),
377 subst,
378 ParamEnvAndCrate {
379 param_env: db
380 .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))),
381 krate: def.krate(db),
382 }
383 .store(),
384 )?;
385 let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?;
386 Ok(c.store())
387 }
388
389 pub(crate) fn const_eval_cycle_result(
390 _: &dyn HirDatabase,
391 _: salsa::Id,
392 _: ConstId,
393 _: StoredGenericArgs,
394 _: Option<StoredParamEnvAndCrate>,
395 ) -> Result<StoredConst, ConstEvalError> {
396 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
397 }
398}
399
400pub(crate) fn const_eval_static<'db>(
401 db: &'db dyn HirDatabase,
402 def: StaticId,
403) -> Result<Const<'db>, ConstEvalError> {
404 return match const_eval_static_query(db, def) {
405 Ok(konst) => Ok(konst.as_ref()),
406 Err(err) => Err(err.clone()),
407 };
408
409 #[salsa::tracked(returns(ref), cycle_result = const_eval_static_cycle_result)]
410 pub(crate) fn const_eval_static_query<'db>(
411 db: &'db dyn HirDatabase,
412 def: StaticId,
413 ) -> Result<StoredConst, ConstEvalError> {
414 let interner = DbInterner::new_no_crate(db);
415 let body = db.monomorphized_mir_body(
416 def.into(),
417 GenericArgs::empty(interner).store(),
418 ParamEnvAndCrate {
419 param_env: db
420 .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))),
421 krate: def.krate(db),
422 }
423 .store(),
424 )?;
425 let c = interpret_mir(db, body, false, None)?.0?;
426 Ok(c.store())
427 }
428
429 pub(crate) fn const_eval_static_cycle_result(
430 _: &dyn HirDatabase,
431 _: salsa::Id,
432 _: StaticId,
433 ) -> Result<StoredConst, ConstEvalError> {
434 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
435 }
436}