2021-05-14 07:59:30 +00:00
|
|
|
//! Constant evaluation details
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
use base_db::CrateId;
|
|
|
|
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
|
2021-05-14 07:59:30 +00:00
|
|
|
use hir_def::{
|
2023-04-06 17:36:25 +00:00
|
|
|
hir::Expr,
|
2023-03-08 17:28:52 +00:00
|
|
|
path::Path,
|
2023-02-03 11:16:25 +00:00
|
|
|
resolver::{Resolver, ValueNs},
|
|
|
|
type_ref::ConstRef,
|
2023-04-28 17:14:30 +00:00
|
|
|
DefWithBodyId, EnumVariantId,
|
2021-05-14 07:59:30 +00:00
|
|
|
};
|
2023-02-03 11:16:25 +00:00
|
|
|
use la_arena::{Idx, RawIdx};
|
2022-03-09 18:50:24 +00:00
|
|
|
use stdx::never;
|
2021-05-14 07:59:30 +00:00
|
|
|
|
2022-03-09 18:50:24 +00:00
|
|
|
use crate::{
|
2023-02-03 11:16:25 +00:00
|
|
|
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
|
|
|
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
2023-03-20 22:50:30 +00:00
|
|
|
Interner, MemoryMap, Substitution, Ty, TyBuilder,
|
2022-03-09 18:50:24 +00:00
|
|
|
};
|
2021-05-14 07:59:30 +00:00
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
|
|
|
|
2021-05-14 07:59:30 +00:00
|
|
|
/// Extension trait for [`Const`]
|
2021-05-16 01:51:18 +00:00
|
|
|
pub trait ConstExt {
|
2021-05-14 07:59:30 +00:00
|
|
|
/// Is a [`Const`] unknown?
|
|
|
|
fn is_unknown(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
2021-05-16 01:51:18 +00:00
|
|
|
impl ConstExt for Const {
|
2021-05-14 07:59:30 +00:00
|
|
|
fn is_unknown(&self) -> bool {
|
2021-12-19 16:58:39 +00:00
|
|
|
match self.data(Interner).value {
|
2021-05-14 07:59:30 +00:00
|
|
|
// interned Unknown
|
|
|
|
chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
|
|
|
|
interned: ConstScalar::Unknown,
|
|
|
|
}) => true,
|
|
|
|
|
|
|
|
// interned concrete anything else
|
|
|
|
chalk_ir::ConstValue::Concrete(..) => false,
|
|
|
|
|
|
|
|
_ => {
|
2021-08-15 12:46:13 +00:00
|
|
|
tracing::error!(
|
|
|
|
"is_unknown was called on a non-concrete constant value! {:?}",
|
|
|
|
self
|
|
|
|
);
|
2021-05-14 07:59:30 +00:00
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-20 13:45:28 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2021-12-04 22:21:36 +00:00
|
|
|
pub enum ConstEvalError {
|
2023-02-03 11:16:25 +00:00
|
|
|
MirLowerError(MirLowerError),
|
|
|
|
MirEvalError(MirEvalError),
|
2021-12-04 22:21:36 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
impl From<MirLowerError> for ConstEvalError {
|
|
|
|
fn from(value: MirLowerError) -> Self {
|
|
|
|
match value {
|
2023-04-28 17:14:30 +00:00
|
|
|
MirLowerError::ConstEvalError(_, e) => *e,
|
2023-02-03 11:16:25 +00:00
|
|
|
_ => ConstEvalError::MirLowerError(value),
|
2021-12-04 22:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
impl From<MirEvalError> for ConstEvalError {
|
|
|
|
fn from(value: MirEvalError) -> Self {
|
|
|
|
ConstEvalError::MirEvalError(value)
|
2021-12-04 22:21:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-09 18:50:24 +00:00
|
|
|
pub(crate) fn path_to_const(
|
|
|
|
db: &dyn HirDatabase,
|
|
|
|
resolver: &Resolver,
|
2023-03-08 17:28:52 +00:00
|
|
|
path: &Path,
|
2022-03-09 18:50:24 +00:00
|
|
|
mode: ParamLoweringMode,
|
|
|
|
args_lazy: impl FnOnce() -> Generics,
|
|
|
|
debruijn: DebruijnIndex,
|
|
|
|
) -> Option<Const> {
|
2022-12-30 08:05:03 +00:00
|
|
|
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
|
2022-03-09 18:50:24 +00:00
|
|
|
Some(ValueNs::GenericParam(p)) => {
|
|
|
|
let ty = db.const_param_ty(p);
|
|
|
|
let args = args_lazy();
|
|
|
|
let value = match mode {
|
|
|
|
ParamLoweringMode::Placeholder => {
|
|
|
|
ConstValue::Placeholder(to_placeholder_idx(db, p.into()))
|
|
|
|
}
|
|
|
|
ParamLoweringMode::Variable => match args.param_idx(p.into()) {
|
|
|
|
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
|
|
|
|
None => {
|
|
|
|
never!(
|
2023-03-08 17:28:52 +00:00
|
|
|
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
|
2022-03-09 18:50:24 +00:00
|
|
|
args,
|
|
|
|
path,
|
|
|
|
p
|
|
|
|
);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
Some(ConstData { ty, value }.intern(Interner))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unknown_const(ty: Ty) -> Const {
|
|
|
|
ConstData {
|
|
|
|
ty,
|
|
|
|
value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }),
|
|
|
|
}
|
|
|
|
.intern(Interner)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
|
|
|
|
GenericArgData::Const(unknown_const(ty)).intern(Interner)
|
|
|
|
}
|
|
|
|
|
2022-04-07 01:00:33 +00:00
|
|
|
/// Interns a constant scalar with the given type
|
2022-07-17 15:22:11 +00:00
|
|
|
pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
|
2022-04-07 01:00:33 +00:00
|
|
|
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
|
|
|
|
.intern(Interner)
|
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
/// Interns a constant scalar with the given type
|
|
|
|
pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
|
|
|
|
let bytes = match value {
|
|
|
|
ConstRef::Int(i) => {
|
|
|
|
// FIXME: We should handle failure of layout better.
|
|
|
|
let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
|
|
|
|
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
|
|
|
}
|
|
|
|
ConstRef::UInt(i) => {
|
|
|
|
let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
|
|
|
|
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
|
|
|
|
}
|
|
|
|
ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
|
|
|
|
ConstRef::Char(c) => {
|
|
|
|
ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
|
|
|
|
}
|
|
|
|
ConstRef::Unknown => ConstScalar::Unknown,
|
|
|
|
};
|
|
|
|
intern_const_scalar(bytes, ty)
|
|
|
|
}
|
|
|
|
|
2021-05-14 07:59:30 +00:00
|
|
|
/// Interns a possibly-unknown target usize
|
2023-02-03 11:16:25 +00:00
|
|
|
pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
|
|
|
|
intern_const_ref(
|
|
|
|
db,
|
|
|
|
&value.map_or(ConstRef::Unknown, ConstRef::UInt),
|
|
|
|
TyBuilder::usize(),
|
|
|
|
krate,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_const_usize(c: &Const) -> Option<u128> {
|
|
|
|
match &c.data(Interner).value {
|
|
|
|
chalk_ir::ConstValue::BoundVar(_) => None,
|
|
|
|
chalk_ir::ConstValue::InferenceVar(_) => None,
|
|
|
|
chalk_ir::ConstValue::Placeholder(_) => None,
|
|
|
|
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
|
|
|
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
|
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
}
|
2022-07-17 01:18:53 +00:00
|
|
|
}
|
|
|
|
|
2022-03-20 13:45:28 +00:00
|
|
|
pub(crate) fn const_eval_recover(
|
|
|
|
_: &dyn HirDatabase,
|
|
|
|
_: &[String],
|
2023-04-28 17:14:30 +00:00
|
|
|
_: &DefWithBodyId,
|
2023-03-20 22:50:30 +00:00
|
|
|
_: &Substitution,
|
2023-02-03 11:16:25 +00:00
|
|
|
) -> Result<Const, ConstEvalError> {
|
|
|
|
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
2022-03-20 13:45:28 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
pub(crate) fn const_eval_discriminant_recover(
|
2022-08-06 16:50:21 +00:00
|
|
|
_: &dyn HirDatabase,
|
|
|
|
_: &[String],
|
|
|
|
_: &EnumVariantId,
|
2023-02-03 11:16:25 +00:00
|
|
|
) -> Result<i128, ConstEvalError> {
|
|
|
|
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
2022-08-06 16:50:21 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
pub(crate) fn const_eval_query(
|
2022-03-20 13:45:28 +00:00
|
|
|
db: &dyn HirDatabase,
|
2023-04-28 17:14:30 +00:00
|
|
|
def: DefWithBodyId,
|
2023-03-20 22:50:30 +00:00
|
|
|
subst: Substitution,
|
2023-02-03 11:16:25 +00:00
|
|
|
) -> Result<Const, ConstEvalError> {
|
|
|
|
let body = db.mir_body(def)?;
|
2023-03-20 22:50:30 +00:00
|
|
|
let c = interpret_mir(db, &body, subst, false)?;
|
2023-02-03 11:16:25 +00:00
|
|
|
Ok(c)
|
2022-03-20 13:45:28 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
pub(crate) fn const_eval_discriminant_variant(
|
2022-08-06 16:50:21 +00:00
|
|
|
db: &dyn HirDatabase,
|
|
|
|
variant_id: EnumVariantId,
|
2023-02-03 11:16:25 +00:00
|
|
|
) -> Result<i128, ConstEvalError> {
|
2022-08-06 16:50:21 +00:00
|
|
|
let def = variant_id.into();
|
|
|
|
let body = db.body(def);
|
2023-02-03 11:16:25 +00:00
|
|
|
if body.exprs[body.body_expr] == Expr::Missing {
|
|
|
|
let prev_idx: u32 = variant_id.local_id.into_raw().into();
|
|
|
|
let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
|
|
|
|
let value = match prev_idx {
|
|
|
|
Some(local_id) => {
|
|
|
|
let prev_variant = EnumVariantId { local_id, parent: variant_id.parent };
|
|
|
|
1 + db.const_eval_discriminant(prev_variant)?
|
|
|
|
}
|
|
|
|
_ => 0,
|
|
|
|
};
|
|
|
|
return Ok(value);
|
|
|
|
}
|
|
|
|
let mir_body = db.mir_body(def)?;
|
2023-03-20 22:50:30 +00:00
|
|
|
let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?;
|
2023-02-03 11:16:25 +00:00
|
|
|
let c = try_const_usize(&c).unwrap() as i128;
|
|
|
|
Ok(c)
|
2022-08-06 16:50:21 +00:00
|
|
|
}
|
|
|
|
|
2023-02-03 11:16:25 +00:00
|
|
|
// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
|
|
|
|
// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
|
|
|
|
// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
|
2022-12-30 09:00:42 +00:00
|
|
|
pub(crate) fn eval_to_const(
|
2022-03-09 18:50:24 +00:00
|
|
|
expr: Idx<Expr>,
|
|
|
|
mode: ParamLoweringMode,
|
2022-12-30 09:00:42 +00:00
|
|
|
ctx: &mut InferenceContext<'_>,
|
2022-03-09 18:50:24 +00:00
|
|
|
args: impl FnOnce() -> Generics,
|
|
|
|
debruijn: DebruijnIndex,
|
|
|
|
) -> Const {
|
2023-02-03 11:16:25 +00:00
|
|
|
let db = ctx.db;
|
2022-03-09 18:50:24 +00:00
|
|
|
if let Expr::Path(p) = &ctx.body.exprs[expr] {
|
|
|
|
let resolver = &ctx.resolver;
|
2023-03-08 17:28:52 +00:00
|
|
|
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn) {
|
2022-03-09 18:50:24 +00:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
2023-02-03 11:16:25 +00:00
|
|
|
let infer = ctx.clone().resolve_all();
|
|
|
|
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
2023-03-20 22:50:30 +00:00
|
|
|
if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
|
2023-02-03 11:16:25 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unknown_const(infer[expr].clone())
|
2022-03-09 18:50:24 +00:00
|
|
|
}
|
2022-03-24 08:36:27 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|