Skip to content

Evaluate constants in array repeat expression #11112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1540,11 +1540,11 @@ impl Const {
let infer = infer.as_ref();
let result = eval_const(
root,
ConstEvalCtx {
&mut ConstEvalCtx {
exprs: &body.exprs,
pats: &body.pats,
local_data: HashMap::default(),
infer,
infer: &mut |x| infer[x].clone(),
},
);
result
Expand Down
59 changes: 37 additions & 22 deletions crates/hir_ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ use std::{collections::HashMap, convert::TryInto, fmt::Display};

use chalk_ir::{IntTy, Scalar};
use hir_def::{
builtin_type::BuiltinUint,
expr::{ArithOp, BinaryOp, Expr, Literal, Pat},
type_ref::ConstScalar,
};
use hir_expand::name::Name;
use la_arena::Arena;
use la_arena::{Arena, Idx};

use crate::{Const, ConstData, ConstValue, InferenceResult, Interner, TyKind};
use crate::{Const, ConstData, ConstValue, Interner, Ty, TyKind};

/// Extension trait for [`Const`]
pub trait ConstExt {
Expand Down Expand Up @@ -41,12 +40,11 @@ impl ConstExt for Const {
}
}

#[derive(Clone)]
pub struct ConstEvalCtx<'a> {
pub exprs: &'a Arena<Expr>,
pub pats: &'a Arena<Pat>,
pub local_data: HashMap<Name, ComputedExpr>,
pub infer: &'a InferenceResult,
pub infer: &'a mut dyn FnMut(Idx<Expr>) -> Ty,
}

#[derive(Debug, Clone)]
Expand All @@ -57,7 +55,7 @@ pub enum ConstEvalError {
Panic(String),
}

#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum ComputedExpr {
Literal(Literal),
Tuple(Box<[ComputedExpr]>),
Expand Down Expand Up @@ -130,11 +128,11 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
}
}

pub fn eval_const(expr: &Expr, mut ctx: ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> {
pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> {
match expr {
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
&Expr::UnaryOp { expr, op } => {
let ty = &ctx.infer[expr];
let ty = &(ctx.infer)(expr);
let ev = eval_const(&ctx.exprs[expr], ctx)?;
match op {
hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
Expand Down Expand Up @@ -190,9 +188,9 @@ pub fn eval_const(expr: &Expr, mut ctx: ConstEvalCtx<'_>) -> Result<ComputedExpr
}
}
&Expr::BinaryOp { lhs, rhs, op } => {
let ty = &ctx.infer[lhs];
let lhs = eval_const(&ctx.exprs[lhs], ctx.clone())?;
let rhs = eval_const(&ctx.exprs[rhs], ctx.clone())?;
let ty = &(ctx.infer)(lhs);
let lhs = eval_const(&ctx.exprs[lhs], ctx)?;
let rhs = eval_const(&ctx.exprs[rhs], ctx)?;
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
let v1 = match lhs {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
Expand Down Expand Up @@ -241,6 +239,7 @@ pub fn eval_const(expr: &Expr, mut ctx: ConstEvalCtx<'_>) -> Result<ComputedExpr
}
}
Expr::Block { statements, tail, .. } => {
let mut prev_values = HashMap::<Name, Option<ComputedExpr>>::default();
for statement in &**statements {
match statement {
&hir_def::expr::Statement::Let { pat, initializer, .. } => {
Expand All @@ -252,21 +251,33 @@ pub fn eval_const(expr: &Expr, mut ctx: ConstEvalCtx<'_>) -> Result<ComputedExpr
}
};
let value = match initializer {
Some(x) => eval_const(&ctx.exprs[x], ctx.clone())?,
Some(x) => eval_const(&ctx.exprs[x], ctx)?,
None => continue,
};
ctx.local_data.insert(name, value);
if !prev_values.contains_key(&name) {
let prev = ctx.local_data.insert(name.clone(), value);
prev_values.insert(name, prev);
} else {
ctx.local_data.insert(name, value);
}
}
&hir_def::expr::Statement::Expr { .. } => {
return Err(ConstEvalError::NotSupported("this kind of statement"))
}
}
}
let tail_expr = match tail {
&Some(x) => &ctx.exprs[x],
None => return Ok(ComputedExpr::Tuple(Box::new([]))),
let r = match tail {
&Some(x) => eval_const(&ctx.exprs[x], ctx),
None => Ok(ComputedExpr::Tuple(Box::new([]))),
};
eval_const(tail_expr, ctx)
// clean up local data, so caller will receive the exact map that passed to us
for (name, val) in prev_values {
match val {
Some(x) => ctx.local_data.insert(name, x),
None => ctx.local_data.remove(&name),
};
}
r
}
Expr::Path(p) => {
let name = p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?;
Expand All @@ -280,12 +291,16 @@ pub fn eval_const(expr: &Expr, mut ctx: ConstEvalCtx<'_>) -> Result<ComputedExpr
}
}

// FIXME: support more than just evaluating literals
pub fn eval_usize(expr: &Expr) -> Option<u64> {
match expr {
Expr::Literal(Literal::Uint(v, None | Some(BuiltinUint::Usize))) => (*v).try_into().ok(),
_ => None,
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(),
ComputedExpr::Literal(Literal::Uint(x, _)) => return x.try_into().ok(),
_ => {}
}
}
None
}

/// Interns a possibly-unknown target usize
Expand Down
11 changes: 9 additions & 2 deletions crates/hir_ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,8 +799,15 @@ impl<'a> InferenceContext<'a> {
),
);

let repeat_expr = &self.body.exprs[repeat];
consteval::eval_usize(repeat_expr)
consteval::eval_usize(
repeat,
consteval::ConstEvalCtx {
exprs: &body.exprs,
pats: &body.pats,
local_data: Default::default(),
infer: &mut |x| self.infer_expr(x, &expected),
},
)
}
};

Expand Down
43 changes: 43 additions & 0 deletions crates/ide/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3350,6 +3350,31 @@ const FOO$0: usize = 1 << 10;
check(
r#"
/// This is a doc
const FOO$0: usize = {
let b = 4;
let a = { let b = 2; let a = b; a } + { let a = 1; a + b };
a
};
"#,
expect![[r#"
*FOO*

```rust
test
```

```rust
const FOO: usize = 7
```

---

This is a doc
"#]],
);
check(
r#"
/// This is a doc
const FOO$0: usize = 2 - 3;
"#,
expect![[r#"
Expand Down Expand Up @@ -3443,6 +3468,24 @@ fn foo() {
);
}

#[test]
fn array_repeat_exp() {
check(
r#"
fn main() {
let til$0e4 = [0_u32; (4 * 8 * 8) / 32];
}
"#,
expect![[r#"
*tile4*

```rust
let tile4: [u32; 8]
```
"#]],
);
}

#[test]
fn hover_mod_def() {
check(
Expand Down
4 changes: 3 additions & 1 deletion crates/ide_assists/src/handlers/add_explicit_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,10 @@ fn main() {
check_assist_not_applicable(
add_explicit_type,
r#"
//- minicore: option

fn main() {
let $0l = [0.0; 2+2];
let $0l = [0.0; Some(2).unwrap()];
}
"#,
);
Expand Down