Skip to content

Commit a4fbac1

Browse files
committed
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.
1 parent 0cc64a3 commit a4fbac1

File tree

10 files changed

+132
-6
lines changed

10 files changed

+132
-6
lines changed

compiler/rustc_middle/src/mir/interpret/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
1313
use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic};
1414

1515
/// Represents the result of const evaluation via the `eval_to_allocation` query.
16-
#[derive(Clone, HashStable, TyEncodable, TyDecodable, Debug)]
16+
#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
1717
pub struct ConstAlloc<'tcx> {
1818
// the value lives here, at offset 0, and that allocation definitely is a `AllocKind::Memory`
1919
// (so you can use `AllocMap::unwrap_memory`).

compiler/rustc_middle/src/query/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,14 @@ rustc_queries! {
785785
cache_on_disk_if { true }
786786
}
787787

788+
/// Convert an evaluated constant to a type level constant or
789+
/// return `None` if that is not possible.
790+
query const_to_valtree(
791+
key: ty::ParamEnvAnd<'tcx, ConstAlloc<'tcx>>
792+
) -> Option<ty::ValTree> {
793+
desc { "destructure constant" }
794+
}
795+
788796
/// Destructure a constant ADT or array into its variant index and its
789797
/// field values.
790798
query destructure_const(

compiler/rustc_middle/src/ty/consts.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ use rustc_macros::HashStable;
1010

1111
mod int;
1212
mod kind;
13+
mod valtree;
1314

1415
pub use int::*;
1516
pub use kind::*;
17+
pub use valtree::*;
1618

1719
/// Typed constant value.
1820
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use super::ScalarInt;
2+
use rustc_macros::HashStable;
3+
4+
#[derive(Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
5+
#[derive(HashStable)]
6+
pub enum ValTree {
7+
Leaf(ScalarInt),
8+
Branch(Vec<ValTree>),
9+
}
10+
11+
impl ValTree {
12+
pub fn zst() -> Self {
13+
Self::Branch(Vec::new())
14+
}
15+
}

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub use rustc_type_ir::*;
5555

5656
pub use self::binding::BindingMode;
5757
pub use self::binding::BindingMode::*;
58-
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt};
58+
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, ValTree};
5959
pub use self::context::{
6060
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
6161
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,

compiler/rustc_middle/src/ty/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLife
1414
use crate::middle::stability::{self, DeprecationEntry};
1515
use crate::mir;
1616
use crate::mir::interpret::GlobalId;
17+
use crate::mir::interpret::{ConstAlloc, LitToConstError, LitToConstInput};
1718
use crate::mir::interpret::{ConstValue, EvalToAllocationRawResult, EvalToConstValueResult};
18-
use crate::mir::interpret::{LitToConstError, LitToConstInput};
1919
use crate::mir::mono::CodegenUnit;
2020
use crate::traits::query::{
2121
CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,

compiler/rustc_mir/src/const_eval/mod.rs

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
use std::convert::TryFrom;
44

55
use rustc_hir::Mutability;
6-
use rustc_middle::mir;
76
use rustc_middle::ty::{self, TyCtxt};
7+
use rustc_middle::{
8+
mir::{self, interpret::ConstAlloc},
9+
ty::ScalarInt,
10+
};
811
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
912

1013
use crate::interpret::{
11-
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
14+
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
1215
};
1316

1417
mod error;
@@ -35,6 +38,91 @@ pub(crate) fn const_caller_location(
3538
ConstValue::Scalar(loc_place.ptr)
3639
}
3740

41+
/// Convert an evaluated constant to a type level constant
42+
pub(crate) fn const_to_valtree<'tcx>(
43+
tcx: TyCtxt<'tcx>,
44+
param_env: ty::ParamEnv<'tcx>,
45+
raw: ConstAlloc<'tcx>,
46+
) -> Option<ty::ValTree> {
47+
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
48+
let place = ecx.raw_const_to_mplace(raw).unwrap();
49+
const_to_valtree_inner(&ecx, &place)
50+
}
51+
52+
fn const_to_valtree_inner<'tcx>(
53+
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
54+
place: &MPlaceTy<'tcx>,
55+
) -> Option<ty::ValTree> {
56+
let branches = |n, variant| {
57+
let place = match variant {
58+
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
59+
None => *place,
60+
};
61+
let variant =
62+
variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
63+
let fields = (0..n).map(|i| {
64+
let field = ecx.mplace_field(&place, i).unwrap();
65+
const_to_valtree_inner(ecx, &field)
66+
});
67+
Some(ty::ValTree::Branch(variant.into_iter().chain(fields).collect::<Option<_>>()?))
68+
};
69+
match place.layout.ty.kind() {
70+
ty::FnDef(..) => Some(ty::ValTree::zst()),
71+
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
72+
let val = ecx.read_immediate(&place.into()).unwrap();
73+
let val = val.to_scalar().unwrap();
74+
Some(ty::ValTree::Leaf(val.assert_int()))
75+
}
76+
77+
// Raw pointers are not allowed in type level constants, as raw pointers cannot be treated
78+
// like references. If we looked behind the raw pointer, we may be breaking the meaning of
79+
// the raw pointer. Equality on raw pointers is performed on the pointer and not on the pointee,
80+
// and we cannot guarantee any kind of pointer stability in the type system.
81+
// Technically we could allow function pointers, but they are not guaranteed to be the
82+
// same as the function pointers at runtime.
83+
ty::FnPtr(_) | ty::RawPtr(_) => None,
84+
ty::Ref(..) => unimplemented!("need to use deref_const"),
85+
86+
ty::Dynamic(..) => unimplemented!(
87+
"for trait objects we must look at the vtable and figure out the real type"
88+
),
89+
90+
ty::Slice(_) | ty::Str => {
91+
unimplemented!("need to find the backing data of the slice/str and recurse on that")
92+
}
93+
ty::Tuple(substs) => branches(substs.len(), None),
94+
ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),
95+
96+
ty::Adt(def, _) => {
97+
if def.variants.is_empty() {
98+
// Uninhabited
99+
return None;
100+
}
101+
102+
let variant = ecx.read_discriminant(&place.into()).unwrap().1;
103+
104+
branches(def.variants[variant].fields.len(), Some(variant))
105+
}
106+
107+
ty::Never
108+
| ty::Error(_)
109+
| ty::Foreign(..)
110+
| ty::Infer(ty::FreshIntTy(_))
111+
| ty::Infer(ty::FreshFloatTy(_))
112+
| ty::Projection(..)
113+
| ty::Param(_)
114+
| ty::Bound(..)
115+
| ty::Placeholder(..)
116+
// FIXME(oli-obk): we could look behind opaque types
117+
| ty::Opaque(..)
118+
| ty::Infer(_)
119+
// FIXME(oli-obk): we can probably encode closures just like structs
120+
| ty::Closure(..)
121+
| ty::Generator(..)
122+
| ty::GeneratorWitness(..) => None,
123+
}
124+
}
125+
38126
/// This function uses `unwrap` copiously, because an already validated constant
39127
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
40128
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.

