1#[cfg(test)]
4mod tests;
5
6use base_db::Crate;
7use hir_def::{
8 ConstId, EnumVariantId, GeneralConstId, HasModule, StaticId,
9 attrs::AttrFlags,
10 expr_store::Body,
11 hir::{Expr, ExprId},
12 type_ref::LiteralConstRef,
13};
14use hir_expand::Lookup;
15use rustc_type_ir::inherent::IntoKind;
16use triomphe::Arc;
17
18use crate::{
19 LifetimeElisionKind, MemoryMap, ParamEnvAndCrate, TyLoweringContext,
20 db::HirDatabase,
21 display::DisplayTarget,
22 infer::InferenceContext,
23 mir::{MirEvalError, MirLowerError},
24 next_solver::{
25 Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
26 ParamEnv, Ty, ValueConst,
27 },
28};
29
30use super::mir::{interpret_mir, lower_to_mir, pad16};
31
32pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> {
33 Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
34}
35
36pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> {
37 unknown_const(ty).into()
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum ConstEvalError<'db> {
42 MirLowerError(MirLowerError<'db>),
43 MirEvalError(MirEvalError<'db>),
44}
45
46impl ConstEvalError<'_> {
47 pub fn pretty_print(
48 &self,
49 f: &mut String,
50 db: &dyn HirDatabase,
51 span_formatter: impl Fn(span::FileId, span::TextRange) -> String,
52 display_target: DisplayTarget,
53 ) -> std::result::Result<(), std::fmt::Error> {
54 match self {
55 ConstEvalError::MirLowerError(e) => {
56 e.pretty_print(f, db, span_formatter, display_target)
57 }
58 ConstEvalError::MirEvalError(e) => {
59 e.pretty_print(f, db, span_formatter, display_target)
60 }
61 }
62 }
63}
64
65impl<'db> From<MirLowerError<'db>> for ConstEvalError<'db> {
66 fn from(value: MirLowerError<'db>) -> Self {
67 match value {
68 MirLowerError::ConstEvalError(_, e) => *e,
69 _ => ConstEvalError::MirLowerError(value),
70 }
71 }
72}
73
74impl<'db> From<MirEvalError<'db>> for ConstEvalError<'db> {
75 fn from(value: MirEvalError<'db>) -> Self {
76 ConstEvalError::MirEvalError(value)
77 }
78}
79
80pub fn intern_const_ref<'a>(
82 db: &'a dyn HirDatabase,
83 value: &LiteralConstRef,
84 ty: Ty<'a>,
85 krate: Crate,
86) -> Const<'a> {
87 let interner = DbInterner::new_no_crate(db);
88 let layout = db.layout_of_ty(ty, ParamEnvAndCrate { param_env: ParamEnv::empty(), krate });
89 let kind = match value {
90 LiteralConstRef::Int(i) => {
91 let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
93 rustc_type_ir::ConstKind::Value(ValueConst::new(
94 ty,
95 ConstBytes {
96 memory: i.to_le_bytes()[0..size].into(),
97 memory_map: MemoryMap::default(),
98 },
99 ))
100 }
101 LiteralConstRef::UInt(i) => {
102 let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
103 rustc_type_ir::ConstKind::Value(ValueConst::new(
104 ty,
105 ConstBytes {
106 memory: i.to_le_bytes()[0..size].into(),
107 memory_map: MemoryMap::default(),
108 },
109 ))
110 }
111 LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new(
112 ty,
113 ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
114 )),
115 LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new(
116 ty,
117 ConstBytes {
118 memory: (*c as u32).to_le_bytes().into(),
119 memory_map: MemoryMap::default(),
120 },
121 )),
122 LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
123 };
124 Const::new(interner, kind)
125}
126
127pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
129 intern_const_ref(
130 db,
131 &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
132 Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize),
133 krate,
134 )
135}
136
137pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
138 match c.kind() {
139 ConstKind::Param(_) => None,
140 ConstKind::Infer(_) => None,
141 ConstKind::Bound(_, _) => None,
142 ConstKind::Placeholder(_) => None,
143 ConstKind::Unevaluated(unevaluated_const) => match unevaluated_const.def.0 {
144 GeneralConstId::ConstId(id) => {
145 let subst = unevaluated_const.args;
146 let ec = db.const_eval(id, subst, None).ok()?;
147 try_const_usize(db, ec)
148 }
149 GeneralConstId::StaticId(id) => {
150 let ec = db.const_eval_static(id).ok()?;
151 try_const_usize(db, ec)
152 }
153 },
154 ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))),
155 ConstKind::Error(_) => None,
156 ConstKind::Expr(_) => None,
157 }
158}
159
160pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
161 match (*c).kind() {
162 ConstKind::Param(_) => None,
163 ConstKind::Infer(_) => None,
164 ConstKind::Bound(_, _) => None,
165 ConstKind::Placeholder(_) => None,
166 ConstKind::Unevaluated(unevaluated_const) => match unevaluated_const.def.0 {
167 GeneralConstId::ConstId(id) => {
168 let subst = unevaluated_const.args;
169 let ec = db.const_eval(id, subst, None).ok()?;
170 try_const_isize(db, &ec)
171 }
172 GeneralConstId::StaticId(id) => {
173 let ec = db.const_eval_static(id).ok()?;
174 try_const_isize(db, &ec)
175 }
176 },
177 ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))),
178 ConstKind::Error(_) => None,
179 ConstKind::Expr(_) => None,
180 }
181}
182
183pub(crate) fn const_eval_discriminant_variant<'db>(
184 db: &'db dyn HirDatabase,
185 variant_id: EnumVariantId,
186) -> Result<i128, ConstEvalError<'db>> {
187 let interner = DbInterner::new_no_crate(db);
188 let def = variant_id.into();
189 let body = db.body(def);
190 let loc = variant_id.lookup(db);
191 if matches!(body[body.body_expr], Expr::Missing) {
192 let prev_idx = loc.index.checked_sub(1);
193 let value = match prev_idx {
194 Some(prev_idx) => {
195 1 + db.const_eval_discriminant(
196 loc.parent.enum_variants(db).variants[prev_idx as usize].0,
197 )?
198 }
199 _ => 0,
200 };
201 return Ok(value);
202 }
203
204 let repr = AttrFlags::repr(db, loc.parent.into());
205 let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
206
207 let mir_body = db.monomorphized_mir_body(
208 def,
209 GenericArgs::new_from_iter(interner, []),
210 ParamEnvAndCrate { param_env: db.trait_environment_for_body(def), krate: def.krate(db) },
211 )?;
212 let c = interpret_mir(db, mir_body, false, None)?.0?;
213 let c = if is_signed {
214 try_const_isize(db, &c).unwrap()
215 } else {
216 try_const_usize(db, c).unwrap() as i128
217 };
218 Ok(c)
219}
220
221pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> {
225 let infer = ctx.fixme_resolve_all_clone();
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]);
237 }
238 if let Expr::Path(p) = &ctx.body[expr] {
239 let mut ctx = TyLoweringContext::new(
240 ctx.db,
241 &ctx.resolver,
242 ctx.body,
243 ctx.generic_def,
244 LifetimeElisionKind::Infer,
245 );
246 if let Some(c) = ctx.path_to_const(p) {
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;
254 }
255 unknown_const(infer[expr])
256}
257
258pub(crate) fn const_eval_cycle_result<'db>(
259 _: &'db dyn HirDatabase,
260 _: ConstId,
261 _: GenericArgs<'db>,
262 _: Option<ParamEnvAndCrate<'db>>,
263) -> Result<Const<'db>, ConstEvalError<'db>> {
264 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
265}
266
267pub(crate) fn const_eval_static_cycle_result<'db>(
268 _: &'db dyn HirDatabase,
269 _: StaticId,
270) -> Result<Const<'db>, ConstEvalError<'db>> {
271 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
272}
273
274pub(crate) fn const_eval_discriminant_cycle_result<'db>(
275 _: &'db dyn HirDatabase,
276 _: EnumVariantId,
277) -> Result<i128, ConstEvalError<'db>> {
278 Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
279}
280
281pub(crate) fn const_eval_query<'db>(
282 db: &'db dyn HirDatabase,
283 def: ConstId,
284 subst: GenericArgs<'db>,
285 trait_env: Option<ParamEnvAndCrate<'db>>,
286) -> Result<Const<'db>, ConstEvalError<'db>> {
287 let body = db.monomorphized_mir_body(
288 def.into(),
289 subst,
290 ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) },
291 )?;
292 let c = interpret_mir(db, body, false, trait_env)?.0?;
293 Ok(c)
294}
295
296pub(crate) fn const_eval_static_query<'db>(
297 db: &'db dyn HirDatabase,
298 def: StaticId,
299) -> Result<Const<'db>, ConstEvalError<'db>> {
300 let interner = DbInterner::new_no_crate(db);
301 let body = db.monomorphized_mir_body(
302 def.into(),
303 GenericArgs::new_from_iter(interner, []),
304 ParamEnvAndCrate {
305 param_env: db.trait_environment_for_body(def.into()),
306 krate: def.krate(db),
307 },
308 )?;
309 let c = interpret_mir(db, body, false, None)?.0?;
310 Ok(c)
311}