Implement valtree
valtree is a version of constants that is inherently safe to be used within types. This is in contrast to ty::Const which can have different representations of the same value. These representation differences can show up in hashing or equality comparisons, breaking type equality of otherwise equal types. valtrees do not have this problem.
This commit is contained in:
parent
0cc64a34e9
commit
a4fbac163e
@ -13,7 +13,7 @@
|
|||||||
use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
|
use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
|
||||||
|
|
||||||
/// Represents the result of const evaluation via the `eval_to_allocation` query.
|
/// Represents the result of const evaluation via the `eval_to_allocation` query.
|
||||||
#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)]
|
#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
|
||||||
pub struct ConstAlloc<'tcx> {
|
pub struct ConstAlloc<'tcx> {
|
||||||
// the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
|
// the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
|
||||||
// (so you can use `AllocMap::unwrap_memory`).
|
// (so you can use `AllocMap::unwrap_memory`).
|
||||||
|
@ -785,6 +785,14 @@
|
|||||||
cache_on_disk_if { true }
|
cache_on_disk_if { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an evaluated constant to a type level constant or
|
||||||
|
/// return `None` if that is not possible.
|
||||||
|
query const_to_valtree(
|
||||||
|
key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>>
|
||||||
|
) -> Option<ty::ValTree> {
|
||||||
|
desc { "destructure constant" }
|
||||||
|
}
|
||||||
|
|
||||||
/// Destructure a constant ADT or array into its variant index and its
|
/// Destructure a constant ADT or array into its variant index and its
|
||||||
/// field values.
|
/// field values.
|
||||||
query destructure_const(
|
query destructure_const(
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
mod int;
|
mod int;
|
||||||
mod kind;
|
mod kind;
|
||||||
|
mod valtree;
|
||||||
|
|
||||||
pub use int::*;
|
pub use int::*;
|
||||||
pub use kind::*;
|
pub use kind::*;
|
||||||
|
pub use valtree::*;
|
||||||
|
|
||||||
/// Typed constant value.
|
/// Typed constant value.
|
||||||
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
15
compiler/rustc_middle/src/ty/consts/valtree.rs
Normal file
15
compiler/rustc_middle/src/ty/consts/valtree.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use super::ScalarInt;
|
||||||
|
use rustc_macros::HashStable;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
#[derive(HashStable)]
|
||||||
|
pub enum ValTree {
|
||||||
|
Leaf(ScalarInt),
|
||||||
|
Branch(Vec<ValTree>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValTree {
|
||||||
|
pub fn zst() -> Self {
|
||||||
|
Self::Branch(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
pub use self::binding::BindingMode;
|
pub use self::binding::BindingMode;
|
||||||
pub use self::binding::BindingMode::*;
|
pub use self::binding::BindingMode::*;
|
||||||
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt};
|
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, ValTree};
|
||||||
pub use self::context::{
|
pub use self::context::{
|
||||||
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
|
||||||
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,
|
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
use crate::middle::stability::{self, DeprecationEntry};
|
use crate::middle::stability::{self, DeprecationEntry};
|
||||||
use crate::mir;
|
use crate::mir;
|
||||||
use crate::mir::interpret::GlobalId;
|
use crate::mir::interpret::GlobalId;
|
||||||
|
use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput};
|
||||||
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
|
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
|
||||||
use crate::mir::interpret::{LitToConstError, LitToConstInput};
|
|
||||||
use crate::mir::mono::CodegenUnit;
|
use crate::mir::mono::CodegenUnit;
|
||||||
use crate::traits::query::{
|
use crate::traits::query::{
|
||||||
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
|
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
|
||||||
|
@ -3,12 +3,15 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use rustc_hir::Mutability;
|
use rustc_hir::Mutability;
|
||||||
use rustc_middle::mir;
|
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
use rustc_middle::{
|
||||||
|
mir::{self, interpret::ConstAlloc},
|
||||||
|
ty::ScalarInt,
|
||||||
|
};
|
||||||
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
|
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
|
||||||
|
|
||||||
use crate::interpret::{
|
use crate::interpret::{
|
||||||
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
|
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
@ -35,6 +38,91 @@ pub(crate) fn const_caller_location(
|
|||||||
ConstValue::Scalar(loc_place.ptr)
|
ConstValue::Scalar(loc_place.ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an evaluated constant to a type level constant
|
||||||
|
pub(crate) fn const_to_valtree<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
raw: ConstAlloc<'tcx>,
|
||||||
|
) -> Option<ty::ValTree> {
|
||||||
|
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
|
||||||
|
let place = ecx.raw_const_to_mplace(raw).unwrap();
|
||||||
|
const_to_valtree_inner(&ecx, &place)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn const_to_valtree_inner<'tcx>(
|
||||||
|
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
|
||||||
|
place: &MPlaceTy<'tcx>,
|
||||||
|
) -> Option<ty::ValTree> {
|
||||||
|
let branches = |n, variant| {
|
||||||
|
let place = match variant {
|
||||||
|
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
|
||||||
|
None => *place,
|
||||||
|
};
|
||||||
|
let variant =
|
||||||
|
variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
|
||||||
|
let fields = (0..n).map(|i| {
|
||||||
|
let field = ecx.mplace_field(&place, i).unwrap();
|
||||||
|
const_to_valtree_inner(ecx, &field)
|
||||||
|
});
|
||||||
|
Some(ty::ValTree::Branch(variant.into_iter().chain(fields).collect::<Option<_>>()?))
|
||||||
|
};
|
||||||
|
match place.layout.ty.kind() {
|
||||||
|
ty::FnDef(..) => Some(ty::ValTree::zst()),
|
||||||
|
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
|
||||||
|
let val = ecx.read_immediate(&place.into()).unwrap();
|
||||||
|
let val = val.to_scalar().unwrap();
|
||||||
|
Some(ty::ValTree::Leaf(val.assert_int()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw pointers are not allowed in type level constants, as raw pointers cannot be treated
|
||||||
|
// like references. If we looked behind the raw pointer, we may be breaking the meaning of
|
||||||
|
// the raw pointer. Equality on raw pointers is performed on the pointer and not on the pointee,
|
||||||
|
// and we cannot guarantee any kind of pointer stability in the type system.
|
||||||
|
// Technically we could allow function pointers, but they are not guaranteed to be the
|
||||||
|
// same as the function pointers at runtime.
|
||||||
|
ty::FnPtr(_) | ty::RawPtr(_) => None,
|
||||||
|
ty::Ref(..) => unimplemented!("need to use deref_const"),
|
||||||
|
|
||||||
|
ty::Dynamic(..) => unimplemented!(
|
||||||
|
"for trait objects we must look at the vtable and figure out the real type"
|
||||||
|
),
|
||||||
|
|
||||||
|
ty::Slice(_) | ty::Str => {
|
||||||
|
unimplemented!("need to find the backing data of the slice/str and recurse on that")
|
||||||
|
}
|
||||||
|
ty::Tuple(substs) => branches(substs.len(), None),
|
||||||
|
ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),
|
||||||
|
|
||||||
|
ty::Adt(def, _) => {
|
||||||
|
if def.variants.is_empty() {
|
||||||
|
// Uninhabited
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let variant = ecx.read_discriminant(&place.into()).unwrap().1;
|
||||||
|
|
||||||
|
branches(def.variants[variant].fields.len(), Some(variant))
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::Never
|
||||||
|
| ty::Error(_)
|
||||||
|
| ty::Foreign(..)
|
||||||
|
| ty::Infer(ty::FreshIntTy(_))
|
||||||
|
| ty::Infer(ty::FreshFloatTy(_))
|
||||||
|
| ty::Projection(..)
|
||||||
|
| ty::Param(_)
|
||||||
|
| ty::Bound(..)
|
||||||
|
| ty::Placeholder(..)
|
||||||
|
// FIXME(oli-obk): we could look behind opaque types
|
||||||
|
| ty::Opaque(..)
|
||||||
|
| ty::Infer(_)
|
||||||
|
// FIXME(oli-obk): we can probably encode closures just like structs
|
||||||
|
| ty::Closure(..)
|
||||||
|
| ty::Generator(..)
|
||||||
|
| ty::GeneratorWitness(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This function uses `unwrap` copiously, because an already validated constant
|
/// This function uses `unwrap` copiously, because an already validated constant
|
||||||
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
|
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
|
||||||
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
|
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
|
||||||
|
@ -531,7 +531,7 @@ fn mplace_subslice(
|
|||||||
base.offset(from_offset, meta, layout, self)
|
base.offset(from_offset, meta, layout, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn mplace_downcast(
|
pub(crate) fn mplace_downcast(
|
||||||
&self,
|
&self,
|
||||||
base: &MPlaceTy<'tcx, M::PointerTag>,
|
base: &MPlaceTy<'tcx, M::PointerTag>,
|
||||||
variant: VariantIdx,
|
variant: VariantIdx,
|
||||||
|
@ -63,6 +63,10 @@ pub fn provide(providers: &mut Providers) {
|
|||||||
let (param_env, value) = param_env_and_value.into_parts();
|
let (param_env, value) = param_env_and_value.into_parts();
|
||||||
const_eval::destructure_const(tcx, param_env, value)
|
const_eval::destructure_const(tcx, param_env, value)
|
||||||
};
|
};
|
||||||
|
providers.const_to_valtree = |tcx, param_env_and_value| {
|
||||||
|
let (param_env, raw) = param_env_and_value.into_parts();
|
||||||
|
const_eval::const_to_valtree(tcx, param_env, raw)
|
||||||
|
};
|
||||||
providers.deref_const = |tcx, param_env_and_value| {
|
providers.deref_const = |tcx, param_env_and_value| {
|
||||||
let (param_env, value) = param_env_and_value.into_parts();
|
let (param_env, value) = param_env_and_value.into_parts();
|
||||||
const_eval::deref_const(tcx, param_env, value)
|
const_eval::deref_const(tcx, param_env, value)
|
||||||
|
@ -228,6 +228,15 @@ fn default_span(&self, _: TyCtxt<'_>) -> Span {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
|
||||||
|
fn query_crate(&self) -> CrateNum {
|
||||||
|
LOCAL_CRATE
|
||||||
|
}
|
||||||
|
fn default_span(&self, _: TyCtxt<'_>) -> Span {
|
||||||
|
DUMMY_SP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
|
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
|
||||||
fn query_crate(&self) -> CrateNum {
|
fn query_crate(&self) -> CrateNum {
|
||||||
self.def_id().krate
|
self.def_id().krate
|
||||||
|
Loading…
Reference in New Issue
Block a user