Skip to content

Commit 9fe39ba

Browse files
committed
Auto merge of #1250 - RalfJung:error-context, r=oli-obk
give some context in error messages ### Some examples for how different errors look now Unsupported operation: ``` error: unsupported operation: Miri does not support threading --> /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/src/libstd/sys/unix/thread.rs:68:19 | 68 | let ret = libc::pthread_create(&mut native, &attr, thread_start, &*p as *const _ as *mut _); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Miri does not support threading | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support ``` Unsupported operation that works without isolation: ``` error: unsupported operation: `clock_gettime` not available when isolation is enabled --> /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/src/libstd/sys/unix/time.rs:349:22 | 349 | cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `clock_gettime` not available when isolation is enabled | = help: pass the flag `-Zmiri-disable-isolation` to disable isolation ``` Program abort: ``` error: program stopped: the evaluated program aborted execution --> /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/src/libstd/panicking.rs:530:18 | 530 | unsafe { intrinsics::abort() } | ^^^^^^^^^^^^^^^^^^^ the evaluated program aborted execution | ``` UB: ``` error: Undefined Behavior: type validation failed: encountered 2, but expected a boolean --> tests/compile-fail/validity/invalid_bool.rs:2:23 | 2 | let _b = unsafe { std::mem::transmute::<u8, bool>(2) }; //~ ERROR encountered 2, but expected a boolean | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 2, but expected a boolean | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior ``` Experimental UB: ``` error: Undefined Behavior: not granting access to tag <1562> because incompatible item is protected: [Unique for <1567> (call 1189)] --> tests/compile-fail/stacked_borrows/aliasing_mut1.rs:3:1 | 3 | pub fn safe(_x: &mut i32, _y: &mut i32) {} //~ ERROR protect | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <1562> because incompatible item is protected: [Unique for <1567> (call 1189)] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental ``` Fixes #417
2 parents d7d2266 + 6e302b8 commit 9fe39ba

File tree

5 files changed

+123
-68
lines changed

5 files changed

+123
-68
lines changed

src/diagnostics.rs

+97-45
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,127 @@
1-
use rustc_mir::interpret::InterpErrorInfo;
21
use std::cell::RefCell;
32

3+
use rustc_span::DUMMY_SP;
4+
45
use crate::*;
56

7+
/// Details of premature program termination.
8+
pub enum TerminationInfo {
9+
Exit(i64),
10+
Abort(Option<String>),
11+
UnsupportedInIsolation(String),
12+
ExperimentalUb { msg: String, url: String }
13+
}
14+
615
/// Miri specific diagnostics
716
pub enum NonHaltingDiagnostic {
817
PoppedTrackedPointerTag(Item),
918
CreatedAlloc(AllocId),
1019
}
1120

