Skip to content

Commit f3d1a53

Browse files
bors[bot]HKalbasi
andauthored
Merge #11772
11772: Support constants in const eval r=HKalbasi a=HKalbasi This PR enables evaluating things like this: ```rust const X: usize = 2; const Y: usize = 3 + X; // = 5 ``` My target was nalgebra's `U5`, `U22`, ... which are defined as `type U5 = Const<{ SomeType5::SOME_ASSOC_CONST }>` but I didn't find out how to find the `ConstId` of the implementation of the trait, not the trait itself (possibly related to #4558 ? We can find associated type alias, so maybe this is doable already) So it doesn't help for nalgebra currently, but it is useful anyway. Co-authored-by: hkalbasi <[email protected]>
2 parents b8e1d09 + bf4a1e4 commit f3d1a53

File tree

7 files changed

+298
-141
lines changed

7 files changed

+298
-141
lines changed

crates/hir/src/lib.rs

+3-18
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};
@@ -55,9 +55,7 @@ use hir_def::{
5555
use hir_expand::{name::name, MacroCallKind};
5656
use hir_ty::{
5757
autoderef,
58-
consteval::{
59-
eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
60-
},
58+
consteval::{unknown_const_as_generic, ComputedExpr, ConstEvalError, ConstExt},
6159
diagnostics::BodyValidationDiagnostic,
6260
method_resolution::{self, TyFingerprint},
6361
primitive::UintTy,
@@ -1602,20 +1600,7 @@ impl Const {
16021600
}
16031601

16041602
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
1603+
db.const_eval(self.id)
16191604
}
16201605
}
16211606

crates/hir_ty/src/consteval.rs

+118-44
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,19 @@ 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, PatId},
1212
path::ModPath,
13-
resolver::{Resolver, ValueNs},
13+
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
1414
type_ref::ConstScalar,
15+
ConstId, DefWithBodyId,
1516
};
16-
use hir_expand::name::Name;
1717
use la_arena::{Arena, Idx};
1818
use stdx::never;
1919

2020
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,
21+
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
22+
utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty,
23+
TyKind,
2724
};
2825

2926
/// Extension trait for [`Const`]
@@ -55,21 +52,30 @@ impl ConstExt for Const {
5552
}
5653

5754
pub struct ConstEvalCtx<'a> {
55+
pub db: &'a dyn HirDatabase,
56+
pub owner: DefWithBodyId,
5857
pub exprs: &'a Arena<Expr>,
5958
pub pats: &'a Arena<Pat>,
60-
pub local_data: HashMap<Name, ComputedExpr>,
61-
pub infer: &'a mut dyn FnMut(Idx<Expr>) -> Ty,
59+
pub local_data: HashMap<PatId, ComputedExpr>,
60+
infer: &'a InferenceResult,
6261
}
6362

