Implemented basic enum const eval

This commit is contained in:
OleStrohm 2022-08-06 18:50:21 +02:00
parent f64c95600c
commit 997fc46efa
14 changed files with 227 additions and 17 deletions

View File

@ -27,7 +27,7 @@
macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
src::HasSource,
src::{HasChildSource, HasSource},
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
UnresolvedMacro,
};
@ -324,6 +324,13 @@ pub(crate) fn body_with_source_map_query(
let src = s.source(db);
(src.file_id, s.module(db), src.value.body())
}
DefWithBodyId::VariantId(v) => {
let e = v.parent.lookup(db);
let src = v.parent.child_source(db);
let variant = &src.value[v.local_id];
// TODO(ole): Handle missing exprs (+1 to the prev)
(src.file_id, e.container, variant.expr())
}
};
let expander = Expander::new(db, file_id, module);
let (mut body, source_map) = Body::new(db, expander, params, body);

View File

@ -474,16 +474,25 @@ pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
VariantId(EnumVariantId),
}
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
// FIXME: Rename EnumVariantId to VariantId so that the macro above can be used
impl From<EnumVariantId> for DefWithBodyId {
fn from(id: EnumVariantId) -> Self {
DefWithBodyId::VariantId(id)
}
}
impl DefWithBodyId {
pub fn as_generic_def_id(self) -> Option<GenericDefId> {
match self {
DefWithBodyId::FunctionId(f) => Some(f.into()),
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
}
}
}
@ -681,6 +690,7 @@ fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
}
}
}
@ -691,6 +701,7 @@ pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
}
}
}

View File

@ -839,6 +839,7 @@ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
DefWithBodyId::ConstId(c) => c.resolver(db),
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
}
}
}

View File

@ -11,7 +11,7 @@
path::ModPath,
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
type_ref::ConstScalar,
ConstId, DefWithBodyId,
ConstId, DefWithBodyId, EnumVariantId,
};
use la_arena::{Arena, Idx};
use stdx::never;
@ -339,6 +339,7 @@ pub fn eval_const(
ValueNs::GenericParam(_) => {
Err(ConstEvalError::NotSupported("const generic without substitution"))
}
ValueNs::EnumVariantId(id) => ctx.db.const_eval_variant(id), // TODO(ole): Assuming this is all that has to happen?
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
}
}
@ -412,6 +413,14 @@ pub(crate) fn const_eval_recover(
Err(ConstEvalError::Loop)
}
pub(crate) fn const_eval_recover_variant(
_: &dyn HirDatabase,
_: &[String],
_: &EnumVariantId,
) -> Result<ComputedExpr, ConstEvalError> {
Err(ConstEvalError::Loop)
}
pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
const_id: ConstId,
@ -433,6 +442,26 @@ pub(crate) fn const_eval_query(
result
}
pub(crate) fn const_eval_query_variant(
db: &dyn HirDatabase,
variant_id: EnumVariantId,
) -> Result<ComputedExpr, ConstEvalError> {
let def = variant_id.into();
let body = db.body(def);
let infer = &db.infer(def);
eval_const(
body.body_expr,
&mut ConstEvalCtx {
db,
owner: def,
exprs: &body.exprs,
pats: &body.pats,
local_data: HashMap::default(),
infer,
},
)
}
pub(crate) fn eval_to_const<'a>(
expr: Idx<Expr>,
mode: ParamLoweringMode,

View File

@ -6,8 +6,9 @@
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, Lookup, TypeOrConstParamId,
VariantId,
};
use la_arena::ArenaMap;
@ -47,6 +48,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_query_variant)]
#[salsa::cycle(crate::consteval::const_eval_recover_variant)]
fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
#[salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
@ -188,6 +193,13 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
DefWithBodyId::ConstId(it) => {
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
}
DefWithBodyId::VariantId(it) => {
let up_db: &dyn DefDatabase = db.upcast();
let loc = it.parent.lookup(up_db);
let item_tree = loc.id.item_tree(up_db);
let konst = &item_tree[loc.id.value];
konst.name.to_string()
}
});
db.infer_query(def)
}

View File

@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
false
}
};
if is_unsafe {
return res;

View File

@ -67,6 +67,14 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
DefWithBodyId::VariantId(v) => {
//let def = AttrDefId::EnumVariantId(v);
//let attrs = db.attrs(def);
//let repr = attrs.by_key("repr").attrs().next().unwrap();
//let ident = repr.single_ident_value().unwrap().text;
// TODO(ole): Get the real type
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
}
}
ctx.infer_body();

View File

@ -16,7 +16,7 @@
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
db::DefDatabase,
db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId},
item_scope::ItemScope,
nameres::DefMap,
@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
DefWithBodyId::VariantId(it) => {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
DefWithBodyId::VariantId(it) => {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
});
for def in defs {
let (_body, source_map) = db.body_with_source_map(def);

View File

@ -140,6 +140,7 @@ fn from(def: DefWithBody) -> Self {
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
}
}
}
@ -150,6 +151,7 @@ fn from(def: DefWithBodyId) -> Self {
DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()),
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
}
}
}
@ -172,9 +174,7 @@ fn from(def: GenericDef) -> Self {
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
GenericDef::Variant(it) => {
GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id })
}
GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
}
}
@ -188,9 +188,7 @@ fn from(def: GenericDefId) -> Self {
GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
GenericDefId::EnumVariantId(it) => {
GenericDef::Variant(Variant { parent: it.parent.into(), id: it.local_id })
}
GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
}
}