1221
/// Emit a custom diagnostic without going through the miri-engine machinery
13-
pub fn report_diagnostic<'tcx, 'mir>(
22+
pub fn report_error<'tcx, 'mir>(
1423
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
1524
mut e: InterpErrorInfo<'tcx>,
1625
) -> Option<i64> {
17-
// Special treatment for some error kinds
18-
let msg = match e.kind {
19-
InterpError::MachineStop(ref info) => {
26+
use InterpError::*;
27+
28+
e.print_backtrace();
29+
let (title, msg, helps) = match e.kind {
30+
MachineStop(info) => {
2031
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
21-
match info {
22-
TerminationInfo::Exit(code) => return Some(*code),
23-
TerminationInfo::Abort(None) => format!("the evaluated program aborted execution"),
24-
TerminationInfo::Abort(Some(msg)) => format!("the evaluated program aborted execution: {}", msg),
25-
}
32+
use TerminationInfo::*;
33+
let (title, msg) = match info {
34+
Exit(code) => return Some(*code),
35+
Abort(None) =>
36+
("abnormal termination", format!("the evaluated program aborted execution")),
37+
Abort(Some(msg)) =>
38+
("abnormal termination", format!("the evaluated program aborted execution: {}", msg)),
39+
UnsupportedInIsolation(msg) =>
40+
("unsupported operation", format!("{}", msg)),
41+
ExperimentalUb { msg, .. } =>
42+
("Undefined Behavior", format!("{}", msg)),
43+
};
44+
let helps = match info {
45+
UnsupportedInIsolation(_) =>
46+
vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")],
47+
ExperimentalUb { url, .. } =>
48+
vec![
49+
format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"),
50+
format!("see {} for further information", url),
51+
],
52+
_ => vec![],
53+
};
54+
(title, msg, helps)
55+
}
56+
_ => {
57+
let (title, msg) = match e.kind {
58+
Unsupported(_) =>
59+
("unsupported operation", e.to_string()),
60+
UndefinedBehavior(_) =>
61+
("Undefined Behavior", e.to_string()),
62+
ResourceExhaustion(_) =>
63+
("resource exhaustion", e.to_string()),
64+
_ =>
65+
bug!("This error should be impossible in Miri: {}", e),
66+
};
67+
let helps = match e.kind {
68+
Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
69+
vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
70+
Unsupported(_) =>
71+
vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
72+
UndefinedBehavior(_) =>
73+
vec![
74+
format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"),
75+
format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"),
76+
],
77+
_ => vec![],
78+
};
79+
(title, msg, helps)
2680
}
27-
err_unsup!(NoMirFor(..)) => format!(
28-
"{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.",
29-
e
30-
),
31-
InterpError::InvalidProgram(_) => bug!("This error should be impossible in Miri: {}", e),
32-
_ => e.to_string(),
3381
};
34-
e.print_backtrace();
35-
report_msg(ecx, msg, true)
82+
report_msg(ecx, &format!("{}: {}", title, msg), msg, &helps, true)
3683
}
3784

3885
/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
3986
/// Also emits a full stacktrace of the interpreter stack.
40-
pub fn report_msg<'tcx, 'mir>(
87+
fn report_msg<'tcx, 'mir>(
4188
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
42-
msg: String,
89+
title: &str,
90+
span_msg: String,
91+
helps: &[String],
4392
error: bool,
4493
) -> Option<i64> {
45-
if let Some(frame) = ecx.stack().last() {
46-
let span = frame.current_source_info().unwrap().span;
47-
48-
let mut err = if error {
49-
let msg = format!("Miri evaluation error: {}", msg);
50-
ecx.tcx.sess.struct_span_err(span, msg.as_str())
94+
let span = if let Some(frame) = ecx.stack().last() {
95+
frame.current_source_info().unwrap().span
96+
} else {
97+
DUMMY_SP
98+
};
99+
let mut err = if error {
100+
ecx.tcx.sess.struct_span_err(span, title)
101+
} else {
102+
ecx.tcx.sess.diagnostic().span_note_diag(span, title)
103+
};
104+
err.span_label(span, span_msg);
105+
for help in helps {
106+
err.help(help);
107+
}
108+
// Add backtrace
109+
let frames = ecx.generate_stacktrace(None);
110+
// We iterate with indices because we need to look at the next frame (the caller).
111+
for idx in 0..frames.len() {
112+
let frame_info = &frames[idx];
113+
let call_site_is_local = frames
114+
.get(idx + 1)
115+
.map_or(false, |caller_info| caller_info.instance.def_id().is_local());
116+
if call_site_is_local {
117+
err.span_note(frame_info.call_site, &frame_info.to_string());
51118
} else {
52-
ecx.tcx.sess.diagnostic().span_note_diag(span, msg.as_str())
53-
};
54-
let frames = ecx.generate_stacktrace(None);
55-
err.span_label(span, msg);
56-
// We iterate with indices because we need to look at the next frame (the caller).
57-
for idx in 0..frames.len() {
58-
let frame_info = &frames[idx];
59-
let call_site_is_local = frames
60-
.get(idx + 1)
61-
.map_or(false, |caller_info| caller_info.instance.def_id().is_local());
62-
if call_site_is_local {
63-
err.span_note(frame_info.call_site, &frame_info.to_string());
64-
} else {
65-
err.note(&frame_info.to_string());
66-
}
119+
err.note(&frame_info.to_string());
67120
}
68-
err.emit();
69-
} else {
70-
ecx.tcx.sess.err(&msg);
71121
}
72122

123+
err.emit();
124+
73125
for (i, frame) in ecx.stack().iter().enumerate() {
74126
trace!("-------------------");
75127
trace!("Frame {}", i);
@@ -106,7 +158,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
106158
CreatedAlloc(AllocId(id)) =>
107159
format!("created allocation with id {}", id),
108160
};
109-
report_msg(this, msg, false);
161+
report_msg(this, "tracking was triggered", msg, &[], false);
110162
}
111163
});
112164
}

src/eval.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ impl Default for MiriConfig {
5151
}
5252
}
5353

54-
/// Details of premature program termination.
55-
pub enum TerminationInfo {
56-
Exit(i64),
57-
Abort(Option<String>),
58-
}
59-
6054
/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
6155
/// the location where the return value of the `start` lang item will be
6256
/// written to.
@@ -229,6 +223,6 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
229223
}
230224
Some(return_code)
231225
}
232-
Err(e) => report_diagnostic(&ecx, e),
226+
Err(e) => report_error(&ecx, e),
233227
}
234228
}

