Skip to content

Commit 0e2989e

Browse files
committed
Support constants in const eval
1 parent b8e1d09 commit 0e2989e

File tree

5 files changed

+198
-50
lines changed

5 files changed

+198
-50
lines changed

crates/hir/src/lib.rs

+3-15
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub mod symbols;
3232

3333
mod display;
3434

35-
use std::{collections::HashMap, iter, ops::ControlFlow, sync::Arc};
35+
use std::{iter, ops::ControlFlow, sync::Arc};
3636

3737
use arrayvec::ArrayVec;
3838
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
@@ -58,6 +58,7 @@ use hir_ty::{
5858
consteval::{
5959
eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
6060
},
61+
could_unify,
6162
diagnostics::BodyValidationDiagnostic,
6263
method_resolution::{self, TyFingerprint},
6364
primitive::UintTy,
@@ -1602,20 +1603,7 @@ impl Const {
16021603
}
16031604

16041605
pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
1605-
let body = db.body(self.id.into());
1606-
let root = &body.exprs[body.body_expr];
1607-
let infer = db.infer_query(self.id.into());
1608-
let infer = infer.as_ref();
1609-
let result = eval_const(
1610-
root,
1611-
&mut ConstEvalCtx {
1612-
exprs: &body.exprs,
1613-
pats: &body.pats,
1614-
local_data: HashMap::default(),
1615-
infer: &mut |x| infer[x].clone(),
1616-
},
1617-
);
1618-
result
1606+
db.const_eval(self.id)
16191607
}
16201608
}
16211609

crates/hir_ty/src/consteval.rs

+101-31
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,20 @@ use std::{
88

99
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
1010
use hir_def::{
11-
expr::{ArithOp, BinaryOp, Expr, Literal, Pat},
11+
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat},
1212
path::ModPath,
13-
resolver::{Resolver, ValueNs},
13+
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
1414
type_ref::ConstScalar,
15+
ConstId, DefWithBodyId,
1516
};
1617
use hir_expand::name::Name;
1718
use la_arena::{Arena, Idx};
1819
use stdx::never;
1920

2021
use crate::{
21-
db::HirDatabase,
22-
infer::{Expectation, InferenceContext},
23-
lower::ParamLoweringMode,
24-
to_placeholder_idx,
25-
utils::Generics,
26-
Const, ConstData, ConstValue, GenericArg, Interner, Ty, TyKind,
22+
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
23+
utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty,
24+
TyKind,
2725
};
2826

2927
/// Extension trait for [`Const`]
@@ -55,21 +53,30 @@ impl ConstExt for Const {
5553
}
5654

5755
pub struct ConstEvalCtx<'a> {
56+
pub db: &'a dyn HirDatabase,
57+
pub owner: DefWithBodyId,
5858
pub exprs: &'a Arena<Expr>,
5959
pub pats: &'a Arena<Pat>,
6060
pub local_data: HashMap<Name, ComputedExpr>,
61-
pub infer: &'a mut dyn FnMut(Idx<Expr>) -> Ty,
61+
infer: &'a InferenceResult,
6262
}
6363

