From 3cb27f584b70fa8dc4f89768df2860e0092a6eeb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 18 Sep 2022 21:52:38 +0200 Subject: [PATCH] avoid thread-local var indirection for non-halting diagnostics --- src/concurrency/weak_memory.rs | 2 +- src/diagnostics.rs | 277 +++++++++++------------------ src/eval.rs | 11 -- src/helpers.rs | 13 +- src/intptrcast.rs | 2 +- src/lib.rs | 2 +- src/machine.rs | 10 +- src/stacked_borrows/diagnostics.rs | 2 +- src/stacked_borrows/mod.rs | 14 +- 9 files changed, 133 insertions(+), 200 deletions(-) diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs index 0e579a38af85..7e13b44e3f1e 100644 --- a/src/concurrency/weak_memory.rs +++ b/src/concurrency/weak_memory.rs @@ -544,7 +544,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: validate, )?; if global.track_outdated_loads && recency == LoadRecency::Outdated { - register_diagnostic(NonHaltingDiagnostic::WeakMemoryOutdatedLoad); + this.emit_diagnostic(NonHaltingDiagnostic::WeakMemoryOutdatedLoad); } return Ok(loaded); diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 8ba2995662ba..0118f9f421e3 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -1,11 +1,10 @@ -use std::cell::RefCell; use std::fmt; use std::num::NonZeroU64; use log::trace; -use rustc_middle::ty; -use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol}; +use rustc_middle::ty::TyCtxt; +use rustc_span::{source_map::DUMMY_SP, SpanData, Symbol}; use rustc_target::abi::{Align, Size}; use crate::stacked_borrows::{diagnostics::TagHistory, AccessKind}; @@ -89,15 +88,16 @@ enum DiagLevel { /// Attempts to prune a stacktrace to omit the Rust runtime, and returns a bool indicating if any /// frames were pruned. If the stacktrace does not have any local frames, we conclude that it must /// be pointing to a problem in the Rust runtime itself, and do not prune it at all. -fn prune_stacktrace<'mir, 'tcx>( - ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, +fn prune_stacktrace<'tcx>( mut stacktrace: Vec>, + machine: &Evaluator<'_, 'tcx>, + tcx: TyCtxt<'tcx>, ) -> (Vec>, bool) { - match ecx.machine.backtrace_style { + match machine.backtrace_style { BacktraceStyle::Off => { // Remove all frames marked with `caller_location` -- that attribute indicates we // usually want to point at the caller, not them. - stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx)); + stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(tcx)); // Retain one frame so that we can print a span for the error itself stacktrace.truncate(1); (stacktrace, false) @@ -107,11 +107,11 @@ fn prune_stacktrace<'mir, 'tcx>( // Only prune frames if there is at least one local frame. This check ensures that if // we get a backtrace that never makes it to the user code because it has detected a // bug in the Rust runtime, we don't prune away every frame. - let has_local_frame = stacktrace.iter().any(|frame| ecx.machine.is_local(frame)); + let has_local_frame = stacktrace.iter().any(|frame| machine.is_local(frame)); if has_local_frame { // Remove all frames marked with `caller_location` -- that attribute indicates we // usually want to point at the caller, not them. - stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx)); + stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(tcx)); // This is part of the logic that `std` uses to select the relevant part of a // backtrace. But here, we only look for __rust_begin_short_backtrace, not @@ -121,7 +121,7 @@ fn prune_stacktrace<'mir, 'tcx>( .into_iter() .take_while(|frame| { let def_id = frame.instance.def_id(); - let path = ecx.tcx.tcx.def_path_str(def_id); + let path = tcx.def_path_str(def_id); !path.contains("__rust_begin_short_backtrace") }) .collect::>(); @@ -132,7 +132,7 @@ fn prune_stacktrace<'mir, 'tcx>( // This len check ensures that we don't somehow remove every frame, as doing so breaks // the primary error message. while stacktrace.len() > 1 - && stacktrace.last().map_or(false, |frame| !ecx.machine.is_local(frame)) + && stacktrace.last().map_or(false, |frame| !machine.is_local(frame)) { stacktrace.pop(); } @@ -256,17 +256,18 @@ pub fn report_error<'tcx, 'mir>( }; let stacktrace = ecx.generate_stacktrace(); - let (stacktrace, was_pruned) = prune_stacktrace(ecx, stacktrace); + let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine, *ecx.tcx); e.print_backtrace(); msg.insert(0, e.to_string()); report_msg( - ecx, DiagLevel::Error, &if let Some(title) = title { format!("{}: {}", title, msg[0]) } else { msg[0].clone() }, msg, vec![], helps, &stacktrace, + &ecx.machine, + *ecx.tcx, ); // Include a note like `std` does when we omit frames from a backtrace @@ -306,21 +307,21 @@ pub fn report_error<'tcx, 'mir>( /// We want to present a multi-line span message for some errors. Diagnostics do not support this /// directly, so we pass the lines as a `Vec` and display each line after the first with an /// additional `span_label` or `note` call. -fn report_msg<'mir, 'tcx>( - ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, +fn report_msg<'tcx>( diag_level: DiagLevel, title: &str, span_msg: Vec, notes: Vec<(Option, String)>, helps: Vec<(Option, String)>, stacktrace: &[FrameInfo<'tcx>], + machine: &Evaluator<'_, 'tcx>, + tcx: TyCtxt<'tcx>, ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); - let sess = ecx.tcx.sess; let mut err = match diag_level { - DiagLevel::Error => sess.struct_span_err(span, title).forget_guarantee(), - DiagLevel::Warning => sess.struct_span_warn(span, title), - DiagLevel::Note => sess.diagnostic().span_note_diag(span, title), + DiagLevel::Error => tcx.sess.struct_span_err(span, title).forget_guarantee(), + DiagLevel::Warning => tcx.sess.struct_span_warn(span, title), + DiagLevel::Note => tcx.sess.diagnostic().span_note_diag(span, title), }; // Show main message. @@ -357,7 +358,7 @@ fn report_msg<'mir, 'tcx>( } // Add backtrace for (idx, frame_info) in stacktrace.iter().enumerate() { - let is_local = ecx.machine.is_local(frame_info); + let is_local = machine.is_local(frame_info); // No span for non-local frames and the first frame (which is the error site). if is_local && idx > 0 { err.span_note(frame_info.span, &frame_info.to_string()); @@ -369,164 +370,95 @@ fn report_msg<'mir, 'tcx>( err.emit(); } -thread_local! { - static DIAGNOSTICS: RefCell> = RefCell::new(Vec::new()); -} +pub fn emit_diagnostic<'tcx>(e: NonHaltingDiagnostic, machine: &Evaluator<'_, 'tcx>, tcx: TyCtxt<'tcx>) { + use NonHaltingDiagnostic::*; -/// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available. -/// The diagnostic will be emitted after the current interpreter step is finished. -pub fn register_diagnostic(e: NonHaltingDiagnostic) { - DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e)); -} + let stacktrace = MiriEvalContext::generate_stacktrace_from_stack(machine.threads.active_thread_stack()); + let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, machine, tcx); -/// Remember enough about the topmost frame so that we can restore the stack -/// after a step was taken. -pub struct TopFrameInfo<'tcx> { - stack_size: usize, - instance: Option>, - span: Span, + let (title, diag_level) = match e { + RejectedIsolatedOp(_) => + ("operation rejected by isolation", DiagLevel::Warning), + Int2Ptr { .. } => ("integer-to-pointer cast", DiagLevel::Warning), + CreatedPointerTag(..) + | PoppedPointerTag(..) + | CreatedCallId(..) + | CreatedAlloc(..) + | FreedAlloc(..) + | ProgressReport { .. } + | WeakMemoryOutdatedLoad => + ("tracking was triggered", DiagLevel::Note), + }; + + let msg = match e { + CreatedPointerTag(tag, None) => + format!("created tag {tag:?}"), + CreatedPointerTag(tag, Some((alloc_id, range))) => + format!("created tag {tag:?} at {alloc_id:?}{range:?}"), + PoppedPointerTag(item, tag) => + match tag { + None => + format!( + "popped tracked tag for item {item:?} due to deallocation", + ), + Some((tag, access)) => { + format!( + "popped tracked tag for item {item:?} due to {access:?} access for {tag:?}", + ) + } + }, + CreatedCallId(id) => + format!("function call with id {id}"), + CreatedAlloc(AllocId(id), size, align, kind) => + format!( + "created {kind} allocation of {size} bytes (alignment {align} bytes) with id {id}", + size = size.bytes(), + align = align.bytes(), + ), + FreedAlloc(AllocId(id)) => + format!("freed allocation with id {id}"), + RejectedIsolatedOp(ref op) => + format!("{op} was made to return an error due to isolation"), + ProgressReport { .. } => + format!("progress report: current operation being executed is here"), + Int2Ptr { .. } => + format!("integer-to-pointer cast"), + WeakMemoryOutdatedLoad => + format!("weak memory emulation: outdated value returned from load"), + }; + + let notes = match e { + ProgressReport { block_count } => { + // It is important that each progress report is slightly different, since + // identical diagnostics are being deduplicated. + vec![ + (None, format!("so far, {block_count} basic blocks have been executed")), + ] + } + _ => vec![], + }; + + let helps = match e { + Int2Ptr { details: true } => + vec![ + (None, format!("This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,")), + (None, format!("which means that Miri might miss pointer bugs in this program.")), + (None, format!("See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.")), + (None, format!("To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.")), + (None, format!("You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.")), + (None, format!("Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.")), + ], + _ => vec![], + }; + + report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, machine, tcx); } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { - fn preprocess_diagnostics(&self) -> TopFrameInfo<'tcx> { - // Ensure we have no lingering diagnostics. - DIAGNOSTICS.with(|diagnostics| assert!(diagnostics.borrow().is_empty())); - + fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { let this = self.eval_context_ref(); - if this.active_thread_stack().is_empty() { - // Diagnostics can happen even with the empty stack (e.g. deallocation of thread-local statics). - return TopFrameInfo { stack_size: 0, instance: None, span: DUMMY_SP }; - } - let frame = this.frame(); - - TopFrameInfo { - stack_size: this.active_thread_stack().len(), - instance: Some(frame.instance), - span: frame.current_span(), - } - } - - /// Emit all diagnostics that were registed with `register_diagnostics` - fn process_diagnostics(&self, info: TopFrameInfo<'tcx>) { - let this = self.eval_context_ref(); - DIAGNOSTICS.with(|diagnostics| { - let mut diagnostics = diagnostics.borrow_mut(); - if diagnostics.is_empty() { - return; - } - // We need to fix up the stack trace, because the machine has already - // stepped to the next statement. - let mut stacktrace = this.generate_stacktrace(); - // Remove newly pushed frames. - while stacktrace.len() > info.stack_size { - stacktrace.remove(0); - } - // Add popped frame back. - if stacktrace.len() < info.stack_size { - assert!( - stacktrace.len() == info.stack_size - 1, - "we should never pop more than one frame at once" - ); - let frame_info = FrameInfo { - instance: info.instance.unwrap(), - span: info.span, - lint_root: None, - }; - stacktrace.insert(0, frame_info); - } else if let Some(instance) = info.instance { - // Adjust topmost frame. - stacktrace[0].span = info.span; - assert_eq!( - stacktrace[0].instance, instance, - "we should not pop and push a frame in one step" - ); - } - - let (stacktrace, _was_pruned) = prune_stacktrace(this, stacktrace); - - // Show diagnostics. - for e in diagnostics.drain(..) { - use NonHaltingDiagnostic::*; - - let (title, diag_level) = match e { - RejectedIsolatedOp(_) => - ("operation rejected by isolation", DiagLevel::Warning), - Int2Ptr { .. } => ("integer-to-pointer cast", DiagLevel::Warning), - CreatedPointerTag(..) - | PoppedPointerTag(..) - | CreatedCallId(..) - | CreatedAlloc(..) - | FreedAlloc(..) - | ProgressReport { .. } - | WeakMemoryOutdatedLoad => - ("tracking was triggered", DiagLevel::Note), - }; - - let msg = match e { - CreatedPointerTag(tag, None) => - format!("created tag {tag:?}"), - CreatedPointerTag(tag, Some((alloc_id, range))) => - format!("created tag {tag:?} at {alloc_id:?}{range:?}"), - PoppedPointerTag(item, tag) => - match tag { - None => - format!( - "popped tracked tag for item {item:?} due to deallocation", - ), - Some((tag, access)) => { - format!( - "popped tracked tag for item {item:?} due to {access:?} access for {tag:?}", - ) - } - }, - CreatedCallId(id) => - format!("function call with id {id}"), - CreatedAlloc(AllocId(id), size, align, kind) => - format!( - "created {kind} allocation of {size} bytes (alignment {align} bytes) with id {id}", - size = size.bytes(), - align = align.bytes(), - ), - FreedAlloc(AllocId(id)) => - format!("freed allocation with id {id}"), - RejectedIsolatedOp(ref op) => - format!("{op} was made to return an error due to isolation"), - ProgressReport { .. } => - format!("progress report: current operation being executed is here"), - Int2Ptr { .. } => - format!("integer-to-pointer cast"), - WeakMemoryOutdatedLoad => - format!("weak memory emulation: outdated value returned from load"), - }; - - let notes = match e { - ProgressReport { block_count } => { - // It is important that each progress report is slightly different, since - // identical diagnostics are being deduplicated. - vec![ - (None, format!("so far, {block_count} basic blocks have been executed")), - ] - } - _ => vec![], - }; - - let helps = match e { - Int2Ptr { details: true } => - vec![ - (None, format!("This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,")), - (None, format!("which means that Miri might miss pointer bugs in this program.")), - (None, format!("See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.")), - (None, format!("To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.")), - (None, format!("You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.")), - (None, format!("Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.")), - ], - _ => vec![], - }; - - report_msg(this, diag_level, title, vec![msg], notes, helps, &stacktrace); - } - }); + emit_diagnostic(e, &this.machine, *this.tcx); } /// We had a panic in Miri itself, try to print something useful. @@ -538,13 +470,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_ref(); let stacktrace = this.generate_stacktrace(); report_msg( - this, DiagLevel::Note, "the place in the program where the ICE was triggered", vec![], vec![], vec![], &stacktrace, + &this.machine, + *this.tcx, ); } } diff --git a/src/eval.rs b/src/eval.rs index 1be2a8990a68..7562d0e36f16 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -190,11 +190,6 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( Evaluator::new(config, layout_cx), ); - // Capture the current interpreter stack state (which should be empty) so that we can emit - // allocation-tracking and tag-tracking diagnostics for allocations which are part of the - // early runtime setup. - let info = ecx.preprocess_diagnostics(); - // Some parts of initialization require a full `InterpCx`. Evaluator::late_init(&mut ecx, config)?; @@ -324,10 +319,6 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( } } - // Emit any diagnostics related to the setup process for the runtime, so that when the - // interpreter loop starts there are no unprocessed diagnostics. - ecx.process_diagnostics(info); - Ok((ecx, ret_place)) } @@ -356,7 +347,6 @@ pub fn eval_entry<'tcx>( let res: thread::Result> = panic::catch_unwind(AssertUnwindSafe(|| { // Main loop. loop { - let info = ecx.preprocess_diagnostics(); match ecx.schedule()? { SchedulingAction::ExecuteStep => { assert!(ecx.step()?, "a terminated thread was scheduled for execution"); @@ -374,7 +364,6 @@ pub fn eval_entry<'tcx>( break; } } - ecx.process_diagnostics(info); } let return_code = ecx.read_scalar(&ret_place.into())?.to_machine_isize(&ecx)?; Ok(return_code) diff --git a/src/helpers.rs b/src/helpers.rs index 57c9fd3389ce..84815ee76910 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -508,7 +508,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(()) } RejectOpWith::Warning => { - register_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string())); + this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string())); Ok(()) } RejectOpWith::NoWarning => Ok(()), // no warning @@ -881,6 +881,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx None => tcx.item_name(def_id), } } + + fn current_span(&self) -> CurrentSpan<'_, 'mir, 'tcx> { + let this = self.eval_context_ref(); + CurrentSpan { current_frame_idx: None, machine: &this.machine, tcx: *this.tcx } + } } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { @@ -901,6 +906,12 @@ pub struct CurrentSpan<'a, 'mir, 'tcx> { } impl<'a, 'mir: 'a, 'tcx: 'a + 'mir> CurrentSpan<'a, 'mir, 'tcx> { + /// Not really about the `CurrentSpan`, but we just happen to have all the things needed to emit + /// diagnostics like that. + pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { + emit_diagnostic(e, self.machine, self.tcx); + } + /// Get the current span, skipping non-local frames. /// This function is backed by a cache, and can be assumed to be very fast. pub fn get(&mut self) -> Span { diff --git a/src/intptrcast.rs b/src/intptrcast.rs index 99fc086a229c..a7b967ece51a 100644 --- a/src/intptrcast.rs +++ b/src/intptrcast.rs @@ -142,7 +142,7 @@ impl<'mir, 'tcx> GlobalStateInner { let first = past_warnings.is_empty(); if past_warnings.insert(ecx.cur_span()) { // Newly inserted, so first time we see this span. - register_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first }); + ecx.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first }); } }); } diff --git a/src/lib.rs b/src/lib.rs index 9b3be6128ca1..900daa88d274 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,7 +94,7 @@ pub use crate::concurrency::{ }, }; pub use crate::diagnostics::{ - register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt, + emit_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt, NonHaltingDiagnostic, TerminationInfo, }; pub use crate::eval::{ diff --git a/src/machine.rs b/src/machine.rs index bd2c43004653..19978e550c32 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -755,7 +755,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ) -> InterpResult<'tcx, Cow<'b, Allocation>> { let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { - register_diagnostic(NonHaltingDiagnostic::CreatedAlloc( + ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc( id, alloc.size(), alloc.align, @@ -813,7 +813,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { } let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr); let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows { - stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance) + stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.current_span()) } else { // Value does not matter, SB is disabled SbTag::default() @@ -937,7 +937,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { range: AllocRange, ) -> InterpResult<'tcx> { if machine.tracked_alloc_ids.contains(&alloc_id) { - register_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); + emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id), machine, tcx); } if let Some(data_race) = &mut alloc_extra.data_race { data_race.deallocate( @@ -993,7 +993,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { let stacked_borrows = ecx.machine.stacked_borrows.as_ref(); let extra = FrameData { - stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame()), + stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.current_span())), catch_unwind: None, timing, }; @@ -1018,7 +1018,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { // Possibly report our progress. if let Some(report_progress) = ecx.machine.report_progress { if ecx.machine.basic_block_count % u64::from(report_progress) == 0 { - register_diagnostic(NonHaltingDiagnostic::ProgressReport { + ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport { block_count: ecx.machine.basic_block_count, }); } diff --git a/src/stacked_borrows/diagnostics.rs b/src/stacked_borrows/diagnostics.rs index 461715cedd8f..278058c9f5f1 100644 --- a/src/stacked_borrows/diagnostics.rs +++ b/src/stacked_borrows/diagnostics.rs @@ -471,7 +471,7 @@ impl<'span, 'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'span, 'history, 'ecx, 'mir Some((orig_tag, kind)) } }; - register_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, summary)); + self.current_span.emit_diagnostic(NonHaltingDiagnostic::PoppedPointerTag(*item, summary)); } } diff --git a/src/stacked_borrows/mod.rs b/src/stacked_borrows/mod.rs index 925762845663..b4edf2785681 100644 --- a/src/stacked_borrows/mod.rs +++ b/src/stacked_borrows/mod.rs @@ -178,11 +178,11 @@ impl GlobalStateInner { id } - pub fn new_frame(&mut self) -> FrameExtra { + pub fn new_frame(&mut self, current_span: &CurrentSpan<'_, '_, '_>) -> FrameExtra { let call_id = self.next_call_id; trace!("new_frame: Assigning call ID {}", call_id); if self.tracked_call_ids.contains(&call_id) { - register_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id)); + current_span.emit_diagnostic(NonHaltingDiagnostic::CreatedCallId(call_id)); } self.next_call_id = NonZeroU64::new(call_id.get() + 1).unwrap(); FrameExtra { call_id, protected_tags: SmallVec::new() } @@ -199,11 +199,11 @@ impl GlobalStateInner { } } - pub fn base_ptr_tag(&mut self, id: AllocId) -> SbTag { + pub fn base_ptr_tag(&mut self, id: AllocId, current_span: &CurrentSpan<'_, '_, '_>) -> SbTag { self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| { let tag = self.new_ptr(); if self.tracked_pointer_tags.contains(&tag) { - register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(tag.0, None)); + current_span.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(tag.0, None)); } trace!("New allocation {:?} has base tag {:?}", id, tag); self.base_ptr_tags.try_insert(id, tag).unwrap(); @@ -572,9 +572,9 @@ impl Stacks { // not through a pointer). That is, whenever we directly write to a local, this will pop // everything else off the stack, invalidating all previous pointers, // and in particular, *all* raw pointers. - MemoryKind::Stack => (extra.base_ptr_tag(id), Permission::Unique), + MemoryKind::Stack => (extra.base_ptr_tag(id, ¤t_span), Permission::Unique), // Everything else is shared by default. - _ => (extra.base_ptr_tag(id), Permission::SharedReadWrite), + _ => (extra.base_ptr_tag(id, ¤t_span), Permission::SharedReadWrite), }; Stacks::new(size, perm, base_tag, id, &mut current_span) } @@ -674,7 +674,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriEvalContextEx -> InterpResult<'tcx> { let global = this.machine.stacked_borrows.as_ref().unwrap().borrow(); if global.tracked_pointer_tags.contains(&new_tag) { - register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( + this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( new_tag.0, loc.map(|(alloc_id, base_offset, _)| (alloc_id, alloc_range(base_offset, size))), ));