Rollup merge of #75339 - RalfJung:eval-required, r=oli-obk
evaluate required_consts when pushing stack frame in Miri engine [Just like codegen](https://github.com/rust-lang/rust/pull/70820/files#diff-32c57af5c8e23eb048f55d1e955e5cd5R194), Miri needs to make sure all `required_consts` evaluate successfully, to catch post-monomorphization errors. While at it I also moved the const_eval error reporting logic into rustc_mir::const_eval::error; there is no reason it should be in `rustc_middle`. I kept this in a separate commit for easier reviewing. Helps with https://github.com/rust-lang/miri/issues/1382. I will add a test on the Miri side (done now: https://github.com/rust-lang/miri/pull/1504). r? @oli-obk
This commit is contained in:
commit
9edec571cb
@ -1,17 +1,13 @@
|
||||
use super::{AllocId, Pointer, RawConst, Scalar};
|
||||
|
||||
use crate::mir::interpret::ConstValue;
|
||||
use crate::ty::layout::LayoutError;
|
||||
use crate::ty::query::TyCtxtAt;
|
||||
use crate::ty::{self, layout, tls, FnSig, Ty};
|
||||
use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty};
|
||||
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::definitions::DefPathData;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_session::CtfeBacktrace;
|
||||
use rustc_span::{def_id::DefId, Pos, Span};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use std::{any::Any, backtrace::Backtrace, fmt, mem};
|
||||
|
||||
@ -34,167 +30,6 @@ pub enum ErrorHandled {
|
||||
pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
|
||||
pub type ConstEvalResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConstEvalErr<'tcx> {
|
||||
pub span: Span,
|
||||
pub error: crate::mir::interpret::InterpError<'tcx>,
|
||||
pub stacktrace: Vec<FrameInfo<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
pub lint_root: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
write!(f, "inside closure")?;
|
||||
} else {
|
||||
write!(f, "inside `{}`", self.instance)?;
|
||||
}
|
||||
if !self.span.is_dummy() {
|
||||
let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
|
||||
write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEvalErr<'tcx> {
|
||||
pub fn struct_error(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(tcx, message, emit, None)
|
||||
}
|
||||
|
||||
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
|
||||
self.struct_error(tcx, message, |mut e| e.emit())
|
||||
}
|
||||
|
||||
pub fn report_as_lint(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
lint_root: hir::HirId,
|
||||
span: Option<Span>,
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(
|
||||
tcx,
|
||||
message,
|
||||
|mut lint: DiagnosticBuilder<'_>| {
|
||||
// Apply the span.
|
||||
if let Some(span) = span {
|
||||
let primary_spans = lint.span.primary_spans().to_vec();
|
||||
// point at the actual error as the primary span
|
||||
lint.replace_span_with(span);
|
||||
// point to the `const` statement as a secondary span
|
||||
// they don't have any label
|
||||
for sp in primary_spans {
|
||||
if sp != span {
|
||||
lint.span_label(sp, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
lint.emit();
|
||||
},
|
||||
Some(lint_root),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a diagnostic for this const eval error.
|
||||
///
|
||||
/// Sets the message passed in via `message` and adds span labels with detailed error
|
||||
/// information before handing control back to `emit` to do any final processing.
|
||||
/// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
|
||||
/// function to dispose of the diagnostic properly.
|
||||
///
|
||||
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
|
||||
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
|
||||
fn struct_generic(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
lint_root: Option<hir::HirId>,
|
||||
) -> ErrorHandled {
|
||||
let must_error = match self.error {
|
||||
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
|
||||
return ErrorHandled::TooGeneric;
|
||||
}
|
||||
err_inval!(TypeckError(error_reported)) => {
|
||||
return ErrorHandled::Reported(error_reported);
|
||||
}
|
||||
// We must *always* hard error on these, even if the caller wants just a lint.
|
||||
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
|
||||
_ => false,
|
||||
};
|
||||
trace!("reporting const eval failure at {:?}", self.span);
|
||||
|
||||
let err_msg = match &self.error {
|
||||
InterpError::MachineStop(msg) => {
|
||||
// A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
|
||||
// Should be turned into a string by now.
|
||||
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
|
||||
}
|
||||
err => err.to_string(),
|
||||
};
|
||||
|
||||
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
|
||||
if let Some(span_msg) = span_msg {
|
||||
err.span_label(self.span, span_msg);
|
||||
}
|
||||
// Add spans for the stacktrace. Don't print a single-line backtrace though.
|
||||
if self.stacktrace.len() > 1 {
|
||||
for frame_info in &self.stacktrace {
|
||||
err.span_label(frame_info.span, frame_info.to_string());
|
||||
}
|
||||
}
|
||||
// Let the caller finish the job.
|
||||
emit(err)
|
||||
};
|
||||
|
||||
if must_error {
|
||||
// The `message` makes little sense here, this is a more serious error than the
|
||||
// caller thinks anyway.
|
||||
// See <https://github.com/rust-lang/rust/pull/63152>.
|
||||
finish(struct_error(tcx, &err_msg), None);
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
} else {
|
||||
// Regular case.
|
||||
if let Some(lint_root) = lint_root {
|
||||
// Report as lint.
|
||||
let hir_id = self
|
||||
.stacktrace
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|frame| frame.lint_root)
|
||||
.unwrap_or(lint_root);
|
||||
tcx.struct_span_lint_hir(
|
||||
rustc_session::lint::builtin::CONST_ERR,
|
||||
hir_id,
|
||||
tcx.span,
|
||||
|lint| finish(lint.build(message), Some(err_msg)),
|
||||
);
|
||||
ErrorHandled::Linted
|
||||
} else {
|
||||
// Report as hard error.
|
||||
finish(struct_error(tcx, message), Some(err_msg));
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
|
||||
struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
|
||||
}
|
||||
|
@ -117,9 +117,9 @@ macro_rules! throw_machine_stop {
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
pub use self::error::{
|
||||
struct_error, CheckInAllocMsg, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled,
|
||||
FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType,
|
||||
ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
struct_error, CheckInAllocMsg, ConstEvalRawResult, ConstEvalResult, ErrorHandled, InterpError,
|
||||
InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, ResourceExhaustionInfo,
|
||||
UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
};
|
||||
|
||||
pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit};
|
||||
|
@ -1,12 +1,16 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_errors::{DiagnosticBuilder, ErrorReported};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::ty::ConstInt;
|
||||
use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use super::InterpCx;
|
||||
use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine};
|
||||
use crate::interpret::{
|
||||
struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine,
|
||||
};
|
||||
|
||||
/// The CTFE machine has some custom error kinds.
|
||||
#[derive(Clone, Debug)]
|
||||
@ -48,15 +52,155 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
impl Error for ConstEvalErrKind {}
|
||||
|
||||
/// Turn an interpreter error into something to report to the user.
|
||||
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
||||
/// Should be called only if the error is actually going to to be reported!
|
||||
pub fn error_to_const_error<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
span: Option<Span>,
|
||||
) -> ConstEvalErr<'tcx> {
|
||||
error.print_backtrace();
|
||||
let stacktrace = ecx.generate_stacktrace();
|
||||
ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) }
|
||||
/// When const-evaluation errors, this type is constructed with the resulting information,
|
||||
/// and then used to emit the error as a lint or hard error.
|
||||
#[derive(Debug)]
|
||||
pub struct ConstEvalErr<'tcx> {
|
||||
pub span: Span,
|
||||
pub error: InterpError<'tcx>,
|
||||
pub stacktrace: Vec<FrameInfo<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> ConstEvalErr<'tcx> {
|
||||
/// Turn an interpreter error into something to report to the user.
|
||||
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
|
||||
/// Should be called only if the error is actually going to to be reported!
|
||||
pub fn new<'mir, M: Machine<'mir, 'tcx>>(
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
error: InterpErrorInfo<'tcx>,
|
||||
span: Option<Span>,
|
||||
) -> ConstEvalErr<'tcx>
|
||||
where
|
||||
'tcx: 'mir,
|
||||
{
|
||||
error.print_backtrace();
|
||||
let stacktrace = ecx.generate_stacktrace();
|
||||
ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) }
|
||||
}
|
||||
|
||||
pub fn struct_error(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(tcx, message, emit, None)
|
||||
}
|
||||
|
||||
pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
|
||||
self.struct_error(tcx, message, |mut e| e.emit())
|
||||
}
|
||||
|
||||
pub fn report_as_lint(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
lint_root: hir::HirId,
|
||||
span: Option<Span>,
|
||||
) -> ErrorHandled {
|
||||
self.struct_generic(
|
||||
tcx,
|
||||
message,
|
||||
|mut lint: DiagnosticBuilder<'_>| {
|
||||
// Apply the span.
|
||||
if let Some(span) = span {
|
||||
let primary_spans = lint.span.primary_spans().to_vec();
|
||||
// point at the actual error as the primary span
|
||||
lint.replace_span_with(span);
|
||||
// point to the `const` statement as a secondary span
|
||||
// they don't have any label
|
||||
for sp in primary_spans {
|
||||
if sp != span {
|
||||
lint.span_label(sp, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
lint.emit();
|
||||
},
|
||||
Some(lint_root),
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a diagnostic for this const eval error.
|
||||
///
|
||||
/// Sets the message passed in via `message` and adds span labels with detailed error
|
||||
/// information before handing control back to `emit` to do any final processing.
|
||||
/// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
|
||||
/// function to dispose of the diagnostic properly.
|
||||
///
|
||||
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
|
||||
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
|
||||
fn struct_generic(
|
||||
&self,
|
||||
tcx: TyCtxtAt<'tcx>,
|
||||
message: &str,
|
||||
emit: impl FnOnce(DiagnosticBuilder<'_>),
|
||||
lint_root: Option<hir::HirId>,
|
||||
) -> ErrorHandled {
|
||||
let must_error = match self.error {
|
||||
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
|
||||
return ErrorHandled::TooGeneric;
|
||||
}
|
||||
err_inval!(TypeckError(error_reported)) => {
|
||||
return ErrorHandled::Reported(error_reported);
|
||||
}
|
||||
// We must *always* hard error on these, even if the caller wants just a lint.
|
||||
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
|
||||
_ => false,
|
||||
};
|
||||
trace!("reporting const eval failure at {:?}", self.span);
|
||||
|
||||
let err_msg = match &self.error {
|
||||
InterpError::MachineStop(msg) => {
|
||||
// A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
|
||||
// Should be turned into a string by now.
|
||||
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
|
||||
}
|
||||
err => err.to_string(),
|
||||
};
|
||||
|
||||
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
|
||||
if let Some(span_msg) = span_msg {
|
||||
err.span_label(self.span, span_msg);
|
||||
}
|
||||
// Add spans for the stacktrace. Don't print a single-line backtrace though.
|
||||
if self.stacktrace.len() > 1 {
|
||||
for frame_info in &self.stacktrace {
|
||||
err.span_label(frame_info.span, frame_info.to_string());
|
||||
}
|
||||
}
|
||||
// Let the caller finish the job.
|
||||
emit(err)
|
||||
};
|
||||
|
||||
if must_error {
|
||||
// The `message` makes little sense here, this is a more serious error than the
|
||||
// caller thinks anyway.
|
||||
// See <https://github.com/rust-lang/rust/pull/63152>.
|
||||
finish(struct_error(tcx, &err_msg), None);
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
} else {
|
||||
// Regular case.
|
||||
if let Some(lint_root) = lint_root {
|
||||
// Report as lint.
|
||||
let hir_id = self
|
||||
.stacktrace
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|frame| frame.lint_root)
|
||||
.unwrap_or(lint_root);
|
||||
tcx.struct_span_lint_hir(
|
||||
rustc_session::lint::builtin::CONST_ERR,
|
||||
hir_id,
|
||||
tcx.span,
|
||||
|lint| finish(lint.build(message), Some(err_msg)),
|
||||
);
|
||||
ErrorHandled::Linted
|
||||
} else {
|
||||
// Report as hard error.
|
||||
finish(struct_error(tcx, message), Some(err_msg));
|
||||
ErrorHandled::Reported(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use super::{error_to_const_error, CompileTimeEvalContext, CompileTimeInterpreter, MemoryExtra};
|
||||
use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra};
|
||||
use crate::interpret::eval_nullary_intrinsic;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, Immediate, InternKind,
|
||||
InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RawConst, RefTracking, Scalar,
|
||||
ScalarMaybeUninit, StackPopCleanup,
|
||||
};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{ConstEvalErr, ErrorHandled};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{self, subst::Subst, TyCtxt};
|
||||
use rustc_span::source_map::Span;
|
||||
@ -213,7 +214,7 @@ fn validate_and_turn_into_const<'tcx>(
|
||||
})();
|
||||
|
||||
val.map_err(|error| {
|
||||
let err = error_to_const_error(&ecx, error, None);
|
||||
let err = ConstEvalErr::new(&ecx, error, None);
|
||||
err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| {
|
||||
diag.note(note_on_undefined_behavior_error());
|
||||
diag.emit();
|
||||
@ -312,7 +313,7 @@ pub fn const_eval_raw_provider<'tcx>(
|
||||
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body))
|
||||
.map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })
|
||||
.map_err(|error| {
|
||||
let err = error_to_const_error(&ecx, error, None);
|
||||
let err = ConstEvalErr::new(&ecx, error, None);
|
||||
// errors in statics are always emitted as fatal errors
|
||||
if is_static {
|
||||
// Ensure that if the above error was either `TooGeneric` or `Reported`
|
||||
|
@ -1,22 +1,22 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, def::DefKind, def_id::DefId, definitions::DefPathData};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_middle::ich::StableHashingContext;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{
|
||||
sign_extend, truncate, FrameInfo, GlobalId, InterpResult, Pointer, Scalar,
|
||||
sign_extend, truncate, GlobalId, InterpResult, Pointer, Scalar,
|
||||
};
|
||||
use rustc_middle::ty::layout::{self, TyAndLayout};
|
||||
use rustc_middle::ty::{
|
||||
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
|
||||
};
|
||||
use rustc_span::{source_map::DUMMY_SP, Span};
|
||||
use rustc_span::{source_map::DUMMY_SP, Pos, Span};
|
||||
use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size, TargetDataLayout};
|
||||
|
||||
use super::{
|
||||
@ -88,6 +88,14 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> {
|
||||
pub loc: Option<mir::Location>,
|
||||
}
|
||||
|
||||
/// What we store about a frame in an interpreter backtrace.
|
||||
#[derive(Debug)]
|
||||
pub struct FrameInfo<'tcx> {
|
||||
pub instance: ty::Instance<'tcx>,
|
||||
pub span: Span,
|
||||
pub lint_root: Option<hir::HirId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, HashStable)] // Miri debug-prints these
|
||||
pub enum StackPopCleanup {
|
||||
/// Jump to the next block in the caller, or cause UB if None (that's a function
|
||||
@ -185,6 +193,25 @@ pub fn current_source_info(&self) -> Option<&mir::SourceInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ty::tls::with(|tcx| {
|
||||
if tcx.def_key(self.instance.def_id()).disambiguated_data.data
|
||||
== DefPathData::ClosureExpr
|
||||
{
|
||||
write!(f, "inside closure")?;
|
||||
} else {
|
||||
write!(f, "inside `{}`", self.instance)?;
|
||||
}
|
||||
if !self.span.is_dummy() {
|
||||
let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
|
||||
write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
@ -625,6 +652,13 @@ pub fn push_stack_frame(
|
||||
let frame = M::init_frame_extra(self, pre_frame)?;
|
||||
self.stack_mut().push(frame);
|
||||
|
||||
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
|
||||
for const_ in &body.required_consts {
|
||||
let const_ =
|
||||
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
|
||||
self.const_to_op(const_, None)?;
|
||||
}
|
||||
|
||||
// Locals are initially uninitialized.
|
||||
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
|
||||
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
|
||||
|
||||
pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup};
|
||||
pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
|
||||
pub use self::intern::{intern_const_alloc_recursive, InternKind};
|
||||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
|
||||
pub use self::memory::{get_static, AllocCheck, FnVal, Memory, MemoryKind};
|
||||
|
@ -26,7 +26,7 @@
|
||||
use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout};
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
use crate::const_eval::error_to_const_error;
|
||||
use crate::const_eval::ConstEvalErr;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, truncate, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx,
|
||||
LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy,
|
||||
@ -451,7 +451,7 @@ fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Opti
|
||||
Ok(op) => Some(op),
|
||||
Err(error) => {
|
||||
let tcx = self.ecx.tcx.at(c.span);
|
||||
let err = error_to_const_error(&self.ecx, error, Some(c.span));
|
||||
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
|
||||
if let Some(lint_root) = self.lint_root(source_info) {
|
||||
let lint_only = match c.literal.val {
|
||||
// Promoteds must lint and not error as the user didn't ask for them
|
||||
|
@ -30,41 +30,41 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
alloc0 (static: FOO, size: 8, align: 4) {
|
||||
╾─alloc21─╼ 03 00 00 00 │ ╾──╼....
|
||||
╾─alloc23─╼ 03 00 00 00 │ ╾──╼....
|
||||
}
|
||||
|
||||
alloc21 (size: 48, align: 4) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc4──╼ 00 00 00 00 │ ....░░░░╾──╼....
|
||||
0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼....
|
||||
0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc19─╼ 03 00 00 00 │ ....*...╾──╼....
|
||||
alloc23 (size: 48, align: 4) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 00 00 00 00 │ ....░░░░╾──╼....
|
||||
0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc13─╼ 02 00 00 00 │ ....░░░░╾──╼....
|
||||
0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc21─╼ 03 00 00 00 │ ....*...╾──╼....
|
||||
}
|
||||
|
||||
alloc4 (size: 0, align: 4) {}
|
||||
alloc8 (size: 0, align: 4) {}
|
||||
|
||||
alloc9 (size: 8, align: 4) {
|
||||
╾─alloc7──╼ ╾─alloc8──╼ │ ╾──╼╾──╼
|
||||
alloc13 (size: 8, align: 4) {
|
||||
╾─alloc11─╼ ╾─alloc12─╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
alloc7 (size: 1, align: 1) {
|
||||
alloc11 (size: 1, align: 1) {
|
||||
05 │ .
|
||||
}
|
||||
|
||||
alloc8 (size: 1, align: 1) {
|
||||
alloc12 (size: 1, align: 1) {
|
||||
06 │ .
|
||||
}
|
||||
|
||||
alloc19 (size: 12, align: 4) {
|
||||
╾─a15+0x3─╼ ╾─alloc16─╼ ╾─a18+0x2─╼ │ ╾──╼╾──╼╾──╼
|
||||
alloc21 (size: 12, align: 4) {
|
||||
╾─a17+0x3─╼ ╾─alloc18─╼ ╾─a20+0x2─╼ │ ╾──╼╾──╼╾──╼
|
||||
}
|
||||
|
||||
alloc15 (size: 4, align: 1) {
|
||||
alloc17 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
||||
alloc16 (size: 1, align: 1) {
|
||||
alloc18 (size: 1, align: 1) {
|
||||
2a │ *
|
||||
}
|
||||
|
||||
alloc18 (size: 4, align: 1) {
|
||||
alloc20 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
@ -30,44 +30,44 @@ fn main() -> () {
|
||||
}
|
||||
|
||||
alloc0 (static: FOO, size: 16, align: 8) {
|
||||
╾───────alloc21───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
╾───────alloc23───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
}
|
||||
|
||||
alloc21 (size: 72, align: 8) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc4────────╼ │ ....░░░░╾──────╼
|
||||
alloc23 (size: 72, align: 8) {
|
||||
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc8────────╼ │ ....░░░░╾──────╼
|
||||
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
|
||||
0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc19───────╼ │ ....*...╾──────╼
|
||||
0x20 │ ╾───────alloc13───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc21───────╼ │ ....*...╾──────╼
|
||||
0x40 │ 03 00 00 00 00 00 00 00 │ ........
|
||||
}
|
||||
|
||||
alloc4 (size: 0, align: 8) {}
|
||||
alloc8 (size: 0, align: 8) {}
|
||||
|
||||
alloc9 (size: 16, align: 8) {
|
||||
╾───────alloc7────────╼ ╾───────alloc8────────╼ │ ╾──────╼╾──────╼
|
||||
alloc13 (size: 16, align: 8) {
|
||||
╾───────alloc11───────╼ ╾───────alloc12───────╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
alloc7 (size: 1, align: 1) {
|
||||
alloc11 (size: 1, align: 1) {
|
||||
05 │ .
|
||||
}
|
||||
|
||||
alloc8 (size: 1, align: 1) {
|
||||
alloc12 (size: 1, align: 1) {
|
||||
06 │ .
|
||||
}
|
||||
|
||||
alloc19 (size: 24, align: 8) {
|
||||
0x00 │ ╾─────alloc15+0x3─────╼ ╾───────alloc16───────╼ │ ╾──────╼╾──────╼
|
||||
0x10 │ ╾─────alloc18+0x2─────╼ │ ╾──────╼
|
||||
alloc21 (size: 24, align: 8) {
|
||||
0x00 │ ╾─────alloc17+0x3─────╼ ╾───────alloc18───────╼ │ ╾──────╼╾──────╼
|
||||
0x10 │ ╾─────alloc20+0x2─────╼ │ ╾──────╼
|
||||
}
|
||||
|
||||
alloc15 (size: 4, align: 1) {
|
||||
alloc17 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
||||
alloc16 (size: 1, align: 1) {
|
||||
alloc18 (size: 1, align: 1) {
|
||||
2a │ *
|
||||
}
|
||||
|
||||
alloc18 (size: 4, align: 1) {
|
||||
alloc20 (size: 4, align: 1) {
|
||||
2a 45 15 6f │ *E.o
|
||||
}
|
||||
|
20
src/test/ui/consts/const-eval/erroneous-const.rs
Normal file
20
src/test/ui/consts/const-eval/erroneous-const.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Make sure we error on erroneous consts even if they are unused.
|
||||
#![warn(const_err, unconditional_panic)]
|
||||
|
||||
struct PrintName<T>(T);
|
||||
impl<T> PrintName<T> {
|
||||
const VOID: () = [()][2]; //~WARN any use of this value will cause an error
|
||||
//~^ WARN this operation will panic at runtime
|
||||
}
|
||||
|
||||
const fn no_codegen<T>() {
|
||||
if false { //~ERROR evaluation of constant value failed
|
||||
let _ = PrintName::<T>::VOID;
|
||||
}
|
||||
}
|
||||
|
||||
pub static FOO: () = no_codegen::<i32>(); //~ERROR could not evaluate static initializer
|
||||
|
||||
fn main() {
|
||||
FOO
|
||||
}
|
43
src/test/ui/consts/const-eval/erroneous-const.stderr
Normal file
43
src/test/ui/consts/const-eval/erroneous-const.stderr
Normal file
@ -0,0 +1,43 @@
|
||||
warning: this operation will panic at runtime
|
||||
--> $DIR/erroneous-const.rs:6:22
|
||||
|
|
||||
LL | const VOID: () = [()][2];
|
||||
| ^^^^^^^ index out of bounds: the len is 1 but the index is 2
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/erroneous-const.rs:2:20
|
||||
|
|
||||
LL | #![warn(const_err, unconditional_panic)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: any use of this value will cause an error
|
||||
--> $DIR/erroneous-const.rs:6:22
|
||||
|
|
||||
LL | const VOID: () = [()][2];
|
||||
| -----------------^^^^^^^-
|
||||
| |
|
||||
| index out of bounds: the len is 1 but the index is 2
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/erroneous-const.rs:2:9
|
||||
|
|
||||
LL | #![warn(const_err, unconditional_panic)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/erroneous-const.rs:11:5
|
||||
|
|
||||
LL | / if false {
|
||||
LL | | let _ = PrintName::<T>::VOID;
|
||||
LL | | }
|
||||
| |_____^ referenced constant has errors
|
||||
|
||||
error[E0080]: could not evaluate static initializer
|
||||
--> $DIR/erroneous-const.rs:16:22
|
||||
|
|
||||
LL | pub static FOO: () = no_codegen::<i32>();
|
||||
| ^^^^^^^^^^^^^^^^^^^ referenced constant has errors
|
||||
|
||||
error: aborting due to 2 previous errors; 2 warnings emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
Loading…
Reference in New Issue
Block a user