1use base_db::Crate;
4use hir_def::{
5 ConstId, EnumVariantId, ExpressionStoreOwnerId, HasModule, StaticId,
6 attrs::AttrFlags,
7 expr_store::{Body, ExpressionStore, HygieneId, path::Path},
8 hir::{Expr, ExprId, Literal},
9 resolver::{Resolver, ValueNs},
10};
11use hir_expand::Lookup;
12use rustc_abi::Size;
13use rustc_apfloat::Float;
14use rustc_ast_ir::Mutability;
15use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
16use salsa::SalsaValue;
17
18use crate::{
19 ParamEnvAndCrate, Span,
20 db::{AnonConstId, AnonConstLoc, GeneralConstId, HirDatabase},
21 generics::Generics,
22 lower::LoweringMode,
23 mir::{IsSigned, MirEvalError, MirLowerError, pad16},
24 next_solver::{
25 Allocation, Const, ConstKind, Consts, DbInterner, DefaultAny, GenericArgs, ParamConst,
26 ScalarInt, StoredAllocation, StoredEarlyBinder, StoredGenericArgs, Ty, TyKind,
27 UnevaluatedConst, ValTreeKind, default_types,
28 },
29 traits::StoredParamEnvAndCrate,
30};
31
32use super::mir::interpret_mir;
33
34#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)]
35pub enum ConstEvalError<'db> {
36 MirLowerError(MirLowerError<'db>),
37 MirEvalError(MirEvalError<'db>),
38}
39
40impl<'db> From<MirLowerError<'db>> for ConstEvalError<'db> {
41 fn from(value: MirLowerError<'db>) -> Self {
42 match value {
43 MirLowerError::ConstEvalError(_, e) => *e,
44 _ => ConstEvalError::MirLowerError(value),
45 }
46 }
47}
48
49impl<'db> From<MirEvalError<'db>> for ConstEvalError<'db> {
50 fn from(value: MirEvalError<'db>) -> Self {
51 ConstEvalError::MirEvalError(value)
52 }
53}
54
55fn intern_const_ref<'db>(
57 interner: DbInterner<'db>,
58 value: &Literal,
59 ty: Ty<'db>,
60) -> Option<Result<Const<'db>, CreateConstError<'db>>> {
61 let Ok(data_layout) = interner.db.target_data_layout(interner.expect_crate()) else {
62 return Some(Ok(Const::error(interner)));
63 };
64 let valtree = match (ty.kind(), value) {
65 (TyKind::Uint(uint), Literal::Uint(value, _)) => {
66 let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
67 let Some(scalar) = ScalarInt::try_from_uint(*value, size) else {
68 return Some(Ok(Const::error(interner)));
69 };
70 ValTreeKind::Leaf(scalar)
71 }
72 (TyKind::Uint(uint), Literal::Int(value, _)) => {
73 let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
75 let Some(scalar) = ScalarInt::try_from_uint(*value as u128, size) else {
76 return Some(Ok(Const::error(interner)));
77 };
78 ValTreeKind::Leaf(scalar)
79 }
80 (TyKind::Int(int), Literal::Int(value, _)) => {
81 let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
82 let Some(scalar) = ScalarInt::try_from_int(*value, size) else {
83 return Some(Ok(Const::error(interner)));
84 };
85 ValTreeKind::Leaf(scalar)
86 }
87 (TyKind::Bool, Literal::Bool(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
88 (TyKind::Char, Literal::Char(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
89 (TyKind::Float(float), Literal::Float(value, _)) => {
90 let size = Size::from_bits(float.bit_width());
91 let value = match float {
92 rustc_ast_ir::FloatTy::F16 => value.to_f16().to_bits(),
93 rustc_ast_ir::FloatTy::F32 => value.to_f32().to_bits(),
94 rustc_ast_ir::FloatTy::F64 => value.to_f64().to_bits(),
95 rustc_ast_ir::FloatTy::F128 => value.to_f128().to_bits(),
96 };
97 let scalar = ScalarInt::try_from_uint(value, size).unwrap();
98 ValTreeKind::Leaf(scalar)
99 }
100 (TyKind::Ref(_, inner_ty, _), Literal::String(value))
101 if matches!(inner_ty.kind(), TyKind::Str) =>
102 {
103 let u8_values = &interner.default_types().consts.u8_values;
104 ValTreeKind::Branch(Consts::new_from_iter(
105 value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]),
106 ))
107 }
108 (_, Literal::ByteString(_) | Literal::CString(_)) => {
109 return None;
112 }
113 _ => {
114 let actual = literal_ty(
115 interner,
116 value,
117 |types| types.types.i32,
118 |types| types.types.u32,
119 |types| types.types.f64,
120 );
121 return Some(Err(CreateConstError::TypeMismatch { actual }));
122 }
123 };
124 Some(Ok(Const::new_valtree(interner, ty, valtree)))
125}
126
127pub(crate) fn literal_ty<'db>(
128 interner: DbInterner<'db>,
129 value: &Literal,
130 default_int: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
131 default_uint: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
132 default_float: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
133) -> Ty<'db> {
134 let types = interner.default_types();
135 match value {
136 Literal::Bool(..) => types.types.bool,
137 Literal::String(..) => types.types.static_str_ref,
138 Literal::ByteString(bs) => {
139 let byte_type = types.types.u8;
140 let array_type = Ty::new_array(interner, byte_type, bs.len() as u64);
141 Ty::new_ref(interner, types.regions.statik, array_type, Mutability::Not)
142 }
143 Literal::CString(..) => Ty::new_ref(
144 interner,
145 types.regions.statik,
146 interner.lang_items().CStr.map_or(types.types.error, |strukt| {
147 Ty::new_adt(interner, strukt.into(), types.empty.generic_args)
148 }),
149 Mutability::Not,
150 ),
151 Literal::Char(..) => types.types.char,
152 Literal::Int(_v, ty) => match ty {
153 Some(int_ty) => match int_ty {
154 hir_def::builtin_type::BuiltinInt::Isize => types.types.isize,
155 hir_def::builtin_type::BuiltinInt::I8 => types.types.i8,
156 hir_def::builtin_type::BuiltinInt::I16 => types.types.i16,
157 hir_def::builtin_type::BuiltinInt::I32 => types.types.i32,
158 hir_def::builtin_type::BuiltinInt::I64 => types.types.i64,
159 hir_def::builtin_type::BuiltinInt::I128 => types.types.i128,
160 },
161 None => default_int(types),
162 },
163 Literal::Uint(_v, ty) => match ty {
164 Some(int_ty) => match int_ty {
165 hir_def::builtin_type::BuiltinUint::Usize => types.types.usize,
166 hir_def::builtin_type::BuiltinUint::U8 => types.types.u8,
167 hir_def::builtin_type::BuiltinUint::U16 => types.types.u16,
168 hir_def::builtin_type::BuiltinUint::U32 => types.types.u32,
169 hir_def::builtin_type::BuiltinUint::U64 => types.types.u64,
170 hir_def::builtin_type::BuiltinUint::U128 => types.types.u128,
171 },
172 None => default_uint(types),
173 },
174 Literal::Float(_v, ty) => match ty {
175 Some(float_ty) => match float_ty {
176 hir_def::builtin_type::BuiltinFloat::F16 => types.types.f16,
177 hir_def::builtin_type::BuiltinFloat::F32 => types.types.f32,
178 hir_def::builtin_type::BuiltinFloat::F64 => types.types.f64,
179 hir_def::builtin_type::BuiltinFloat::F128 => types.types.f128,
180 },
181 None => default_float(types),
182 },
183 }
184}
185
186pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
188 let interner = DbInterner::new_no_crate(db);
189 let value = match value {
190 Some(value) => value,
191 None => {
192 return Const::error(interner);
193 }
194 };
195 let Ok(data_layout) = db.target_data_layout(krate) else {
196 return Const::error(interner);
197 };
198 let usize_ty = interner.default_types().types.usize;
199 let Some(scalar) = ScalarInt::try_from_uint(value, data_layout.pointer_size()) else {
200 return Const::error(interner);
201 };
202 Const::new_valtree(interner, usize_ty, ValTreeKind::Leaf(scalar))
203}
204
205pub fn allocation_as_usize(ec: Allocation<'_>) -> u128 {
206 u128::from_le_bytes(pad16(&ec.memory, IsSigned::No))
207}
208
209pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
210 match c.kind() {
211 ConstKind::Param(_) => None,
212 ConstKind::Infer(_) => None,
213 ConstKind::Bound(_, _) => None,
214 ConstKind::Placeholder(_) => None,
215 ConstKind::Unevaluated(unevaluated_const) => match unevaluated_const.def.0 {
216 GeneralConstId::ConstId(id) => {
217 let subst = unevaluated_const.args;
218 let ec = db.const_eval(id, subst, None).ok()?;
219 Some(allocation_as_usize(ec))
220 }
221 GeneralConstId::StaticId(id) => {
222 let ec = db.const_eval_static(id).ok()?;
223 Some(allocation_as_usize(ec))
224 }
225 GeneralConstId::AnonConstId(id) => {
226 let subst = unevaluated_const.args;
227 let ec = db.anon_const_eval(id, subst, None).ok()?;
228 Some(allocation_as_usize(ec))
229 }
230 },
231 ConstKind::Value(val) => {
232 if val.ty == default_types().types.usize {
233 Some(val.value.inner().to_leaf().to_uint_unchecked())
234 } else {
235 None
236 }
237 }
238 ConstKind::Error(_) => None,
239 ConstKind::Expr(_) => None,
240 }
241}
242
243pub fn allocation_as_isize(ec: Allocation<'_>) -> i128 {
244 i128::from_le_bytes(pad16(&ec.memory, IsSigned::Yes))
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 Some(allocation_as_isize(ec))
258 }
259 GeneralConstId::StaticId(id) => {
260 let ec = db.const_eval_static(id).ok()?;
261 Some(allocation_as_isize(ec))
262 }
263 GeneralConstId::AnonConstId(id) => {
264 let subst = unevaluated_const.args;
265 let ec = db.anon_const_eval(id, subst, None).ok()?;
266 Some(allocation_as_isize(ec))
267 }
268 },
269 ConstKind::Value(val) => {
270 if val.ty == default_types().types.isize {
271 Some(val.value.inner().to_leaf().to_int_unchecked())
272 } else {
273 None
274 }
275 }
276 ConstKind::Error(_) => None,
277 ConstKind::Expr(_) => None,
278 }
279}
280
281#[derive(Debug)]
282pub(crate) enum CreateConstError<'db> {
283 UsedForbiddenParam,
284 ResolveToNonConst,
285 DoesNotResolve,
286 ConstHasGenerics,
287 UnderscoreExpr,
288 AnonConstInterningDisabled,
289 TypeMismatch {
290 #[expect(unused, reason = "will need this for diagnostics")]
291 actual: Ty<'db>,
292 },
293}
294
295pub(crate) fn path_to_const<'a, 'db>(
296 db: &'db dyn HirDatabase,
297 resolver: &Resolver<'db>,
298 generics: &dyn Fn() -> &'a Generics<'db>,
299 forbid_params_after: Option<u32>,
300 path: &Path,
301) -> Result<Const<'db>, CreateConstError<'db>> {
302 let interner = DbInterner::new_no_crate(db);
303 let resolution = resolver
304 .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT)
305 .ok_or(CreateConstError::DoesNotResolve)?;
306 let no_generics = |def| crate::generics::generics(db, def).has_no_params();
307 let konst = match resolution {
308 ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id),
309 ValueNs::StaticId(id) => GeneralConstId::StaticId(id),
310 ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics),
311 ValueNs::GenericParam(param) => {
312 let index = generics().type_or_const_param_idx(param.into());
313 if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) {
314 return Err(CreateConstError::UsedForbiddenParam);
315 }
316 return Ok(Const::new_param(interner, ParamConst { id: param, index }));
317 }
318 ValueNs::ImplSelf(_)
321 | ValueNs::LocalBinding(_)
322 | ValueNs::FunctionId(_)
323 | ValueNs::StructId(_)
324 | ValueNs::EnumVariantId(_) => return Err(CreateConstError::ResolveToNonConst),
325 };
326 let args = GenericArgs::empty();
327 Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args }))
328}
329
330pub(crate) fn create_anon_const<'a, 'db>(
341 interner: DbInterner<'db>,
342 owner: ExpressionStoreOwnerId,
343 store: &ExpressionStore,
344 expr_id: ExprId,
345 resolver: &Resolver<'db>,
346 expected_ty: Ty<'db>,
347 generics: &dyn Fn() -> &'a Generics<'db>,
348 create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>,
349 lowering_mode: LoweringMode,
350 forbid_params_after: Option<u32>,
351 allow_anon_const_using_generic_params: bool,
352) -> Result<Const<'db>, CreateConstError<'db>> {
353 let mut expr = &store[expr_id];
354 if let Expr::Block { statements, tail: Some(tail), .. } = expr
355 && statements.is_empty()
356 {
357 expr = &store[*tail];
361 }
362 match expr {
363 Expr::Literal(literal)
364 if let Some(literal) = intern_const_ref(interner, literal, expected_ty) =>
365 {
366 literal
367 }
368 Expr::Underscore => match create_var {
369 Some(create_var) => Ok(create_var(expr_id.into())),
370 None => Err(CreateConstError::UnderscoreExpr),
371 },
372 Expr::Path(path)
373 if let konst =
374 path_to_const(interner.db, resolver, generics, forbid_params_after, path)
375 && !matches!(
376 konst,
377 Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics)
378 ) =>
379 {
380 konst
381 }
382 _ => {
383 let Some(token) = lowering_mode.allow_tracked_structs() else {
384 return Err(CreateConstError::AnonConstInterningDisabled);
385 };
386
387 let konst = AnonConstId::new(
388 interner.db,
389 AnonConstLoc {
390 owner,
391 expr: expr_id,
392 ty: StoredEarlyBinder::bind(expected_ty.store()),
393 allow_using_generic_params: allow_anon_const_using_generic_params,
394 },
395 token,
396 );
397 let args = if allow_anon_const_using_generic_params {
398 GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into())
399 } else {
400 GenericArgs::empty()
401 };
402 Ok(Const::new_unevaluated(
403 interner,
404 UnevaluatedConst { def: GeneralConstId::AnonConstId(konst).into(), args },
405 ))
406 }
407 }
408}
409
410#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result, returns(clone))]
411pub(crate) fn const_eval_discriminant_variant<'db>(
412 db: &'db dyn HirDatabase,
413 variant_id: EnumVariantId,
414) -> Result<i128, ConstEvalError<'db>> {
415 let def = variant_id.into();
416 let body = Body::of(db, def);
417 let loc = variant_id.lookup(db);
418 if matches!(body[body.root_expr()], Expr::Missing) {
419 let prev_idx = loc.index(db).checked_sub(1);
420 let value = match prev_idx {
421 Some(prev_idx) => {
422 1 + db.const_eval_discriminant(loc.parent.enum_variants(db).variants[prev_idx].0)?
423 }
424 _ => 0,
425 };
426 return Ok(value);
427 }
428
429 let repr = AttrFlags::repr(db, loc.parent.into());
430 let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
431
432 let mir_body = db.monomorphized_mir_body(
433 def.into(),
434 GenericArgs::empty().store(),
435 ParamEnvAndCrate {
436 param_env: db.trait_environment(def.generic_def(db)),
437 krate: def.krate(db),
438 }
439 .store(),
440 )?;
441 let c = interpret_mir(db, mir_body, false, None)?.0?;
442 let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 };
443 Ok(c)
444}
445
446fn const_eval_discriminant_cycle_result<'db>(
447 _: &'db dyn HirDatabase,
448 _: salsa::Id,
449 _: EnumVariantId,
450) -> Result<i128, ConstEvalError<'db>> {
451 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
452}
453
454pub(crate) fn const_eval<'db>(
455 db: &'db dyn HirDatabase,
456 def: ConstId,
457 subst: GenericArgs<'db>,
458 trait_env: Option<ParamEnvAndCrate<'db>>,
459) -> Result<Allocation<'db>, ConstEvalError<'db>> {
460 return match const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
461 Ok(konst) => Ok(konst.as_ref()),
462 Err(err) => Err(err.clone()),
463 };
464
465 #[salsa::tracked(returns(ref), cycle_result = const_eval_cycle_result)]
466 pub(crate) fn const_eval_query<'db>(
467 db: &'db dyn HirDatabase,
468 def: ConstId,
469 subst: StoredGenericArgs,
470 trait_env: Option<StoredParamEnvAndCrate>,
471 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
472 let body = db.monomorphized_mir_body(
473 def.into(),
474 subst,
475 ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) }
476 .store(),
477 )?;
478 let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref(db)))?.0?;
479 Ok(c.store())
480 }
481
482 pub(crate) fn const_eval_cycle_result<'db>(
483 _: &'db dyn HirDatabase,
484 _: salsa::Id,
485 _: ConstId,
486 _: StoredGenericArgs,
487 _: Option<StoredParamEnvAndCrate>,
488 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
489 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
490 }
491}
492
493pub(crate) fn anon_const_eval<'db>(
494 db: &'db dyn HirDatabase,
495 def: AnonConstId<'db>,
496 subst: GenericArgs<'db>,
497 trait_env: Option<ParamEnvAndCrate<'db>>,
498) -> Result<Allocation<'db>, ConstEvalError<'db>> {
499 return match anon_const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
500 Ok(konst) => Ok(konst.as_ref()),
501 Err(err) => Err(err.clone()),
502 };
503
504 #[salsa::tracked(returns(ref), cycle_result = anon_const_eval_cycle_result)]
505 pub(crate) fn anon_const_eval_query<'db>(
506 db: &'db dyn HirDatabase,
507 def: AnonConstId<'db>,
508 subst: StoredGenericArgs,
509 trait_env: Option<StoredParamEnvAndCrate>,
510 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
511 let body = db.monomorphized_mir_body(
512 def.into(),
513 subst,
514 ParamEnvAndCrate {
515 param_env: db.trait_environment(def.loc(db).owner.generic_def(db)),
516 krate: def.krate(db),
517 }
518 .store(),
519 )?;
520 let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref(db)))?.0?;
521 Ok(c.store())
522 }
523
524 pub(crate) fn anon_const_eval_cycle_result<'db>(
525 _: &'db dyn HirDatabase,
526 _: salsa::Id,
527 _: AnonConstId<'db>,
528 _: StoredGenericArgs,
529 _: Option<StoredParamEnvAndCrate>,
530 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
531 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
532 }
533}
534
535pub(crate) fn const_eval_static<'db>(
536 db: &'db dyn HirDatabase,
537 def: StaticId,
538) -> Result<Allocation<'db>, ConstEvalError<'db>> {
539 return match const_eval_static_query(db, def) {
540 Ok(konst) => Ok(konst.as_ref()),
541 Err(err) => Err(err.clone()),
542 };
543
544 #[salsa::tracked(returns(ref), cycle_result = const_eval_static_cycle_result)]
545 pub(crate) fn const_eval_static_query<'db>(
546 db: &'db dyn HirDatabase,
547 def: StaticId,
548 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
549 let body = db.monomorphized_mir_body(
550 def.into(),
551 GenericArgs::empty().store(),
552 ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) }
553 .store(),
554 )?;
555 let c = interpret_mir(db, body, false, None)?.0?;
556 Ok(c.store())
557 }
558
559 pub(crate) fn const_eval_static_cycle_result<'db>(
560 _: &'db dyn HirDatabase,
561 _: salsa::Id,
562 _: StaticId,
563 ) -> Result<StoredAllocation, ConstEvalError<'db>> {
564 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
565 }
566}