Skip to content

Commit

Permalink
Auto merge of #116835 - oli-obk:evaluated_static_in_metadata2, r=<try>
Browse files Browse the repository at this point in the history
Various const eval cleanups

This pulls out the pure refactorings from #116564

r? `@RalfJung`
  • Loading branch information
bors committed Oct 17, 2023
2 parents 631a116 + 76869b4 commit 7f8df04
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 86 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/const_eval/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ where
/// This will use the `mk` function for creating the error which will get passed labels according to
/// the `InterpError` and the span and a stacktrace of current execution according to
/// `get_span_and_frames`.
pub(super) fn report<'tcx, C, F, E>(
pub(crate) fn report<'tcx, C, F, E>(
tcx: TyCtxt<'tcx>,
error: InterpError<'tcx>,
span: Option<Span>,
Expand Down
75 changes: 18 additions & 57 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use std::mem;
use either::{Left, Right};

use rustc_hir::def::DefKind;
use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
use rustc_middle::mir::pretty::write_allocation_bytes;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::{self, ConstAlloc, ConstValue};
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
Expand All @@ -19,8 +18,8 @@ use crate::errors;
use crate::errors::ConstEvalError;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup,
intern_const_alloc_recursive, GlobalId, Immediate, InternKind, InterpCx, InterpResult,
MPlaceTy, MemoryKind, OpTy, StackPopCleanup,
};

// Returns a pointer to where the result lives
Expand Down Expand Up @@ -285,15 +284,22 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let def = cid.instance.def.def_id();
let is_static = tcx.is_static(def);

let mut ecx = InterpCx::new(
let ecx = InterpCx::new(
tcx,
tcx.def_span(def),
key.param_env,
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
);
eval_in_interpreter(ecx, cid, is_static)
}

pub fn eval_in_interpreter<'mir, 'tcx>(
mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
cid: GlobalId<'tcx>,
is_static: bool,
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
let res = ecx.load_mir(cid.instance.def, cid.promoted);
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
Err(error) => {
Expand All @@ -306,7 +312,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// If the current item has generics, we'd like to enrich the message with the
// instance and its args: to show the actual compile-time values, in addition to
// the expression, leading to the const eval error.
let instance = &key.value.instance;
let instance = &cid.instance;
if !instance.args.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
("const_with_path", instance)
Expand All @@ -331,60 +337,15 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
Ok(mplace) => {
// Since evaluation had no errors, validate the resulting constant.
// This is a separate `try` block to provide more targeted error reporting.
let validation: Result<_, InterpErrorInfo<'_>> = try {
let mut ref_tracking = RefTracking::new(mplace.clone());
let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
let mode = match tcx.static_mutability(cid.instance.def_id()) {
Some(_) if cid.promoted.is_some() => {
// Promoteds in statics are allowed to point to statics.
CtfeValidationMode::Const { inner, allow_static_ptrs: true }
}
Some(_) => CtfeValidationMode::Regular, // a `static`
None => CtfeValidationMode::Const { inner, allow_static_ptrs: false },
};
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
inner = true;
}
};
let alloc_id = mplace.ptr().provenance.unwrap();
let validation = ecx.const_validate_mplace(&mplace, is_static, cid.promoted.is_some());

// Validation failed, report an error.
if let Err(error) = validation {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();

let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});

let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
let mut bytes = String::new();
if alloc.size() != abi::Size::ZERO {
bytes = "\n".into();
// FIXME(translation) there might be pieces that are translatable.
write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
}
let raw_bytes = errors::RawBytesNote {
size: alloc.size().bytes(),
align: alloc.align.bytes(),
bytes,
};
let alloc_id = mplace.ptr().provenance.unwrap();

Err(super::report(
*ecx.tcx,
error,
None,
|| super::get_span_and_frames(&ecx),
move |span, frames| errors::UndefinedBehavior {
span,
ub_note,
frames,
raw_bytes,
},
))
} else {
validation
// Validation failed, report an error.
.map_err(|error| ecx.const_report_error(error, alloc_id))
// Convert to raw constant
Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty })
}
.map(|()| ConstAlloc { alloc_id, ty: mplace.layout.ty })
}
}
}
37 changes: 24 additions & 13 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::mir::interpret::{alloc_range, ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
Expand All @@ -17,6 +17,7 @@ use rustc_middle::ty::{self, GenericArgsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_session::Limit;
use rustc_span::Span;
use rustc_target::abi::{self, Abi};
use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};

use super::{
Expand Down Expand Up @@ -1069,23 +1070,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
})
}

