Rollup merge of #95426 - b-naber:valtrees-slice, r=RalfJung,oli-obk

Include Refs in Valtree Creation

This adds references to `const_to_valtree`, which isn't used in the compiler yet, but after the previous changes we made to the thir and mir representations and this change we should be able to finally introduce them in the next PR.

I wasn't able to properly test this code, except indirectly by including a call of `const_to_valtree` in the code that currently creates constants (`turn_into_const_value`).

r? `@lcnr`

cc `@oli-obk` `@RalfJung`
This commit is contained in:
Dylan DPC 2022-04-16 19:42:03 +02:00 committed by GitHub
commit 1dc672a766
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 27 deletions

View File

@ -188,6 +188,7 @@ pub(super) fn op_to_const<'tcx>(
} }
} }
#[instrument(skip(tcx), level = "debug")]
fn turn_into_const_value<'tcx>( fn turn_into_const_value<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
constant: ConstAlloc<'tcx>, constant: ConstAlloc<'tcx>,
@ -206,6 +207,7 @@ fn turn_into_const_value<'tcx>(
!is_static || cid.promoted.is_some(), !is_static || cid.promoted.is_some(),
"the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead" "the `eval_to_const_value_raw` query should not be used for statics, use `eval_to_allocation` instead"
); );
// Turn this into a proper constant. // Turn this into a proper constant.
op_to_const(&ecx, &mplace.into()) op_to_const(&ecx, &mplace.into())
} }

View File

@ -3,12 +3,14 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use rustc_hir::Mutability; use rustc_hir::Mutability;
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{ use rustc_middle::{
mir::{self, interpret::ConstAlloc}, mir::{self, interpret::ConstAlloc},
ty::ScalarInt, ty::ScalarInt,
}; };
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
use rustc_target::abi::VariantIdx;
use crate::interpret::{ use crate::interpret::{
intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy, intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
@ -55,28 +57,48 @@ pub(crate) fn const_to_valtree<'tcx>(
const_to_valtree_inner(&ecx, &place) const_to_valtree_inner(&ecx, &place)
} }
#[instrument(skip(ecx), level = "debug")]
fn branches<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
n: usize,
variant: Option<VariantIdx>,
) -> Option<ty::ValTree<'tcx>> {
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()))));
debug!(?place, ?variant);
let fields = (0..n).map(|i| {
let field = ecx.mplace_field(&place, i).unwrap();
const_to_valtree_inner(ecx, &field)
});
// For enums, we prepend their variant index before the variant's fields so we can figure out
// the variant again when just seeing a valtree.
let branches = variant.into_iter().chain(fields);
Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
}
fn slice_branches<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let n = place.len(&ecx.tcx()).expect(&format!("expected to use len of place {:?}", place));
let branches = (0..n).map(|i| {
let place_elem = ecx.mplace_index(place, i).unwrap();
const_to_valtree_inner(ecx, &place_elem)
});
Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
}
#[instrument(skip(ecx), level = "debug")]
fn const_to_valtree_inner<'tcx>( fn const_to_valtree_inner<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>, ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>, place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> { ) -> Option<ty::ValTree<'tcx>> {
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)
});
// For enums, we preped their variant index before the variant's fields so we can figure out
// the variant again when just seeing a valtree.
let branches = variant.into_iter().chain(fields);
Some(ty::ValTree::Branch(
ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
))
};
match place.layout.ty.kind() { match place.layout.ty.kind() {
ty::FnDef(..) => Some(ty::ValTree::zst()), ty::FnDef(..) => Some(ty::ValTree::zst()),
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
@ -90,19 +112,27 @@ fn const_to_valtree_inner<'tcx>(
// Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
// agree with runtime equality tests. // agree with runtime equality tests.
ty::FnPtr(_) | ty::RawPtr(_) => None, ty::FnPtr(_) | ty::RawPtr(_) => None,
ty::Ref(..) => unimplemented!("need to use deref_const"),
ty::Ref(_, _, _) => {
let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
debug!(?derefd_place);
const_to_valtree_inner(ecx, &derefd_place)
}
ty::Str | ty::Slice(_) | ty::Array(_, _) => {
let valtree = slice_branches(ecx, place);
debug!(?valtree);
valtree
}
// Trait objects are not allowed in type level constants, as we have no concept for // Trait objects are not allowed in type level constants, as we have no concept for
// resolving their backing type, even if we can do that at const eval time. We may // resolving their backing type, even if we can do that at const eval time. We may
// hypothetically be able to allow `dyn StructuralEq` trait objects in the future, // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
// but it is unclear if this is useful. // but it is unclear if this is useful.
ty::Dynamic(..) => None, ty::Dynamic(..) => None,
ty::Slice(_) | ty::Str => { ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
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, _) => { ty::Adt(def, _) => {
if def.variants().is_empty() { if def.variants().is_empty() {
@ -111,7 +141,7 @@ fn const_to_valtree_inner<'tcx>(
let variant = ecx.read_discriminant(&place.into()).unwrap().1; let variant = ecx.read_discriminant(&place.into()).unwrap().1;
branches(def.variant(variant).fields.len(), def.is_enum().then_some(variant)) branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
} }
ty::Never ty::Never

View File

@ -191,7 +191,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
} }
#[inline] #[inline]
pub(super) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
if self.layout.is_unsized() { if self.layout.is_unsized() {
// We need to consult `meta` metadata // We need to consult `meta` metadata
match self.layout.ty.kind() { match self.layout.ty.kind() {

View File

@ -1,5 +1,5 @@
use super::ScalarInt; use super::ScalarInt;
use rustc_macros::HashStable; use rustc_macros::{HashStable, TyDecodable, TyEncodable};
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)] #[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
#[derive(HashStable)] #[derive(HashStable)]