rust-analyzer/crates/hir-ty/src/consteval.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

305 lines
9.6 KiB
Rust
Raw Normal View History

//! Constant evaluation details
2023-02-03 11:16:25 +00:00
use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{
hir::Expr,
2023-03-08 17:28:52 +00:00
path::Path,
2023-02-03 11:16:25 +00:00
resolver::{Resolver, ValueNs},
2023-06-05 11:27:19 +00:00
type_ref::LiteralConstRef,
2023-05-12 14:47:15 +00:00
EnumVariantId, GeneralConstId, StaticId,
};
2023-02-03 11:16:25 +00:00
use la_arena::{Idx, RawIdx};
2022-03-09 18:50:24 +00:00
use stdx::never;
2023-05-12 14:47:15 +00:00
use triomphe::Arc;
2022-03-09 18:50:24 +00:00
use crate::{
2023-05-25 21:15:37 +00:00
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
2022-03-09 18:50:24 +00:00
};
2023-02-03 11:16:25 +00:00
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
/// Extension trait for [`Const`]
pub trait ConstExt {
/// Is a [`Const`] unknown?
fn is_unknown(&self) -> bool;
}
impl ConstExt for Const {
fn is_unknown(&self) -> bool {
2021-12-19 16:58:39 +00:00
match self.data(Interner).value {
// 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
);
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,
expected_ty: Ty,
2022-03-09 18:50:24 +00:00
) -> 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))
}
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
expected_ty,
)),
2022-03-09 18:50:24 +00:00
_ => 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
2023-06-05 11:27:19 +00:00
pub fn intern_const_ref(
db: &dyn HirDatabase,
value: &LiteralConstRef,
ty: Ty,
krate: CrateId,
) -> Const {
2023-05-25 21:15:37 +00:00
let layout = db.layout_of_ty(ty.clone(), krate);
2023-02-03 11:16:25 +00:00
let bytes = match value {
2023-06-05 11:27:19 +00:00
LiteralConstRef::Int(i) => {
2023-02-03 11:16:25 +00:00
// FIXME: We should handle failure of layout better.
2023-05-25 21:15:37 +00:00
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
2023-02-03 11:16:25 +00:00
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
2023-06-05 11:27:19 +00:00
LiteralConstRef::UInt(i) => {
2023-05-25 21:15:37 +00:00
let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
2023-02-03 11:16:25 +00:00
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
2023-06-05 11:27:19 +00:00
LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
LiteralConstRef::Char(c) => {
2023-02-03 11:16:25 +00:00
ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
}
2023-06-05 11:27:19 +00:00
LiteralConstRef::Unknown => ConstScalar::Unknown,
2023-02-03 11:16:25 +00:00
};
intern_const_scalar(bytes, ty)
}
/// 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,
2023-06-05 11:27:19 +00:00
&value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
2023-02-03 11:16:25 +00:00
TyBuilder::usize(),
krate,
)
}
2023-05-12 14:47:15 +00:00
pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
2023-02-03 11:16:25 +00:00
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))),
2023-05-12 14:47:15 +00:00
ConstScalar::UnevaluatedConst(c, subst) => {
let ec = db.const_eval(*c, subst.clone()).ok()?;
try_const_usize(db, &ec)
}
2023-02-03 11:16:25 +00:00
_ => None,
},
}
}
2022-03-20 13:45:28 +00:00
pub(crate) fn const_eval_recover(
_: &dyn HirDatabase,
_: &[String],
2023-05-12 14:47:15 +00:00
_: &GeneralConstId,
_: &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-05-12 14:47:15 +00:00
pub(crate) fn const_eval_static_recover(
_: &dyn HirDatabase,
_: &[String],
_: &StaticId,
) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
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-05-12 14:47:15 +00:00
def: GeneralConstId,
subst: Substitution,
2023-02-03 11:16:25 +00:00
) -> Result<Const, ConstEvalError> {
2023-05-12 14:47:15 +00:00
let body = match def {
2023-05-25 21:15:37 +00:00
GeneralConstId::ConstId(c) => {
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
}
2023-05-12 14:47:15 +00:00
GeneralConstId::AnonymousConstId(c) => {
let (def, root) = db.lookup_intern_anonymous_const(c);
let body = db.body(def);
let infer = db.infer(def);
2023-05-25 21:15:37 +00:00
Arc::new(monomorphize_mir_body_bad(
db,
lower_to_mir(db, def, &body, &infer, root)?,
subst,
db.trait_environment_for_body(def),
)?)
2023-05-12 14:47:15 +00:00
}
2023-06-05 11:27:19 +00:00
GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
2023-05-12 14:47:15 +00:00
};
2023-05-25 21:15:37 +00:00
let c = interpret_mir(db, &body, false).0?;
2023-05-12 14:47:15 +00:00
Ok(c)
}
pub(crate) fn const_eval_static_query(
db: &dyn HirDatabase,
def: StaticId,
) -> Result<Const, ConstEvalError> {
2023-05-25 21:15:37 +00:00
let body = db.monomorphized_mir_body(
def.into(),
Substitution::empty(Interner),
db.trait_environment_for_body(def.into()),
)?;
let c = interpret_mir(db, &body, false).0?;
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);
}
2023-05-25 21:15:37 +00:00
let mir_body = db.monomorphized_mir_body(
def,
Substitution::empty(Interner),
db.trait_environment_for_body(def),
)?;
let c = interpret_mir(db, &mir_body, false).0?;
2023-05-12 14:47:15 +00:00
let c = try_const_usize(db, &c).unwrap() as i128;
2023-02-03 11:16:25 +00:00
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`.
pub(crate) fn eval_to_const(
2022-03-09 18:50:24 +00:00
expr: Idx<Expr>,
mode: ParamLoweringMode,
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;
let infer = ctx.clone().resolve_all();
2022-03-09 18:50:24 +00:00
if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver;
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
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-05-25 21:15:37 +00:00
if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
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;