View File

@ -73,7 +73,7 @@
use rustc_hash::FxHashSet;
use stdx::{impl_from, never};
use syntax::{
ast::{self, HasAttrs as _, HasDocComments, HasName},
ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
};
@ -962,11 +962,16 @@ fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Variant {
pub(crate) parent: Enum,
pub(crate) id: LocalEnumVariantId,
pub parent: Enum,
pub id: LocalEnumVariantId,
}
impl Variant {
pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> {
// TODO(ole): Handle missing exprs (+1 to the prev)
self.source(db)?.value.expr()
}
pub fn module(self, db: &dyn HirDatabase) -> Module {
self.parent.module(db)
}
@ -1129,6 +1134,7 @@ pub enum DefWithBody {
Function(Function),
Static(Static),
Const(Const),
Variant(Variant),
}
impl_from!(Function, Const, Static for DefWithBody);
@ -1138,6 +1144,7 @@ pub fn module(self, db: &dyn HirDatabase) -> Module {
DefWithBody::Const(c) => c.module(db),
DefWithBody::Function(f) => f.module(db),
DefWithBody::Static(s) => s.module(db),
DefWithBody::Variant(v) => v.module(db),
}
}
@ -1146,6 +1153,7 @@ pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
DefWithBody::Function(f) => Some(f.name(db)),
DefWithBody::Static(s) => Some(s.name(db)),
DefWithBody::Const(c) => c.name(db),
DefWithBody::Variant(v) => Some(v.name(db)),
}
}
@ -1155,6 +1163,7 @@ pub fn body_type(self, db: &dyn HirDatabase) -> Type {
DefWithBody::Function(it) => it.ret_type(db),
DefWithBody::Static(it) => it.ty(db),
DefWithBody::Const(it) => it.ty(db),
DefWithBody::Variant(it) => it.parent.ty(db),
}
}
@ -1379,6 +1388,7 @@ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
DefWithBody::Function(it) => it.into(),
DefWithBody::Static(it) => it.into(),
DefWithBody::Const(it) => it.into(),
DefWithBody::Variant(it) => it.into(),
};
for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
acc.push(diag.into())

View File

@ -1,6 +1,7 @@
//! File symbol extraction.
use base_db::FileRange;
use hir_def::db::DefDatabase;
use hir_def::{
item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId,
HasModule, ImplId, ItemContainerId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId,
@ -244,6 +245,10 @@ fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
DefWithBodyId::ConstId(id) => Some(
id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
),
DefWithBodyId::VariantId(id) => Some({
let up_db: &dyn DefDatabase = self.db.upcast();
up_db.lookup_intern_enum(id.parent).source(up_db).value.name()?.text().into()
}),
}
}

View File

@ -236,6 +236,7 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
};
return match def {
Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),

View File

@ -2,7 +2,9 @@
use std::fmt::Display;
use either::Either;
use hir::{AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo};
use hir::{
db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
@ -346,7 +348,14 @@ pub(super) fn definition(
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
Definition::Adt(it) => label_and_docs(db, it),
Definition::Variant(it) => label_and_docs(db, it),
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
let hir_db: &dyn HirDatabase = db;
let body = hir_db.const_eval_variant(it.into());
match body {
Ok(x) => Some(format!("{}", x)),
Err(_) => it.value(db).map(|s| format!("{}", s)),
}
}),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.eval(db);
match body {

View File

@ -3527,6 +3527,86 @@ impl<const LEN: usize> Foo<LEN$0> {}
);
}
#[test]
fn hover_const_eval_variant() {
// show hex for <10
check(
r#"
#[repr(u8)]
enum E {
/// This is a doc
A$0 = 1 << 3,
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 8
```
---
This is a doc
"#]],
);
// show hex for >10
check(
r#"
#[repr(u8)]
enum E {
/// This is a doc
A$0 = (1 << 3) + (1 << 2),
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 12 (0xC)
```
---
This is a doc
"#]],
);
// enums in const eval
check(
r#"
#[repr(u8)]
enum E {
A = 1,
/// This is a doc
B$0 = E::A + 1,
}
"#,
expect![[r#"
*B*
```rust
test::E
```
```rust
B = 2
```
---
This is a doc
"#]],
);
}
#[test]
fn hover_const_eval() {
// show hex for <10
@ -3820,6 +3900,35 @@ fn foo() {
---
This is a doc
"#]],
);
check(
r#"
enum E {
/// This is a doc
A = 3,
}
fn foo(e: E) {
match e {
E::A$0 => (),
_ => ()
}
}
"#,
expect![[r#"
*A*
```rust
test::E
```
```rust
A = 3
```
---
This is a doc
"#]],
);