Skip to content

Commit

Permalink
Rollup merge of #121396 - RalfJung:mir-const-value-inspect, r=oli-obk
Browse files Browse the repository at this point in the history
make it possible for outside crates to inspect a mir::ConstValue with the interpreter

For MiniRust we need to convert MIR constant values into MiniRust constant values. However, it's not currently possible to get nice high-level access to the inerts of a `ConstValue`: we can access the raw contents (the allocation / `ScalarInt`), but if it is e.g. of enum type and we want to determine which variant is encoded, we are stuck. There's only `try_destructure_mir_constant_for_user_output` which is meant for diagnostics, so it doesn't fit.

The interpreter has all the APIs to traverse such a value, so we just need a way to get such a ConstValue into an interpreter instance. This adds the public functions necessary to make that happen.
  • Loading branch information
fmease authored Feb 21, 2024
2 parents ae01e99 + 9eabdc2 commit bd7ba27
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 12 deletions.
25 changes: 21 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use either::{Left, Right};
use rustc_hir::def::DefKind;
use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_target::abi::{self, Abi};
Expand Down Expand Up @@ -87,13 +88,16 @@ fn eval_body_using_ecx<'mir, 'tcx>(
}

/// The `InterpCx` is only meant to be used to do field and index projections into constants for
/// `simd_shuffle` and const patterns in match arms. It never performs alignment checks.
/// `simd_shuffle` and const patterns in match arms.
///
/// This should *not* be used to do any actual interpretation. In particular, alignment checks are
/// turned off!
///
/// The function containing the `match` that is currently being analyzed may have generic bounds
/// that inform us about the generic bounds of the constant. E.g., using an associated constant
/// of a function's generic parameter will require knowledge about the bounds on the generic
/// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
pub(crate) fn mk_eval_cx<'mir, 'tcx>(
pub(crate) fn mk_eval_cx_to_read_const_val<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
root_span: Span,
param_env: ty::ParamEnv<'tcx>,
Expand All @@ -108,6 +112,19 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
)
}

/// Create an interpreter context to inspect the given `ConstValue`.
/// Returns both the context and an `OpTy` that represents the constant.
pub fn mk_eval_cx_for_const_val<'mir, 'tcx>(
tcx: TyCtxtAt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
val: mir::ConstValue<'tcx>,
ty: Ty<'tcx>,
) -> Option<(CompileTimeEvalContext<'mir, 'tcx>, OpTy<'tcx>)> {
let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
let op = ecx.const_val_to_op(val, ty, None).ok()?;
Some((ecx, op))
}

/// This function converts an interpreter value into a MIR constant.
///
/// The `for_diagnostics` flag turns the usual rules for returning `ConstValue::Scalar` into a
Expand Down Expand Up @@ -203,7 +220,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
let def_id = cid.instance.def.def_id();
let is_static = tcx.is_static(def_id);
// This is just accessing an already computed constant, so no need to check alignment here.
let ecx = mk_eval_cx(
let ecx = mk_eval_cx_to_read_const_val(
tcx,
tcx.def_span(key.value.instance.def_id()),
key.param_env,
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_const_eval/src/const_eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
ty: Ty<'tcx>,
) -> Option<mir::DestructuredConstant<'tcx>> {
let param_env = ty::ParamEnv::reveal_all();
let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
let op = ecx.const_val_to_op(val, ty, None).ok()?;
let (ecx, op) = mk_eval_cx_for_const_val(tcx, param_env, val, ty)?;

// We go to `usize` as we cannot allocate anything bigger anyway.
let (field_count, variant, down) = match ty.kind() {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Abi, VariantIdx};

use super::eval_queries::{mk_eval_cx, op_to_const};
use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
use crate::const_eval::CanAccessMutGlobal;
Expand Down Expand Up @@ -223,7 +223,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;

// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
let ecx = mk_eval_cx_to_read_const_val(
tcx,
DUMMY_SP,
param_env,
Expand Down Expand Up @@ -287,7 +287,8 @@ pub fn valtree_to_const_value<'tcx>(
}
}
ty::Ref(_, inner_ty, _) => {
let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
let mut ecx =
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
Expand All @@ -314,7 +315,8 @@ pub fn valtree_to_const_value<'tcx>(
bug!("could not find non-ZST field during in {layout:#?}");
}

let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
let mut ecx =
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);

// Need to create a place for this valtree.
let place = create_valtree_place(&mut ecx, layout, valtree);
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_const_eval/src/util/caller_location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_span::symbol::Symbol;
use rustc_type_ir::Mutability;

use crate::const_eval::{mk_eval_cx, CanAccessMutGlobal, CompileTimeEvalContext};
use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext};
use crate::interpret::*;

/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
Expand Down Expand Up @@ -57,7 +57,12 @@ pub(crate) fn const_caller_location_provider(
col: u32,
) -> mir::ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessMutGlobal::No);
let mut ecx = mk_eval_cx_to_read_const_val(
tcx.tcx,
tcx.span,
ty::ParamEnv::reveal_all(),
CanAccessMutGlobal::No,
);

let loc_place = alloc_caller_location(&mut ecx, file, line, col);
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
Expand Down

0 comments on commit bd7ba27

Please sign in to comment.