compiler/rustc_mir/src/interpret/place.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ where
531531
base.offset(from_offset, meta, layout, self)
532532
}
533533

534-
pub(super) fn mplace_downcast(
534+
pub(crate) fn mplace_downcast(
535535
&self,
536536
base: &MPlaceTy<'tcx, M::PointerTag>,
537537
variant: VariantIdx,

compiler/rustc_mir/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ pub fn provide(providers: &mut Providers) {
6363
let (param_env, value) = param_env_and_value.into_parts();
6464
const_eval::destructure_const(tcx, param_env, value)
6565
};
66+
providers.const_to_valtree = |tcx, param_env_and_value| {
67+
let (param_env, raw) = param_env_and_value.into_parts();
68+
const_eval::const_to_valtree(tcx, param_env, raw)
69+
};
6670
providers.deref_const = |tcx, param_env_and_value| {
6771
let (param_env, value) = param_env_and_value.into_parts();
6872
const_eval::deref_const(tcx, param_env, value)

compiler/rustc_query_impl/src/keys.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,15 @@ impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
228228
}
229229
}
230230

231+
impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
232+
fn query_crate(&self) -> CrateNum {
233+
LOCAL_CRATE
234+
}
235+
fn default_span(&self, _: TyCtxt<'_>) -> Span {
236+
DUMMY_SP
237+
}
238+
}
239+
231240
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
232241
fn query_crate(&self) -> CrateNum {
233242
self.def_id().krate

0 commit comments

Comments
 (0)