mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
avoid thread-local var indirection for non-halting diagnostics
This commit is contained in:
parent
4b9463c5b7
commit
3cb27f584b
@ -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);
|
||||
|
@ -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::*;
|
||||
|
||||
/// 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<ty::Instance<'tcx>>,
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
11
src/eval.rs
11
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)
|
||||
|
@ -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 {
|
||||
|
@ -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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))),
|
||||
));
|
||||
|
Loading…
Reference in New Issue
Block a user