From 71f1943cbf436436a0a8d64c86055ba0c0770c94 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 13 Mar 2024 09:20:16 +0000 Subject: [PATCH 01/12] Fix accidental re-addition of removed code in a previous PR --- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5f4408ebbc6c2..a66a95a5f0c21 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -381,10 +381,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>( Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. - // Temporarily allow access to the static_root_ids for the purpose of validation. - let static_root_ids = ecx.machine.static_root_ids.take(); let res = const_validate_mplace(&ecx, &mplace, cid); - ecx.machine.static_root_ids = static_root_ids; let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); From d6c999754c5a4d6d2a1e264825e71c56b394cbb0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 12:17:30 +0000 Subject: [PATCH 02/12] Generalize `eval_in_interpreter` with a helper trait --- .../src/const_eval/eval_queries.rs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index a66a95a5f0c21..1b401cc5cc0d1 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -290,10 +290,36 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id; - let alloc = take_static_root_alloc(&mut ecx, alloc_id); - let alloc = tcx.mk_const_alloc(alloc); - Ok(alloc) + eval_in_interpreter(&mut ecx, cid, true) +} + +trait InterpretationResult<'tcx> { + /// This function takes the place where the result of the evaluation is stored + /// and prepares it for returning it in the appropriate format needed by the specific + /// evaluation query. + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self; +} + +impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + let alloc = take_static_root_alloc(ecx, mplace.ptr().provenance.unwrap().alloc_id()); + ecx.tcx.mk_const_alloc(alloc) + } +} + +impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } + } } #[instrument(skip(tcx), level = "debug")] @@ -336,11 +362,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>( eval_in_interpreter(&mut ecx, cid, is_static) } -pub fn eval_in_interpreter<'mir, 'tcx>( +fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, is_static: bool, -) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { +) -> Result { // `is_static` just means "in static", it could still be a promoted! debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); @@ -383,14 +409,12 @@ pub fn eval_in_interpreter<'mir, 'tcx>( let res = const_validate_mplace(&ecx, &mplace, cid); - let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); - // Validation failed, report an error. if let Err(error) = res { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); Err(const_report_error(&ecx, error, alloc_id)) } else { - // Convert to raw constant - Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) + Ok(R::make_result(mplace, ecx)) } } } From 93888cd0a401581bc46f4bd85a1bf33d8ac14c7f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 12:33:09 +0000 Subject: [PATCH 03/12] Move only usage of `take_static_root_alloc` to its definition and inline it --- .../src/const_eval/eval_queries.rs | 18 ++++------------ .../rustc_const_eval/src/interpret/intern.rs | 2 +- .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../rustc_const_eval/src/interpret/util.rs | 21 ++++++++++++------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 1b401cc5cc0d1..63b1d485a24f2 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,9 +18,9 @@ use crate::errors; use crate::errors::ConstEvalError; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode, - GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, - OpTy, RefTracking, StackPopCleanup, + create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, + InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, + StackPopCleanup, }; // Returns a pointer to where the result lives @@ -293,7 +293,7 @@ pub fn eval_static_initializer_provider<'tcx>( eval_in_interpreter(&mut ecx, cid, true) } -trait InterpretationResult<'tcx> { +pub trait InterpretationResult<'tcx> { /// This function takes the place where the result of the evaluation is stored /// and prepares it for returning it in the appropriate format needed by the specific /// evaluation query. @@ -303,16 +303,6 @@ trait InterpretationResult<'tcx> { ) -> Self; } -impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { - fn make_result<'mir>( - mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - ) -> Self { - let alloc = take_static_root_alloc(ecx, mplace.ptr().provenance.unwrap().alloc_id()); - ecx.tcx.mk_const_alloc(alloc) - } -} - impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index c30a13624178b..17bb59aae8f17 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -176,7 +176,7 @@ pub fn intern_const_alloc_recursive< // This gives us the initial set of nested allocations, which will then all be processed // recursively in the loop below. let mut todo: Vec<_> = if is_static { - // Do not steal the root allocation, we need it later for `take_static_root_alloc` + // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); alloc.1.mutability = base_mutability; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2ed879ca72b5f..474d35b2aa3a2 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -39,5 +39,5 @@ use self::{ }; pub(crate) use self::intrinsics::eval_nullary_intrinsic; -pub(crate) use self::util::{create_static_alloc, take_static_root_alloc}; +pub(crate) use self::util::create_static_alloc; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 086475f72c5d4..c83ef14c03fe7 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,14 +1,15 @@ -use crate::const_eval::CompileTimeEvalContext; +use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult}; use crate::interpret::{MemPlaceMeta, MemoryKind}; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer}; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use std::ops::ControlFlow; -use super::MPlaceTy; +use super::{InterpCx, MPlaceTy}; /// Checks whether a type contains generic parameters which must be instantiated. /// @@ -80,11 +81,15 @@ where } } -pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, - alloc_id: AllocId, -) -> Allocation { - ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1 +impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); + let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; + ecx.tcx.mk_const_alloc(alloc) + } } pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>( From 8b8efd157b075d2e3c35bd0d3adc23981f919b07 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:02:33 +0000 Subject: [PATCH 04/12] Move error handling into const_validate_mplace --- .../src/const_eval/eval_queries.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 63b1d485a24f2..6da8cf433c618 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -396,16 +396,9 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( } Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. + const_validate_mplace(&ecx, &mplace, cid)?; - let res = const_validate_mplace(&ecx, &mplace, cid); - - // Validation failed, report an error. - if let Err(error) = res { - let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); - Err(const_report_error(&ecx, error, alloc_id)) - } else { - Ok(R::make_result(mplace, ecx)) - } + Ok(R::make_result(mplace, ecx)) } } } @@ -415,7 +408,8 @@ pub fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, -) -> InterpResult<'tcx> { +) -> Result<(), ErrorHandled> { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { @@ -429,7 +423,8 @@ pub fn const_validate_mplace<'mir, 'tcx>( CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner } } }; - ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; + ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) + .map_err(|error| const_report_error(&ecx, error, alloc_id))?; inner = true; } From 6b936b6c081394a77fa8272bace9c1ce22332a1b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:04:05 +0000 Subject: [PATCH 05/12] Move InterpCx into eval_in_interpreter --- .../src/const_eval/eval_queries.rs | 16 ++++++++-------- compiler/rustc_const_eval/src/interpret/util.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 6da8cf433c618..3ad53731e8a79 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -282,7 +282,7 @@ pub fn eval_static_initializer_provider<'tcx>( let instance = ty::Instance::mono(tcx, def_id.to_def_id()); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; - let mut ecx = InterpCx::new( + let ecx = InterpCx::new( tcx, tcx.def_span(def_id), ty::ParamEnv::reveal_all(), @@ -290,7 +290,7 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - eval_in_interpreter(&mut ecx, cid, true) + eval_in_interpreter(ecx, cid, true) } pub trait InterpretationResult<'tcx> { @@ -299,14 +299,14 @@ pub trait InterpretationResult<'tcx> { /// evaluation query. fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + _ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -339,7 +339,7 @@ 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, @@ -349,11 +349,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(&mut ecx, cid, is_static) + eval_in_interpreter(ecx, cid, is_static) } fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, is_static: bool, ) -> Result { @@ -361,7 +361,7 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); let res = ecx.load_mir(cid.instance.def, cid.promoted); - match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) { + match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { Err(error) => { let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index c83ef14c03fe7..10b5e3ff1df5a 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -84,7 +84,7 @@ where impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; From d2d2bd273686717aa0359d994cf2333738af7071 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:20:12 +0000 Subject: [PATCH 06/12] Move generate_stacktrace_from_stack away from InterpCx to avoid having to know the `Machine` type --- .../rustc_const_eval/src/const_eval/error.rs | 9 +-- .../rustc_const_eval/src/const_eval/mod.rs | 2 +- .../src/interpret/eval_context.rs | 56 +++++++++---------- src/tools/miri/src/diagnostics.rs | 2 +- 4 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index b6adee435ba3a..c74f39dd1635a 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -8,9 +8,9 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_span::{Span, Symbol, DUMMY_SP}; -use super::{CompileTimeInterpreter, InterpCx}; +use super::CompileTimeInterpreter; use crate::errors::{self, FrameNote, ReportErrorExt}; -use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType}; +use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType}; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] @@ -63,10 +63,7 @@ pub fn get_span_and_frames<'tcx, 'mir>( where 'tcx: 'mir, { - let mut stacktrace = - InterpCx::>::generate_stacktrace_from_stack( - &machine.stack, - ); + let mut stacktrace = Frame::generate_stacktrace_from_stack(&machine.stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 289dcb7d01d68..d0d6adbfad069 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::InterpErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, Ty}; -use crate::interpret::{format_interp_error, InterpCx}; +use crate::interpret::format_interp_error; mod error; mod eval_queries; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7526acf145436..09e9cc4b35d31 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -283,6 +283,32 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { pub(super) fn locals_addr(&self) -> usize { self.locals.raw.as_ptr().addr() } + + #[must_use] + pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { + let mut frames = Vec::new(); + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + for frame in stack.iter().rev() { + let span = match frame.loc { + Left(loc) => { + // If the stacktrace passes through MIR-inlined source scopes, add them. + let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); + let mut scope_data = &frame.body.source_scopes[scope]; + while let Some((instance, call_span)) = scope_data.inlined { + frames.push(FrameInfo { span, instance }); + span = call_span; + scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; + } + span + } + Right(span) => span, + }; + frames.push(FrameInfo { span, instance: frame.instance }); + } + trace!("generate stacktrace: {:#?}", frames); + frames + } } // FIXME: only used by miri, should be removed once translatable. @@ -1170,37 +1196,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { PlacePrinter { ecx: self, place: *place.place() } } - #[must_use] - pub fn generate_stacktrace_from_stack( - stack: &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>], - ) -> Vec> { - let mut frames = Vec::new(); - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - for frame in stack.iter().rev() { - let span = match frame.loc { - Left(loc) => { - // If the stacktrace passes through MIR-inlined source scopes, add them. - let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); - let mut scope_data = &frame.body.source_scopes[scope]; - while let Some((instance, call_span)) = scope_data.inlined { - frames.push(FrameInfo { span, instance }); - span = call_span; - scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; - } - span - } - Right(span) => span, - }; - frames.push(FrameInfo { span, instance: frame.instance }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } - #[must_use] pub fn generate_stacktrace(&self) -> Vec> { - Self::generate_stacktrace_from_stack(self.stack()) + Frame::generate_stacktrace_from_stack(self.stack()) } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4683965159d73..6e612ea34a70f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -528,7 +528,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { use NonHaltingDiagnostic::*; let stacktrace = - MiriInterpCx::generate_stacktrace_from_stack(self.threads.active_thread_stack()); + Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack()); let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self); let (title, diag_level) = match &e { From d3b7b558aa876e563d35090ae02b0b61430818de Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:21:42 +0000 Subject: [PATCH 07/12] Directly pass in the stack instead of computing it from a machine --- compiler/rustc_const_eval/src/const_eval/error.rs | 7 ++++--- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index c74f39dd1635a..763344207c467 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,6 +2,7 @@ use std::mem; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg}; use rustc_hir::CRATE_HIR_ID; +use rustc_middle::mir::interpret::Provenance; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::TyCtxt; @@ -58,12 +59,12 @@ impl<'tcx> Into> for ConstEvalErrKind { pub fn get_span_and_frames<'tcx, 'mir>( tcx: TyCtxtAt<'tcx>, - machine: &CompileTimeInterpreter<'mir, 'tcx>, + stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>], ) -> (Span, Vec) where 'tcx: 'mir, { - let mut stacktrace = Frame::generate_stacktrace_from_stack(&machine.stack); + let mut stacktrace = Frame::generate_stacktrace_from_stack(stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); @@ -167,7 +168,7 @@ pub(super) fn lint<'tcx, 'mir, L>( ) where L: for<'a> rustc_errors::LintDiagnostic<'a, ()>, { - let (span, frames) = get_span_and_frames(tcx, machine); + let (span, frames) = get_span_and_frames(tcx, &machine.stack); tcx.emit_node_span_lint( lint, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 3ad53731e8a79..439cb5b03b943 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -385,7 +385,7 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( *ecx.tcx, error, None, - || super::get_span_and_frames(ecx.tcx, &ecx.machine), + || super::get_span_and_frames(ecx.tcx, ecx.stack()), |span, frames| ConstEvalError { span, error_kind: kind, @@ -450,7 +450,7 @@ pub fn const_report_error<'mir, 'tcx>( *ecx.tcx, error, None, - || crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine), + || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, ) } From 02a0ac805823fa696b2d5b3b8c6da3324190d21a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 09:34:57 +0000 Subject: [PATCH 08/12] Remove an argument that can be computed cheaply --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 439cb5b03b943..7d0ce9930d5c7 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -290,7 +290,7 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid, true) + eval_in_interpreter(ecx, cid) } pub trait InterpretationResult<'tcx> { @@ -349,24 +349,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid, is_static) + eval_in_interpreter(ecx, cid) } fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, - is_static: bool, ) -> Result { - // `is_static` just means "in static", it could still be a promoted! - debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); - 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) => { let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); - let (kind, instance) = if is_static { + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { ("static", String::new()) } else { // If the current item has generics, we'd like to enrich the message with the From cc7e0b22003defc5f99fe8048e09ad4f730768c4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 09:41:49 +0000 Subject: [PATCH 09/12] Share the `InterpCx` creation between static and const evaluation --- .../src/const_eval/eval_queries.rs | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 7d0ce9930d5c7..2608107826fcd 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -282,15 +282,7 @@ pub fn eval_static_initializer_provider<'tcx>( let instance = ty::Instance::mono(tcx, def_id.to_def_id()); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; - let ecx = InterpCx::new( - tcx, - tcx.def_span(def_id), - ty::ParamEnv::reveal_all(), - // 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(CanAccessMutGlobal::Yes, CheckAlignment::Error), - ); - eval_in_interpreter(ecx, cid) + eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all()) } pub trait InterpretationResult<'tcx> { @@ -335,27 +327,27 @@ pub fn eval_to_allocation_raw_provider<'tcx>( trace!("const eval: {:?} ({})", key, instance); } - let cid = key.value; + eval_in_interpreter(tcx, key.value, key.param_env) +} + +fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( + tcx: TyCtxt<'tcx>, + cid: GlobalId<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result { let def = cid.instance.def.def_id(); let is_static = tcx.is_static(def); - let ecx = InterpCx::new( + let mut ecx = InterpCx::new( tcx, tcx.def_span(def), - key.param_env, + param_env, // Statics (and promoteds inside statics) may access mutable global memory, because unlike consts // they do not have to behave "as if" they were evaluated at runtime. // For consts however we want to ensure they behave "as if" they were evaluated at runtime, // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid) -} - -fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( - mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - cid: GlobalId<'tcx>, -) -> Result { 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) => { From 2e6c4900b63c401cea4e4b0492e075def65508dd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 10:04:36 +0000 Subject: [PATCH 10/12] Move validation into eval_body_using_ecx --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 2608107826fcd..4f41c977f2e20 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -84,6 +84,9 @@ fn eval_body_using_ecx<'mir, 'tcx>( // Intern the result intern_const_alloc_recursive(ecx, intern_kind, &ret)?; + // Since evaluation had no errors, validate the resulting constant. + const_validate_mplace(&ecx, &ret, cid)?; + Ok(ret) } @@ -382,12 +385,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( }, )) } - Ok(mplace) => { - // Since evaluation had no errors, validate the resulting constant. - const_validate_mplace(&ecx, &mplace, cid)?; - - Ok(R::make_result(mplace, ecx)) - } + Ok(mplace) => Ok(R::make_result(mplace, ecx)), } } From 16046c77aad72d7be16253ec029b568b157d82d4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 13:41:41 +0000 Subject: [PATCH 11/12] Move the entire success path into `eval_body_using_ecx` --- .../src/const_eval/eval_queries.rs | 72 +++++++++---------- .../rustc_const_eval/src/interpret/util.rs | 2 +- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 4f41c977f2e20..d62ab39d0ecfc 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -24,12 +24,12 @@ use crate::interpret::{ }; // Returns a pointer to where the result lives -#[instrument(level = "trace", skip(ecx, body), ret)] -fn eval_body_using_ecx<'mir, 'tcx>( +#[instrument(level = "trace", skip(ecx, body))] +fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, cid: GlobalId<'tcx>, body: &'mir mir::Body<'tcx>, -) -> InterpResult<'tcx, MPlaceTy<'tcx>> { +) -> InterpResult<'tcx, R> { trace!(?ecx.param_env); let tcx = *ecx.tcx; assert!( @@ -87,7 +87,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( // Since evaluation had no errors, validate the resulting constant. const_validate_mplace(&ecx, &ret, cid)?; - Ok(ret) + Ok(R::make_result(ret, ecx)) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for @@ -294,14 +294,14 @@ pub trait InterpretationResult<'tcx> { /// evaluation query. fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - _ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -352,41 +352,33 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); 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) => { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); - - let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { - ("static", String::new()) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); + + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { + ("static", String::new()) + } else { + // 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 = &cid.instance; + if !instance.args.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + ("const_with_path", instance) } else { - // 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 = &cid.instance; - if !instance.args.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - ("const_with_path", instance) - } else { - ("const", String::new()) - } - }; - - Err(super::report( - *ecx.tcx, - error, - None, - || super::get_span_and_frames(ecx.tcx, ecx.stack()), - |span, frames| ConstEvalError { - span, - error_kind: kind, - instance, - frame_notes: frames, - }, - )) - } - Ok(mplace) => Ok(R::make_result(mplace, ecx)), - } + ("const", String::new()) + } + }; + + super::report( + *ecx.tcx, + error, + None, + || super::get_span_and_frames(ecx.tcx, ecx.stack()), + |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, + ) + }) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 10b5e3ff1df5a..c83ef14c03fe7 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -84,7 +84,7 @@ where impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; From a316c21dc8aa1ebfb961a2c789757593fd1db9ef Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 13 Mar 2024 09:25:20 +0000 Subject: [PATCH 12/12] Rename some things around validation error reporting to signal that it is in fact about validation failures --- compiler/rustc_const_eval/messages.ftl | 12 ++++++------ .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 ++++++---- compiler/rustc_const_eval/src/errors.rs | 6 +++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f3af633b4e561..0046190d20cc7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -374,12 +374,6 @@ const_eval_unallowed_op_in_const_context = const_eval_unavailable_target_features_for_fn = calling a function that requires unavailable target features: {$unavailable_feats} -const_eval_undefined_behavior = - it is undefined behavior to use this value - -const_eval_undefined_behavior_note = - The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - const_eval_uninhabited_enum_variant_read = read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = @@ -434,6 +428,12 @@ const_eval_validation_expected_raw_ptr = expected a raw pointer const_eval_validation_expected_ref = expected a reference const_eval_validation_expected_str = expected a string +const_eval_validation_failure = + it is undefined behavior to use this value + +const_eval_validation_failure_note = + The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + const_eval_validation_front_matter_invalid_value = constructing invalid value const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index d62ab39d0ecfc..5a1c7cc4209ad 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -382,7 +382,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( } #[inline(always)] -pub fn const_validate_mplace<'mir, 'tcx>( +fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, @@ -402,7 +402,9 @@ pub fn const_validate_mplace<'mir, 'tcx>( } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) - .map_err(|error| const_report_error(&ecx, error, alloc_id))?; + // Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted + // error about the validation failure. + .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; inner = true; } @@ -410,7 +412,7 @@ pub fn const_validate_mplace<'mir, 'tcx>( } #[inline(always)] -pub fn const_report_error<'mir, 'tcx>( +fn report_validation_error<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, @@ -429,6 +431,6 @@ pub fn const_report_error<'mir, 'tcx>( error, None, || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), - move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, + move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 46790264359b7..cc32640408b7e 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -412,11 +412,11 @@ pub struct NullaryIntrinsicError { } #[derive(Diagnostic)] -#[diag(const_eval_undefined_behavior, code = E0080)] -pub struct UndefinedBehavior { +#[diag(const_eval_validation_failure, code = E0080)] +pub struct ValidationFailure { #[primary_span] pub span: Span, - #[note(const_eval_undefined_behavior_note)] + #[note(const_eval_validation_failure_note)] pub ub_note: Option<()>, #[subdiagnostic] pub frames: Vec,