Auto merge of #14947 - HKalbasi:render-const, r=HKalbasi
Add enum, reference, array and slice to `render_const_scalar`
This commit is contained in:
commit
0677c204ca
@ -17,21 +17,26 @@ use hir_def::{
|
|||||||
path::{Path, PathKind},
|
path::{Path, PathKind},
|
||||||
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
|
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
|
||||||
visibility::Visibility,
|
visibility::Visibility,
|
||||||
HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
|
EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId,
|
||||||
|
TraitId,
|
||||||
};
|
};
|
||||||
use hir_expand::{hygiene::Hygiene, name::Name};
|
use hir_expand::{hygiene::Hygiene, name::Name};
|
||||||
use intern::{Internable, Interned};
|
use intern::{Internable, Interned};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use la_arena::ArenaMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
consteval::try_const_usize,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
|
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
|
||||||
|
layout::Layout,
|
||||||
|
lt_from_placeholder_idx,
|
||||||
mapping::from_chalk,
|
mapping::from_chalk,
|
||||||
mir::pad16,
|
mir::pad16,
|
||||||
primitive, to_assoc_type_id,
|
primitive, to_assoc_type_id,
|
||||||
utils::{self, generics, ClosureSubst},
|
utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
|
||||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
||||||
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
|
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
|
||||||
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
|
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
|
||||||
@ -470,7 +475,7 @@ fn render_const_scalar(
|
|||||||
// infrastructure and have it here as a field on `f`.
|
// infrastructure and have it here as a field on `f`.
|
||||||
let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
|
let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
|
||||||
match ty.kind(Interner) {
|
match ty.kind(Interner) {
|
||||||
chalk_ir::TyKind::Scalar(s) => match s {
|
TyKind::Scalar(s) => match s {
|
||||||
Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
|
Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
|
||||||
Scalar::Char => {
|
Scalar::Char => {
|
||||||
let x = u128::from_le_bytes(pad16(b, false)) as u32;
|
let x = u128::from_le_bytes(pad16(b, false)) as u32;
|
||||||
@ -498,17 +503,54 @@ fn render_const_scalar(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
|
TyKind::Ref(_, _, t) => match t.kind(Interner) {
|
||||||
chalk_ir::TyKind::Str => {
|
TyKind::Str => {
|
||||||
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
||||||
let bytes = memory_map.memory.get(&addr).map(|x| &**x).unwrap_or(&[]);
|
let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
|
||||||
let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
|
let Some(bytes) = memory_map.get(addr, size) else {
|
||||||
|
return f.write_str("<ref-data-not-available>");
|
||||||
|
};
|
||||||
|
let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");
|
||||||
write!(f, "{s:?}")
|
write!(f, "{s:?}")
|
||||||
}
|
}
|
||||||
_ => f.write_str("<ref-not-supported>"),
|
TyKind::Slice(ty) => {
|
||||||
|
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
||||||
|
let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
|
||||||
|
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
|
||||||
|
return f.write_str("<layout-error>");
|
||||||
|
};
|
||||||
|
let size_one = layout.size.bytes_usize();
|
||||||
|
let Some(bytes) = memory_map.get(addr, size_one * count) else {
|
||||||
|
return f.write_str("<ref-data-not-available>");
|
||||||
|
};
|
||||||
|
f.write_str("&[")?;
|
||||||
|
let mut first = true;
|
||||||
|
for i in 0..count {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
f.write_str(", ")?;
|
||||||
|
}
|
||||||
|
let offset = size_one * i;
|
||||||
|
render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?;
|
||||||
|
}
|
||||||
|
f.write_str("]")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let addr = usize::from_le_bytes(b.try_into().unwrap());
|
||||||
|
let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
|
||||||
|
return f.write_str("<layout-error>");
|
||||||
|
};
|
||||||
|
let size = layout.size.bytes_usize();
|
||||||
|
let Some(bytes) = memory_map.get(addr, size) else {
|
||||||
|
return f.write_str("<ref-data-not-available>");
|
||||||
|
};
|
||||||
|
f.write_str("&")?;
|
||||||
|
render_const_scalar(f, bytes, memory_map, t)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
chalk_ir::TyKind::Tuple(_, subst) => {
|
TyKind::Tuple(_, subst) => {
|
||||||
let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else {
|
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
|
||||||
return f.write_str("<layout-error>");
|
return f.write_str("<layout-error>");
|
||||||
};
|
};
|
||||||
f.write_str("(")?;
|
f.write_str("(")?;
|
||||||
@ -530,69 +572,144 @@ fn render_const_scalar(
|
|||||||
}
|
}
|
||||||
f.write_str(")")
|
f.write_str(")")
|
||||||
}
|
}
|
||||||
chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
|
TyKind::Adt(adt, subst) => {
|
||||||
hir_def::AdtId::StructId(s) => {
|
let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else {
|
||||||
let data = f.db.struct_data(s);
|
return f.write_str("<layout-error>");
|
||||||
let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else {
|
};
|
||||||
return f.write_str("<layout-error>");
|
match adt.0 {
|
||||||
};
|
hir_def::AdtId::StructId(s) => {
|
||||||
match data.variant_data.as_ref() {
|
let data = f.db.struct_data(s);
|
||||||
VariantData::Record(fields) | VariantData::Tuple(fields) => {
|
write!(f, "{}", data.name.display(f.db.upcast()))?;
|
||||||
let field_types = f.db.field_types(s.into());
|
let field_types = f.db.field_types(s.into());
|
||||||
let krate = adt.0.module(f.db.upcast()).krate();
|
render_variant_after_name(
|
||||||
let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
|
&data.variant_data,
|
||||||
let offset = layout
|
f,
|
||||||
.fields
|
&field_types,
|
||||||
.offset(u32::from(id.into_raw()) as usize)
|
adt.0.module(f.db.upcast()).krate(),
|
||||||
.bytes_usize();
|
&layout,
|
||||||
let ty = field_types[id].clone().substitute(Interner, subst);
|
subst,
|
||||||
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
|
b,
|
||||||
return f.write_str("<layout-error>");
|
memory_map,
|
||||||
};
|
)
|
||||||
let size = layout.size.bytes_usize();
|
}
|
||||||
render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
|
hir_def::AdtId::UnionId(u) => {
|
||||||
};
|
write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
|
||||||
let mut it = fields.iter();
|
}
|
||||||
if matches!(data.variant_data.as_ref(), VariantData::Record(_)) {
|
hir_def::AdtId::EnumId(e) => {
|
||||||
write!(f, "{} {{", data.name.display(f.db.upcast()))?;
|
let Some((var_id, var_layout)) =
|
||||||
if let Some((id, data)) = it.next() {
|
detect_variant_from_bytes(&layout, f.db, krate, b, e) else {
|
||||||
write!(f, " {}: ", data.name.display(f.db.upcast()))?;
|
return f.write_str("<failed-to-detect-variant>");
|
||||||
render_field(f, id)?;
|
};
|
||||||
}
|
let data = &f.db.enum_data(e).variants[var_id];
|
||||||
for (id, data) in it {
|
write!(f, "{}", data.name.display(f.db.upcast()))?;
|
||||||
write!(f, ", {}: ", data.name.display(f.db.upcast()))?;
|
let field_types =
|
||||||
render_field(f, id)?;
|
f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into());
|
||||||
}
|
render_variant_after_name(
|
||||||
write!(f, " }}")?;
|
&data.variant_data,
|
||||||
} else {
|
f,
|
||||||
let mut it = it.map(|x| x.0);
|
&field_types,
|
||||||
write!(f, "{}(", data.name.display(f.db.upcast()))?;
|
adt.0.module(f.db.upcast()).krate(),
|
||||||
if let Some(id) = it.next() {
|
&var_layout,
|
||||||
render_field(f, id)?;
|
subst,
|
||||||
}
|
b,
|
||||||
for id in it {
|
memory_map,
|
||||||
write!(f, ", ")?;
|
)
|
||||||
render_field(f, id)?;
|
|
||||||
}
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
VariantData::Unit => write!(f, "{}", data.name.display(f.db.upcast())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir_def::AdtId::UnionId(u) => {
|
}
|
||||||
write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
|
TyKind::FnDef(..) => ty.hir_fmt(f),
|
||||||
}
|
TyKind::Function(_) | TyKind::Raw(_, _) => {
|
||||||
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
|
|
||||||
},
|
|
||||||
chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
|
|
||||||
chalk_ir::TyKind::Raw(_, _) => {
|
|
||||||
let x = u128::from_le_bytes(pad16(b, false));
|
let x = u128::from_le_bytes(pad16(b, false));
|
||||||
write!(f, "{:#X} as ", x)?;
|
write!(f, "{:#X} as ", x)?;
|
||||||
ty.hir_fmt(f)
|
ty.hir_fmt(f)
|
||||||
}
|
}
|
||||||
_ => f.write_str("<not-supported>"),
|
TyKind::Array(ty, len) => {
|
||||||
|
let Some(len) = try_const_usize(f.db, len) else {
|
||||||
|
return f.write_str("<unknown-array-len>");
|
||||||
|
};
|
||||||
|
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
|
||||||
|
return f.write_str("<layout-error>");
|
||||||
|
};
|
||||||
|
let size_one = layout.size.bytes_usize();
|
||||||
|
f.write_str("[")?;
|
||||||
|
let mut first = true;
|
||||||
|
for i in 0..len as usize {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
f.write_str(", ")?;
|
||||||
|
}
|
||||||
|
let offset = size_one * i;
|
||||||
|
render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?;
|
||||||
|
}
|
||||||
|
f.write_str("]")
|
||||||
|
}
|
||||||
|
TyKind::Never => f.write_str("!"),
|
||||||
|
TyKind::Closure(_, _) => f.write_str("<closure>"),
|
||||||
|
TyKind::Generator(_, _) => f.write_str("<generator>"),
|
||||||
|
TyKind::GeneratorWitness(_, _) => f.write_str("<generator-witness>"),
|
||||||
|
// The below arms are unreachable, since const eval will bail out before here.
|
||||||
|
TyKind::Foreign(_) => f.write_str("<extern-type>"),
|
||||||
|
TyKind::Error
|
||||||
|
| TyKind::Placeholder(_)
|
||||||
|
| TyKind::Alias(_)
|
||||||
|
| TyKind::AssociatedType(_, _)
|
||||||
|
| TyKind::OpaqueType(_, _)
|
||||||
|
| TyKind::BoundVar(_)
|
||||||
|
| TyKind::InferenceVar(_, _) => f.write_str("<placeholder-or-unknown-type>"),
|
||||||
|
// The below arms are unreachable, since we handled them in ref case.
|
||||||
|
TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str("<unsized-value>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_variant_after_name(
|
||||||
|
data: &VariantData,
|
||||||
|
f: &mut HirFormatter<'_>,
|
||||||
|
field_types: &ArenaMap<LocalFieldId, Binders<Ty>>,
|
||||||
|
krate: CrateId,
|
||||||
|
layout: &Layout,
|
||||||
|
subst: &Substitution,
|
||||||
|
b: &[u8],
|
||||||
|
memory_map: &MemoryMap,
|
||||||
|
) -> Result<(), HirDisplayError> {
|
||||||
|
match data {
|
||||||
|
VariantData::Record(fields) | VariantData::Tuple(fields) => {
|
||||||
|
let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
|
||||||
|
let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();
|
||||||
|
let ty = field_types[id].clone().substitute(Interner, subst);
|
||||||
|
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
|
||||||
|
return f.write_str("<layout-error>");
|
||||||
|
};
|
||||||
|
let size = layout.size.bytes_usize();
|
||||||
|
render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
|
||||||
|
};
|
||||||
|
let mut it = fields.iter();
|
||||||
|
if matches!(data, VariantData::Record(_)) {
|
||||||
|
write!(f, " {{")?;
|
||||||
|
if let Some((id, data)) = it.next() {
|
||||||
|
write!(f, " {}: ", data.name.display(f.db.upcast()))?;
|
||||||
|
render_field(f, id)?;
|
||||||
|
}
|
||||||
|
for (id, data) in it {
|
||||||
|
write!(f, ", {}: ", data.name.display(f.db.upcast()))?;
|
||||||
|
render_field(f, id)?;
|
||||||
|
}
|
||||||
|
write!(f, " }}")?;
|
||||||
|
} else {
|
||||||
|
let mut it = it.map(|x| x.0);
|
||||||
|
write!(f, "(")?;
|
||||||
|
if let Some(id) = it.next() {
|
||||||
|
render_field(f, id)?;
|
||||||
|
}
|
||||||
|
for id in it {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
render_field(f, id)?;
|
||||||
|
}
|
||||||
|
write!(f, ")")?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
VariantData::Unit => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,10 @@ mod tests;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_db;
|
mod test_db;
|
||||||
|
|
||||||
use std::{collections::HashMap, hash::Hash};
|
use std::{
|
||||||
|
collections::{hash_map::Entry, HashMap},
|
||||||
|
hash::Hash,
|
||||||
|
};
|
||||||
|
|
||||||
use chalk_ir::{
|
use chalk_ir::{
|
||||||
fold::{Shift, TypeFoldable},
|
fold::{Shift, TypeFoldable},
|
||||||
@ -160,7 +163,16 @@ pub struct MemoryMap {
|
|||||||
|
|
||||||
impl MemoryMap {
|
impl MemoryMap {
|
||||||
fn insert(&mut self, addr: usize, x: Vec<u8>) {
|
fn insert(&mut self, addr: usize, x: Vec<u8>) {
|
||||||
self.memory.insert(addr, x);
|
match self.memory.entry(addr) {
|
||||||
|
Entry::Occupied(mut e) => {
|
||||||
|
if e.get().len() < x.len() {
|
||||||
|
e.insert(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
e.insert(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
|
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
|
||||||
@ -172,6 +184,14 @@ impl MemoryMap {
|
|||||||
) -> Result<HashMap<usize, usize>, MirEvalError> {
|
) -> Result<HashMap<usize, usize>, MirEvalError> {
|
||||||
self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
|
self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> {
|
||||||
|
if size == 0 {
|
||||||
|
Some(&[])
|
||||||
|
} else {
|
||||||
|
self.memory.get(&addr)?.get(0..size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A concrete constant value
|
/// A concrete constant value
|
||||||
|
@ -30,7 +30,7 @@ use crate::{
|
|||||||
method_resolution::{is_dyn_method, lookup_impl_method},
|
method_resolution::{is_dyn_method, lookup_impl_method},
|
||||||
name, static_lifetime,
|
name, static_lifetime,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
utils::ClosureSubst,
|
utils::{detect_variant_from_bytes, ClosureSubst},
|
||||||
CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
|
CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
|
||||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
@ -1536,36 +1536,99 @@ impl Evaluator<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
|
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
|
||||||
// FIXME: support indirect references
|
fn rec(
|
||||||
let mut mm = MemoryMap::default();
|
this: &Evaluator<'_>,
|
||||||
match ty.kind(Interner) {
|
bytes: &[u8],
|
||||||
TyKind::Ref(_, _, t) => {
|
ty: &Ty,
|
||||||
let size = self.size_align_of(t, locals)?;
|
locals: &Locals<'_>,
|
||||||
match size {
|
mm: &mut MemoryMap,
|
||||||
Some((size, _)) => {
|
) -> Result<()> {
|
||||||
let addr_usize = from_bytes!(usize, bytes);
|
match ty.kind(Interner) {
|
||||||
mm.insert(
|
TyKind::Ref(_, _, t) => {
|
||||||
addr_usize,
|
let size = this.size_align_of(t, locals)?;
|
||||||
self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
|
match size {
|
||||||
)
|
Some((size, _)) => {
|
||||||
}
|
let addr_usize = from_bytes!(usize, bytes);
|
||||||
None => {
|
mm.insert(
|
||||||
let element_size = match t.kind(Interner) {
|
addr_usize,
|
||||||
TyKind::Str => 1,
|
this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
|
||||||
TyKind::Slice(t) => {
|
)
|
||||||
self.size_of_sized(t, locals, "slice inner type")?
|
}
|
||||||
|
None => {
|
||||||
|
let mut check_inner = None;
|
||||||
|
let element_size = match t.kind(Interner) {
|
||||||
|
TyKind::Str => 1,
|
||||||
|
TyKind::Slice(t) => {
|
||||||
|
check_inner = Some(t);
|
||||||
|
this.size_of_sized(t, locals, "slice inner type")?
|
||||||
|
}
|
||||||
|
_ => return Ok(()), // FIXME: support other kind of unsized types
|
||||||
|
};
|
||||||
|
let (addr, meta) = bytes.split_at(bytes.len() / 2);
|
||||||
|
let count = from_bytes!(usize, meta);
|
||||||
|
let size = element_size * count;
|
||||||
|
let addr = Address::from_bytes(addr)?;
|
||||||
|
let b = this.read_memory(addr, size)?;
|
||||||
|
mm.insert(addr.to_usize(), b.to_vec());
|
||||||
|
if let Some(ty) = check_inner {
|
||||||
|
for i in 0..count {
|
||||||
|
let offset = element_size * i;
|
||||||
|
rec(this, &b[offset..offset + element_size], ty, locals, mm)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => return Ok(mm), // FIXME: support other kind of unsized types
|
}
|
||||||
};
|
|
||||||
let (addr, meta) = bytes.split_at(bytes.len() / 2);
|
|
||||||
let size = element_size * from_bytes!(usize, meta);
|
|
||||||
let addr = Address::from_bytes(addr)?;
|
|
||||||
mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chalk_ir::TyKind::Tuple(_, subst) => {
|
||||||
|
let layout = this.layout(ty)?;
|
||||||
|
for (id, ty) in subst.iter(Interner).enumerate() {
|
||||||
|
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
|
||||||
|
let offset = layout.fields.offset(id).bytes_usize();
|
||||||
|
let size = this.layout(ty)?.size.bytes_usize();
|
||||||
|
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
|
||||||
|
AdtId::StructId(s) => {
|
||||||
|
let data = this.db.struct_data(s);
|
||||||
|
let layout = this.layout(ty)?;
|
||||||
|
let field_types = this.db.field_types(s.into());
|
||||||
|
for (f, _) in data.variant_data.fields().iter() {
|
||||||
|
let offset = layout
|
||||||
|
.fields
|
||||||
|
.offset(u32::from(f.into_raw()) as usize)
|
||||||
|
.bytes_usize();
|
||||||
|
let ty = &field_types[f].clone().substitute(Interner, subst);
|
||||||
|
let size = this.layout(ty)?.size.bytes_usize();
|
||||||
|
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdtId::EnumId(e) => {
|
||||||
|
let layout = this.layout(ty)?;
|
||||||
|
if let Some((v, l)) =
|
||||||
|
detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e)
|
||||||
|
{
|
||||||
|
let data = &this.db.enum_data(e).variants[v].variant_data;
|
||||||
|
let field_types = this
|
||||||
|
.db
|
||||||
|
.field_types(EnumVariantId { parent: e, local_id: v }.into());
|
||||||
|
for (f, _) in data.fields().iter() {
|
||||||
|
let offset =
|
||||||
|
l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
|
||||||
|
let ty = &field_types[f].clone().substitute(Interner, subst);
|
||||||
|
let size = this.layout(ty)?.size.bytes_usize();
|
||||||
|
rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdtId::UnionId(_) => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
_ => (),
|
Ok(())
|
||||||
}
|
}
|
||||||
|
let mut mm = MemoryMap::default();
|
||||||
|
rec(self, bytes, ty, locals, &mut mm)?;
|
||||||
Ok(mm)
|
Ok(mm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1855,7 +1855,7 @@ pub fn lower_to_mir(
|
|||||||
}
|
}
|
||||||
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
|
let mut ctx = MirLowerCtx::new(db, owner, body, infer);
|
||||||
// 0 is return local
|
// 0 is return local
|
||||||
ctx.result.locals.alloc(Local { ty: infer[root_expr].clone() });
|
ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
|
||||||
let binding_picker = |b: BindingId| {
|
let binding_picker = |b: BindingId| {
|
||||||
if root_expr == body.body_expr {
|
if root_expr == body.body_expr {
|
||||||
body[b].owner.is_none()
|
body[b].owner.is_none()
|
||||||
|
@ -20,8 +20,8 @@ use hir_def::{
|
|||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
resolver::{HasResolver, TypeNs},
|
resolver::{HasResolver, TypeNs},
|
||||||
type_ref::{TraitBoundModifier, TypeRef},
|
type_ref::{TraitBoundModifier, TypeRef},
|
||||||
ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId,
|
ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId,
|
||||||
TypeOrConstParamId, TypeParamId,
|
LocalEnumVariantId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
@ -30,8 +30,12 @@ use smallvec::{smallvec, SmallVec};
|
|||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::unknown_const, db::HirDatabase, ChalkTraitId, Const, ConstScalar, GenericArg,
|
consteval::unknown_const,
|
||||||
Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt, WhereClause,
|
db::HirDatabase,
|
||||||
|
layout::{Layout, TagEncoding},
|
||||||
|
mir::pad16,
|
||||||
|
ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt,
|
||||||
|
Ty, TyExt, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn fn_traits(
|
pub(crate) fn fn_traits(
|
||||||
@ -440,3 +444,41 @@ impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
|
|||||||
Ok(constant)
|
Ok(constant)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn detect_variant_from_bytes<'a>(
|
||||||
|
layout: &'a Layout,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
b: &[u8],
|
||||||
|
e: EnumId,
|
||||||
|
) -> Option<(LocalEnumVariantId, &'a Layout)> {
|
||||||
|
let (var_id, var_layout) = match &layout.variants {
|
||||||
|
hir_def::layout::Variants::Single { index } => (index.0, &*layout),
|
||||||
|
hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
|
||||||
|
let target_data_layout = db.target_data_layout(krate)?;
|
||||||
|
let size = tag.size(&*target_data_layout).bytes_usize();
|
||||||
|
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
|
||||||
|
let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
|
||||||
|
match tag_encoding {
|
||||||
|
TagEncoding::Direct => {
|
||||||
|
let x = variants.iter_enumerated().find(|x| {
|
||||||
|
db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 })
|
||||||
|
== Ok(tag)
|
||||||
|
})?;
|
||||||
|
(x.0 .0, x.1)
|
||||||
|
}
|
||||||
|
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
|
||||||
|
let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
|
||||||
|
let variant = variants
|
||||||
|
.iter_enumerated()
|
||||||
|
.map(|(x, _)| x)
|
||||||
|
.filter(|x| x != untagged_variant)
|
||||||
|
.nth(candidate_tag)
|
||||||
|
.unwrap_or(*untagged_variant);
|
||||||
|
(variant.0, &variants[variant])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some((var_id, var_layout))
|
||||||
|
}
|
||||||
|
@ -2106,14 +2106,6 @@ impl Const {
|
|||||||
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
||||||
let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
|
let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
|
||||||
let r = format!("{}", HexifiedConst(c).display(db));
|
let r = format!("{}", HexifiedConst(c).display(db));
|
||||||
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
|
||||||
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
|
||||||
// the user.
|
|
||||||
if r.contains("not-supported>") {
|
|
||||||
return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
|
|
||||||
"rendering complex constants".to_string(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
return Ok(r);
|
return Ok(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4337,6 +4337,211 @@ const FOO$0: f64 = 1.0f64;
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_const_eval_enum() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
enum Enum {
|
||||||
|
V1,
|
||||||
|
V2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const VX: Enum = Enum::V1;
|
||||||
|
|
||||||
|
const FOO$0: Enum = VX;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: Enum = V1
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
const FOO$0: Option<i32> = Some(2);
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: Option<i32> = Some(2)
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
const FOO$0: Option<&i32> = Some(2).as_ref();
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: Option<&i32> = Some(&2)
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_const_eval_slice() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: slice, index, coerce_unsized
|
||||||
|
const FOO$0: &[i32] = &[1, 2, 3 + 4];
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: &[i32] = &[1, 2, 7]
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: slice, index, coerce_unsized
|
||||||
|
const FOO$0: &[i32; 5] = &[12; 5];
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: &[i32; 5] = &[12, 12, 12, 12, 12]
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: slice, index, coerce_unsized
|
||||||
|
|
||||||
|
const FOO$0: (&i32, &[i32], &i32) = {
|
||||||
|
let a: &[i32] = &[1, 2, 3];
|
||||||
|
(&a[0], a, &a[0])
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1)
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: slice, index, coerce_unsized
|
||||||
|
|
||||||
|
struct Tree(&[Tree]);
|
||||||
|
|
||||||
|
const FOO$0: Tree = {
|
||||||
|
let x = &[Tree(&[]), Tree(&[Tree(&[])])];
|
||||||
|
Tree(&[Tree(x), Tree(x)])
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])])
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_const_eval_str() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
const FOO$0: &str = "foo";
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: &str = "foo"
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct X {
|
||||||
|
a: &'static str,
|
||||||
|
b: &'static str,
|
||||||
|
}
|
||||||
|
const FOO$0: X = X {
|
||||||
|
a: "axiom",
|
||||||
|
b: "buy N large",
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: X = X { a: "axiom", b: "buy N large" }
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
const FOO$0: (&str, &str) = {
|
||||||
|
let x = "foo";
|
||||||
|
(x, x)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*FOO*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const FOO: (&str, &str) = ("foo", "foo")
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_const_eval_in_generic_trait() {
|
fn hover_const_eval_in_generic_trait() {
|
||||||
// Doesn't compile, but we shouldn't crash.
|
// Doesn't compile, but we shouldn't crash.
|
||||||
|
@ -474,7 +474,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9165..9173,
|
range: 9332..9340,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -487,7 +487,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9197..9201,
|
range: 9364..9368,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -511,7 +511,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9165..9173,
|
range: 9332..9340,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -524,7 +524,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9197..9201,
|
range: 9364..9368,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -548,7 +548,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9165..9173,
|
range: 9332..9340,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -561,7 +561,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9197..9201,
|
range: 9364..9368,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -941,6 +941,13 @@ pub mod option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn as_ref(&self) -> Option<&T> {
|
||||||
|
match self {
|
||||||
|
Some(x) => Some(x),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn and<U>(self, optb: Option<U>) -> Option<U> {
|
pub fn and<U>(self, optb: Option<U>) -> Option<U> {
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user