mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
Support evaluating inherent associated constants with generics
This commit is contained in:
parent
3303a6eff5
commit
8a3ad7c3d5
7 changed files with 22 additions and 33 deletions
|
@ -15,7 +15,7 @@ use stdx::never;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
|
||||||
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
|
||||||
Interner, MemoryMap, Ty, TyBuilder,
|
Interner, MemoryMap, Substitution, Ty, TyBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
|
||||||
|
@ -169,6 +169,7 @@ pub(crate) fn const_eval_recover(
|
||||||
_: &dyn HirDatabase,
|
_: &dyn HirDatabase,
|
||||||
_: &[String],
|
_: &[String],
|
||||||
_: &ConstId,
|
_: &ConstId,
|
||||||
|
_: &Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||||
}
|
}
|
||||||
|
@ -184,10 +185,11 @@ pub(crate) fn const_eval_discriminant_recover(
|
||||||
pub(crate) fn const_eval_query(
|
pub(crate) fn const_eval_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
const_id: ConstId,
|
const_id: ConstId,
|
||||||
|
subst: Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
let def = const_id.into();
|
let def = const_id.into();
|
||||||
let body = db.mir_body(def)?;
|
let body = db.mir_body(def)?;
|
||||||
let c = interpret_mir(db, &body, false)?;
|
let c = interpret_mir(db, &body, subst, false)?;
|
||||||
Ok(c)
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant(
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
let mir_body = db.mir_body(def)?;
|
let mir_body = db.mir_body(def)?;
|
||||||
let c = interpret_mir(db, &mir_body, false)?;
|
let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?;
|
||||||
let c = try_const_usize(&c).unwrap() as i128;
|
let c = try_const_usize(&c).unwrap() as i128;
|
||||||
Ok(c)
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
@ -234,7 +236,7 @@ pub(crate) fn eval_to_const(
|
||||||
}
|
}
|
||||||
let infer = ctx.clone().resolve_all();
|
let infer = ctx.clone().resolve_all();
|
||||||
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
|
||||||
if let Ok(result) = interpret_mir(db, &mir_body, true) {
|
if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use base_db::fixture::WithFixture;
|
use base_db::fixture::WithFixture;
|
||||||
|
use chalk_ir::Substitution;
|
||||||
use hir_def::db::DefDatabase;
|
use hir_def::db::DefDatabase;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -64,7 +65,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.const_eval(const_id)
|
db.const_eval(const_id, Substitution::empty(Interner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1519,8 +1520,7 @@ fn const_generic_subst_fn() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn const_generic_subst_assoc_const_impl() {
|
fn const_generic_subst_assoc_const_impl() {
|
||||||
// FIXME: this should evaluate to 5
|
check_number(
|
||||||
check_fail(
|
|
||||||
r#"
|
r#"
|
||||||
struct Adder<const N: usize, const M: usize>;
|
struct Adder<const N: usize, const M: usize>;
|
||||||
impl<const N: usize, const M: usize> Adder<N, M> {
|
impl<const N: usize, const M: usize> Adder<N, M> {
|
||||||
|
@ -1528,7 +1528,7 @@ fn const_generic_subst_assoc_const_impl() {
|
||||||
}
|
}
|
||||||
const GOAL: usize = Adder::<2, 3>::VAL;
|
const GOAL: usize = Adder::<2, 3>::VAL;
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
|
5,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
||||||
fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
|
fn const_eval(&self, def: ConstId, subst: Substitution) -> Result<Const, ConstEvalError>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
||||||
|
|
|
@ -612,21 +612,6 @@ impl<'a> InferenceContext<'a> {
|
||||||
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
|
||||||
}
|
}
|
||||||
// Expr::Try { expr } => {
|
|
||||||
// let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
|
||||||
// if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
|
|
||||||
// if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
|
|
||||||
// let subst = TyBuilder::subst_for_def(self.db, trait_, None)
|
|
||||||
// .push(inner_ty.clone())
|
|
||||||
// .build();
|
|
||||||
// self.write_method_resolution(tgt_expr, func, subst.clone());
|
|
||||||
// }
|
|
||||||
// let try_output = self.resolve_output_on(trait_);
|
|
||||||
// self.resolve_associated_type(inner_ty, try_output)
|
|
||||||
// } else {
|
|
||||||
// self.err_ty()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Expr::Cast { expr, type_ref } => {
|
Expr::Cast { expr, type_ref } => {
|
||||||
let cast_ty = self.make_ty(type_ref);
|
let cast_ty = self.make_ty(type_ref);
|
||||||
// FIXME: propagate the "castable to" expectation
|
// FIXME: propagate the "castable to" expectation
|
||||||
|
|
|
@ -282,6 +282,7 @@ struct Locals<'a> {
|
||||||
pub fn interpret_mir(
|
pub fn interpret_mir(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
body: &MirBody,
|
body: &MirBody,
|
||||||
|
subst: Substitution,
|
||||||
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
|
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
|
||||||
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
|
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
|
||||||
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
|
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
|
||||||
|
@ -291,11 +292,11 @@ pub fn interpret_mir(
|
||||||
) -> Result<Const> {
|
) -> Result<Const> {
|
||||||
let ty = body.locals[return_slot()].ty.clone();
|
let ty = body.locals[return_slot()].ty.clone();
|
||||||
let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
|
let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
|
||||||
let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
|
let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?;
|
||||||
let memory_map = evaluator.create_memory_map(
|
let memory_map = evaluator.create_memory_map(
|
||||||
&bytes,
|
&bytes,
|
||||||
&ty,
|
&ty,
|
||||||
&Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
|
&Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst },
|
||||||
)?;
|
)?;
|
||||||
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
|
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,13 +231,13 @@ impl MirLowerCtx<'_> {
|
||||||
let pr = match pr {
|
let pr = match pr {
|
||||||
ResolveValueResult::ValueNs(v) => v,
|
ResolveValueResult::ValueNs(v) => v,
|
||||||
ResolveValueResult::Partial(..) => {
|
ResolveValueResult::Partial(..) => {
|
||||||
if let Some(assoc) = self
|
if let Some((assoc, subst)) = self
|
||||||
.infer
|
.infer
|
||||||
.assoc_resolutions_for_expr(expr_id)
|
.assoc_resolutions_for_expr(expr_id)
|
||||||
{
|
{
|
||||||
match assoc.0 {
|
match assoc {
|
||||||
hir_def::AssocItemId::ConstId(c) => {
|
hir_def::AssocItemId::ConstId(c) => {
|
||||||
self.lower_const(c, current, place, expr_id.into())?;
|
self.lower_const(c, current, place, subst, expr_id.into())?;
|
||||||
return Ok(Some(current))
|
return Ok(Some(current))
|
||||||
},
|
},
|
||||||
hir_def::AssocItemId::FunctionId(_) => {
|
hir_def::AssocItemId::FunctionId(_) => {
|
||||||
|
@ -274,7 +274,7 @@ impl MirLowerCtx<'_> {
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::ConstId(const_id) => {
|
ValueNs::ConstId(const_id) => {
|
||||||
self.lower_const(const_id, current, place, expr_id.into())?;
|
self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?;
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::EnumVariantId(variant_id) => {
|
ValueNs::EnumVariantId(variant_id) => {
|
||||||
|
@ -951,9 +951,10 @@ impl MirLowerCtx<'_> {
|
||||||
const_id: hir_def::ConstId,
|
const_id: hir_def::ConstId,
|
||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
place: Place,
|
place: Place,
|
||||||
|
subst: Substitution,
|
||||||
span: MirSpan,
|
span: MirSpan,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let c = self.db.const_eval(const_id)?;
|
let c = self.db.const_eval(const_id, subst)?;
|
||||||
self.write_const_to_place(c, prev_block, place, span)
|
self.write_const_to_place(c, prev_block, place, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1801,7 +1801,7 @@ impl Function {
|
||||||
let body = db
|
let body = db
|
||||||
.mir_body(self.id.into())
|
.mir_body(self.id.into())
|
||||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
||||||
interpret_mir(db, &body, false)?;
|
interpret_mir(db, &body, Substitution::empty(Interner), false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1947,7 +1947,7 @@ impl Const {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
||||||
let c = db.const_eval(self.id)?;
|
let c = db.const_eval(self.id, Substitution::empty(Interner))?;
|
||||||
let r = format!("{}", HexifiedConst(c).display(db));
|
let r = format!("{}", HexifiedConst(c).display(db));
|
||||||
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
||||||
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue