Skip to content

Commit 817a6a8

Browse files
committed
Auto merge of rust-lang#12966 - OleStrohm:master, r=Veykril
feat: Display the value of enum variant on hover fixes rust-lang#12955 This PR adds const eval support for enums, as well as showing their value on hover, just as consts currently have. I developed these two things at the same time, but I've realized now that they are separate. However since the hover is just a 10 line change (not including tests), I figured I may as well put them in the same PR. Though if you want them split up into "enum const eval support" and "show enum variant value on hover", I think that's reasonable too. Since this adds const eval support for enums this also allows consts that reference enums to have their values computed now too. The const evaluation itself is quite rudimentary, it doesn't keep track of the actual type of the enum, but it turns out that Rust doesn't actually either, and `E::A as u8` is valid regardless of the `repr` on `E`. It also doesn't really care about what expression the enum variant contains, it could for example be a string, despite that not being allowed, but I guess it's up to the `cargo check` diagnostics to inform of such issues anyway?
2 parents 09600a3 + f87ad8d commit 817a6a8

File tree

16 files changed

+366
-16
lines changed

16 files changed

+366
-16
lines changed

crates/hir-def/src/body.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::{
2727
macro_id_to_def_id,
2828
nameres::DefMap,
2929
path::{ModPath, Path},
30-
src::HasSource,
30+
src::{HasChildSource, HasSource},
3131
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
3232
UnresolvedMacro,
3333
};
@@ -324,6 +324,12 @@ impl Body {
324324
let src = s.source(db);
325325
(src.file_id, s.module(db), src.value.body())
326326
}
327+
DefWithBodyId::VariantId(v) => {
328+
let e = v.parent.lookup(db);
329+
let src = v.parent.child_source(db);
330+
let variant = &src.value[v.local_id];
331+
(src.file_id, e.container, variant.expr())
332+
}
327333
};
328334
let expander = Expander::new(db, file_id, module);
329335
let (mut body, source_map) = Body::new(db, expander, params, body);

crates/hir-def/src/body/pretty.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use std::fmt::{self, Write};
44

5+
use syntax::ast::HasName;
6+
57
use crate::{
68
expr::{Array, BindingAnnotation, Literal, Statement},
79
pretty::{print_generic_args, print_path, print_type_ref},
@@ -32,6 +34,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
3234
};
3335
format!("const {} = ", name)
3436
}
37+
DefWithBodyId::VariantId(it) => {
38+
needs_semi = false;
39+
let src = it.parent.child_source(db);
40+
let variant = &src.value[it.local_id];
41+
let name = match &variant.name() {
42+
Some(name) => name.to_string(),
43+
None => "_".to_string(),
44+
};
45+
format!("{}", name)
46+
}
3547
};
3648

3749
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };

crates/hir-def/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,16 +474,25 @@ pub enum DefWithBodyId {
474474
FunctionId(FunctionId),
475475
StaticId(StaticId),
476476
ConstId(ConstId),
477+
VariantId(EnumVariantId),
477478
}
478479

479480
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
480481

482+
// FIXME: Rename EnumVariantId to VariantId so that the macro above can be used
483+
impl From<EnumVariantId> for DefWithBodyId {
484+
fn from(id: EnumVariantId) -> Self {
485+
DefWithBodyId::VariantId(id)
486+
}
487+
}
488+
481489
impl DefWithBodyId {
482490
pub fn as_generic_def_id(self) -> Option<GenericDefId> {
483491
match self {
484492
DefWithBodyId::FunctionId(f) => Some(f.into()),
485493
DefWithBodyId::StaticId(_) => None,
486494
DefWithBodyId::ConstId(c) => Some(c.into()),
495+
DefWithBodyId::VariantId(c) => Some(c.into()),
487496
}
488497
}
489498
}
@@ -681,6 +690,7 @@ impl HasModule for DefWithBodyId {
681690
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
682691
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
683692
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
693+
DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
684694
}
685695
}
686696
}
@@ -691,6 +701,7 @@ impl DefWithBodyId {
691701
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
692702
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
693703
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
704+
DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
694705
}
695706
}
696707
}

crates/hir-def/src/resolver.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ impl HasResolver for DefWithBodyId {
839839
DefWithBodyId::ConstId(c) => c.resolver(db),
840840
DefWithBodyId::FunctionId(f) => f.resolver(db),
841841
DefWithBodyId::StaticId(s) => s.resolver(db),
842+
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
842843
}
843844
}
844845
}

crates/hir-ty/src/consteval.rs

Lines changed: 82 additions & 3 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,
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 {
@@ -148,13 +153,47 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
148153
}
149154
}
150155

156+
fn get_name(variant: EnumVariantId, ctx: &mut ConstEvalCtx<'_>) -> String {
157+
let loc = variant.parent.lookup(ctx.db.upcast());
158+
let children = variant.parent.child_source(ctx.db.upcast());
159+
let item_tree = loc.id.item_tree(ctx.db.upcast());
160+
161+
let variant_name = children.value[variant.local_id].name();
162+
let enum_name = item_tree[loc.id.value].name.to_string();
163+
enum_name + "::" + &variant_name.unwrap().to_string()
164+
}
165+
151166
pub fn eval_const(
152167
expr_id: ExprId,
153168
ctx: &mut ConstEvalCtx<'_>,
154169
) -> Result<ComputedExpr, ConstEvalError> {
155170
let expr = &ctx.exprs[expr_id];
156171
match expr {
157-
Expr::Missing => Err(ConstEvalError::IncompleteExpr),
172+
Expr::Missing => match ctx.owner {
173+
DefWithBodyId::VariantId(variant) => {
174+
let prev_idx: u32 = variant.local_id.into_raw().into();
175+
let prev_idx = prev_idx.checked_sub(1).map(|idx| Idx::from_raw(RawIdx::from(idx)));
176+
let value = match prev_idx {
177+
Some(prev) => {
178+
let prev_variant = EnumVariantId { local_id: prev, ..variant };
179+
1 + match ctx.db.const_eval_variant(prev_variant)? {
180+
ComputedExpr::Literal(Literal::Int(v, _)) => v,
181+
ComputedExpr::Literal(Literal::Uint(v, _)) => v
182+
.try_into()
183+
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
184+
_ => {
185+
return Err(ConstEvalError::NotSupported(
186+
"Enum can't contain this kind of value",
187+
))
188+
}
189+
}
190+
}
191+
_ => 0,
192+
};
193+
Ok(ComputedExpr::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
194+
}
195+
_ => Err(ConstEvalError::IncompleteExpr),
196+
},
158197
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
159198
&Expr::UnaryOp { expr, op } => {
160199
let ty = &ctx.expr_ty(expr);
@@ -339,9 +378,21 @@ pub fn eval_const(
339378
ValueNs::GenericParam(_) => {
340379
Err(ConstEvalError::NotSupported("const generic without substitution"))
341380
}
381+
ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
382+
ComputedExpr::Literal(lit) => {
383+
Ok(ComputedExpr::Enum(get_name(id, ctx), id, lit))
384+
}
385+
_ => Err(ConstEvalError::NotSupported(
386+
"Enums can't evalute to anything but numbers",
387+
)),
388+
},
342389
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
343390
}
344391
}
392+
&Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
393+
ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
394+
_ => Err(ConstEvalError::NotSupported("Can't cast these types")),
395+
},
345396
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
346397
}
347398
}
@@ -412,6 +463,14 @@ pub(crate) fn const_eval_recover(
412463
Err(ConstEvalError::Loop)
413464
}
414465

466+
pub(crate) fn const_eval_recover_variant(
467+
_: &dyn HirDatabase,
468+
_: &[String],
469+
_: &EnumVariantId,
470+
) -> Result<ComputedExpr, ConstEvalError> {
471+
Err(ConstEvalError::Loop)
472+
}
473+
415474
pub(crate) fn const_eval_query(
416475
db: &dyn HirDatabase,
417476
const_id: ConstId,
@@ -433,6 +492,26 @@ pub(crate) fn const_eval_query(
433492
result
434493
}
435494

495+
pub(crate) fn const_eval_query_variant(
496+
db: &dyn HirDatabase,
497+
variant_id: EnumVariantId,
498+
) -> Result<ComputedExpr, ConstEvalError> {
499+
let def = variant_id.into();
500+
let body = db.body(def);
501+
let infer = &db.infer(def);
502+
eval_const(
503+
body.body_expr,
504+
&mut ConstEvalCtx {
505+
db,
506+
owner: def,
507+
exprs: &body.exprs,
508+
pats: &body.pats,
509+
local_data: HashMap::default(),
510+
infer,
511+
},
512+
)
513+
}
514+
436515
pub(crate) fn eval_to_const<'a>(
437516
expr: Idx<Expr>,
438517
mode: ParamLoweringMode,

crates/hir-ty/src/consteval/tests.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,49 @@ fn consts() {
8787
);
8888
}
8989

90+
#[test]
91+
fn enums() {
92+
check_number(
93+
r#"
94+
enum E {
95+
F1 = 1,
96+
F2 = 2 * E::F1 as u8,
97+
F3 = 3 * E::F2 as u8,
98+
}
99+
const GOAL: i32 = E::F3 as u8;
100+
"#,
101+
6,
102+
);
103+
check_number(
104+
r#"
105+
enum E { F1 = 1, F2, }
106+
const GOAL: i32 = E::F2 as u8;
107+
"#,
108+
2,
109+
);
110+
check_number(
111+
r#"
112+
enum E { F1, }
113+
const GOAL: i32 = E::F1 as u8;
114+
"#,
115+
0,
116+
);
117+
let r = eval_goal(
118+
r#"
119+
enum E { A = 1, }
120+
const GOAL: E = E::A;
121+
"#,
122+
)
123+
.unwrap();
124+
match r {
125+
ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
126+
assert_eq!(name, "E::A");
127+
assert_eq!(val, 1);
128+
}
129+
x => panic!("Expected enum but found {:?}", x),
130+
}
131+
}
132+
90133
#[test]
91134
fn const_loop() {
92135
check_fail(

crates/hir-ty/src/db.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use std::sync::Arc;
66
use arrayvec::ArrayVec;
77
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
88
use hir_def::{
9-
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
10-
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
9+
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
10+
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, Lookup, TypeOrConstParamId,
11+
VariantId,
1112
};
1213
use la_arena::ArenaMap;
1314

@@ -47,6 +48,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
4748
#[salsa::cycle(crate::consteval::const_eval_recover)]
4849
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
4950

51+
#[salsa::invoke(crate::consteval::const_eval_query_variant)]
52+
#[salsa::cycle(crate::consteval::const_eval_recover_variant)]
53+
fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
54+
5055
#[salsa::invoke(crate::lower::impl_trait_query)]
5156
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
5257

@@ -188,6 +193,13 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
188193
DefWithBodyId::ConstId(it) => {
189194
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
190195
}
196+
DefWithBodyId::VariantId(it) => {
197+
let up_db: &dyn DefDatabase = db.upcast();
198+
let loc = it.parent.lookup(up_db);
199+
let item_tree = loc.id.item_tree(up_db);
200+
let konst = &item_tree[loc.id.value];
201+
konst.name.to_string()
202+
}
191203
});
192204
db.infer_query(def)
193205
}

crates/hir-ty/src/diagnostics/unsafe_check.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
1818

1919
let is_unsafe = match def {
2020
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
21-
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
21+
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
22+
false
23+
}
2224
};
2325
if is_unsafe {
2426
return res;

crates/hir-ty/src/infer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
6767
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
6868
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
6969
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
70+
DefWithBodyId::VariantId(v) => {
71+
// FIXME: This should return the `repr(...)` type of the enum
72+
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
73+
}
7074
}
7175

7276
ctx.infer_body();

crates/hir-ty/src/tests.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
1616
use expect_test::Expect;
1717
use hir_def::{
1818
body::{Body, BodySourceMap, SyntheticSyntax},
19-
db::DefDatabase,
19+
db::{DefDatabase, InternDatabase},
2020
expr::{ExprId, PatId},
2121
item_scope::ItemScope,
2222
nameres::DefMap,
@@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
135135
let loc = it.lookup(&db);
136136
loc.source(&db).value.syntax().text_range().start()
137137
}
138+
DefWithBodyId::VariantId(it) => {
139+
let loc = db.lookup_intern_enum(it.parent);
140+
loc.source(&db).value.syntax().text_range().start()
141+
}
138142
});
139143
let mut unexpected_type_mismatches = String::new();
140144
for def in defs {
@@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
388392
let loc = it.lookup(&db);
389393
loc.source(&db).value.syntax().text_range().start()
390394
}
395+
DefWithBodyId::VariantId(it) => {
396+
let loc = db.lookup_intern_enum(it.parent);
397+
loc.source(&db).value.syntax().text_range().start()
398+
}
391399
});
392400
for def in defs {
393401
let (_body, source_map) = db.body_with_source_map(def);

0 commit comments

Comments
 (0)