Skip to content

Commit 1157dc7

Browse files
committed
implement valtree -> constvalue conversion
1 parent eaf8cda commit 1157dc7

File tree

9 files changed

+546
-151
lines changed

9 files changed

+546
-151
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+24-14
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
106106

107107
/// This function converts an interpreter value into a constant that is meant for use in the
108108
/// type system.
109+
#[instrument(skip(ecx), level = "debug")]
109110
pub(super) fn op_to_const<'tcx>(
110111
ecx: &CompileTimeEvalContext<'_, 'tcx>,
111112
op: &OpTy<'tcx>,
@@ -140,21 +141,26 @@ pub(super) fn op_to_const<'tcx>(
140141
op.try_as_mplace()
141142
};
142143

144+
debug!(?immediate);
145+
143146
// We know `offset` is relative to the allocation, so we can use `into_parts`.
144-
let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr.into_parts() {
145-
(Some(alloc_id), offset) => {
146-
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
147-
ConstValue::ByRef { alloc, offset }
148-
}
149-
(None, offset) => {
150-
assert!(mplace.layout.is_zst());
151-
assert_eq!(
152-
offset.bytes() % mplace.layout.align.abi.bytes(),
153-
0,
154-
"this MPlaceTy must come from a validated constant, thus we can assume the \
147+
let to_const_value = |mplace: &MPlaceTy<'_>| {
148+
debug!("to_const_value(mplace: {:?})", mplace);
149+
match mplace.ptr.into_parts() {
150+
(Some(alloc_id), offset) => {
151+
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
152+
ConstValue::ByRef { alloc, offset }
153+
}
154+
(None, offset) => {
155+
assert!(mplace.layout.is_zst());
156+
assert_eq!(
157+
offset.bytes() % mplace.layout.align.abi.bytes(),
158+
0,
159+
"this MPlaceTy must come from a validated constant, thus we can assume the \
155160
alignment is correct",
156-
);
157-
ConstValue::Scalar(Scalar::ZST)
161+
);
162+
ConstValue::Scalar(Scalar::ZST)
163+
}
158164
}
159165
};
160166
match immediate {
@@ -166,6 +172,7 @@ pub(super) fn op_to_const<'tcx>(
166172
ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()),
167173
},
168174
Immediate::ScalarPair(a, b) => {
175+
debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
169176
// We know `offset` is relative to the allocation, so we can use `into_parts`.
170177
let (data, start) =
171178
match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() {
@@ -209,7 +216,10 @@ fn turn_into_const_value<'tcx>(
209216
);
210217

211218
// Turn this into a proper constant.
212-
op_to_const(&ecx, &mplace.into())
219+
let const_val = op_to_const(&ecx, &mplace.into());
220+
debug!(?const_val);
221+
222+
const_val
213223
}
214224

215225
pub fn eval_to_const_value_raw_provider<'tcx>(

compiler/rustc_const_eval/src/const_eval/mod.rs

+6-130
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,26 @@
33
use std::convert::TryFrom;
44

55
use rustc_hir::Mutability;
6-
use rustc_middle::ty::layout::HasTyCtxt;
6+
use rustc_middle::mir;
77
use rustc_middle::ty::{self, TyCtxt};
8-
use rustc_middle::{
9-
mir::{self, interpret::ConstAlloc},
10-
ty::ScalarInt,
11-
};
128
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
13-
use rustc_target::abi::VariantIdx;
149

1510
use crate::interpret::{
16-
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
17-
MemPlaceMeta, Scalar,
11+
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
12+
Scalar,
1813
};
1914

2015
mod error;
2116
mod eval_queries;
2217
mod fn_queries;
2318
mod machine;
19+
mod valtrees;
2420

2521
pub use error::*;
2622
pub use eval_queries::*;
2723
pub use fn_queries::*;
2824
pub use machine::*;
25+
pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value};
2926

3027
pub(crate) fn const_caller_location(
3128
tcx: TyCtxt<'_>,
@@ -41,128 +38,6 @@ pub(crate) fn const_caller_location(
4138
ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx))
4239
}
4340

44-
/// Convert an evaluated constant to a type level constant
45-
pub(crate) fn const_to_valtree<'tcx>(
46-
tcx: TyCtxt<'tcx>,
47-
param_env: ty::ParamEnv<'tcx>,
48-
raw: ConstAlloc<'tcx>,
49-
) -> Option<ty::ValTree<'tcx>> {
50-
let ecx = mk_eval_cx(
51-
tcx, DUMMY_SP, param_env,
52-
// It is absolutely crucial for soundness that
53-
// we do not read from static items or other mutable memory.
54-
false,
55-
);
56-
let place = ecx.raw_const_to_mplace(raw).unwrap();
57-
const_to_valtree_inner(&ecx, &place)
58-
}
59-
60-
#[instrument(skip(ecx), level = "debug")]
61-
fn branches<'tcx>(
62-
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
63-
place: &MPlaceTy<'tcx>,
64-
n: usize,
65-
variant: Option<VariantIdx>,
66-
) -> Option<ty::ValTree<'tcx>> {
67-
let place = match variant {
68-
Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
69-
None => *place,
70-
};
71-
let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
72-
debug!(?place, ?variant);
73-
74-
let fields = (0..n).map(|i| {
75-
let field = ecx.mplace_field(&place, i).unwrap();
76-
const_to_valtree_inner(ecx, &field)
77-
});
78-
// For enums, we prepend their variant index before the variant's fields so we can figure out
79-
// the variant again when just seeing a valtree.
80-
let branches = variant.into_iter().chain(fields);
81-
Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
82-
}
83-
84-
fn slice_branches<'tcx>(
85-
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
86-
place: &MPlaceTy<'tcx>,
87-
) -> Option<ty::ValTree<'tcx>> {
88-
let n = place.len(&ecx.tcx()).expect(&format!("expected to use len of place {:?}", place));
89-
let branches = (0..n).map(|i| {
90-
let place_elem = ecx.mplace_index(place, i).unwrap();
91-
const_to_valtree_inner(ecx, &place_elem)
92-
});
93-
94-
Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
95-
}
96-
97-
#[instrument(skip(ecx), level = "debug")]
98-
fn const_to_valtree_inner<'tcx>(
99-
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
100-
place: &MPlaceTy<'tcx>,
101-
) -> Option<ty::ValTree<'tcx>> {
102-
match place.layout.ty.kind() {
103-
ty::FnDef(..) => Some(ty::ValTree::zst()),
104-
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
105-
let val = ecx.read_immediate(&place.into()).unwrap();
106-
let val = val.to_scalar().unwrap();
107-
Some(ty::ValTree::Leaf(val.assert_int()))
108-
}
109-
110-
// Raw pointers are not allowed in type level constants, as we cannot properly test them for
111-
// equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
112-
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
113-
// agree with runtime equality tests.
114-
ty::FnPtr(_) | ty::RawPtr(_) => None,
115-
116-
ty::Ref(_, _, _) => {
117-
let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
118-
debug!(?derefd_place);
119-
120-
const_to_valtree_inner(ecx, &derefd_place)
121-
}
122-
123-
ty::Str | ty::Slice(_) | ty::Array(_, _) => {
124-
let valtree = slice_branches(ecx, place);
125-
debug!(?valtree);
126-
127-
valtree
128-
}
129-
// Trait objects are not allowed in type level constants, as we have no concept for
130-
// resolving their backing type, even if we can do that at const eval time. We may
131-
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
132-
// but it is unclear if this is useful.
133-
ty::Dynamic(..) => None,
134-
135-
ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
136-
137-
ty::Adt(def, _) => {
138-
if def.variants().is_empty() {
139-
bug!("uninhabited types should have errored and never gotten converted to valtree")
140-
}
141-
142-
let variant = ecx.read_discriminant(&place.into()).unwrap().1;
143-
144-
branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
145-
}
146-
147-
ty::Never
148-
| ty::Error(_)
149-
| ty::Foreign(..)
150-
| ty::Infer(ty::FreshIntTy(_))
151-
| ty::Infer(ty::FreshFloatTy(_))
152-
| ty::Projection(..)
153-
| ty::Param(_)
154-
| ty::Bound(..)
155-
| ty::Placeholder(..)
156-
// FIXME(oli-obk): we could look behind opaque types
157-
| ty::Opaque(..)
158-
| ty::Infer(_)
159-
// FIXME(oli-obk): we can probably encode closures just like structs
160-
| ty::Closure(..)
161-
| ty::Generator(..)
162-
| ty::GeneratorWitness(..) => None,
163-
}
164-
}
165-
16641
/// This function should never fail for validated constants. However, it is also invoked from the
16742
/// pretty printer which might attempt to format invalid constants and in that case it might fail.
16843
pub(crate) fn try_destructure_const<'tcx>(
@@ -202,6 +77,7 @@ pub(crate) fn try_destructure_const<'tcx>(
20277
Ok(mir::DestructuredConst { variant, fields })
20378
}
20479

80+
#[instrument(skip(tcx), level = "debug")]
20581
pub(crate) fn deref_const<'tcx>(
20682
tcx: TyCtxt<'tcx>,
20783
param_env: ty::ParamEnv<'tcx>,

0 commit comments

Comments
 (0)