mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-07 12:33:14 +00:00
Auto merge of #115804 - RalfJung:valtree-to-const-val, r=oli-obk
consistently pass ty::Const through valtrees Some drive-by things extracted from https://github.com/rust-lang/rust/pull/115748.
This commit is contained in:
commit
e437e57954
@ -7,7 +7,7 @@ use crate::interpret::{
|
|||||||
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
|
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
|
||||||
MemoryKind, PlaceTy, Projectable, Scalar,
|
MemoryKind, PlaceTy, Projectable, Scalar,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||||
use rustc_span::source_map::DUMMY_SP;
|
use rustc_span::source_map::DUMMY_SP;
|
||||||
use rustc_target::abi::VariantIdx;
|
use rustc_target::abi::VariantIdx;
|
||||||
@ -189,12 +189,11 @@ fn reconstruct_place_meta<'tcx>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(ecx), level = "debug", ret)]
|
#[instrument(skip(ecx), level = "debug", ret)]
|
||||||
fn create_pointee_place<'tcx>(
|
fn create_valtree_place<'tcx>(
|
||||||
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
||||||
ty: Ty<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
valtree: ty::ValTree<'tcx>,
|
valtree: ty::ValTree<'tcx>,
|
||||||
) -> MPlaceTy<'tcx> {
|
) -> MPlaceTy<'tcx> {
|
||||||
let layout = ecx.layout_of(ty).unwrap();
|
|
||||||
let meta = reconstruct_place_meta(layout, valtree, ecx.tcx.tcx);
|
let meta = reconstruct_place_meta(layout, valtree, ecx.tcx.tcx);
|
||||||
ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
|
ecx.allocate_dyn(layout, MemoryKind::Stack, meta).unwrap()
|
||||||
}
|
}
|
||||||
@ -216,11 +215,6 @@ pub fn valtree_to_const_value<'tcx>(
|
|||||||
// FIXME Does this need an example?
|
// FIXME Does this need an example?
|
||||||
|
|
||||||
let (param_env, ty) = param_env_ty.into_parts();
|
let (param_env, ty) = param_env_ty.into_parts();
|
||||||
let mut ecx: crate::interpret::InterpCx<
|
|
||||||
'_,
|
|
||||||
'_,
|
|
||||||
crate::const_eval::CompileTimeInterpreter<'_, '_>,
|
|
||||||
> = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
|
||||||
|
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::FnDef(..) => {
|
ty::FnDef(..) => {
|
||||||
@ -233,33 +227,44 @@ pub fn valtree_to_const_value<'tcx>(
|
|||||||
"ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
|
"ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
|
ty::Ref(_, inner_ty, _) => {
|
||||||
let place = match ty.kind() {
|
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
||||||
ty::Ref(_, inner_ty, _) => {
|
let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
|
||||||
// Need to create a place for the pointee (the reference itself will be an immediate)
|
let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
|
||||||
create_pointee_place(&mut ecx, *inner_ty, valtree)
|
op_to_const(&ecx, &imm.into())
|
||||||
|
}
|
||||||
|
ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
|
||||||
|
let layout = tcx.layout_of(param_env_ty).unwrap();
|
||||||
|
if layout.is_zst() {
|
||||||
|
// Fast path to avoid some allocations.
|
||||||
|
return ConstValue::ZeroSized;
|
||||||
|
}
|
||||||
|
if layout.abi.is_scalar()
|
||||||
|
&& (matches!(ty.kind(), ty::Tuple(_))
|
||||||
|
|| matches!(ty.kind(), ty::Adt(def, _) if def.is_struct()))
|
||||||
|
{
|
||||||
|
// A Scalar tuple/struct; we can avoid creating an allocation.
|
||||||
|
let branches = valtree.unwrap_branch();
|
||||||
|
// Find the non-ZST field. (There can be aligned ZST!)
|
||||||
|
for (i, &inner_valtree) in branches.iter().enumerate() {
|
||||||
|
let field = layout.field(&LayoutCx { tcx, param_env }, i);
|
||||||
|
if !field.is_zst() {
|
||||||
|
return valtree_to_const_value(tcx, param_env.and(field.ty), inner_valtree);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
bug!("could not find non-ZST field during in {layout:#?}");
|
||||||
// Need to create a place for this valtree.
|
}
|
||||||
create_pointee_place(&mut ecx, ty, valtree)
|
|
||||||
}
|
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
|
||||||
};
|
|
||||||
debug!(?place);
|
// Need to create a place for this valtree.
|
||||||
|
let place = create_valtree_place(&mut ecx, layout, valtree);
|
||||||
|
|
||||||
valtree_into_mplace(&mut ecx, &place, valtree);
|
valtree_into_mplace(&mut ecx, &place, valtree);
|
||||||
dump_place(&ecx, &place);
|
dump_place(&ecx, &place);
|
||||||
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
|
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
|
||||||
|
|
||||||
match ty.kind() {
|
op_to_const(&ecx, &place.into())
|
||||||
ty::Ref(_, _, _) => {
|
|
||||||
let ref_place = place.to_ref(&tcx);
|
|
||||||
let imm =
|
|
||||||
ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap());
|
|
||||||
|
|
||||||
op_to_const(&ecx, &imm.into())
|
|
||||||
}
|
|
||||||
_ => op_to_const(&ecx, &place.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ty::Never
|
ty::Never
|
||||||
| ty::Error(_)
|
| ty::Error(_)
|
||||||
@ -283,6 +288,22 @@ pub fn valtree_to_const_value<'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Put a valtree into memory and return a reference to that.
|
||||||
|
fn valtree_to_ref<'tcx>(
|
||||||
|
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
||||||
|
valtree: ty::ValTree<'tcx>,
|
||||||
|
pointee_ty: Ty<'tcx>,
|
||||||
|
) -> Immediate {
|
||||||
|
let pointee_place = create_valtree_place(ecx, ecx.layout_of(pointee_ty).unwrap(), valtree);
|
||||||
|
debug!(?pointee_place);
|
||||||
|
|
||||||
|
valtree_into_mplace(ecx, &pointee_place, valtree);
|
||||||
|
dump_place(ecx, &pointee_place);
|
||||||
|
intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
|
||||||
|
|
||||||
|
pointee_place.to_ref(&ecx.tcx)
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip(ecx), level = "debug")]
|
#[instrument(skip(ecx), level = "debug")]
|
||||||
fn valtree_into_mplace<'tcx>(
|
fn valtree_into_mplace<'tcx>(
|
||||||
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
|
||||||
@ -292,7 +313,6 @@ fn valtree_into_mplace<'tcx>(
|
|||||||
// This will match on valtree and write the value(s) corresponding to the ValTree
|
// This will match on valtree and write the value(s) corresponding to the ValTree
|
||||||
// inside the place recursively.
|
// inside the place recursively.
|
||||||
|
|
||||||
let tcx = ecx.tcx.tcx;
|
|
||||||
let ty = place.layout.ty;
|
let ty = place.layout.ty;
|
||||||
|
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
@ -305,27 +325,8 @@ fn valtree_into_mplace<'tcx>(
|
|||||||
ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap();
|
ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap();
|
||||||
}
|
}
|
||||||
ty::Ref(_, inner_ty, _) => {
|
ty::Ref(_, inner_ty, _) => {
|
||||||
let pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
|
let imm = valtree_to_ref(ecx, valtree, *inner_ty);
|
||||||
debug!(?pointee_place);
|
|
||||||
|
|
||||||
valtree_into_mplace(ecx, &pointee_place, valtree);
|
|
||||||
dump_place(ecx, &pointee_place);
|
|
||||||
intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
|
|
||||||
|
|
||||||
let imm = match inner_ty.kind() {
|
|
||||||
ty::Slice(_) | ty::Str => {
|
|
||||||
let len = valtree.unwrap_branch().len();
|
|
||||||
let len_scalar = Scalar::from_target_usize(len as u64, &tcx);
|
|
||||||
|
|
||||||
Immediate::ScalarPair(
|
|
||||||
Scalar::from_maybe_pointer(pointee_place.ptr(), &tcx),
|
|
||||||
len_scalar,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => pointee_place.to_ref(&tcx),
|
|
||||||
};
|
|
||||||
debug!(?imm);
|
debug!(?imm);
|
||||||
|
|
||||||
ecx.write_immediate(imm, place).unwrap();
|
ecx.write_immediate(imm, place).unwrap();
|
||||||
}
|
}
|
||||||
ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
|
ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
|
||||||
|
@ -2297,7 +2297,11 @@ pub struct Constant<'tcx> {
|
|||||||
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
|
||||||
#[derive(Lift, TypeFoldable, TypeVisitable)]
|
#[derive(Lift, TypeFoldable, TypeVisitable)]
|
||||||
pub enum ConstantKind<'tcx> {
|
pub enum ConstantKind<'tcx> {
|
||||||
/// This constant came from the type system
|
/// This constant came from the type system.
|
||||||
|
///
|
||||||
|
/// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
|
||||||
|
/// this ensures that we consistently produce "clean" values without data in the padding or
|
||||||
|
/// anything like that.
|
||||||
Ty(ty::Const<'tcx>),
|
Ty(ty::Const<'tcx>),
|
||||||
|
|
||||||
/// An unevaluated mir constant which is not part of the type system.
|
/// An unevaluated mir constant which is not part of the type system.
|
||||||
@ -2373,23 +2377,19 @@ impl<'tcx> ConstantKind<'tcx> {
|
|||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
span: Option<Span>,
|
span: Option<Span>,
|
||||||
) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
|
) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
|
||||||
let (uneval, param_env) = match self {
|
match self {
|
||||||
ConstantKind::Ty(c) => {
|
ConstantKind::Ty(c) => {
|
||||||
if let ty::ConstKind::Unevaluated(uneval) = c.kind() {
|
// We want to consistently have a "clean" value for type system constants (i.e., no
|
||||||
// Avoid the round-trip via valtree, evaluate directly to ConstValue.
|
// data hidden in the padding), so we always go through a valtree here.
|
||||||
let (param_env, uneval) = uneval.prepare_for_eval(tcx, param_env);
|
let val = c.eval(tcx, param_env, span)?;
|
||||||
(uneval.expand(), param_env)
|
Ok(tcx.valtree_to_const_val((self.ty(), val)))
|
||||||
} else {
|
|
||||||
// It's already a valtree, or an error.
|
|
||||||
let val = c.eval(tcx, param_env, span)?;
|
|
||||||
return Ok(tcx.valtree_to_const_val((self.ty(), val)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConstantKind::Unevaluated(uneval, _) => (uneval, param_env),
|
ConstantKind::Unevaluated(uneval, _) => {
|
||||||
ConstantKind::Val(val, _) => return Ok(val),
|
// FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
|
||||||
};
|
tcx.const_eval_resolve(param_env, uneval, span)
|
||||||
// FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
|
}
|
||||||
tcx.const_eval_resolve(param_env, uneval, span)
|
ConstantKind::Val(val, _) => Ok(val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalizes the constant to a value or an error if possible.
|
/// Normalizes the constant to a value or an error if possible.
|
||||||
@ -2605,10 +2605,10 @@ impl<'tcx> ConstantKind<'tcx> {
|
|||||||
pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
||||||
match c.kind() {
|
match c.kind() {
|
||||||
ty::ConstKind::Value(valtree) => {
|
ty::ConstKind::Value(valtree) => {
|
||||||
|
// Make sure that if `c` is normalized, then the return value is normalized.
|
||||||
let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
|
let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
|
||||||
Self::Val(const_val, c.ty())
|
Self::Val(const_val, c.ty())
|
||||||
}
|
}
|
||||||
ty::ConstKind::Unevaluated(uv) => Self::Unevaluated(uv.expand(), c.ty()),
|
|
||||||
_ => Self::Ty(c),
|
_ => Self::Ty(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,6 @@ impl rustc_errors::IntoDiagnosticArg for UnevaluatedConst<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> UnevaluatedConst<'tcx> {
|
impl<'tcx> UnevaluatedConst<'tcx> {
|
||||||
#[inline]
|
|
||||||
pub fn expand(self) -> mir::UnevaluatedConst<'tcx> {
|
|
||||||
mir::UnevaluatedConst { def: self.def, args: self.args, promoted: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this
|
/// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this
|
||||||
/// hurts performance.
|
/// hurts performance.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -749,39 +749,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
|||||||
#[instrument(skip(self), level = "debug")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) {
|
fn visit_constant(&mut self, constant: &mir::Constant<'tcx>, location: Location) {
|
||||||
let literal = self.monomorphize(constant.literal);
|
let literal = self.monomorphize(constant.literal);
|
||||||
let val = match literal {
|
let param_env = ty::ParamEnv::reveal_all();
|
||||||
mir::ConstantKind::Val(val, _) => val,
|
let val = match literal.eval(self.tcx, param_env, None) {
|
||||||
mir::ConstantKind::Ty(ct) => match ct.kind() {
|
Ok(v) => v,
|
||||||
ty::ConstKind::Value(val) => self.tcx.valtree_to_const_val((ct.ty(), val)),
|
Err(ErrorHandled::Reported(_)) => return,
|
||||||
ty::ConstKind::Unevaluated(ct) => {
|
Err(ErrorHandled::TooGeneric) => span_bug!(
|
||||||
debug!(?ct);
|
self.body.source_info(location).span,
|
||||||
let param_env = ty::ParamEnv::reveal_all();
|
"collection encountered polymorphic constant: {:?}",
|
||||||
match self.tcx.const_eval_resolve(param_env, ct.expand(), None) {
|
literal
|
||||||
// The `monomorphize` call should have evaluated that constant already.
|
),
|
||||||
Ok(val) => val,
|
|
||||||
Err(ErrorHandled::Reported(_)) => return,
|
|
||||||
Err(ErrorHandled::TooGeneric) => span_bug!(
|
|
||||||
self.body.source_info(location).span,
|
|
||||||
"collection encountered polymorphic constant: {:?}",
|
|
||||||
literal
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return,
|
|
||||||
},
|
|
||||||
mir::ConstantKind::Unevaluated(uv, _) => {
|
|
||||||
let param_env = ty::ParamEnv::reveal_all();
|
|
||||||
match self.tcx.const_eval_resolve(param_env, uv, None) {
|
|
||||||
// The `monomorphize` call should have evaluated that constant already.
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(ErrorHandled::Reported(_)) => return,
|
|
||||||
Err(ErrorHandled::TooGeneric) => span_bug!(
|
|
||||||
self.body.source_info(location).span,
|
|
||||||
"collection encountered polymorphic constant: {:?}",
|
|
||||||
literal
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
collect_const_value(self.tcx, val, self.output);
|
collect_const_value(self.tcx, val, self.output);
|
||||||
MirVisitor::visit_ty(self, literal.ty(), TyContext::Location(location));
|
MirVisitor::visit_ty(self, literal.ty(), TyContext::Location(location));
|
||||||
|
Loading…
Reference in New Issue
Block a user