mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
Allow static initializers to be const evaluated
This commit is contained in:
parent
d1fbfc676b
commit
94c35f6138
7 changed files with 87 additions and 28 deletions
|
@ -837,16 +837,18 @@ impl InTypeConstId {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum GeneralConstId {
|
pub enum GeneralConstId {
|
||||||
ConstId(ConstId),
|
ConstId(ConstId),
|
||||||
|
StaticId(StaticId),
|
||||||
ConstBlockId(ConstBlockId),
|
ConstBlockId(ConstBlockId),
|
||||||
InTypeConstId(InTypeConstId),
|
InTypeConstId(InTypeConstId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId);
|
impl_from!(ConstId, StaticId, ConstBlockId, InTypeConstId for GeneralConstId);
|
||||||
|
|
||||||
impl GeneralConstId {
|
impl GeneralConstId {
|
||||||
pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
|
||||||
match self {
|
match self {
|
||||||
GeneralConstId::ConstId(it) => Some(it.into()),
|
GeneralConstId::ConstId(it) => Some(it.into()),
|
||||||
|
GeneralConstId::StaticId(_) => None,
|
||||||
GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
|
GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db),
|
||||||
GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
|
GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db),
|
||||||
}
|
}
|
||||||
|
@ -854,6 +856,9 @@ impl GeneralConstId {
|
||||||
|
|
||||||
pub fn name(self, db: &dyn DefDatabase) -> String {
|
pub fn name(self, db: &dyn DefDatabase) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
GeneralConstId::StaticId(it) => {
|
||||||
|
db.static_data(it).name.display(db.upcast(), Edition::CURRENT).to_string()
|
||||||
|
}
|
||||||
GeneralConstId::ConstId(const_id) => db
|
GeneralConstId::ConstId(const_id) => db
|
||||||
.const_data(const_id)
|
.const_data(const_id)
|
||||||
.name
|
.name
|
||||||
|
|
|
@ -8,7 +8,7 @@ use hir_def::{
|
||||||
path::Path,
|
path::Path,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{Resolver, ValueNs},
|
||||||
type_ref::LiteralConstRef,
|
type_ref::LiteralConstRef,
|
||||||
ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
|
ConstBlockLoc, EnumVariantId, GeneralConstId, HasModule as _, StaticId,
|
||||||
};
|
};
|
||||||
use hir_expand::Lookup;
|
use hir_expand::Lookup;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
@ -236,6 +236,10 @@ pub(crate) fn const_eval_query(
|
||||||
GeneralConstId::ConstId(c) => {
|
GeneralConstId::ConstId(c) => {
|
||||||
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
|
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
|
||||||
}
|
}
|
||||||
|
GeneralConstId::StaticId(s) => {
|
||||||
|
let krate = s.module(db.upcast()).krate();
|
||||||
|
db.monomorphized_mir_body(s.into(), subst, TraitEnvironment::empty(krate))?
|
||||||
|
}
|
||||||
GeneralConstId::ConstBlockId(c) => {
|
GeneralConstId::ConstBlockId(c) => {
|
||||||
let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c);
|
let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c);
|
||||||
let body = db.body(parent);
|
let body = db.body(parent);
|
||||||
|
|
|
@ -12,8 +12,8 @@ use hir_def::{
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
layout::{TagEncoding, Variants},
|
layout::{TagEncoding, Variants},
|
||||||
resolver::{HasResolver, TypeNs, ValueNs},
|
resolver::{HasResolver, TypeNs, ValueNs},
|
||||||
AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup,
|
AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
|
||||||
StaticId, VariantId,
|
VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{mod_path::path, name::Name, HirFileIdExt, InFile};
|
use hir_expand::{mod_path::path, name::Name, HirFileIdExt, InFile};
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
|
@ -40,8 +40,8 @@ use crate::{
|
||||||
static_lifetime,
|
static_lifetime,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
utils::{detect_variant_from_bytes, ClosureSubst},
|
utils::{detect_variant_from_bytes, ClosureSubst},
|
||||||
CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstScalar, FnDefId, Interner, MemoryMap,
|
CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner,
|
||||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
MemoryMap, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -1899,8 +1899,8 @@ impl Evaluator<'_> {
|
||||||
|
|
||||||
#[allow(clippy::double_parens)]
|
#[allow(clippy::double_parens)]
|
||||||
fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<Interval> {
|
fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<Interval> {
|
||||||
let ty = &konst.data(Interner).ty;
|
let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner)
|
||||||
let chalk_ir::ConstValue::Concrete(c) = &konst.data(Interner).value else {
|
else {
|
||||||
not_supported!("evaluating non concrete constant");
|
not_supported!("evaluating non concrete constant");
|
||||||
};
|
};
|
||||||
let result_owner;
|
let result_owner;
|
||||||
|
@ -2908,14 +2908,14 @@ impl Evaluator<'_> {
|
||||||
|
|
||||||
pub fn render_const_using_debug_impl(
|
pub fn render_const_using_debug_impl(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
owner: ConstId,
|
owner: DefWithBodyId,
|
||||||
c: &Const,
|
c: &Const,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let mut evaluator = Evaluator::new(db, owner.into(), false, None)?;
|
let mut evaluator = Evaluator::new(db, owner, false, None)?;
|
||||||
let locals = &Locals {
|
let locals = &Locals {
|
||||||
ptr: ArenaMap::new(),
|
ptr: ArenaMap::new(),
|
||||||
body: db
|
body: db
|
||||||
.mir_body(owner.into())
|
.mir_body(owner)
|
||||||
.map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
|
.map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
|
||||||
drop_flags: DropFlags::default(),
|
drop_flags: DropFlags::default(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -2600,7 +2600,7 @@ impl Const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) {
|
if let Ok(s) = mir::render_const_using_debug_impl(db, self.id.into(), &c) {
|
||||||
Ok(s)
|
Ok(s)
|
||||||
} else {
|
} else {
|
||||||
Ok(format!("{}", c.display(db, edition)))
|
Ok(format!("{}", c.display(db, edition)))
|
||||||
|
@ -2639,6 +2639,45 @@ impl Static {
|
||||||
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
pub fn ty(self, db: &dyn HirDatabase) -> Type {
|
||||||
Type::from_value_def(db, self.id)
|
Type::from_value_def(db, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate the constant and return the result as a string, with more detailed information.
|
||||||
|
///
|
||||||
|
/// This function is intended for user-facing display.
|
||||||
|
pub fn render_eval(
|
||||||
|
self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
edition: Edition,
|
||||||
|
) -> Result<String, ConstEvalError> {
|
||||||
|
let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?;
|
||||||
|
let data = &c.data(Interner);
|
||||||
|
if let TyKind::Scalar(s) = data.ty.kind(Interner) {
|
||||||
|
if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
|
||||||
|
if let hir_ty::ConstValue::Concrete(c) = &data.value {
|
||||||
|
if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned {
|
||||||
|
let value = u128::from_le_bytes(mir::pad16(b, false));
|
||||||
|
let value_signed =
|
||||||
|
i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_))));
|
||||||
|
let mut result = if let Scalar::Int(_) = s {
|
||||||
|
value_signed.to_string()
|
||||||
|
} else {
|
||||||
|
value.to_string()
|
||||||
|
};
|
||||||
|
if value >= 10 {
|
||||||
|
format_to!(result, " ({value:#X})");
|
||||||
|
return Ok(result);
|
||||||
|
} else {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(s) = mir::render_const_using_debug_impl(db, self.id.into(), &c) {
|
||||||
|
Ok(s)
|
||||||
|
} else {
|
||||||
|
Ok(format!("{}", c.display(db, edition)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for Static {
|
impl HasVisibility for Static {
|
||||||
|
|
|
@ -486,13 +486,19 @@ pub(super) fn definition(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Definition::Static(it) => {
|
Definition::Static(it) => {
|
||||||
let source = it.source(db)?;
|
let body = it.render_eval(db, edition);
|
||||||
let mut body = source.value.body()?.syntax().clone();
|
match body {
|
||||||
if let Some(macro_file) = source.file_id.macro_file() {
|
Ok(it) => Some(it),
|
||||||
let span_map = db.expansion_span_map(macro_file);
|
Err(_) => {
|
||||||
body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
|
let source = it.source(db)?;
|
||||||
|
let mut body = source.value.body()?.syntax().clone();
|
||||||
|
if let Some(macro_file) = source.file_id.macro_file() {
|
||||||
|
let span_map = db.expansion_span_map(macro_file);
|
||||||
|
body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
|
||||||
|
}
|
||||||
|
Some(body.to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(body.to_string())
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1544,7 +1544,7 @@ const foo$0: u32 = {
|
||||||
```
|
```
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
static foo: u32 = 456
|
static foo: u32 = 456 (0x1C8)
|
||||||
```
|
```
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
|
@ -172,7 +172,6 @@ impl flags::AnalysisStats {
|
||||||
let mut num_decls = 0;
|
let mut num_decls = 0;
|
||||||
let mut bodies = Vec::new();
|
let mut bodies = Vec::new();
|
||||||
let mut adts = Vec::new();
|
let mut adts = Vec::new();
|
||||||
let mut consts = Vec::new();
|
|
||||||
let mut file_ids = Vec::new();
|
let mut file_ids = Vec::new();
|
||||||
while let Some(module) = visit_queue.pop() {
|
while let Some(module) = visit_queue.pop() {
|
||||||
if visited_modules.insert(module) {
|
if visited_modules.insert(module) {
|
||||||
|
@ -193,7 +192,6 @@ impl flags::AnalysisStats {
|
||||||
}
|
}
|
||||||
ModuleDef::Const(c) => {
|
ModuleDef::Const(c) => {
|
||||||
bodies.push(DefWithBody::from(c));
|
bodies.push(DefWithBody::from(c));
|
||||||
consts.push(c)
|
|
||||||
}
|
}
|
||||||
ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
|
ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -207,7 +205,6 @@ impl flags::AnalysisStats {
|
||||||
AssocItem::Function(f) => bodies.push(DefWithBody::from(f)),
|
AssocItem::Function(f) => bodies.push(DefWithBody::from(f)),
|
||||||
AssocItem::Const(c) => {
|
AssocItem::Const(c) => {
|
||||||
bodies.push(DefWithBody::from(c));
|
bodies.push(DefWithBody::from(c));
|
||||||
consts.push(c);
|
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -220,7 +217,10 @@ impl flags::AnalysisStats {
|
||||||
visited_modules.len(),
|
visited_modules.len(),
|
||||||
bodies.len(),
|
bodies.len(),
|
||||||
adts.len(),
|
adts.len(),
|
||||||
consts.len(),
|
bodies
|
||||||
|
.iter()
|
||||||
|
.filter(|it| matches!(it, DefWithBody::Const(_) | DefWithBody::Static(_)))
|
||||||
|
.count(),
|
||||||
);
|
);
|
||||||
let crate_def_map_time = crate_def_map_sw.elapsed();
|
let crate_def_map_time = crate_def_map_sw.elapsed();
|
||||||
eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time);
|
eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time);
|
||||||
|
@ -247,7 +247,7 @@ impl flags::AnalysisStats {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.skip_const_eval {
|
if !self.skip_const_eval {
|
||||||
self.run_const_eval(db, &consts, verbosity);
|
self.run_const_eval(db, &bodies, verbosity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.run_all_ide_things {
|
if self.run_all_ide_things {
|
||||||
|
@ -320,18 +320,23 @@ impl flags::AnalysisStats {
|
||||||
report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms");
|
report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) {
|
fn run_const_eval(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
|
||||||
let mut sw = self.stop_watch();
|
let mut sw = self.stop_watch();
|
||||||
let mut all = 0;
|
let mut all = 0;
|
||||||
let mut fail = 0;
|
let mut fail = 0;
|
||||||
for &c in consts {
|
for &b in bodies {
|
||||||
|
let res = match b {
|
||||||
|
DefWithBody::Const(c) => c.render_eval(db, Edition::LATEST),
|
||||||
|
DefWithBody::Static(s) => s.render_eval(db, Edition::LATEST),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
all += 1;
|
all += 1;
|
||||||
let Err(error) = c.render_eval(db, Edition::LATEST) else {
|
let Err(error) = res else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if verbosity.is_spammy() {
|
if verbosity.is_spammy() {
|
||||||
let full_name =
|
let full_name =
|
||||||
full_name_of_item(db, c.module(db), c.name(db).unwrap_or(Name::missing()));
|
full_name_of_item(db, b.module(db), b.name(db).unwrap_or(Name::missing()));
|
||||||
println!("Const eval for {full_name} failed due {error:?}");
|
println!("Const eval for {full_name} failed due {error:?}");
|
||||||
}
|
}
|
||||||
fail += 1;
|
fail += 1;
|
||||||
|
|
Loading…
Reference in a new issue