pub fn eval_global(
pub fn eval_global_scalar(
&self,
instance: ty::Instance<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
let gid = GlobalId { instance, promoted: None };
// For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
// and thus don't care about the parameter environment. While we could just use
// `self.param_env`, that would mean we invoke the query to evaluate the static
// with different parameter environments, thus causing the static to be evaluated
// multiple times.
let param_env = if self.tcx.is_static(gid.instance.def_id()) {
ty::ParamEnv::reveal_all()
let (alloc_id, alloc) = if self.tcx.is_static(gid.instance.def_id()) {
(
self.tcx.reserve_and_set_static_alloc(gid.instance.def_id()),
self.ctfe_query(|tcx| tcx.eval_static_initializer(gid.instance.def_id()))?,
)
} else {
self.param_env
let val = self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(self.param_env.and(gid)))?;
(val.alloc_id, self.tcx.global_alloc(val.alloc_id).unwrap_memory())
};
let val = self.ctfe_query(|tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
self.raw_const_to_mplace(val)

let ty = instance.ty(self.tcx.tcx, self.param_env);
let layout = self.layout_of(ty)?;
let read_provenance = matches!(
layout.abi,
Abi::Scalar(abi::Scalar::Initialized { value: abi::Primitive::Pointer(..), .. })
);

let scalar = alloc
.inner()
.read_scalar(self, alloc_range(Size::ZERO, layout.size), read_provenance)
.map_err(|err| err.to_interp_error(alloc_id))?;
self.adjust_const_scalar(scalar)
}

pub fn eval_mir_constant(
Expand Down
18 changes: 11 additions & 7 deletions compiler/rustc_const_eval/src/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,19 +723,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(op)
}

pub(super) fn adjust_const_scalar(
&self,
scalar: Scalar,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
Ok(match scalar {
Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size),
Scalar::Int(int) => Scalar::Int(int),
})
}

pub(crate) fn const_val_to_op(
&self,
val_val: mir::ConstValue<'tcx>,
ty: Ty<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
// Other cases need layout.
let adjust_scalar = |scalar| -> InterpResult<'tcx, _> {
Ok(match scalar {
Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size),
Scalar::Int(int) => Scalar::Int(int),
})
};
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let imm = match val_val {
mir::ConstValue::Indirect { alloc_id, offset } => {
Expand All @@ -744,7 +748,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
}
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
mir::ConstValue::Scalar(x) => self.adjust_const_scalar(x)?.into(),
mir::ConstValue::ZeroSized => Immediate::Uninit,
mir::ConstValue::Slice { data, meta } => {
// We rely on mutability being set correctly in `data` to prevent writes
Expand Down
69 changes: 65 additions & 4 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
ValidationErrorKind, ValidationErrorKind::*,
ErrorHandled, ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment,
PointerKind, ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
};
use rustc_middle::mir::pretty::write_allocation_bytes;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
self, Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
};

use std::hash::Hash;

use crate::errors;

use super::{
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor,
Expand Down Expand Up @@ -929,7 +932,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// - no pointers to statics.
/// - no `UnsafeCell` or non-ZST `&mut`.
#[inline(always)]
pub fn const_validate_operand(
fn const_validate_operand(
&self,
op: &OpTy<'tcx, M::Provenance>,
path: Vec<PathElem>,
Expand All @@ -939,6 +942,64 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode))
}

#[inline(always)]
pub fn const_validate_mplace(
&self,
mplace: &MPlaceTy<'tcx, M::Provenance>,
is_static: bool,
is_promoted: bool,
) -> InterpResult<'tcx> {
let mut ref_tracking = RefTracking::new(mplace.clone());
let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
let mode = if is_static {
if is_promoted {
// Promoteds in statics are allowed to point to statics.
CtfeValidationMode::Const { inner, allow_static_ptrs: true }
} else {
// a `static`
CtfeValidationMode::Regular
}
} else {
CtfeValidationMode::Const { inner, allow_static_ptrs: false }
};
self.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
inner = true;
}

Ok(())
}

#[inline(always)]
pub fn const_report_error(
&self,
error: InterpErrorInfo<'tcx>,
alloc_id: AllocId,
) -> ErrorHandled {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();

let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});

let alloc = self.tcx.global_alloc(alloc_id).unwrap_memory().inner();
let mut bytes = String::new();
if alloc.size() != abi::Size::ZERO {
bytes = "\n".into();
// FIXME(translation) there might be pieces that are translatable.
write_allocation_bytes(*self.tcx, alloc, &mut bytes, " ").unwrap();
}
let raw_bytes =
errors::RawBytesNote { size: alloc.size().bytes(), align: alloc.align.bytes(), bytes };

crate::const_eval::report(
*self.tcx,
error,
None,
|| crate::const_eval::get_span_and_frames(self),
move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes },
)
}

/// This function checks the data at `op` to be runtime-valid.
/// `op` is assumed to cover valid memory if it is an indirect operand.
/// It will error if the bits at the destination do not match the ones described by the layout.
Expand Down
6 changes: 2 additions & 4 deletions src/tools/miri/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_ref();
let instance = this.resolve_path(path, Namespace::ValueNS);
// We don't give a span -- this isn't actually used directly by the program anyway.
let const_val = this.eval_global(instance).unwrap_or_else(|err| {
this.eval_global_scalar(instance).unwrap_or_else(|err| {
panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
});
this.read_scalar(&const_val)
.unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
})
}

/// Helper function to get a `libc` constant as a `Scalar`.
Expand Down

0 comments on commit 7f8df04

Please sign in to comment.