Skip to content

fix: Add enum, reference, array and slice to render_const_scalar #14947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 185 additions & 68 deletions crates/hir-ty/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,26 @@ use hir_def::{
path::{Path, PathKind},
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
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 intern::{Internable, Interned};
use itertools::Itertools;
use la_arena::ArenaMap;
use smallvec::SmallVec;
use stdx::never;

use crate::{
consteval::try_const_usize,
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,
mir::pad16,
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,
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
Expand Down Expand Up @@ -469,7 +474,7 @@ fn render_const_scalar(
// infrastructure and have it here as a field on `f`.
let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
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::Char => {
let x = u128::from_le_bytes(pad16(b, false)) as u32;
Expand Down Expand Up @@ -497,17 +502,54 @@ fn render_const_scalar(
}
},
},
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
chalk_ir::TyKind::Str => {
TyKind::Ref(_, _, t) => match t.kind(Interner) {
TyKind::Str => {
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 s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
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:?}")
}
_ => 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) => {
let Ok(layout) = f.db.layout_of_ty( ty.clone(), krate) else {
TyKind::Tuple(_, subst) => {
let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
return f.write_str("<layout-error>");
};
f.write_str("(")?;
Expand All @@ -529,69 +571,144 @@ fn render_const_scalar(
}
f.write_str(")")
}
chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
hir_def::AdtId::StructId(s) => {
let data = f.db.struct_data(s);
let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else {
return f.write_str("<layout-error>");
};
match data.variant_data.as_ref() {
VariantData::Record(fields) | VariantData::Tuple(fields) => {
let field_types = f.db.field_types(s.into());
let krate = adt.0.module(f.db.upcast()).krate();
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.variant_data.as_ref(), VariantData::Record(_)) {
write!(f, "{} {{", data.name.display(f.db.upcast()))?;
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, "{}(", data.name.display(f.db.upcast()))?;
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 => write!(f, "{}", data.name.display(f.db.upcast())),
TyKind::Adt(adt, subst) => {
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) => {
let data = f.db.struct_data(s);
write!(f, "{}", data.name.display(f.db.upcast()))?;
let field_types = f.db.field_types(s.into());
render_variant_after_name(
&data.variant_data,
f,
&field_types,
adt.0.module(f.db.upcast()).krate(),
&layout,
subst,
b,
memory_map,
)
}
hir_def::AdtId::UnionId(u) => {
write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
}
hir_def::AdtId::EnumId(e) => {
let Some((var_id, var_layout)) =
detect_variant_from_bytes(&layout, f.db, krate, b, e) else {
return f.write_str("<failed-to-detect-variant>");
};
let data = &f.db.enum_data(e).variants[var_id];
write!(f, "{}", data.name.display(f.db.upcast()))?;
let field_types =
f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into());
render_variant_after_name(
&data.variant_data,
f,
&field_types,
adt.0.module(f.db.upcast()).krate(),
&var_layout,
subst,
b,
memory_map,
)
}
}
hir_def::AdtId::UnionId(u) => {
write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
}
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
},
chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
chalk_ir::TyKind::Raw(_, _) => {
}
TyKind::FnDef(..) => ty.hir_fmt(f),
TyKind::Function(_) | TyKind::Raw(_, _) => {
let x = u128::from_le_bytes(pad16(b, false));
write!(f, "{:#X} as ", x)?;
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(()),
}
}

Expand Down
24 changes: 22 additions & 2 deletions crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ mod tests;
#[cfg(test)]
mod test_db;

use std::{collections::HashMap, hash::Hash};
use std::{
collections::{hash_map::Entry, HashMap},
hash::Hash,
};

use chalk_ir::{
fold::{Shift, TypeFoldable},
Expand Down Expand Up @@ -160,7 +163,16 @@ pub struct MemoryMap {

impl MemoryMap {
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
Expand All @@ -172,6 +184,14 @@ impl MemoryMap {
) -> Result<HashMap<usize, usize>, MirEvalError> {
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
Expand Down
Loading