64-
#[derive(Debug, Clone)]
63+
impl ConstEvalCtx<'_> {
64+
fn expr_ty(&mut self, expr: ExprId) -> Ty {
65+
self.infer[expr].clone()
66+
}
67+
}
68+
69+
#[derive(Debug, Clone, PartialEq, Eq)]
6570
pub enum ConstEvalError {
6671
NotSupported(&'static str),
67-
TypeError,
72+
SemanticError(&'static str),
73+
Loop,
6874
IncompleteExpr,
6975
Panic(String),
7076
}
7177

72-
#[derive(Debug, Clone)]
78+
#[derive(Debug, Clone, PartialEq, Eq)]
7379
pub enum ComputedExpr {
7480
Literal(Literal),
7581
Tuple(Box<[ComputedExpr]>),
@@ -80,14 +86,14 @@ impl Display for ComputedExpr {
8086
match self {
8187
ComputedExpr::Literal(l) => match l {
8288
Literal::Int(x, _) => {
83-
if *x >= 16 {
89+
if *x >= 10 {
8490
write!(f, "{} ({:#X})", x, x)
8591
} else {
8692
x.fmt(f)
8793
}
8894
}
8995
Literal::Uint(x, _) => {
90-
if *x >= 16 {
96+
if *x >= 10 {
9197
write!(f, "{} ({:#X})", x, x)
9298
} else {
9399
x.fmt(f)
@@ -143,12 +149,17 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
143149
}
144150
}
145151

146-
pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> {
152+
pub fn eval_const(
153+
expr_id: ExprId,
154+
ctx: &mut ConstEvalCtx<'_>,
155+
) -> Result<ComputedExpr, ConstEvalError> {
156+
let expr = &ctx.exprs[expr_id];
147157
match expr {
158+
Expr::Missing => Err(ConstEvalError::IncompleteExpr),
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,31 +260,31 @@ 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
}
256267
Expr::Block { statements, tail, .. } => {
257-
let mut prev_values = HashMap::<Name, Option<ComputedExpr>>::default();
268+
let mut prev_values = HashMap::<PatId, Option<ComputedExpr>>::default();
258269
for statement in &**statements {
259270
match *statement {
260-
hir_def::expr::Statement::Let { pat, initializer, .. } => {
261-
let pat = &ctx.pats[pat];
262-
let name = match pat {
263-
Pat::Bind { name, subpat, .. } if subpat.is_none() => name.clone(),
271+
hir_def::expr::Statement::Let { pat: pat_id, initializer, .. } => {
272+
let pat = &ctx.pats[pat_id];
273+
match pat {
274+
Pat::Bind { subpat, .. } if subpat.is_none() => (),
264275
_ => {
265276
return Err(ConstEvalError::NotSupported("complex patterns in let"))
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
};
272-
if !prev_values.contains_key(&name) {
273-
let prev = ctx.local_data.insert(name.clone(), value);
274-
prev_values.insert(name, prev);
283+
if !prev_values.contains_key(&pat_id) {
284+
let prev = ctx.local_data.insert(pat_id, value);
285+
prev_values.insert(pat_id, prev);
275286
} else {
276-
ctx.local_data.insert(name, value);
287+
ctx.local_data.insert(pat_id, value);
277288
}
278289
}
279290
hir_def::expr::Statement::Expr { .. } => {
@@ -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,48 @@ 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(pat_id) => {
333+
let r = ctx
334+
.local_data
335+
.get(&pat_id)
336+
.ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?;
337+
Ok(r.clone())
338+
}
339+
ValueNs::ConstId(id) => ctx.db.const_eval(id),
340+
ValueNs::GenericParam(_) => {
341+
Err(ConstEvalError::NotSupported("const generic without substitution"))
342+
}
343+
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
344+
}
304345
}
305346
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
306347
}
307348
}
308349

309350
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
310-
let expr = &ctx.exprs[expr];
311351
if let Ok(ce) = eval_const(expr, &mut ctx) {
312352
match ce {
313353
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
@@ -380,10 +420,39 @@ pub fn usize_const(value: Option<u64>) -> Const {
380420
.intern(Interner)
381421
}
382422

383-
pub(crate) fn eval_to_const(
423+
pub(crate) fn const_eval_recover(
424+
_: &dyn HirDatabase,
425+
_: &[String],
426+
_: &ConstId,
427+
) -> Result<ComputedExpr, ConstEvalError> {
428+
Err(ConstEvalError::Loop)
429+
}
430+
431+
pub(crate) fn const_eval_query(
432+
db: &dyn HirDatabase,
433+
const_id: ConstId,
434+
) -> Result<ComputedExpr, ConstEvalError> {
435+
let def = const_id.into();
436+
let body = db.body(def);
437+
let infer = &db.infer(def);
438+
let result = eval_const(
439+
body.body_expr,
440+
&mut ConstEvalCtx {
441+
db,
442+
owner: const_id.into(),
443+
exprs: &body.exprs,
444+
pats: &body.pats,
445+
local_data: HashMap::default(),
446+
infer,
447+
},
448+
);
449+
result
450+
}
451+
452+
pub(crate) fn eval_to_const<'a>(
384453
expr: Idx<Expr>,
385454
mode: ParamLoweringMode,
386-
ctx: &mut InferenceContext,
455+
ctx: &mut InferenceContext<'a>,
387456
args: impl FnOnce() -> Generics,
388457
debruijn: DebruijnIndex,
389458
) -> Const {
@@ -396,10 +465,15 @@ pub(crate) fn eval_to_const(
396465
}
397466
let body = ctx.body.clone();
398467
let ctx = ConstEvalCtx {
468+
db: ctx.db,
469+
owner: ctx.owner,
399470
exprs: &body.exprs,
400471
pats: &body.pats,
401472
local_data: HashMap::default(),
402-
infer: &mut |x| ctx.infer_expr(x, &Expectation::None),
473+
infer: &ctx.result,
403474
};
404475
usize_const(eval_usize(expr, ctx))
405476
}
477+
478+
#[cfg(test)]
479+
mod tests;

0 commit comments

Comments
 (0)