Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_ty/src/consteval.rs')
| -rw-r--r-- | crates/hir_ty/src/consteval.rs | 132 |
1 files changed, 101 insertions, 31 deletions
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs index 1ba291528f..4829482e70 100644 --- a/crates/hir_ty/src/consteval.rs +++ b/crates/hir_ty/src/consteval.rs @@ -8,22 +8,20 @@ use std::{ use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar}; use hir_def::{ - expr::{ArithOp, BinaryOp, Expr, Literal, Pat}, + expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat}, path::ModPath, - resolver::{Resolver, ValueNs}, + resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, type_ref::ConstScalar, + ConstId, DefWithBodyId, }; use hir_expand::name::Name; use la_arena::{Arena, Idx}; use stdx::never; use crate::{ - db::HirDatabase, - infer::{Expectation, InferenceContext}, - lower::ParamLoweringMode, - to_placeholder_idx, - utils::Generics, - Const, ConstData, ConstValue, GenericArg, Interner, Ty, TyKind, + db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx, + utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty, + TyKind, }; /// Extension trait for [`Const`] @@ -55,21 +53,30 @@ impl ConstExt for Const { } pub struct ConstEvalCtx<'a> { + pub db: &'a dyn HirDatabase, + pub owner: DefWithBodyId, pub exprs: &'a Arena<Expr>, pub pats: &'a Arena<Pat>, pub local_data: HashMap<Name, ComputedExpr>, - pub infer: &'a mut dyn FnMut(Idx<Expr>) -> Ty, + infer: &'a InferenceResult, } -#[derive(Debug, Clone)] +impl ConstEvalCtx<'_> { + fn expr_ty(&mut self, expr: ExprId) -> Ty { + self.infer[expr].clone() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstEvalError { NotSupported(&'static str), - TypeError, + SemanticError(&'static str), + Loop, IncompleteExpr, Panic(String), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ComputedExpr { Literal(Literal), Tuple(Box<[ComputedExpr]>), @@ -143,12 +150,16 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool { } } -pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> { +pub fn eval_const( + expr_id: ExprId, + ctx: &mut ConstEvalCtx<'_>, +) -> Result<ComputedExpr, ConstEvalError> { + let expr = &ctx.exprs[expr_id]; match expr { Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())), &Expr::UnaryOp { expr, op } => { - let ty = &(ctx.infer)(expr); - let ev = eval_const(&ctx.exprs[expr], ctx)?; + let ty = &ctx.expr_ty(expr); + let ev = eval_const(expr, ctx)?; match op { hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")), hir_def::expr::UnaryOp::Not => { @@ -203,9 +214,9 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp } } &Expr::BinaryOp { lhs, rhs, op } => { - let ty = &(ctx.infer)(lhs); - let lhs = eval_const(&ctx.exprs[lhs], ctx)?; - let rhs = eval_const(&ctx.exprs[rhs], ctx)?; + let ty = &ctx.expr_ty(lhs); + let lhs = eval_const(lhs, ctx)?; + let rhs = eval_const(rhs, ctx)?; let op = op.ok_or(ConstEvalError::IncompleteExpr)?; let v1 = match lhs { ComputedExpr::Literal(Literal::Int(v, _)) => v, @@ -249,7 +260,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp } Ok(ComputedExpr::Literal(Literal::Int(r, None))) } - BinaryOp::LogicOp(_) => Err(ConstEvalError::TypeError), + BinaryOp::LogicOp(_) => Err(ConstEvalError::SemanticError("logic op on numbers")), _ => Err(ConstEvalError::NotSupported("bin op on this operators")), } } @@ -266,7 +277,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp } }; let value = match initializer { - Some(x) => eval_const(&ctx.exprs[x], ctx)?, + Some(x) => eval_const(x, ctx)?, None => continue, }; if !prev_values.contains_key(&name) { @@ -282,7 +293,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp } } let r = match tail { - &Some(x) => eval_const(&ctx.exprs[x], ctx), + &Some(x) => eval_const(x, ctx), None => Ok(ComputedExpr::Tuple(Box::new([]))), }; // 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 r } Expr::Path(p) => { - let name = p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?; - let r = ctx - .local_data - .get(name) - .ok_or(ConstEvalError::NotSupported("Non local name resolution"))?; - Ok(r.clone()) + let resolver = resolver_for_expr(ctx.db.upcast(), ctx.owner, expr_id); + let pr = resolver + .resolve_path_in_value_ns(ctx.db.upcast(), p.mod_path()) + .ok_or(ConstEvalError::SemanticError("unresolved path"))?; + let pr = match pr { + ResolveValueResult::ValueNs(v) => v, + ResolveValueResult::Partial(..) => { + return match ctx + .infer + .assoc_resolutions_for_expr(expr_id) + .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))? + { + hir_def::AssocItemId::FunctionId(_) => { + Err(ConstEvalError::NotSupported("assoc function")) + } + hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c), + hir_def::AssocItemId::TypeAliasId(_) => { + Err(ConstEvalError::NotSupported("assoc type alias")) + } + } + } + }; + match pr { + ValueNs::LocalBinding(_) => { + let name = + p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?; + let r = ctx + .local_data + .get(name) + .ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?; + Ok(r.clone()) + } + ValueNs::ConstId(id) => ctx.db.const_eval(id), + _ => Err(ConstEvalError::NotSupported("path that are not const or local")), + } } _ => Err(ConstEvalError::NotSupported("This kind of expression")), } } pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> { - let expr = &ctx.exprs[expr]; if let Ok(ce) = eval_const(expr, &mut ctx) { match ce { ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(), @@ -380,10 +419,39 @@ pub fn usize_const(value: Option<u64>) -> Const { .intern(Interner) } -pub(crate) fn eval_to_const( +pub(crate) fn const_eval_recover( + _: &dyn HirDatabase, + _: &[String], + _: &ConstId, +) -> Result<ComputedExpr, ConstEvalError> { + Err(ConstEvalError::Loop) +} + +pub(crate) fn const_eval_query( + db: &dyn HirDatabase, + const_id: ConstId, +) -> Result<ComputedExpr, ConstEvalError> { + let def = const_id.into(); + let body = db.body(def); + let mut infer = db.infer_query(def); + let result = eval_const( + body.body_expr, + &mut ConstEvalCtx { + db, + owner: const_id.into(), + exprs: &body.exprs, + pats: &body.pats, + local_data: HashMap::default(), + infer: &mut infer, + }, + ); + result +} + +pub(crate) fn eval_to_const<'a>( expr: Idx<Expr>, mode: ParamLoweringMode, - ctx: &mut InferenceContext, + ctx: &mut InferenceContext<'a>, args: impl FnOnce() -> Generics, debruijn: DebruijnIndex, ) -> Const { @@ -396,10 +464,12 @@ pub(crate) fn eval_to_const( } let body = ctx.body.clone(); let ctx = ConstEvalCtx { + db: ctx.db, + owner: ctx.owner, exprs: &body.exprs, pats: &body.pats, local_data: HashMap::default(), - infer: &mut |x| ctx.infer_expr(x, &Expectation::None), + infer: &ctx.result, }; usize_const(eval_usize(expr, ctx)) } |