Unnamed repository; edit this file 'description' to name the repository.
Separate between normal evaluated consts and type-system consts
Type system consts (consts that participate in the type system, e.g. const generic params) have special properties: they must have strict equality independent of the user. For example, unions are not possible there. Therefore they are stored as a `ValTree`, a very simple representation that is good, mostly, for comparing things. General consts can be anything (almost) - arbitrary bytes, even uninit bytes. They are stored like all consts previously were (in the type-system's `Const`), with bytes memory, in a new type called `Allocation` (named borrowed from rustc, although it isn't exactly accurate because in r-a it can represent multiple allocations). The trigger for this change was a new requirement from rustc_type_ir, that type-system `Const` will be able to represent as a `ValTree`.
Chayim Refael Friedman 5 weeks ago
parent 2880f88 · commit 20b02ef
-rw-r--r--Cargo.toml7
-rw-r--r--crates/hir-def/src/hir.rs10
-rw-r--r--crates/hir-ty/src/consteval.rs251
-rw-r--r--crates/hir-ty/src/consteval/tests.rs43
-rw-r--r--crates/hir-ty/src/db.rs8
-rw-r--r--crates/hir-ty/src/display.rs165
-rw-r--r--crates/hir-ty/src/mir.rs23
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs4
-rw-r--r--crates/hir-ty/src/mir/eval.rs193
-rw-r--r--crates/hir-ty/src/mir/lower.rs37
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs17
-rw-r--r--crates/hir-ty/src/mir/pretty.rs3
-rw-r--r--crates/hir-ty/src/next_solver.rs16
-rw-r--r--crates/hir-ty/src/next_solver/allocation.rs73
-rw-r--r--crates/hir-ty/src/next_solver/consts.rs164
-rw-r--r--crates/hir-ty/src/next_solver/consts/valtree.rs712
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs25
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs36
-rw-r--r--crates/hir/src/lib.rs19
19 files changed, 1381 insertions, 425 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 4372afa0f5..0ae65922d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -127,7 +127,7 @@ object = { version = "0.36.7", default-features = false, features = [
"macho",
"pe",
] }
-postcard = {version = "1.1.3", features = ["alloc"]}
+postcard = { version = "1.1.3", features = ["alloc"] }
process-wrap = { version = "8.2.1", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
@@ -186,7 +186,10 @@ hashbrown = { version = "0.14.*", features = [
elided_lifetimes_in_paths = "warn"
explicit_outlives_requirements = "warn"
unsafe_op_in_unsafe_fn = "warn"
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)', "cfg(no_salsa_async_drops)"] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(bootstrap)',
+ "cfg(no_salsa_async_drops)",
+] }
unused_extern_crates = "warn"
unused_lifetimes = "warn"
unreachable_pub = "warn"
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 4dd113d419..9e51d0eac9 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -21,7 +21,7 @@ use std::fmt;
use hir_expand::{MacroDefId, name::Name};
use intern::Symbol;
use la_arena::Idx;
-use rustc_apfloat::ieee::{Half as f16, Quad as f128};
+use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use syntax::ast;
use type_ref::TypeRefId;
@@ -94,19 +94,19 @@ impl FloatTypeWrapper {
Self(sym)
}
- pub fn to_f128(&self) -> f128 {
+ pub fn to_f128(&self) -> Quad {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f64(&self) -> f64 {
+ pub fn to_f64(&self) -> Double {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f32(&self) -> f32 {
+ pub fn to_f32(&self) -> Single {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f16(&self) -> f16 {
+ pub fn to_f16(&self) -> Half {
self.0.as_str().parse().unwrap_or_default()
}
}
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 928396c63a..80e7e05d76 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -8,28 +8,30 @@ use hir_def::{
ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule,
StaticId,
attrs::AttrFlags,
- builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::{Body, ExpressionStore},
hir::{Expr, ExprId, Literal},
};
use hir_expand::Lookup;
+use rustc_abi::Size;
+use rustc_apfloat::Float;
use rustc_type_ir::inherent::IntoKind;
+use stdx::never;
use triomphe::Arc;
use crate::{
- LifetimeElisionKind, MemoryMap, ParamEnvAndCrate, TyLoweringContext,
+ LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext,
db::HirDatabase,
display::DisplayTarget,
infer::InferenceContext,
- mir::{MirEvalError, MirLowerError},
+ mir::{MirEvalError, MirLowerError, pad16},
next_solver::{
- Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
- StoredConst, StoredGenericArgs, Ty, ValueConst,
+ Allocation, Const, ConstKind, Consts, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+ ScalarInt, StoredAllocation, StoredGenericArgs, Ty, TyKind, ValTreeKind, default_types,
},
traits::StoredParamEnvAndCrate,
};
-use super::mir::{interpret_mir, lower_body_to_mir, pad16};
+use super::mir::{interpret_mir, lower_body_to_mir};
pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> {
Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
@@ -84,140 +86,87 @@ pub fn intern_const_ref<'a>(
db: &'a dyn HirDatabase,
value: &Literal,
ty: Ty<'a>,
- _krate: Crate,
+ krate: Crate,
) -> Const<'a> {
let interner = DbInterner::new_no_crate(db);
- let kind = match value {
- &Literal::Uint(i, builtin_ty)
- if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
- BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
- BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
- BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
- BuiltinUint::U128 => Box::new((i).to_le_bytes()),
- BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ let Ok(data_layout) = db.target_data_layout(krate) else {
+ return Const::error(interner);
+ };
+ let valtree = match (ty.kind(), value) {
+ (TyKind::Uint(uint), Literal::Uint(value, _)) => {
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(*value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- &Literal::Int(i, None)
- if ty
- .as_builtin()
- .is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
- BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
- BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
- BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
- BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()),
- BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ (TyKind::Uint(uint), Literal::Int(value, _)) => {
+ // `Literal::Int` is the default, so we also need to account for the type being uint.
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(*value as u128, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- &Literal::Int(i, builtin_ty)
- if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Int(builtin_int)) => match builtin_int {
- BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()),
- BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()),
- BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()),
- BuiltinInt::I128 => Box::new((i).to_le_bytes()),
- BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ (TyKind::Int(int), Literal::Int(value, _)) => {
+ let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_int(*value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- Literal::Float(float_type_wrapper, builtin_float)
- if builtin_float.is_none()
- || ty.as_builtin() == builtin_float.map(BuiltinType::Float) =>
- {
- let memory = match ty.as_builtin().unwrap() {
- BuiltinType::Float(builtin_float) => match builtin_float {
- // FIXME:
- hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>,
- hir_def::builtin_type::BuiltinFloat::F32 => {
- Box::new(float_type_wrapper.to_f32().to_le_bytes())
- }
- hir_def::builtin_type::BuiltinFloat::F64 => {
- Box::new(float_type_wrapper.to_f64().to_le_bytes())
- }
- // FIXME:
- hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]),
- },
- _ => unreachable!(),
+ (TyKind::Bool, Literal::Bool(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
+ (TyKind::Char, Literal::Char(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
+ (TyKind::Float(float), Literal::Float(value, _)) => {
+ let size = Size::from_bits(float.bit_width());
+ let value = match float {
+ rustc_ast_ir::FloatTy::F16 => value.to_f16().to_bits(),
+ rustc_ast_ir::FloatTy::F32 => value.to_f32().to_bits(),
+ rustc_ast_ir::FloatTy::F64 => value.to_f64().to_bits(),
+ rustc_ast_ir::FloatTy::F128 => value.to_f128().to_bits(),
};
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
+ let scalar = ScalarInt::try_from_uint(value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ (_, Literal::String(value)) => {
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]),
))
}
- Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
- )),
- Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes {
- memory: (*c as u32).to_le_bytes().into(),
- memory_map: MemoryMap::default(),
- },
- )),
- Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes {
- memory: symbol.as_str().as_bytes().into(),
- memory_map: MemoryMap::default(),
- },
- )),
- Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => {
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() },
+ (_, Literal::ByteString(value)) => {
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ value.iter().map(|&byte| u8_values[usize::from(byte)]),
))
}
- // FIXME
- Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
- _ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
+ (_, Literal::CString(_)) => {
+ // FIXME:
+ return Const::error(interner);
+ }
+ _ => {
+ never!("mismatching type for literal");
+ return Const::error(interner);
+ }
};
- Const::new(interner, kind)
+ Const::new_valtree(interner, ty, valtree)
}
/// Interns a possibly-unknown target usize
pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
- intern_const_ref(
- db,
- &match value {
- Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)),
- None => {
- return Const::new(
- DbInterner::new_no_crate(db),
- rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
- );
- }
- },
- Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize),
- krate,
- )
+ let interner = DbInterner::new_no_crate(db);
+ let value = match value {
+ Some(value) => value,
+ None => {
+ return Const::error(interner);
+ }
+ };
+ let Ok(data_layout) = db.target_data_layout(krate) else {
+ return Const::error(interner);
+ };
+ let usize_ty = interner.default_types().types.usize;
+ let scalar = ScalarInt::try_from_uint(value, data_layout.pointer_size()).unwrap();
+ Const::new_valtree(interner, usize_ty, ValTreeKind::Leaf(scalar))
+}
+
+pub fn allocation_as_usize(ec: Allocation<'_>) -> u128 {
+ u128::from_le_bytes(pad16(&ec.memory, false))
}
pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
@@ -230,20 +179,30 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u
GeneralConstId::ConstId(id) => {
let subst = unevaluated_const.args;
let ec = db.const_eval(id, subst, None).ok()?;
- try_const_usize(db, ec)
+ Some(allocation_as_usize(ec))
}
GeneralConstId::StaticId(id) => {
let ec = db.const_eval_static(id).ok()?;
- try_const_usize(db, ec)
+ Some(allocation_as_usize(ec))
}
GeneralConstId::AnonConstId(_) => None,
},
- ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))),
+ ConstKind::Value(val) => {
+ if val.ty == default_types(db).types.usize {
+ Some(val.value.inner().to_leaf().to_uint_unchecked())
+ } else {
+ None
+ }
+ }
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
}
+pub fn allocation_as_isize(ec: Allocation<'_>) -> i128 {
+ i128::from_le_bytes(pad16(&ec.memory, true))
+}
+
pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
match (*c).kind() {
ConstKind::Param(_) => None,
@@ -254,15 +213,21 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<
GeneralConstId::ConstId(id) => {
let subst = unevaluated_const.args;
let ec = db.const_eval(id, subst, None).ok()?;
- try_const_isize(db, &ec)
+ Some(allocation_as_isize(ec))
}
GeneralConstId::StaticId(id) => {
let ec = db.const_eval_static(id).ok()?;
- try_const_isize(db, &ec)
+ Some(allocation_as_isize(ec))
}
GeneralConstId::AnonConstId(_) => None,
},
- ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))),
+ ConstKind::Value(val) => {
+ if val.ty == default_types(db).types.isize {
+ Some(val.value.inner().to_leaf().to_int_unchecked())
+ } else {
+ None
+ }
+ }
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
@@ -299,11 +264,7 @@ pub(crate) fn const_eval_discriminant_variant(
.store(),
)?;
let c = interpret_mir(db, mir_body, false, None)?.0?;
- let c = if is_signed {
- try_const_isize(db, &c).unwrap()
- } else {
- try_const_usize(db, c).unwrap() as i128
- };
+ let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 };
Ok(c)
}
@@ -341,7 +302,11 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd
lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr)
&& let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None)
{
- return result;
+ return Const::new_from_allocation(
+ ctx.interner(),
+ &result,
+ ParamEnvAndCrate { param_env: ctx.table.param_env, krate: ctx.resolver.krate() },
+ );
}
Const::error(ctx.interner())
}
@@ -359,7 +324,7 @@ pub(crate) fn const_eval<'db>(
def: ConstId,
subst: GenericArgs<'db>,
trait_env: Option<ParamEnvAndCrate<'db>>,
-) -> Result<Const<'db>, ConstEvalError> {
+) -> Result<Allocation<'db>, ConstEvalError> {
return match const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
Ok(konst) => Ok(konst.as_ref()),
Err(err) => Err(err.clone()),
@@ -371,7 +336,7 @@ pub(crate) fn const_eval<'db>(
def: ConstId,
subst: StoredGenericArgs,
trait_env: Option<StoredParamEnvAndCrate>,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
let body = db.monomorphized_mir_body(
def.into(),
subst,
@@ -392,7 +357,7 @@ pub(crate) fn const_eval<'db>(
_: ConstId,
_: StoredGenericArgs,
_: Option<StoredParamEnvAndCrate>,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
}
@@ -400,7 +365,7 @@ pub(crate) fn const_eval<'db>(
pub(crate) fn const_eval_static<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
-) -> Result<Const<'db>, ConstEvalError> {
+) -> Result<Allocation<'db>, ConstEvalError> {
return match const_eval_static_query(db, def) {
Ok(konst) => Ok(konst.as_ref()),
Err(err) => Err(err.clone()),
@@ -410,7 +375,7 @@ pub(crate) fn const_eval_static<'db>(
pub(crate) fn const_eval_static_query<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
let interner = DbInterner::new_no_crate(db);
let body = db.monomorphized_mir_body(
def.into(),
@@ -430,7 +395,7 @@ pub(crate) fn const_eval_static<'db>(
_: &dyn HirDatabase,
_: salsa::Id,
_: StaticId,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index aee27dcfde..5d5b5a8e64 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -5,17 +5,16 @@ use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
};
-use rustc_type_ir::inherent::IntoKind;
use test_fixture::WithFixture;
use test_utils::skip_slow_tests;
use crate::{
MemoryMap,
- consteval::try_const_usize,
+ consteval::allocation_as_usize,
db::HirDatabase,
display::DisplayTarget,
mir::pad16,
- next_solver::{Const, ConstBytes, ConstKind, DbInterner, GenericArgs},
+ next_solver::{Allocation, DbInterner, GenericArgs},
setup_tracing,
test_db::TestDB,
};
@@ -45,7 +44,11 @@ fn check_fail(
crate::attach_db(&db, || match eval_goal(&db, file_id) {
Ok(_) => panic!("Expected fail, but it succeeded"),
Err(e) => {
- assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db))
+ assert!(
+ error(simplify(e.clone())),
+ "Actual error was: {}\n{e:?}",
+ pretty_print_err(e.clone(), &db)
+ )
}
})
}
@@ -94,13 +97,7 @@ fn check_answer(
panic!("Error in evaluating goal: {err}");
}
};
- match r.kind() {
- ConstKind::Value(value) => {
- let ConstBytes { memory, memory_map } = value.value.inner();
- check(memory, memory_map);
- }
- _ => panic!("Expected number but found {r:?}"),
- }
+ check(&r.memory, &r.memory_map);
});
}
@@ -121,7 +118,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String {
err
}
-fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const<'_>, ConstEvalError> {
+fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Allocation<'_>, ConstEvalError> {
let _tracing = setup_tracing();
let interner = DbInterner::new_no_crate(db);
let module_id = db.module_for_file(file_id.file_id(db));
@@ -2524,7 +2521,7 @@ fn enums() {
);
crate::attach_db(&db, || {
let r = eval_goal(&db, file_id).unwrap();
- assert_eq!(try_const_usize(&db, r), Some(1));
+ assert_eq!(allocation_as_usize(r), 1);
})
}
@@ -2537,7 +2534,15 @@ fn const_loop() {
const F2: i32 = 2 * F1;
const GOAL: i32 = F3;
"#,
- |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
+ |e| {
+ if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e
+ && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner
+ {
+ true
+ } else {
+ false
+ }
+ },
);
}
@@ -2940,6 +2945,14 @@ fn recursive_adt() {
TAG_TREE
};
"#,
- |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::Loop)),
+ |e| {
+ if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e
+ && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner
+ {
+ true
+ } else {
+ false
+ }
+ },
);
}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 54c4b8d3ac..3bf2d9a6a6 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -21,8 +21,8 @@ use crate::{
lower::{Diagnostics, GenericDefaults},
mir::{BorrowckResult, MirBody, MirLowerError},
next_solver::{
- Const, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, StoredGenericArgs,
- StoredTy, TraitRef, Ty, VariancesOf,
+ Allocation, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder,
+ StoredGenericArgs, StoredTy, TraitRef, Ty, VariancesOf,
},
traits::{ParamEnvAndCrate, StoredParamEnvAndCrate},
};
@@ -68,11 +68,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
def: ConstId,
subst: GenericArgs<'db>,
trait_env: Option<ParamEnvAndCrate<'db>>,
- ) -> Result<Const<'db>, ConstEvalError>;
+ ) -> Result<Allocation<'db>, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_static)]
#[salsa::transparent]
- fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Const<'db>, ConstEvalError>;
+ fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Allocation<'db>, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)]
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 1e4c2985cf..1e9ac5dce6 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -54,9 +54,10 @@ use crate::{
lower::GenericPredicates,
mir::pad16,
next_solver::{
- AliasTy, Clause, ClauseKind, Const, ConstKind, DbInterner, ExistentialPredicate, FnSig,
- GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId,
- StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, TypingMode,
+ AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner,
+ ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig,
+ Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind,
+ TypingMode, ValTree,
abi::Safety,
infer::{DbInternerInferExt, traits::ObligationCause},
},
@@ -691,6 +692,12 @@ impl<'db> HirDisplay<'db> for GenericArg<'db> {
}
}
+impl<'db> HirDisplay<'db> for Allocation<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ render_const_scalar(f, &self.memory, &self.memory_map, self.ty)
+ }
+}
+
impl<'db> HirDisplay<'db> for Const<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
match self.kind() {
@@ -710,12 +717,7 @@ impl<'db> HirDisplay<'db> for Const<'db> {
f.end_location_link();
Ok(())
}
- ConstKind::Value(const_bytes) => render_const_scalar(
- f,
- &const_bytes.value.inner().memory,
- &const_bytes.value.inner().memory_map,
- const_bytes.ty,
- ),
+ ConstKind::Value(value) => render_const_scalar_from_valtree(f, value.ty, value.value),
ConstKind::Unevaluated(unev) => {
let c = unev.def.0;
write!(f, "{}", c.name(f.db))?;
@@ -1014,6 +1016,151 @@ fn render_const_scalar_inner<'db>(
}
}
+fn render_const_scalar_from_valtree<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ ty: Ty<'db>,
+ valtree: ValTree<'db>,
+) -> Result {
+ let param_env = ParamEnv::empty();
+ let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);
+ let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty);
+ render_const_scalar_from_valtree_inner(f, ty, valtree, param_env)
+}
+
+fn render_const_scalar_from_valtree_inner<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ ty: Ty<'db>,
+ valtree: ValTree<'db>,
+ _param_env: ParamEnv<'db>,
+) -> Result {
+ use TyKind;
+ match ty.kind() {
+ TyKind::Bool => write!(f, "{}", valtree.inner().to_leaf().try_to_bool().unwrap()),
+ TyKind::Char => {
+ let it = valtree.inner().to_leaf().to_u32();
+ let Ok(c) = char::try_from(it) else {
+ return f.write_str("<unicode-error>");
+ };
+ write!(f, "{c:?}")
+ }
+ TyKind::Int(_) => {
+ let it = valtree.inner().to_leaf().to_int_unchecked();
+ write!(f, "{it}")
+ }
+ TyKind::Uint(_) => {
+ let it = valtree.inner().to_leaf().to_uint_unchecked();
+ write!(f, "{it}")
+ }
+ TyKind::Float(fl) => match fl {
+ FloatTy::F16 => {
+ // FIXME(#17451): Replace with builtins once they are stabilised.
+ let it = f16::from_bits(valtree.inner().to_leaf().to_u16() as u128);
+ let s = it.to_string();
+ if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
+ // Match Rust debug formatting
+ write!(f, "{s}.0")
+ } else {
+ write!(f, "{s}")
+ }
+ }
+ FloatTy::F32 => {
+ let it = f32::from_bits(valtree.inner().to_leaf().to_u32());
+ write!(f, "{it:?}")
+ }
+ FloatTy::F64 => {
+ let it = f64::from_bits(valtree.inner().to_leaf().to_u64());
+ write!(f, "{it:?}")
+ }
+ FloatTy::F128 => {
+ // FIXME(#17451): Replace with builtins once they are stabilised.
+ let it = f128::from_bits(valtree.inner().to_leaf().to_u128());
+ let s = it.to_string();
+ if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
+ // Match Rust debug formatting
+ write!(f, "{s}.0")
+ } else {
+ write!(f, "{s}")
+ }
+ }
+ },
+ TyKind::Ref(_, inner_ty, _) => {
+ render_const_scalar_from_valtree_inner(f, inner_ty, valtree, _param_env)
+ }
+ TyKind::Str => {
+ let bytes = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => Some(value.value.inner().to_leaf().to_u8()),
+ _ => None,
+ })
+ .collect::<Option<Vec<_>>>();
+ let Some(bytes) = bytes else { return f.write_str("<invalid-str>") };
+ let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");
+ write!(f, "{s:?}")
+ }
+ TyKind::Slice(inner_ty) | TyKind::Array(inner_ty, _) => {
+ let mut first = true;
+ write!(f, "[")?;
+ for item in valtree.inner().to_branch() {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+ let ConstKind::Value(value) = item.kind() else {
+ return f.write_str("<invalid-const>");
+ };
+ render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;
+ }
+ write!(f, "]")
+ }
+ TyKind::Tuple(tys) => {
+ let mut first = true;
+ write!(f, "(")?;
+ for (inner_ty, item) in std::iter::zip(tys, valtree.inner().to_branch()) {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+ let ConstKind::Value(value) = item.kind() else {
+ return f.write_str("<invalid-const>");
+ };
+ render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;
+ }
+ write!(f, ")")
+ }
+ TyKind::Adt(..) => {
+ // FIXME: ADTs, requires `adt_const_params`.
+ f.write_str("<adt>")
+ }
+ TyKind::FnDef(..) => ty.hir_fmt(f),
+ TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {
+ let it = valtree.inner().to_leaf().to_uint_unchecked();
+ write!(f, "{it:#X} as ")?;
+ ty.hir_fmt(f)
+ }
+ TyKind::Never => f.write_str("!"),
+ TyKind::Closure(_, _) => f.write_str("<closure>"),
+ TyKind::Coroutine(_, _) => f.write_str("<coroutine>"),
+ TyKind::CoroutineWitness(_, _) => f.write_str("<coroutine-witness>"),
+ TyKind::CoroutineClosure(_, _) => f.write_str("<coroutine-closure>"),
+ TyKind::UnsafeBinder(_) => f.write_str("<unsafe-binder>"),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => f.write_str("<extern-type>"),
+ TyKind::Pat(_, _) => f.write_str("<pat>"),
+ TyKind::Error(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(_, _)
+ | TyKind::Infer(_) => f.write_str("<placeholder-or-unknown-type>"),
+ TyKind::Dynamic(_, _) => f.write_str("<dyn-trait>"),
+ }
+}
+
fn render_variant_after_name<'db>(
data: &VariantFields,
f: &mut HirFormatter<'_, 'db>,
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index a8865cd54e..837a9847af 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -23,8 +23,8 @@ use crate::{
display::{DisplayTarget, HirDisplay},
infer::PointerCast,
next_solver::{
- Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredConst, StoredGenericArgs,
- StoredTy, Ty, TyKind,
+ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv,
+ StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind,
infer::{InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
},
@@ -107,7 +107,13 @@ pub enum OperandKind {
/// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
Move(Place),
/// Constants are already semantically values, and remain unchanged.
- Constant { konst: StoredConst, ty: StoredTy },
+ Constant {
+ konst: StoredConst,
+ ty: StoredTy,
+ },
+ Allocation {
+ allocation: StoredAllocation,
+ },
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
/// handles it with the `Constant` variant somehow.
Static(StaticId),
@@ -115,11 +121,10 @@ pub enum OperandKind {
impl<'db> Operand {
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self {
- let interner = DbInterner::conjure();
Operand {
- kind: OperandKind::Constant {
- konst: Const::new_valtree(interner, ty, data, memory_map).store(),
- ty: ty.store(),
+ kind: OperandKind::Allocation {
+ allocation: Allocation::new(AllocationData { ty, memory: data, memory_map })
+ .store(),
},
span: None,
}
@@ -1095,7 +1100,9 @@ impl MirBody {
OperandKind::Copy(p) | OperandKind::Move(p) => {
f(p, store);
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. }
+ | OperandKind::Static(_)
+ | OperandKind::Allocation { .. } => (),
}
}
for (_, block) in self.basic_blocks.iter_mut() {
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index d843359dcb..664734c85e 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -160,7 +160,7 @@ fn moved_out_of_ref<'db>(
result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.store() });
}
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@@ -254,7 +254,7 @@ fn partially_moved<'db>(
result.push(PartiallyMoved { span, ty: ty.store(), local: p.local });
}
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 17af4ad961..22ecbe2fa2 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -21,7 +21,7 @@ use hir_expand::{InFile, mod_path::path, name::Name};
use intern::sym;
use la_arena::ArenaMap;
use macros::GenericTypeVisitable;
-use rustc_abi::TargetDataLayout;
+use rustc_abi::{Size, TargetDataLayout};
use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
@@ -46,8 +46,8 @@ use crate::{
layout::{Layout, LayoutError, RustcEnumVariantIdx},
method_resolution::{is_dyn_method, lookup_impl_const},
next_solver::{
- AliasTy, Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region,
- StoredConst, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst,
+ AliasTy, Allocation, AllocationData, Const, ConstKind, DbInterner, ErrorGuaranteed,
+ GenericArgs, Region, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValTree,
infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
},
@@ -359,7 +359,7 @@ pub enum MirEvalError {
MirLowerErrorForClosure(InternedClosureId, MirLowerError),
TypeIsUnsized(StoredTy, &'static str),
NotSupported(String),
- InvalidConst(StoredConst),
+ InvalidConst,
InFunction(
Box<MirEvalError>,
Vec<(Either<FunctionId, InternedClosureId>, MirSpan, DefWithBodyId)>,
@@ -484,7 +484,7 @@ impl MirEvalError {
| MirEvalError::MirLowerErrorForClosure(_, _)
| MirEvalError::TypeIsUnsized(_, _)
| MirEvalError::NotSupported(_)
- | MirEvalError::InvalidConst(_)
+ | MirEvalError::InvalidConst
| MirEvalError::ExecutionLimitExceeded
| MirEvalError::StackOverflow
| MirEvalError::CoerceUnsizedError(_)
@@ -537,7 +537,7 @@ impl std::fmt::Debug for MirEvalError {
Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(),
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
- Self::InvalidConst(arg0) => f.debug_tuple("InvalidConst").field(&arg0).finish(),
+ Self::InvalidConst => f.write_str("InvalidConst"),
Self::InFunction(e, stack) => {
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
}
@@ -606,10 +606,10 @@ pub fn interpret_mir<'db>(
// (and probably should) do better here, for example by excluding bindings outside of the target expression.
assert_placeholder_ty_is_unused: bool,
trait_env: Option<ParamEnvAndCrate<'db>>,
-) -> Result<'db, (Result<'db, Const<'db>>, MirOutput)> {
+) -> Result<'db, (Result<'db, Allocation<'db>>, MirOutput)> {
let ty = body.locals[return_slot()].ty.as_ref();
let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
- let it: Result<'db, Const<'db>> = (|| {
+ let it: Result<'db, Allocation<'db>> = (|| {
if evaluator.ptr_size() != size_of::<usize>() {
not_supported!("targets with different pointer size from host");
}
@@ -620,7 +620,7 @@ pub fn interpret_mir<'db>(
ty,
&Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
)?;
- let bytes = bytes.into();
+ let bytes = Box::from(bytes);
let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() {
MemoryMap::Empty
} else {
@@ -628,7 +628,7 @@ pub fn interpret_mir<'db>(
memory_map.vtable.shrink_to_fit();
MemoryMap::Complex(Box::new(memory_map))
};
- Ok(Const::new_valtree(evaluator.interner(), ty, bytes, memory_map))
+ Ok(Allocation::new(AllocationData { ty, memory: bytes, memory_map }))
})();
Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }))
}
@@ -898,6 +898,7 @@ impl<'db> Evaluator<'db> {
Ok(match &o.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?,
OperandKind::Constant { konst: _, ty } => ty.as_ref(),
+ OperandKind::Allocation { allocation } => allocation.as_ref().ty,
&OperandKind::Static(s) => {
let ty = InferenceResult::of(self.db, DefWithBodyId::from(s))
.expr_ty(Body::of(self.db, s.into()).root_expr());
@@ -1927,19 +1928,152 @@ impl<'db> Evaluator<'db> {
OperandKind::Constant { konst, .. } => {
self.allocate_const_in_heap(locals, konst.as_ref())?
}
+ OperandKind::Allocation { allocation } => {
+ self.allocate_allocation_in_heap(locals, allocation.as_ref())?
+ }
})
}
+ fn allocate_valtree_in_heap(
+ &mut self,
+ ty: Ty<'db>,
+ valtree: ValTree<'db>,
+ ) -> Result<'db, Interval> {
+ match ty.kind() {
+ TyKind::Bool => {
+ let value = valtree.inner().to_leaf().try_to_bool().unwrap();
+ let addr = self.heap_allocate(1, 1)?;
+ self.write_memory(addr, &[u8::from(value)])?;
+ Ok(Interval::new(addr, 1))
+ }
+ TyKind::Char => {
+ let value = valtree.inner().to_leaf().to_u32();
+ let addr = self.heap_allocate(4, 4)?;
+ self.write_memory(addr, &value.to_le_bytes())?;
+ Ok(Interval::new(addr, 4))
+ }
+ TyKind::Int(int_ty) => {
+ let size = int_ty.bit_width().unwrap_or(self.ptr_size() as u64);
+ let value = valtree.inner().to_leaf().to_int(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::Uint(uint_ty) => {
+ let size = uint_ty.bit_width().unwrap_or(self.ptr_size() as u64);
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::Float(float_ty) => {
+ let size = float_ty.bit_width();
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::RawPtr(..) => {
+ let size = self.ptr_size();
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size, size)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size])?;
+ Ok(Interval::new(addr, size))
+ }
+ TyKind::Ref(_, inner_ty, _) => match inner_ty.kind() {
+ TyKind::Str => {
+ let bytes = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => Ok(value.value.inner().to_leaf().to_u8()),
+ _ => not_supported!("unsupported const"),
+ })
+ .collect::<Result<'_, Vec<_>>>()?;
+ let bytes_addr = self.heap_allocate(bytes.len(), 1)?;
+ self.write_memory(bytes_addr, &bytes)?;
+ let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
+ self.write_memory(ref_addr, &bytes_addr.to_bytes())?;
+ let mut len = [0; 16];
+ len[..size_of::<usize>()].copy_from_slice(&bytes.len().to_le_bytes());
+ self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
+ Ok(Interval::new(ref_addr, self.ptr_size() * 2))
+ }
+ TyKind::Slice(inner_ty) => {
+ let item_layout = self.layout(inner_ty)?;
+ let items = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => {
+ self.allocate_valtree_in_heap(value.ty, value.value)
+ }
+ _ => not_supported!("unsupported const"),
+ })
+ .collect::<Result<'_, Vec<_>>>()?;
+ let items_addr = self.heap_allocate(
+ items.len() * (item_layout.size.bits() as usize),
+ item_layout.align.bits_usize(),
+ )?;
+ for (i, item) in items.iter().enumerate() {
+ self.copy_from_interval(
+ items_addr.offset(i * (item_layout.size.bits() as usize)),
+ *item,
+ )?;
+ }
+ let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
+ self.write_memory(ref_addr, &items_addr.to_bytes())?;
+ let mut len = [0; 16];
+ len[..size_of::<usize>()].copy_from_slice(&items.len().to_le_bytes());
+ self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
+ Ok(Interval::new(ref_addr, self.ptr_size() * 2))
+ }
+ TyKind::Dynamic(..) => not_supported!("`dyn Trait` consts not supported yet"),
+ _ => {
+ let inner_addr = self.allocate_valtree_in_heap(inner_ty, valtree)?;
+ let ref_addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
+ self.write_memory(ref_addr, &inner_addr.addr.to_bytes())?;
+ Ok(Interval::new(ref_addr, self.ptr_size()))
+ }
+ },
+ TyKind::Adt(_, _) | TyKind::Array(_, _) | TyKind::Tuple(_) => {
+ not_supported!(
+ "ADTs, arrays and tuples are unsupported in consts currently (requires `adt_const_params`)"
+ )
+ }
+ TyKind::Pat(_, _)
+ | TyKind::Slice(_)
+ | TyKind::FnDef(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Dynamic(_, _)
+ | TyKind::UnsafeBinder(..)
+ | TyKind::FnPtr(..)
+ | TyKind::Closure(_, _)
+ | TyKind::CoroutineClosure(_, _)
+ | TyKind::Coroutine(_, _)
+ | TyKind::CoroutineWitness(_, _)
+ | TyKind::Never
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Infer(_)
+ | TyKind::Str
+ | TyKind::Error(_) => not_supported!("unsupported const"),
+ }
+ }
+
#[allow(clippy::double_parens)]
fn allocate_const_in_heap(
&mut self,
locals: &Locals,
konst: Const<'db>,
) -> Result<'db, Interval> {
- let result_owner;
- let value = match konst.kind() {
- ConstKind::Value(value) => value,
- ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => 'b: {
+ match konst.kind() {
+ ConstKind::Value(value) => self.allocate_valtree_in_heap(value.ty, value.value),
+ ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => {
let mut id = const_id.0;
let mut subst = subst;
if let hir_def::GeneralConstId::ConstId(c) = id {
@@ -1947,7 +2081,7 @@ impl<'db> Evaluator<'db> {
id = hir_def::GeneralConstId::ConstId(c);
subst = s;
}
- result_owner = match id {
+ let allocation = match id {
GeneralConstId::ConstId(const_id) => {
self.db.const_eval(const_id, subst, Some(self.param_env)).map_err(|e| {
let name = id.name(self.db);
@@ -1964,21 +2098,24 @@ impl<'db> Evaluator<'db> {
not_supported!("anonymous const evaluation")
}
};
- if let ConstKind::Value(value) = result_owner.kind() {
- break 'b value;
- }
- not_supported!("unevaluatable constant");
+ self.allocate_allocation_in_heap(locals, allocation)
}
_ => not_supported!("evaluating unknown const"),
- };
- let ValueConst { ty, value } = value;
- let ConstBytes { memory: v, memory_map } = value.inner();
+ }
+ }
+
+ fn allocate_allocation_in_heap(
+ &mut self,
+ locals: &Locals,
+ allocation: Allocation<'db>,
+ ) -> Result<'db, Interval> {
+ let AllocationData { ty, memory: ref v, ref memory_map } = *allocation;
let patch_map = memory_map.transform_addresses(|b, align| {
let addr = self.heap_allocate(b.len(), align)?;
self.write_memory(addr, b)?;
Ok(addr.to_usize())
})?;
- let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
+ let (size, align) = self.size_align_of(allocation.ty, locals)?.unwrap_or((v.len(), 1));
let v: Cow<'_, [u8]> = if size != v.len() {
// Handle self enum
if size == 16 && v.len() < 16 {
@@ -1986,7 +2123,7 @@ impl<'db> Evaluator<'db> {
} else if size < 16 && v.len() == 16 {
Cow::Borrowed(&v[0..size])
} else {
- return Err(MirEvalError::InvalidConst(konst.store()));
+ return Err(MirEvalError::InvalidConst);
}
} else {
Cow::Borrowed(v)
@@ -2840,10 +2977,10 @@ impl<'db> Evaluator<'db> {
};
let static_data = StaticSignature::of(self.db, st);
let result = if !static_data.flags.contains(StaticFlags::EXTERN) {
- let konst = self.db.const_eval_static(st).map_err(|e| {
+ let allocation = self.db.const_eval_static(st).map_err(|e| {
MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e))
})?;
- self.allocate_const_in_heap(locals, konst)?
+ self.allocate_allocation_in_heap(locals, allocation)?
} else {
let ty = InferenceResult::of(self.db, DefWithBodyId::from(st))
.expr_ty(Body::of(self.db, st.into()).root_expr());
@@ -3003,7 +3140,7 @@ impl<'db> Evaluator<'db> {
pub fn render_const_using_debug_impl<'db>(
db: &'db dyn HirDatabase,
owner: DefWithBodyId,
- c: Const<'db>,
+ c: Allocation<'db>,
ty: Ty<'db>,
) -> Result<'db, String> {
let mut evaluator = Evaluator::new(db, owner, false, None)?;
@@ -3014,7 +3151,7 @@ pub fn render_const_using_debug_impl<'db>(
.map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
drop_flags: DropFlags::default(),
};
- let data = evaluator.allocate_const_in_heap(locals, c)?;
+ let data = evaluator.allocate_allocation_in_heap(locals, c)?;
let resolver = owner.resolver(db);
let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully(
db,
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index d044019629..f1365e4df8 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1493,8 +1493,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
hir_def::hir::Literal::Float(f, _) => match size()? {
16 => Box::new(f.to_f128().to_bits().to_le_bytes()),
- 8 => Box::new(f.to_f64().to_le_bytes()),
- 4 => Box::new(f.to_f32().to_le_bytes()),
+ 8 => Box::new(f.to_f64().to_bits().to_le_bytes()),
+ 4 => Box::new(f.to_f32().to_bits().to_le_bytes()),
2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),
_ => {
return Err(MirLowerError::TypeError(
@@ -1528,31 +1528,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
subst: GenericArgs<'db>,
const_id: GeneralConstId,
) -> Result<'db, Operand> {
- let konst = if !subst.is_empty() {
- // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
- Const::new_unevaluated(
- self.interner(),
- UnevaluatedConst { def: const_id.into(), args: subst },
- )
- } else {
- match const_id {
- id @ GeneralConstId::ConstId(const_id) => {
- self.db.const_eval(const_id, subst, None).map_err(|e| {
- let name = id.name(self.db);
- MirLowerError::ConstEvalError(name.into(), Box::new(e))
- })?
- }
- GeneralConstId::StaticId(static_id) => {
- self.db.const_eval_static(static_id).map_err(|e| {
- let name = const_id.name(self.db);
- MirLowerError::ConstEvalError(name.into(), Box::new(e))
- })?
- }
- GeneralConstId::AnonConstId(_) => {
- return Err(MirLowerError::IncompleteExpr);
- }
- }
- };
+ if matches!(const_id, GeneralConstId::AnonConstId(_)) {
+ // FIXME:
+ not_supported!("anon consts are not supported yet in const eval");
+ }
+ let konst = Const::new_unevaluated(
+ self.interner(),
+ UnevaluatedConst { def: const_id.into(), args: subst },
+ );
let ty = self
.db
.value_ty(match const_id {
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 5752a3d7fa..41044f00c2 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -16,7 +16,10 @@ use triomphe::Arc;
use crate::{
ParamEnvAndCrate,
- next_solver::{Const, ConstKind, Region, RegionKind, StoredConst, StoredGenericArgs, StoredTy},
+ next_solver::{
+ Allocation, AllocationData, Const, ConstKind, Region, RegionKind, StoredConst,
+ StoredGenericArgs, StoredTy,
+ },
traits::StoredParamEnvAndCrate,
};
use crate::{
@@ -138,6 +141,18 @@ impl<'db> Filler<'db> {
self.fill_const(konst)?;
self.fill_ty(ty)?;
}
+ OperandKind::Allocation { allocation } => {
+ let alloc = allocation.as_ref();
+ let mut ty = alloc.ty.store();
+ self.fill_ty(&mut ty)?;
+ *allocation = Allocation::new(AllocationData {
+ ty: ty.as_ref(),
+ memory: alloc.memory.clone(),
+ // FIXME: Do we need to fill the memory map too?
+ memory_map: alloc.memory_map.clone(),
+ })
+ .store();
+ }
OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (),
}
Ok(())
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 4b654a0fbe..de5ee223a1 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -387,6 +387,9 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> {
w!(self, "Const({})", self.hir_display(&konst.as_ref()))
}
OperandKind::Static(s) => w!(self, "Static({:?})", s),
+ OperandKind::Allocation { allocation } => {
+ w!(self, "Allocation({})", self.hir_display(&allocation.as_ref()))
+ }
}
}
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
index 354ad16ffe..161a3142df 100644
--- a/crates/hir-ty/src/next_solver.rs
+++ b/crates/hir-ty/src/next_solver.rs
@@ -5,6 +5,7 @@
// incorrect lifetime here.
pub mod abi;
+mod allocation;
mod binder;
mod consts;
mod def_id;
@@ -29,6 +30,7 @@ pub mod util;
use std::{mem::ManuallyDrop, sync::OnceLock};
+pub use allocation::*;
pub use binder::*;
pub use consts::*;
pub use def_id::*;
@@ -89,6 +91,7 @@ pub struct DefaultTypes<'db> {
pub struct DefaultConsts<'db> {
pub error: Const<'db>,
+ pub u8_values: [Const<'db>; 256],
}
pub struct DefaultRegions<'db> {
@@ -232,10 +235,11 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
let statik = create_region(RegionKind::ReStatic);
let empty_tys = create_tys(&[]);
let unit = create_ty(TyKind::Tuple(empty_tys));
+ let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8));
DefaultAny {
types: DefaultTypes {
usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)),
- u8: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)),
+ u8,
u16: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U16)),
u32: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U32)),
u64: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U64)),
@@ -259,7 +263,15 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)),
mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)),
},
- consts: DefaultConsts { error: create_const(ConstKind::Error(ErrorGuaranteed)) },
+ consts: DefaultConsts {
+ error: create_const(ConstKind::Error(ErrorGuaranteed)),
+ u8_values: std::array::from_fn(|u8_value| {
+ create_const(ConstKind::Value(ValueConst {
+ ty: u8,
+ value: ValTree::new(ValTreeKind::Leaf(ScalarInt::from(u8_value as u8))),
+ }))
+ }),
+ },
regions: DefaultRegions {
error: create_region(RegionKind::ReError(ErrorGuaranteed)),
statik,
diff --git a/crates/hir-ty/src/next_solver/allocation.rs b/crates/hir-ty/src/next_solver/allocation.rs
new file mode 100644
index 0000000000..d299c89c12
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/allocation.rs
@@ -0,0 +1,73 @@
+use std::{fmt, hash::Hash};
+
+use intern::{Interned, InternedRef, impl_internable};
+use macros::GenericTypeVisitable;
+use rustc_type_ir::GenericTypeVisitable;
+
+use crate::{
+ MemoryMap,
+ next_solver::{Ty, impl_stored_interned},
+};
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Allocation<'db> {
+ interned: InternedRef<'db, AllocationInterned>,
+}
+
+impl<'db> Allocation<'db> {
+ pub fn new(data: AllocationData<'db>) -> Self {
+ let data =
+ unsafe { std::mem::transmute::<AllocationData<'db>, AllocationData<'static>>(data) };
+ Self { interned: Interned::new_gc(AllocationInterned(data)) }
+ }
+}
+
+impl<'db> std::ops::Deref for Allocation<'db> {
+ type Target = AllocationData<'db>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ let inner = &self.interned.0;
+ unsafe { std::mem::transmute::<&AllocationData<'static>, &AllocationData<'db>>(inner) }
+ }
+}
+
+impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Allocation<'db> {
+ fn generic_visit_with(&self, visitor: &mut V) {
+ if visitor.on_interned(self.interned).is_continue() {
+ (**self).generic_visit_with(visitor);
+ }
+ }
+}
+
+impl fmt::Debug for Allocation<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let AllocationData { ty, memory, memory_map } = &**self;
+ f.debug_struct("Allocation")
+ .field("ty", ty)
+ .field("memory", memory)
+ .field("memory_map", memory_map)
+ .finish()
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, GenericTypeVisitable)]
+pub(super) struct AllocationInterned(AllocationData<'static>);
+
+#[derive(Debug, PartialEq, Eq, GenericTypeVisitable)]
+pub struct AllocationData<'db> {
+ pub ty: Ty<'db>,
+ pub memory: Box<[u8]>,
+ pub memory_map: MemoryMap<'db>,
+}
+
+impl<'db> Hash for AllocationData<'db> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ let Self { ty, memory, memory_map: _ } = self;
+ ty.hash(state);
+ memory.hash(state);
+ }
+}
+
+impl_internable!(gc; AllocationInterned);
+impl_stored_interned!(AllocationInterned, Allocation, StoredAllocation);
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
index 8ed515b452..fa90e3d8a0 100644
--- a/crates/hir-ty/src/next_solver/consts.rs
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -1,5 +1,7 @@
//! Things related to consts in the next-trait-solver.
+mod valtree;
+
use std::hash::Hash;
use hir_def::ConstParamId;
@@ -9,21 +11,20 @@ use rustc_ast_ir::visit::VisitorResult;
use rustc_type_ir::{
BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags,
GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
- TypeVisitable, TypeVisitableExt, WithCachedTypeInfo,
- inherent::{IntoKind, ParamEnv as _, SliceLike},
- relate::Relate,
+ TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
};
use crate::{
- MemoryMap,
+ ParamEnvAndCrate,
next_solver::{
- ClauseKind, ParamEnv, impl_foldable_for_interned_slice, impl_stored_interned,
- interned_slice,
+ AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
},
};
use super::{DbInterner, ErrorGuaranteed, GenericArgs, Ty};
+pub use self::valtree::*;
+
pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
@@ -86,18 +87,21 @@ impl<'db> Const<'db> {
Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound))
}
- pub fn new_valtree(
+ pub fn new_valtree(interner: DbInterner<'db>, ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
+ Const::new(interner, ConstKind::Value(ValueConst { ty, value: ValTree::new(kind) }))
+ }
+
+ pub fn new_from_allocation(
interner: DbInterner<'db>,
- ty: Ty<'db>,
- memory: Box<[u8]>,
- memory_map: MemoryMap<'db>,
+ allocation: &AllocationData<'db>,
+ param_env: ParamEnvAndCrate<'db>,
) -> Self {
- Const::new(
+ allocation_to_const(
interner,
- ConstKind::Value(ValueConst {
- ty,
- value: Valtree::new(ConstBytes { memory, memory_map }),
- }),
+ allocation.ty,
+ &allocation.memory,
+ &allocation.memory_map,
+ param_env,
)
}
@@ -142,136 +146,6 @@ impl std::fmt::Debug for ParamConst {
}
}
-impl ParamConst {
- pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> {
- let mut candidates = env.caller_bounds().iter().filter_map(|clause| {
- // `ConstArgHasType` are never desugared to be higher ranked.
- match clause.kind().skip_binder() {
- ClauseKind::ConstArgHasType(param_ct, ty) => {
- assert!(!(param_ct, ty).has_escaping_bound_vars());
-
- match param_ct.kind() {
- ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty),
- _ => None,
- }
- }
- _ => None,
- }
- });
-
- // N.B. it may be tempting to fix ICEs by making this function return
- // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally
- // considered to be a bandaid solution, since it hides more important
- // underlying issues with how we construct generics and predicates of
- // items. It's advised to fix the underlying issue rather than trying
- // to modify this function.
- let ty = candidates.next().unwrap_or_else(|| {
- panic!("cannot find `{self:?}` in param-env: {env:#?}");
- });
- assert!(
- candidates.next().is_none(),
- "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
- );
- ty
- }
-}
-
-pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind<DbInterner<'db>>;
-
-/// A type-level constant value.
-///
-/// Represents a typed, fully evaluated constant.
-#[derive(
- Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable,
-)]
-pub struct ValueConst<'db> {
- pub ty: Ty<'db>,
- // FIXME: Should we ignore this for TypeVisitable, TypeFoldable?
- #[type_visitable(ignore)]
- #[type_foldable(identity)]
- pub value: Valtree<'db>,
-}
-
-impl<'db> ValueConst<'db> {
- pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self {
- let value = Valtree::new(bytes);
- ValueConst { ty, value }
- }
-}
-
-impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
- fn ty(self) -> Ty<'db> {
- self.ty
- }
-
- fn valtree(self) -> Valtree<'db> {
- self.value
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, GenericTypeVisitable)]
-pub struct ConstBytes<'db> {
- pub memory: Box<[u8]>,
- pub memory_map: MemoryMap<'db>,
-}
-
-impl Hash for ConstBytes<'_> {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- self.memory.hash(state)
- }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Valtree<'db> {
- interned: InternedRef<'db, ValtreeInterned>,
-}
-
-impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Valtree<'db> {
- fn generic_visit_with(&self, visitor: &mut V) {
- if visitor.on_interned(self.interned).is_continue() {
- self.inner().generic_visit_with(visitor);
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)]
-pub(super) struct ValtreeInterned(ConstBytes<'static>);
-
-impl_internable!(gc; ValtreeInterned);
-
-const _: () = {
- const fn is_copy<T: Copy>() {}
- is_copy::<Valtree<'static>>();
-};
-
-impl<'db> IntoKind for Valtree<'db> {
- type Kind = ValTreeKind<'db>;
-
- fn kind(self) -> Self::Kind {
- todo!()
- }
-}
-
-impl<'db> Valtree<'db> {
- #[inline]
- pub fn new(bytes: ConstBytes<'db>) -> Self {
- let bytes = unsafe { std::mem::transmute::<ConstBytes<'db>, ConstBytes<'static>>(bytes) };
- Self { interned: Interned::new_gc(ValtreeInterned(bytes)) }
- }
-
- #[inline]
- pub fn inner(&self) -> &ConstBytes<'db> {
- let inner = &self.interned.0;
- unsafe { std::mem::transmute::<&ConstBytes<'static>, &ConstBytes<'db>>(inner) }
- }
-}
-
-impl std::fmt::Debug for Valtree<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.interned.fmt(f)
- }
-}
-
#[derive(
Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable,
)]
diff --git a/crates/hir-ty/src/next_solver/consts/valtree.rs b/crates/hir-ty/src/next_solver/consts/valtree.rs
new file mode 100644
index 0000000000..b856ee5a85
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/consts/valtree.rs
@@ -0,0 +1,712 @@
+use std::{fmt, hash::Hash, num::NonZero};
+
+use intern::{Interned, InternedRef, impl_internable};
+use macros::{GenericTypeVisitable, TypeFoldable, TypeVisitable};
+use rustc_abi::{Size, TargetDataLayout};
+use rustc_type_ir::{GenericTypeVisitable, TypeFoldable, TypeVisitable, inherent::IntoKind};
+use stdx::never;
+
+use crate::{
+ MemoryMap, ParamEnvAndCrate, consteval,
+ mir::pad16,
+ next_solver::{Const, Consts, TyKind, WorldExposer},
+};
+
+use super::{DbInterner, Ty};
+
+pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind<DbInterner<'db>>;
+
+/// A type-level constant value.
+///
+/// Represents a typed, fully evaluated constant.
+#[derive(
+ Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable,
+)]
+pub struct ValueConst<'db> {
+ pub ty: Ty<'db>,
+ pub value: ValTree<'db>,
+}
+
+impl<'db> ValueConst<'db> {
+ pub fn new(ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
+ let value = ValTree::new(kind);
+ ValueConst { ty, value }
+ }
+}
+
+pub(super) fn allocation_to_const<'db>(
+ interner: DbInterner<'db>,
+ ty: Ty<'db>,
+ memory: &[u8],
+ memory_map: &MemoryMap<'db>,
+ param_env: ParamEnvAndCrate<'db>,
+) -> Const<'db> {
+ let Ok(data_layout) = interner.db.target_data_layout(param_env.krate) else {
+ return Const::error(interner);
+ };
+ let valtree = match ty.kind() {
+ TyKind::Bool => ValTreeKind::Leaf(ScalarInt::from(memory[0] != 0)),
+ TyKind::Char => {
+ let it = u128::from_le_bytes(pad16(memory, false)) as u32;
+ let Ok(c) = char::try_from(it) else {
+ return Const::error(interner);
+ };
+ ValTreeKind::Leaf(ScalarInt::from(c))
+ }
+ TyKind::Int(int) => {
+ let it = i128::from_le_bytes(pad16(memory, true));
+ let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_int(it, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Uint(uint) => {
+ let it = u128::from_le_bytes(pad16(memory, false));
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(it, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Float(float) => {
+ let scalar = match float {
+ rustc_ast_ir::FloatTy::F16 => {
+ ScalarInt::from(u16::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F32 => {
+ ScalarInt::from(u32::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F64 => {
+ ScalarInt::from(u64::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F128 => {
+ ScalarInt::from(u128::from_le_bytes(memory.try_into().unwrap()))
+ }
+ };
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Ref(_, t, _) => match t.kind() {
+ TyKind::Str => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let size = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ bytes.iter().map(|&byte| u8_values[usize::from(byte)]),
+ ))
+ }
+ TyKind::Slice(ty) => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let count = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size_one = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size_one * count) else {
+ return Const::error(interner);
+ };
+ let expected_len = count * size_one;
+ if bytes.len() < expected_len {
+ never!(
+ "Memory map size is too small. Expected {expected_len}, got {}",
+ bytes.len(),
+ );
+ return Const::error(interner);
+ }
+ let items = (0..count).map(|i| {
+ let offset = size_one * i;
+ let bytes = &bytes[offset..offset + size_one];
+ allocation_to_const(interner, ty, bytes, memory_map, param_env)
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Dynamic(_, _) => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let ty_id = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Ok(t) = memory_map.vtable_ty(ty_id) else {
+ return Const::error(interner);
+ };
+ let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ return allocation_to_const(interner, t, bytes, memory_map, param_env);
+ }
+ TyKind::Adt(..) if memory.len() == 2 * size_of::<usize>() => {
+ // FIXME: Unsized ADT.
+ return Const::error(interner);
+ }
+ _ => {
+ let addr = usize::from_le_bytes(match memory.try_into() {
+ Ok(b) => b,
+ Err(_) => {
+ never!(
+ "tried rendering ty {:?} in const ref with incorrect byte count {}",
+ t,
+ memory.len()
+ );
+ return Const::error(interner);
+ }
+ });
+ let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ return allocation_to_const(interner, t, bytes, memory_map, param_env);
+ }
+ },
+ TyKind::Tuple(tys) => {
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let items = tys.iter().enumerate().map(|(id, ty)| {
+ let offset = layout.fields.offset(id).bytes_usize();
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ allocation_to_const(
+ interner,
+ ty,
+ &memory[offset..offset + size],
+ memory_map,
+ param_env,
+ )
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Adt(..) => {
+ // FIXME: This requires `adt_const_params`.
+ return Const::error(interner);
+ }
+ TyKind::FnDef(..) => {
+ // FIXME: Fn items.
+ return Const::error(interner);
+ }
+ TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {
+ let it = u128::from_le_bytes(pad16(memory, false));
+ // FIXME: Unsized pointers.
+ let scalar = ScalarInt::try_from_uint(it, data_layout.pointer_size()).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Array(ty, len) => {
+ let Some(len) = consteval::try_const_usize(interner.db, len) else {
+ return Const::error(interner);
+ };
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size_one = layout.size.bytes_usize();
+ let items = (0..len as usize).map(|i| {
+ let offset = size_one * i;
+ allocation_to_const(
+ interner,
+ ty,
+ &memory[offset..offset + size_one],
+ memory_map,
+ param_env,
+ )
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Never => return Const::error(interner),
+ // FIXME:
+ TyKind::Closure(_, _)
+ | TyKind::Coroutine(_, _)
+ | TyKind::CoroutineWitness(_, _)
+ | TyKind::CoroutineClosure(_, _)
+ | TyKind::UnsafeBinder(_) => return Const::error(interner),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => return Const::error(interner),
+ TyKind::Pat(_, _) => return Const::error(interner),
+ TyKind::Error(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(_, _)
+ | TyKind::Infer(_) => return Const::error(interner),
+ // The below arms are unreachable, since we handled them in ref case.
+ TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => {
+ return Const::error(interner);
+ }
+ };
+ Const::new_valtree(interner, ty, valtree)
+}
+
+impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
+ fn ty(self) -> Ty<'db> {
+ self.ty
+ }
+
+ fn valtree(self) -> ValTree<'db> {
+ self.value
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ValTree<'db> {
+ interned: InternedRef<'db, ValTreeInterned>,
+}
+
+impl<'db, V: WorldExposer> GenericTypeVisitable<V> for ValTree<'db> {
+ fn generic_visit_with(&self, visitor: &mut V) {
+ if visitor.on_interned(self.interned).is_continue() {
+ self.inner().generic_visit_with(visitor);
+ }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ValTree<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.inner().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ValTree<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ self.inner().try_fold_with(folder).map(ValTree::new)
+ }
+
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ValTree::new(self.inner().fold_with(folder))
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)]
+pub(in super::super) struct ValTreeInterned(ValTreeKind<'static>);
+
+impl_internable!(gc; ValTreeInterned);
+
+const _: () = {
+ const fn is_copy<T: Copy>() {}
+ is_copy::<ValTree<'static>>();
+};
+
+impl<'db> IntoKind for ValTree<'db> {
+ type Kind = ValTreeKind<'db>;
+
+ fn kind(self) -> Self::Kind {
+ *self.inner()
+ }
+}
+
+impl<'db> ValTree<'db> {
+ #[inline]
+ pub fn new(kind: ValTreeKind<'db>) -> Self {
+ let kind = unsafe { std::mem::transmute::<ValTreeKind<'db>, ValTreeKind<'static>>(kind) };
+ Self { interned: Interned::new_gc(ValTreeInterned(kind)) }
+ }
+
+ #[inline]
+ pub fn inner(&self) -> &ValTreeKind<'db> {
+ let inner = &self.interned.0;
+ unsafe { std::mem::transmute::<&ValTreeKind<'static>, &ValTreeKind<'db>>(inner) }
+ }
+}
+
+impl std::fmt::Debug for ValTree<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.interned.fmt(f)
+ }
+}
+
+/// The raw bytes of a simple value.
+///
+/// This is a packed struct in order to allow this type to be optimally embedded in enums
+/// (like Scalar).
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+#[repr(Rust, packed)]
+pub struct ScalarInt {
+ /// The first `size` bytes of `data` are the value.
+ /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
+ data: u128,
+ size: NonZero<u8>,
+}
+
+impl ScalarInt {
+ pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
+ pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
+
+ fn raw(data: u128, size: Size) -> Self {
+ Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
+ }
+
+ #[inline]
+ pub fn size(self) -> Size {
+ Size::from_bytes(self.size.get())
+ }
+
+ /// Make sure the `data` fits in `size`.
+ /// This is guaranteed by all constructors here, but having had this check saved us from
+ /// bugs many times in the past, so keeping it around is definitely worth it.
+ #[inline(always)]
+ fn check_data(self) {
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `debug_assert_eq` takes references to its arguments and formatting
+ // arguments and would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ debug_assert_eq!(
+ self.size().truncate(self.data),
+ { self.data },
+ "Scalar value {:#x} exceeds size of {} bytes",
+ { self.data },
+ self.size
+ );
+ }
+
+ #[inline]
+ pub fn null(size: Size) -> Self {
+ Self::raw(0, size)
+ }
+
+ #[inline]
+ pub fn is_null(self) -> bool {
+ self.data == 0
+ }
+
+ #[inline]
+ pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
+ let (r, overflow) = Self::truncate_from_uint(i, size);
+ if overflow { None } else { Some(r) }
+ }
+
+ /// Returns the truncated result, and whether truncation changed the value.
+ #[inline]
+ pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
+ let data = i.into();
+ let r = Self::raw(size.truncate(data), size);
+ (r, r.data != data)
+ }
+
+ #[inline]
+ pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
+ let (r, overflow) = Self::truncate_from_int(i, size);
+ if overflow { None } else { Some(r) }
+ }
+
+ /// Returns the truncated result, and whether truncation changed the value.
+ #[inline]
+ pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
+ let data = i.into();
+ // `into` performed sign extension, we have to truncate
+ let r = Self::raw(size.truncate(data as u128), size);
+ (r, size.sign_extend(r.data) != data)
+ }
+
+ #[inline]
+ pub fn try_from_target_usize(
+ i: impl Into<u128>,
+ data_layout: &TargetDataLayout,
+ ) -> Option<Self> {
+ Self::try_from_uint(i, data_layout.pointer_size())
+ }
+
+ /// Try to convert this ScalarInt to the raw underlying bits.
+ /// Fails if the size is wrong. Generally a wrong size should lead to a panic,
+ /// but Miri sometimes wants to be resilient to size mismatches,
+ /// so the interpreter will generally use this `try` method.
+ #[inline]
+ pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
+ assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
+ if target_size.bytes() == u64::from(self.size.get()) {
+ self.check_data();
+ Ok(self.data)
+ } else {
+ Err(self.size())
+ }
+ }
+
+ #[inline]
+ pub fn to_bits(self, target_size: Size) -> u128 {
+ self.try_to_bits(target_size).unwrap_or_else(|size| {
+ panic!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
+ })
+ }
+
+ /// Extracts the bits from the scalar without checking the size.
+ #[inline]
+ pub fn to_bits_unchecked(self) -> u128 {
+ self.check_data();
+ self.data
+ }
+
+ /// Converts the `ScalarInt` to an unsigned integer of the given size.
+ /// Panics if the size of the `ScalarInt` is not equal to `size`.
+ #[inline]
+ pub fn to_uint(self, size: Size) -> u128 {
+ self.to_bits(size)
+ }
+
+ #[inline]
+ pub fn to_uint_unchecked(self) -> u128 {
+ self.data
+ }
+
+ /// Converts the `ScalarInt` to `u8`.
+ /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte.
+ #[inline]
+ pub fn to_u8(self) -> u8 {
+ self.to_uint(Size::from_bits(8)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u16`.
+ /// Panics if the size of the `ScalarInt` in not equal to 2 bytes.
+ #[inline]
+ pub fn to_u16(self) -> u16 {
+ self.to_uint(Size::from_bits(16)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u32`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes.
+ #[inline]
+ pub fn to_u32(self) -> u32 {
+ self.to_uint(Size::from_bits(32)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u64`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes.
+ #[inline]
+ pub fn to_u64(self) -> u64 {
+ self.to_uint(Size::from_bits(64)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u128`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes.
+ #[inline]
+ pub fn to_u128(self) -> u128 {
+ self.to_uint(Size::from_bits(128))
+ }
+
+ #[inline]
+ pub fn to_target_usize(&self, data_layout: &TargetDataLayout) -> u64 {
+ self.to_uint(data_layout.pointer_size()).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `bool`.
+ /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
+ /// Errors if it is not a valid `bool`.
+ #[inline]
+ pub fn try_to_bool(self) -> Result<bool, ()> {
+ match self.to_u8() {
+ 0 => Ok(false),
+ 1 => Ok(true),
+ _ => Err(()),
+ }
+ }
+
+ /// Converts the `ScalarInt` to a signed integer of the given size.
+ /// Panics if the size of the `ScalarInt` is not equal to `size`.
+ #[inline]
+ pub fn to_int(self, size: Size) -> i128 {
+ let b = self.to_bits(size);
+ size.sign_extend(b)
+ }
+
+ #[inline]
+ pub fn to_int_unchecked(self) -> i128 {
+ self.size().sign_extend(self.data)
+ }
+
+ /// Converts the `ScalarInt` to i8.
+ /// Panics if the size of the `ScalarInt` is not equal to 1 byte.
+ pub fn to_i8(self) -> i8 {
+ self.to_int(Size::from_bits(8)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i16.
+ /// Panics if the size of the `ScalarInt` is not equal to 2 bytes.
+ pub fn to_i16(self) -> i16 {
+ self.to_int(Size::from_bits(16)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i32.
+ /// Panics if the size of the `ScalarInt` is not equal to 4 bytes.
+ pub fn to_i32(self) -> i32 {
+ self.to_int(Size::from_bits(32)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i64.
+ /// Panics if the size of the `ScalarInt` is not equal to 8 bytes.
+ pub fn to_i64(self) -> i64 {
+ self.to_int(Size::from_bits(64)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i128.
+ /// Panics if the size of the `ScalarInt` is not equal to 16 bytes.
+ pub fn to_i128(self) -> i128 {
+ self.to_int(Size::from_bits(128))
+ }
+
+ #[inline]
+ pub fn to_target_isize(&self, data_layout: &TargetDataLayout) -> i64 {
+ self.to_int(data_layout.pointer_size()).try_into().unwrap()
+ }
+}
+
+macro_rules! from_x_for_scalar_int {
+ ($($ty:ty),*) => {
+ $(
+ impl From<$ty> for ScalarInt {
+ #[inline]
+ fn from(u: $ty) -> Self {
+ Self {
+ data: u128::from(u),
+ size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
+ }
+ }
+ }
+ )*
+ }
+}
+
+macro_rules! from_scalar_int_for_x {
+ ($($ty:ty),*) => {
+ $(
+ impl From<ScalarInt> for $ty {
+ #[inline]
+ fn from(int: ScalarInt) -> Self {
+ // The `unwrap` cannot fail because to_uint (if it succeeds)
+ // is guaranteed to return a value that fits into the size.
+ int.to_uint(Size::from_bytes(size_of::<$ty>()))
+ .try_into().unwrap()
+ }
+ }
+ )*
+ }
+}
+
+from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool);
+from_scalar_int_for_x!(u8, u16, u32, u64, u128);
+
+impl TryFrom<ScalarInt> for bool {
+ type Error = ();
+ #[inline]
+ fn try_from(int: ScalarInt) -> Result<Self, ()> {
+ int.try_to_bool()
+ }
+}
+
+impl From<char> for ScalarInt {
+ #[inline]
+ fn from(c: char) -> Self {
+ (c as u32).into()
+ }
+}
+
+macro_rules! from_x_for_scalar_int_signed {
+ ($($ty:ty),*) => {
+ $(
+ impl From<$ty> for ScalarInt {
+ #[inline]
+ fn from(u: $ty) -> Self {
+ Self {
+ data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size
+ size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
+ }
+ }
+ }
+ )*
+ }
+}
+
+macro_rules! from_scalar_int_for_x_signed {
+ ($($ty:ty),*) => {
+ $(
+ impl From<ScalarInt> for $ty {
+ #[inline]
+ fn from(int: ScalarInt) -> Self {
+ // The `unwrap` cannot fail because to_int (if it succeeds)
+ // is guaranteed to return a value that fits into the size.
+ int.to_int(Size::from_bytes(size_of::<$ty>()))
+ .try_into().unwrap()
+ }
+ }
+ )*
+ }
+}
+
+from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128);
+from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128);
+
+impl From<std::cmp::Ordering> for ScalarInt {
+ #[inline]
+ fn from(c: std::cmp::Ordering) -> Self {
+ // Here we rely on `cmp::Ordering` having the same values in host and target!
+ ScalarInt::from(c as i8)
+ }
+}
+
+/// Error returned when a conversion from ScalarInt to char fails.
+#[derive(Debug)]
+pub struct CharTryFromScalarInt;
+
+impl TryFrom<ScalarInt> for char {
+ type Error = CharTryFromScalarInt;
+
+ #[inline]
+ fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
+ match char::from_u32(int.to_u32()) {
+ Some(c) => Ok(c),
+ None => Err(CharTryFromScalarInt),
+ }
+ }
+}
+
+impl fmt::Debug for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Dispatch to LowerHex below.
+ write!(f, "0x{self:x}")
+ }
+}
+
+impl fmt::LowerHex for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ if f.alternate() {
+ // Like regular ints, alternate flag adds leading `0x`.
+ write!(f, "0x")?;
+ }
+ // Format as hex number wide enough to fit any value of the given `size`.
+ // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `write!` takes references to its formatting arguments and
+ // would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
+ }
+}
+
+impl fmt::UpperHex for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ // Format as hex number wide enough to fit any value of the given `size`.
+ // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `write!` takes references to its formatting arguments and
+ // would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
+ }
+}
+
+impl fmt::Display for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ write!(f, "{}", { self.data })
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 9a94d1a865..1078a6af42 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -44,8 +44,9 @@ use crate::{
next_solver::{
AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap,
- GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, SimplifiedType,
- SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst,
+ GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, ScalarInt,
+ SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
+ UnevaluatedConst,
util::{explicit_item_bounds, explicit_item_self_bounds},
},
};
@@ -53,7 +54,7 @@ use crate::{
use super::{
Binder, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, ClauseKind, Clauses, Const,
ErrorGuaranteed, ExprConst, ExternalConstraints, GenericArg, GenericArgs, ParamConst, ParamEnv,
- ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst,
+ ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, ValTree, ValueConst,
abi::Safety,
fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
generics::{Generics, generics},
@@ -370,6 +371,11 @@ impl<'db> DbInterner<'db> {
pub fn default_types<'a>(&self) -> &'a crate::next_solver::DefaultAny<'db> {
crate::next_solver::default_types(self.db)
}
+
+ #[inline]
+ pub(crate) fn expect_crate(&self) -> Crate {
+ self.krate.expect("should have a crate")
+ }
}
// This is intentionally left as `()`
@@ -1056,9 +1062,9 @@ impl<'db> Interner for DbInterner<'db> {
type Const = Const<'db>;
type ParamConst = ParamConst;
type ValueConst = ValueConst<'db>;
- type ValTree = Valtree<'db>;
+ type ValTree = ValTree<'db>;
type Consts = Consts<'db>;
- type ScalarInt = ();
+ type ScalarInt = ScalarInt;
type ExprConst = ExprConst;
type Region = Region<'db>;
@@ -2416,6 +2422,7 @@ TrivialTypeTraversalImpls! {
ParamTy,
EarlyParamRegion,
AdtDef,
+ ScalarInt,
}
mod tls_db {
@@ -2608,13 +2615,15 @@ pub unsafe fn collect_ty_garbage() {
let mut gc = intern::GarbageCollector::default();
gc.add_storage::<super::consts::ConstInterned>();
- gc.add_storage::<super::consts::ValtreeInterned>();
+ gc.add_storage::<super::consts::ValTreeInterned>();
+ gc.add_storage::<super::allocation::AllocationInterned>();
gc.add_storage::<PatternInterned>();
gc.add_storage::<super::opaques::ExternalConstraintsInterned>();
gc.add_storage::<super::predicate::PredicateInterned>();
gc.add_storage::<super::region::RegionInterned>();
gc.add_storage::<super::ty::TyInterned>();
+ gc.add_slice_storage::<super::consts::ConstsStorage>();
gc.add_slice_storage::<super::predicate::ClausesStorage>();
gc.add_slice_storage::<super::generic_arg::GenericArgsStorage>();
gc.add_slice_storage::<BoundVarKindsStorage>();
@@ -2649,7 +2658,8 @@ macro_rules! impl_gc_visit {
impl_gc_visit!(
super::consts::ConstInterned,
- super::consts::ValtreeInterned,
+ super::consts::ValTreeInterned,
+ super::allocation::AllocationInterned,
PatternInterned,
super::opaques::ExternalConstraintsInterned,
super::predicate::PredicateInterned,
@@ -2688,4 +2698,5 @@ impl_gc_visit_slice!(
super::predicate::BoundExistentialPredicatesStorage,
super::region::RegionAssumptionsStorage,
super::ty::TysStorage,
+ super::consts::ConstsStorage,
);
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index a9e7de1613..d45ac6c959 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -14,10 +14,13 @@ use rustc_type_ir::{
};
use tracing::debug;
-use crate::next_solver::{
- AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
- ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
- util::sizedness_fast_path,
+use crate::{
+ ParamEnvAndCrate,
+ next_solver::{
+ AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
+ ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst,
+ fold::fold_tys, util::sizedness_fast_path,
+ },
};
use super::{
@@ -248,25 +251,26 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fn evaluate_const(
&self,
- _param_env: ParamEnv<'db>,
- uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
- ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
- match uv.def.0 {
+ param_env: ParamEnv<'db>,
+ uv: UnevaluatedConst<'db>,
+ ) -> Option<Const<'db>> {
+ let ec = match uv.def.0 {
GeneralConstId::ConstId(c) => {
let subst = uv.args;
- let ec = self.cx().db.const_eval(c, subst, None).ok()?;
- Some(ec)
- }
- GeneralConstId::StaticId(c) => {
- let ec = self.cx().db.const_eval_static(c).ok()?;
- Some(ec)
+ self.cx().db.const_eval(c, subst, None).ok()?
}
+ GeneralConstId::StaticId(c) => self.cx().db.const_eval_static(c).ok()?,
// TODO: Wire up const_eval_anon query in Phase 5.
// For now, return an error const so normalization resolves the
// unevaluated const to Error (matching the old behavior where
// complex expressions produced ConstKind::Error directly).
- GeneralConstId::AnonConstId(_) => Some(Const::error(self.cx())),
- }
+ GeneralConstId::AnonConstId(_) => return Some(Const::error(self.cx())),
+ };
+ Some(Const::new_from_allocation(
+ self.interner,
+ &ec,
+ ParamEnvAndCrate { param_env, krate: self.interner.expect_crate() },
+ ))
}
fn compute_goal_fast_path(
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e102572148..ce0956c5ef 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3124,7 +3124,7 @@ impl Const {
let interner = DbInterner::new_no_crate(db);
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst {
- const_: it,
+ allocation: it,
def: self.id.into(),
ty,
})
@@ -3139,22 +3139,19 @@ impl HasVisibility for Const {
pub struct EvaluatedConst<'db> {
def: DefWithBodyId,
- const_: hir_ty::next_solver::Const<'db>,
+ allocation: hir_ty::next_solver::Allocation<'db>,
ty: Ty<'db>,
}
impl<'db> EvaluatedConst<'db> {
pub fn render(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
- format!("{}", self.const_.display(db, display_target))
+ format!("{}", self.allocation.display(db, display_target))
}
pub fn render_debug(&self, db: &'db dyn HirDatabase) -> Result<String, MirEvalError> {
- let kind = self.const_.kind();
- if let ConstKind::Value(c) = kind
- && let ty = c.ty.kind()
- && let TyKind::Int(_) | TyKind::Uint(_) = ty
- {
- let b = &c.value.inner().memory;
+ let ty = self.allocation.ty.kind();
+ if let TyKind::Int(_) | TyKind::Uint(_) = ty {
+ let b = &self.allocation.memory;
let value = u128::from_le_bytes(mir::pad16(b, false));
let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(ty, TyKind::Int(_))));
let mut result =
@@ -3166,7 +3163,7 @@ impl<'db> EvaluatedConst<'db> {
return Ok(result);
}
}
- mir::render_const_using_debug_impl(db, self.def, self.const_, self.ty)
+ mir::render_const_using_debug_impl(db, self.def, self.allocation, self.ty)
}
}
@@ -3207,7 +3204,7 @@ impl Static {
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> {
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
db.const_eval_static(self.id).map(|it| EvaluatedConst {
- const_: it,
+ allocation: it,
def: self.id.into(),
ty,
})