about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-09-18 21:52:38 +0200
committerRalf Jung <post@ralfj.de>2022-09-20 08:37:20 +0200
commit3cb27f584b70fa8dc4f89768df2860e0092a6eeb (patch)
tree08dd8cc7b8f714cd1112b58a0be5a81defc078ad
parent4b9463c5b713350b37ebc26a01122ea16e002451 (diff)
downloadrust-3cb27f584b70fa8dc4f89768df2860e0092a6eeb.tar.gz
rust-3cb27f584b70fa8dc4f89768df2860e0092a6eeb.zip
avoid thread-local var indirection for non-halting diagnostics
-rw-r--r--src/concurrency/weak_memory.rs2
-rw-r--r--src/diagnostics.rs277
-rw-r--r--src/eval.rs11
-rw-r--r--src/helpers.rs13
-rw-r--r--src/intptrcast.rs2
-rw-r--r--src/lib.rs2
-rw-r--r--src/machine.rs10
-rw-r--r--src/stacked_borrows/diagnostics.rs2
-rw-r--r--src/stacked_borrows/mod.rs14
9 files changed, 133 insertions, 200 deletions
diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs
index 0e579a38af8..7e13b44e3f1 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 8ba2995662b..0118f9f421e 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<FrameInfo<'tcx>>,
+    machine: &Evaluator<'_, 'tcx>,
+    tcx: TyCtxt<'tcx>,
 ) -> (Vec<FrameInfo<'tcx>>, 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::<Vec<_>>();
@@ -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<String>` 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<String>,
     notes: Vec<(Option<SpanData>, String)>,
     helps: Vec<(Option<SpanData>, 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<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
-}
+pub fn emit_diagnostic<'tcx>(e: NonHaltingDiagnostic, machine: &Evaluator<'_, 'tcx>, tcx: TyCtxt<'tcx>) {
+    use NonHaltingDiagnostic::*;
+
+    let stacktrace = MiriEvalContext::generate_stacktrace_from_stack(machine.threads.active_thread_stack());
+    let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, machine, tcx);
+
+    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),
+    };
 
-/// 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 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"),
+    };
 
-/// 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<ty::Instance<'tcx>>,
-    span: Span,
+    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 1be2a8990a6..7562d0e36f1 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<InterpResult<'_, i64>> = 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 57c9fd3389c..84815ee7691 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 99fc086a229..a7b967ece51 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 9b3be6128ca..900daa88d27 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 bd2c4300465..19978e550c3 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<Self::Provenance, Self::AllocExtra>>> {
         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 461715cedd8..278058c9f5f 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 92576284566..b4edf278568 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, &current_span), Permission::Unique),
             // Everything else is shared by default.
-            _ => (extra.base_ptr_tag(id), Permission::SharedReadWrite),
+            _ => (extra.base_ptr_tag(id, &current_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))),
                 ));