64-
#[derive(Debug, Clone)]
64+
impl ConstEvalCtx<'_> {
65+
fn expr_ty(&mut self, expr: ExprId) -> Ty {
66+
self.infer[expr].clone()
67+
}
68+
}
69+
70+
#[derive(Debug, Clone, PartialEq, Eq)]
6571
pub enum ConstEvalError {
6672
NotSupported(&'static str),
67-
TypeError,
73+
SemanticError(&'static str),
74+
Loop,
6875
IncompleteExpr,
6976
Panic(String),
7077
}
7178

72-
#[derive(Debug, Clone)]
79+
#[derive(Debug, Clone, PartialEq, Eq)]
7380
pub enum ComputedExpr {
7481
Literal(Literal),
7582
Tuple(Box<[ComputedExpr]>),
@@ -143,12 +150,16 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
143150
}
144151
}
145152

146-
pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> {
153+
pub fn eval_const(
154+
expr_id: ExprId,
155+
ctx: &mut ConstEvalCtx<'_>,
156+
) -> Result<ComputedExpr, ConstEvalError> {
157+
let expr = &ctx.exprs[expr_id];
147158
match expr {
148159
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
149160
&Expr::UnaryOp { expr, op } => {
150-
let ty = &(ctx.infer)(expr);
151-
let ev = eval_const(&ctx.exprs[expr], ctx)?;
161+
let ty = &ctx.expr_ty(expr);
162+
let ev = eval_const(expr, ctx)?;
152163
match op {
153164
hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
154165
hir_def::expr::UnaryOp::Not => {
@@ -203,9 +214,9 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
203214
}
204215
}
205216
&Expr::BinaryOp { lhs, rhs, op } => {
206-
let ty = &(ctx.infer)(lhs);
207-
let lhs = eval_const(&ctx.exprs[lhs], ctx)?;
208-
let rhs = eval_const(&ctx.exprs[rhs], ctx)?;
217+
let ty = &ctx.expr_ty(lhs);
218+
let lhs = eval_const(lhs, ctx)?;
219+
let rhs = eval_const(rhs, ctx)?;
209220
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
210221
let v1 = match lhs {
211222
ComputedExpr::Literal(Literal::Int(v, _)) => v,
@@ -249,7 +260,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
249260
}
250261
Ok(ComputedExpr::Literal(Literal::Int(r, None)))
251262
}
252-
BinaryOp::LogicOp(_) => Err(ConstEvalError::TypeError),
263+
BinaryOp::LogicOp(_) => Err(ConstEvalError::SemanticError("logic op on numbers")),
253264
_ => Err(ConstEvalError::NotSupported("bin op on this operators")),
254265
}
255266
}
@@ -266,7 +277,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
266277
}
267278
};
268279
let value = match initializer {
269-
Some(x) => eval_const(&ctx.exprs[x], ctx)?,
280+
Some(x) => eval_const(x, ctx)?,
270281
None => continue,
271282
};
272283
if !prev_values.contains_key(&name) {
@@ -282,7 +293,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
282293
}
283294
}
284295
let r = match tail {
285-
&Some(x) => eval_const(&ctx.exprs[x], ctx),
296+
&Some(x) => eval_const(x, ctx),
286297
None => Ok(ComputedExpr::Tuple(Box::new([]))),
287298
};
288299
// clean up local data, so caller will receive the exact map that passed to us
@@ -295,19 +306,47 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
295306
r
296307
}
297308
Expr::Path(p) => {
298-
let name = p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?;
299-
let r = ctx
300-
.local_data
301-
.get(name)
302-
.ok_or(ConstEvalError::NotSupported("Non local name resolution"))?;
303-
Ok(r.clone())
309+
let resolver = resolver_for_expr(ctx.db.upcast(), ctx.owner, expr_id);
310+
let pr = resolver
311+
.resolve_path_in_value_ns(ctx.db.upcast(), p.mod_path())
312+
.ok_or(ConstEvalError::SemanticError("unresolved path"))?;
313+
let pr = match pr {
314+
ResolveValueResult::ValueNs(v) => v,
315+
ResolveValueResult::Partial(..) => {
316+
return match ctx
317+
.infer
318+
.assoc_resolutions_for_expr(expr_id)
319+
.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
320+
{
321+
hir_def::AssocItemId::FunctionId(_) => {
322+
Err(ConstEvalError::NotSupported("assoc function"))
323+
}
324+
hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c),
325+
hir_def::AssocItemId::TypeAliasId(_) => {
326+
Err(ConstEvalError::NotSupported("assoc type alias"))
327+
}
328+
}
329+
}
330+
};
331+
match pr {
332+
ValueNs::LocalBinding(_) => {
333+
let name =
334+
p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?;
335+
let r = ctx
336+
.local_data
337+
.get(name)
338+
.ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?;
339+
Ok(r.clone())
340+
}
341+
ValueNs::ConstId(id) => ctx.db.const_eval(id),
342+
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
343+
}
304344
}
305345
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
306346
}
307347
}
308348

