Skip to content

Commit 2f84b6e

Browse files
committed
Almost there
1 parent b63234e commit 2f84b6e

File tree

5 files changed

+141
-22
lines changed

5 files changed

+141
-22
lines changed

crates/hir-def/src/body.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ use crate::{
2828
nameres::DefMap,
2929
path::{ModPath, Path},
3030
src::{HasChildSource, HasSource},
31-
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
32-
UnresolvedMacro,
31+
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId,
32+
ModuleId, UnresolvedMacro,
3333
};
3434

3535
pub use lower::LowerCtx;

crates/hir-ty/src/consteval.rs

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ use std::{
77

88
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
99
use hir_def::{
10+
builtin_type::BuiltinInt,
1011
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
1112
path::ModPath,
1213
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
14+
src::HasChildSource,
1315
type_ref::ConstScalar,
14-
ConstId, DefWithBodyId, EnumVariantId,
16+
ConstId, DefWithBodyId, EnumVariantId, Lookup,
1517
};
16-
use la_arena::{Arena, Idx};
18+
use la_arena::{Arena, Idx, RawIdx};
1719
use stdx::never;
20+
use syntax::ast::HasName;
1821

1922
use crate::{
2023
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
@@ -77,6 +80,7 @@ pub enum ConstEvalError {
7780
#[derive(Debug, Clone, PartialEq, Eq)]
7881
pub enum ComputedExpr {
7982
Literal(Literal),
83+
Enum(String, EnumVariantId, Literal),
8084
Tuple(Box<[ComputedExpr]>),
8185
}
8286

@@ -104,6 +108,7 @@ impl Display for ComputedExpr {
104108
Literal::String(x) => std::fmt::Debug::fmt(x, f),
105109
Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
106110
},
111+
ComputedExpr::Enum(name, _, _) => name.fmt(f),
107112
ComputedExpr::Tuple(t) => {
108113
f.write_char('(')?;
109114
for x in &**t {
@@ -116,6 +121,15 @@ impl Display for ComputedExpr {
116121
}
117122
}
118123

124+
impl ComputedExpr {
125+
pub fn enum_value(&self) -> Option<ComputedExpr> {
126+
match self {
127+
ComputedExpr::Enum(_, _, lit) => Some(ComputedExpr::Literal(lit.clone())),
128+
_ => None,
129+
}
130+
}
131+
}
132+
119133
fn scalar_max(scalar: &Scalar) -> i128 {
120134
match scalar {
121135
Scalar::Bool => 1,
@@ -148,17 +162,56 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
148162
}
149163
}
150164

165+
fn get_name(variant: EnumVariantId, ctx: &mut ConstEvalCtx<'_>) -> String {
166+
let loc = variant.parent.lookup(ctx.db.upcast());
167+
let children = variant.parent.child_source(ctx.db.upcast());
168+
let item_tree = loc.id.item_tree(ctx.db.upcast());
169+
170+
let variant_name = children.value[variant.local_id].name();
171+
let enum_name = item_tree[loc.id.value].name.to_string();
172+
enum_name + "::" + &variant_name.unwrap().to_string()
173+
}
174+
151175
pub fn eval_const(
152176
expr_id: ExprId,
153177
ctx: &mut ConstEvalCtx<'_>,
178+
variant: Option<EnumVariantId>,
154179
) -> Result<ComputedExpr, ConstEvalError> {
155180
let expr = &ctx.exprs[expr_id];
156181
match expr {
157-
Expr::Missing => Err(ConstEvalError::IncompleteExpr),
182+
Expr::Missing => match variant {
183+
Some(variant) => {
184+
let prev_idx: u32 = variant.local_id.into_raw().into();
185+
let prev_idx = prev_idx.checked_sub(1).map(|idx| Idx::from_raw(RawIdx::from(idx)));
186+
let value = match prev_idx {
187+
Some(prev) => {
188+
let prev_variant = EnumVariantId { local_id: prev, ..variant };
189+
1 + match ctx.db.const_eval_variant(prev_variant)? {
190+
ComputedExpr::Literal(Literal::Int(v, _)) => v,
191+
ComputedExpr::Literal(Literal::Uint(v, _)) => v
192+
.try_into()
193+
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
194+
_ => {
195+
return Err(ConstEvalError::NotSupported(
196+
"Enum can't contain this kind of value",
197+
))
198+
}
199+
}
200+
}
201+
_ => 0,
202+
};
203+
Ok(ComputedExpr::Enum(
204+
get_name(variant, ctx),
205+
variant,
206+
Literal::Int(value + 1, Some(BuiltinInt::I128)),
207+
))
208+
}
209+
_ => Err(ConstEvalError::IncompleteExpr),
210+
},
158211
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
159212
&Expr::UnaryOp { expr, op } => {
160213
let ty = &ctx.expr_ty(expr);
161-
let ev = eval_const(expr, ctx)?;
214+
let ev = eval_const(expr, ctx, None)?;
162215
match op {
163216
hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
164217
hir_def::expr::UnaryOp::Not => {
@@ -214,8 +267,8 @@ pub fn eval_const(
214267
}
215268
&Expr::BinaryOp { lhs, rhs, op } => {
216269
let ty = &ctx.expr_ty(lhs);
217-
let lhs = eval_const(lhs, ctx)?;
218-
let rhs = eval_const(rhs, ctx)?;
270+
let lhs = eval_const(lhs, ctx, None)?;
271+
let rhs = eval_const(rhs, ctx, None)?;
219272
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
220273
let v1 = match lhs {
221274
ComputedExpr::Literal(Literal::Int(v, _)) => v,
@@ -276,7 +329,7 @@ pub fn eval_const(
276329
}
277330
};
278331
let value = match initializer {
279-
Some(x) => eval_const(x, ctx)?,
332+
Some(x) => eval_const(x, ctx, None)?,
280333
None => continue,
281334
};
282335
if !prev_values.contains_key(&pat_id) {
@@ -292,7 +345,7 @@ pub fn eval_const(
292345
}
293346
}
294347
let r = match tail {
295-
&Some(x) => eval_const(x, ctx),
348+
&Some(x) => eval_const(x, ctx, None),
296349
None => Ok(ComputedExpr::Tuple(Box::new([]))),
297350
};
298351
// clean up local data, so caller will receive the exact map that passed to us
@@ -339,10 +392,24 @@ pub fn eval_const(
339392
ValueNs::GenericParam(_) => {
340393
Err(ConstEvalError::NotSupported("const generic without substitution"))
341394
}
342-
ValueNs::EnumVariantId(id) => ctx.db.const_eval_variant(id),
395+
ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
396+
ComputedExpr::Literal(lit) => {
397+
Ok(ComputedExpr::Enum(get_name(id, ctx), id, lit))
398+
}
399+
_ => Err(ConstEvalError::NotSupported(
400+
"Enums can't evalute to anything but numbers",
401+
)),
402+
},
343403
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
344404
}
345405
}
406+
Expr::Cast { expr, .. } => match eval_const(*expr, ctx, None)? {
407+
ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
408+
expr => Err(ConstEvalError::NotSupported(Box::leak(Box::new(format!(
409+
"Can't cast type: {:?}",
410+
expr
411+
))))),
412+
},
346413
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
347414
}
348415
}
@@ -438,6 +505,7 @@ pub(crate) fn const_eval_query(
438505
local_data: HashMap::default(),
439506
infer,
440507
},
508+
None,
441509
);
442510
result
443511
}
@@ -459,6 +527,7 @@ pub(crate) fn const_eval_query_variant(
459527
local_data: HashMap::default(),
460528
infer,
461529
},
530+
Some(variant_id),
462531
)
463532
}
464533

@@ -485,7 +554,7 @@ pub(crate) fn eval_to_const<'a>(
485554
local_data: HashMap::default(),
486555
infer: &ctx.result,
487556
};
488-
let computed_expr = eval_const(expr, &mut ctx);
557+
let computed_expr = eval_const(expr, &mut ctx, None);
489558
let const_scalar = match computed_expr {
490559
Ok(ComputedExpr::Literal(literal)) => literal.into(),
491560
_ => ConstScalar::Unknown,

crates/hir-ty/src/infer.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use hir_def::{
2626
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
2727
type_ref::TypeRef,
2828
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
29-
TraitId, TypeAliasId, VariantId
29+
TraitId, TypeAliasId, VariantId,
3030
};
3131
use hir_expand::name::{name, Name};
3232
use itertools::Either;
@@ -68,7 +68,6 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
6868
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
6969
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
7070
DefWithBodyId::VariantId(v) => {
71-
// TODO(ole): Get the real type
7271
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
7372
}
7473
}

crates/ide/src/hover/render.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt::Display;
33

44
use either::Either;
55
use hir::{
6-
db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
6+
db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, StructKind, TypeInfo,
77
};
88
use ide_db::{
99
base_db::SourceDatabase,
@@ -348,12 +348,12 @@ pub(super) fn definition(
348348
Definition::Module(it) => label_and_docs(db, it),
349349
Definition::Function(it) => label_and_docs(db, it),
350350
Definition::Adt(it) => label_and_docs(db, it),
351-
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
352-
let body = it.eval(db);
353-
match body {
354-
Ok(x) => Some(format!("{}", x)),
355-
Err(_) => it.value(db).map(|x| format!("{}", x)),
356-
}
351+
Definition::Variant(it) => label_value_and_docs(db, it, |&it| match it.kind(db) {
352+
StructKind::Unit => match it.eval(db) {
353+
Ok(x) => Some(format!("{}", x.enum_value().unwrap_or(x))),
354+
Err(_) => it.value(db).map(|x| format!("{:?}", x)),
355+
},
356+
_ => None,
357357
}),
358358
Definition::Const(it) => label_value_and_docs(db, it, |it| {
359359
let body = it.eval(db);

crates/ide/src/hover/tests.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3529,6 +3529,31 @@ impl<const LEN: usize> Foo<LEN$0> {}
35293529

35303530
#[test]
35313531
fn hover_const_eval_variant() {
3532+
check(
3533+
r#"
3534+
#[repr(u8)]
3535+
enum E {
3536+
A = 4,
3537+
/// This is a doc
3538+
B$0 = E::A as u8 + 1,
3539+
}
3540+
"#,
3541+
expect![[r#"
3542+
*B*
3543+
3544+
```rust
3545+
test::E
3546+
```
3547+
3548+
```rust
3549+
B = 5
3550+
```
3551+
3552+
---
3553+
3554+
This is a doc
3555+
"#]],
3556+
);
35323557
// show hex for <10
35333558
check(
35343559
r#"
@@ -3586,7 +3611,7 @@ enum E {
35863611
enum E {
35873612
A = 1,
35883613
/// This is a doc
3589-
B$0 = E::A + 1,
3614+
B$0 = E::A as u8 + 1,
35903615
}
35913616
"#,
35923617
expect![[r#"
@@ -3602,6 +3627,32 @@ enum E {
36023627
36033628
---
36043629
3630+
This is a doc
3631+
"#]],
3632+
);
3633+
// unspecified variant should increment by one
3634+
check(
3635+
r#"
3636+
#[repr(u8)]
3637+
enum E {
3638+
A = 4,
3639+
/// This is a doc
3640+
B$0,
3641+
}
3642+
"#,
3643+
expect![[r#"
3644+
*B*
3645+
3646+
```rust
3647+
test::E
3648+
```
3649+
3650+
```rust
3651+
B = 5
3652+
```
3653+
3654+
---
3655+
36053656
This is a doc
36063657
"#]],
36073658
);

0 commit comments

Comments
 (0)