src/helpers.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
367367
/// case.
368368
fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
369369
if !self.eval_context_ref().machine.communicate {
370-
throw_unsup_format!(
371-
"`{}` not available when isolation is enabled (pass the flag `-Zmiri-disable-isolation` to disable isolation)",
370+
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
371+
"`{}` not available when isolation is enabled",
372372
name,
373-
)
373+
)))
374374
}
375375
Ok(())
376376
}

src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};
4545
pub use crate::shims::EvalContextExt as ShimsEvalContextExt;
4646

4747
pub use crate::diagnostics::{
48-
register_diagnostic, report_diagnostic, EvalContextExt as DiagnosticsEvalContextExt,
49-
NonHaltingDiagnostic,
48+
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
49+
TerminationInfo, NonHaltingDiagnostic,
5050
};
51-
pub use crate::eval::{create_ecx, eval_main, MiriConfig, TerminationInfo};
51+
pub use crate::eval::{create_ecx, eval_main, MiriConfig};
5252
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
5353
pub use crate::machine::{
5454
AllocExtra, Evaluator, FrameData, MemoryExtra, MiriEvalContext, MiriEvalContextExt,

src/stacked_borrows.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ impl GlobalState {
192192
}
193193
}
194194

195+
/// Error reporting
196+
fn err_sb_ub(msg: String) -> InterpError<'static> {
197+
// FIXME: use `err_machine_stop!` macro, once that exists.
198+
InterpError::MachineStop(Box::new(TerminationInfo::ExperimentalUb {
199+
msg,
200+
url: format!("https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"),
201+
}))
202+
}
203+
195204
// # Stacked Borrows Core Begin
196205

197206
/// We need to make at least the following things true:
@@ -272,15 +281,15 @@ impl<'tcx> Stack {
272281
if let Some(call) = item.protector {
273282
if global.is_active(call) {
274283
if let Some(tag) = tag {
275-
throw_ub!(UbExperimental(format!(
284+
Err(err_sb_ub(format!(
276285
"not granting access to tag {:?} because incompatible item is protected: {:?}",
277286
tag, item
278-
)));
287+
)))?
279288
} else {
280-
throw_ub!(UbExperimental(format!(
289+
Err(err_sb_ub(format!(
281290
"deallocating while item is protected: {:?}",
282291
item
283-
)));
292+
)))?
284293
}
285294
}
286295
}
@@ -294,10 +303,10 @@ impl<'tcx> Stack {
294303

295304
// Step 1: Find granting item.
296305
let granting_idx = self.find_granting(access, tag).ok_or_else(|| {
297-
err_ub!(UbExperimental(format!(
306+
err_sb_ub(format!(
298307
"no item granting {} to tag {:?} found in borrow stack.",
299308
access, tag
300-
),))
309+
))
301310
})?;
302311

303312
// Step 2: Remove incompatible items above them. Make sure we do not remove protected
@@ -338,10 +347,10 @@ impl<'tcx> Stack {
338347
fn dealloc(&mut self, tag: Tag, global: &GlobalState) -> InterpResult<'tcx> {
339348
// Step 1: Find granting item.
340349
self.find_granting(AccessKind::Write, tag).ok_or_else(|| {
341-
err_ub!(UbExperimental(format!(
350+
err_sb_ub(format!(
342351
"no item granting write access for deallocation to tag {:?} found in borrow stack",
343352
tag,
344-
)))
353+
))
345354
})?;
346355

347356
// Step 2: Remove all items. Also checks for protectors.
@@ -363,10 +372,10 @@ impl<'tcx> Stack {
363372
// Now we figure out which item grants our parent (`derived_from`) this kind of access.
364373
// We use that to determine where to put the new item.
365374
let granting_idx = self.find_granting(access, derived_from)
366-
.ok_or_else(|| err_ub!(UbExperimental(format!(
375+
.ok_or_else(|| err_sb_ub(format!(
367376
"trying to reborrow for {:?}, but parent tag {:?} does not have an appropriate item in the borrow stack",
368377
new.perm, derived_from,
369-
))))?;
378+
)))?;
370379

371380
// Compute where to put the new item.
372381
// Either way, we ensure that we insert the new item in a way such that between

0 commit comments

Comments
 (0)