309349
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
310-
let expr = &ctx.exprs[expr];
311350
if let Ok(ce) = eval_const(expr, &mut ctx) {
312351
match ce {
313352
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
@@ -380,10 +419,39 @@ pub fn usize_const(value: Option<u64>) -> Const {
380419
.intern(Interner)
381420
}
382421

383-
pub(crate) fn eval_to_const(
422+
pub(crate) fn const_eval_recover(
423+
_: &dyn HirDatabase,
424+
_: &[String],
425+
_: &ConstId,
426+
) -> Result<ComputedExpr, ConstEvalError> {
427+
Err(ConstEvalError::Loop)
428+
}
429+
430+
pub(crate) fn const_eval_query(
431+
db: &dyn HirDatabase,
432+
const_id: ConstId,
433+
) -> Result<ComputedExpr, ConstEvalError> {
434+
let def = const_id.into();
435+
let body = db.body(def);
436+
let mut infer = db.infer_query(def);
437+
let result = eval_const(
438+
body.body_expr,
439+
&mut ConstEvalCtx {
440+
db,
441+
owner: const_id.into(),
442+
exprs: &body.exprs,
443+
pats: &body.pats,
444+
local_data: HashMap::default(),
445+
infer: &mut infer,
446+
},
447+
);
448+
result
449+
}
450+
451+
pub(crate) fn eval_to_const<'a>(
384452
expr: Idx<Expr>,
385453
mode: ParamLoweringMode,
386-
ctx: &mut InferenceContext,
454+
ctx: &mut InferenceContext<'a>,
387455
args: impl FnOnce() -> Generics,
388456
debruijn: DebruijnIndex,
389457
) -> Const {
@@ -396,10 +464,12 @@ pub(crate) fn eval_to_const(
396464
}
397465
let body = ctx.body.clone();
398466
let ctx = ConstEvalCtx {
467+
db: ctx.db,
468+
owner: ctx.owner,
399469
exprs: &body.exprs,
400470
pats: &body.pats,
401471
local_data: HashMap::default(),
402-
infer: &mut |x| ctx.infer_expr(x, &Expectation::None),
472+
infer: &ctx.result,
403473
};
404474
usize_const(eval_usize(expr, ctx))
405475
}

crates/hir_ty/src/db.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ use std::sync::Arc;
55

66
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
77
use hir_def::{
8-
db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
9-
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
8+
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
9+
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
1010
};
1111
use la_arena::ArenaMap;
1212

1313
use crate::{
1414
chalk_db,
15+
consteval::{ComputedExpr, ConstEvalError},
1516
method_resolution::{InherentImpls, TraitImpls},
1617
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
1718
QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
@@ -41,6 +42,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
4142
#[salsa::invoke(crate::lower::const_param_ty_query)]
4243
fn const_param_ty(&self, def: ConstParamId) -> Ty;
4344

45+
#[salsa::invoke(crate::consteval::const_eval_query)]
46+
#[salsa::cycle(crate::consteval::const_eval_recover)]
47+
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
48+
4449
#[salsa::invoke(crate::lower::impl_trait_query)]
4550
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
4651

crates/hir_ty/src/infer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,12 @@ impl Index<PatId> for InferenceResult {
358358
#[derive(Clone, Debug)]
359359
pub(crate) struct InferenceContext<'a> {
360360
pub(crate) db: &'a dyn HirDatabase,
361-
owner: DefWithBodyId,
361+
pub(crate) owner: DefWithBodyId,
362362
pub(crate) body: Arc<Body>,
363363
pub(crate) resolver: Resolver,
364364
table: unify::InferenceTable<'a>,
365365
trait_env: Arc<TraitEnvironment>,
366-
result: InferenceResult,
366+
pub(crate) result: InferenceResult,
367367
/// The return type of the function being inferred, the closure or async block if we're
368368
/// currently within one.
369369
///

0 commit comments

Comments
 (0)