Skip to content

Commit 0679711

Browse files
committed
Auto merge of #42938 - est31:col_number, r=eddyb
Output column number info when panicking Outputs the column number when panicking. Useful when you e.g. have code like `foo[i] = bar[k] + bar[l]` and you get a panic with index out of bounds, or when you have an expression like `a = b + c + d + e` and the addition overflows. Now you know which operation to blame! The format is `file:line:column`, just like for compiler errors. Example output with the patch: ``` thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 8', src/main.rs:3:8 ``` As some of the API between the compiler and the library landscape gets broken, this is a bit hackier than I'd originally wanted it to be. * `panic` and `panic_bounds_check` lang items got an additional column param, on stage0 I still have to use the previous version. After a SNAP this should be resolved. * For `#[derive(RustcDeserialze)]`, stage0 requires a fixed signature for `std::rt::begin_panic`, so we can't change it right away. What we need to do instead is to keep the signature, and add a `begin_panic_new` function that we use in later stages instead. After a SNAP we can change the `begin_panic` function and rely on it instead of `begin_panic_new`, and one SNAP later we can remove `begin_panic_new`. * Fortunately I didn't have to break anything about the panic hook API, I could easily extend it. Note that debuginfo remains unchanged, so RUST_BACKTRACE output won't contain any column info. See issue #42921 for discussion on including the column in debuginfo.
2 parents 2a99216 + 3b91f94 commit 0679711

File tree

11 files changed

+150
-60
lines changed

11 files changed

+150
-60
lines changed

src/Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/doc/unstable-book/src/language-features/lang-items.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ pub extern fn rust_eh_unwind_resume() {
143143
#[no_mangle]
144144
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
145145
_file: &'static str,
146-
_line: u32) -> ! {
146+
_line: u32,
147+
_column: u32) -> ! {
147148
unsafe { intrinsics::abort() }
148149
}
149150
```
@@ -187,7 +188,8 @@ pub extern fn rust_eh_unwind_resume() {
187188
#[no_mangle]
188189
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
189190
_file: &'static str,
190-
_line: u32) -> ! {
191+
_line: u32,
192+
_column: u32) -> ! {
191193
unsafe { intrinsics::abort() }
192194
}
193195
```

src/libcore/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@
3636
//! These functions are often provided by the system libc, but can also be
3737
//! provided by the [rlibc crate](https://crates.io/crates/rlibc).
3838
//!
39-
//! * `rust_begin_panic` - This function takes three arguments, a
40-
//! `fmt::Arguments`, a `&'static str`, and a `u32`. These three arguments
39+
//! * `rust_begin_panic` - This function takes four arguments, a
40+
//! `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments
4141
//! dictate the panic message, the file at which panic was invoked, and the
42-
//! line. It is up to consumers of this core library to define this panic
43-
//! function; it is only required to never return. This requires a `lang`
44-
//! attribute named `panic_fmt`.
42+
//! line and column inside the file. It is up to consumers of this core
43+
//! library to define this panic function; it is only required to never
44+
//! return. This requires a `lang` attribute named `panic_fmt`.
4545
//!
4646
//! * `rust_eh_personality` - is used by the failure mechanisms of the
4747
//! compiler. This is often mapped to GCC's personality function, but crates

src/libcore/macros.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,18 @@ macro_rules! panic {
1717
panic!("explicit panic")
1818
);
1919
($msg:expr) => ({
20-
static _MSG_FILE_LINE: (&'static str, &'static str, u32) = ($msg, file!(), line!());
21-
$crate::panicking::panic(&_MSG_FILE_LINE)
20+
static _MSG_FILE_LINE_COL: (&'static str, &'static str, u32, u32) =
21+
($msg, file!(), line!(), column!());
22+
$crate::panicking::panic(&_MSG_FILE_LINE_COL)
2223
});
2324
($fmt:expr, $($arg:tt)*) => ({
2425
// The leading _'s are to avoid dead code warnings if this is
2526
// used inside a dead function. Just `#[allow(dead_code)]` is
2627
// insufficient, since the user may have
2728
// `#[forbid(dead_code)]` and which cannot be overridden.
28-
static _FILE_LINE: (&'static str, u32) = (file!(), line!());
29-
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*), &_FILE_LINE)
29+
static _MSG_FILE_LINE_COL: (&'static str, u32, u32) =
30+
(file!(), line!(), column!());
31+
$crate::panicking::panic_fmt(format_args!($fmt, $($arg)*), &_MSG_FILE_LINE_COL)
3032
});
3133
}
3234

src/libcore/panicking.rs

+31-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
//!
1818
//! ```
1919
//! # use std::fmt;
20-
//! fn panic_impl(fmt: fmt::Arguments, file_line: &(&'static str, u32)) -> !
20+
//! fn panic_impl(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> !
2121
//! # { loop {} }
2222
//! ```
2323
//!
@@ -39,34 +39,55 @@
3939
use fmt;
4040

4141
#[cold] #[inline(never)] // this is the slow path, always
42-
#[lang = "panic"]
43-
pub fn panic(expr_file_line: &(&'static str, &'static str, u32)) -> ! {
42+
#[cfg_attr(not(stage0), lang = "panic")]
43+
pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
4444
// Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially
4545
// reduce size overhead. The format_args! macro uses str's Display trait to
4646
// write expr, which calls Formatter::pad, which must accommodate string
4747
// truncation and padding (even though none is used here). Using
4848
// Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
4949
// output binary, saving up to a few kilobytes.
50+
let (expr, file, line, col) = *expr_file_line_col;
51+
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col))
52+
}
53+
54+
// FIXME: remove when SNAP
55+
#[cold] #[inline(never)]
56+
#[cfg(stage0)]
57+
#[lang = "panic"]
58+
pub fn panic_old(expr_file_line: &(&'static str, &'static str, u32)) -> ! {
5059
let (expr, file, line) = *expr_file_line;
51-
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line))
60+
let expr_file_line_col = (expr, file, line, 0);
61+
panic(&expr_file_line_col)
62+
}
63+
64+
#[cold] #[inline(never)]
65+
#[cfg_attr(not(stage0), lang = "panic_bounds_check")]
66+
fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
67+
index: usize, len: usize) -> ! {
68+
panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}",
69+
len, index), file_line_col)
5270
}
5371

72+
// FIXME: remove when SNAP
5473
#[cold] #[inline(never)]
74+
#[cfg(stage0)]
5575
#[lang = "panic_bounds_check"]
56-
fn panic_bounds_check(file_line: &(&'static str, u32),
76+
fn panic_bounds_check_old(file_line: &(&'static str, u32),
5777
index: usize, len: usize) -> ! {
78+
let (file, line) = *file_line;
5879
panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}",
59-
len, index), file_line)
80+
len, index), &(file, line, 0))
6081
}
6182

6283
#[cold] #[inline(never)]
63-
pub fn panic_fmt(fmt: fmt::Arguments, file_line: &(&'static str, u32)) -> ! {
84+
pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
6485
#[allow(improper_ctypes)]
6586
extern {
6687
#[lang = "panic_fmt"]
6788
#[unwind]
68-
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32) -> !;
89+
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !;
6990
}
70-
let (file, line) = *file_line;
71-
unsafe { panic_impl(fmt, file, line) }
91+
let (file, line, col) = *file_line_col;
92+
unsafe { panic_impl(fmt, file, line, col) }
7293
}

src/librustc_trans/mir/block.rs

+17-15
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use type_of;
2828
use type_::Type;
2929

3030
use syntax::symbol::Symbol;
31+
use syntax_pos::Pos;
3132

3233
use std::cmp;
3334

@@ -333,6 +334,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
333334
let filename = Symbol::intern(&loc.file.name).as_str();
334335
let filename = C_str_slice(bcx.ccx, filename);
335336
let line = C_u32(bcx.ccx, loc.line as u32);
337+
let col = C_u32(bcx.ccx, loc.col.to_usize() as u32 + 1);
336338

337339
// Put together the arguments to the panic entry point.
338340
let (lang_item, args, const_err) = match *msg {
@@ -347,29 +349,29 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
347349
index: index as u64
348350
}));
349351

350-
let file_line = C_struct(bcx.ccx, &[filename, line], false);
351-
let align = llalign_of_min(bcx.ccx, common::val_ty(file_line));
352-
let file_line = consts::addr_of(bcx.ccx,
353-
file_line,
354-
align,
355-
"panic_bounds_check_loc");
352+
let file_line_col = C_struct(bcx.ccx, &[filename, line, col], false);
353+
let align = llalign_of_min(bcx.ccx, common::val_ty(file_line_col));
354+
let file_line_col = consts::addr_of(bcx.ccx,
355+
file_line_col,
356+
align,
357+
"panic_bounds_check_loc");
356358
(lang_items::PanicBoundsCheckFnLangItem,
357-
vec![file_line, index, len],
359+
vec![file_line_col, index, len],
358360
const_err)
359361
}
360362
mir::AssertMessage::Math(ref err) => {
361363
let msg_str = Symbol::intern(err.description()).as_str();
362364
let msg_str = C_str_slice(bcx.ccx, msg_str);
363-
let msg_file_line = C_struct(bcx.ccx,
364-
&[msg_str, filename, line],
365+
let msg_file_line_col = C_struct(bcx.ccx,
366+
&[msg_str, filename, line, col],
365367
false);
366-
let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line));
367-
let msg_file_line = consts::addr_of(bcx.ccx,
368-
msg_file_line,
369-
align,
370-
"panic_loc");
368+
let align = llalign_of_min(bcx.ccx, common::val_ty(msg_file_line_col));
369+
let msg_file_line_col = consts::addr_of(bcx.ccx,
370+
msg_file_line_col,
371+
align,
372+
"panic_loc");
371373
(lang_items::PanicFnLangItem,
372-
vec![msg_file_line],
374+
vec![msg_file_line_col],
373375
Some(ErrKind::Math(err.clone())))
374376
}
375377
};

src/libstd/macros.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ macro_rules! panic {
4141
panic!("explicit panic")
4242
});
4343
($msg:expr) => ({
44-
$crate::rt::begin_panic($msg, {
44+
$crate::rt::begin_panic_new($msg, {
4545
// static requires less code at runtime, more constant data
46-
static _FILE_LINE: (&'static str, u32) = (file!(), line!());
47-
&_FILE_LINE
46+
static _FILE_LINE_COL: (&'static str, u32, u32) = (file!(), line!(), column!());
47+
&_FILE_LINE_COL
4848
})
4949
});
5050
($fmt:expr, $($arg:tt)+) => ({
@@ -53,8 +53,8 @@ macro_rules! panic {
5353
// used inside a dead function. Just `#[allow(dead_code)]` is
5454
// insufficient, since the user may have
5555
// `#[forbid(dead_code)]` and which cannot be overridden.
56-
static _FILE_LINE: (&'static str, u32) = (file!(), line!());
57-
&_FILE_LINE
56+
static _FILE_LINE_COL: (&'static str, u32, u32) = (file!(), line!(), column!());
57+
&_FILE_LINE_COL
5858
})
5959
});
6060
}

src/libstd/panicking.rs

+73-11
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ impl<'a> PanicInfo<'a> {
262262
pub struct Location<'a> {
263263
file: &'a str,
264264
line: u32,
265+
col: u32,
265266
}
266267

267268
impl<'a> Location<'a> {
@@ -308,6 +309,29 @@ impl<'a> Location<'a> {
308309
pub fn line(&self) -> u32 {
309310
self.line
310311
}
312+
313+
/// Returns the column from which the panic originated.
314+
///
315+
/// # Examples
316+
///
317+
/// ```should_panic
318+
/// #![feature(panic_col)]
319+
/// use std::panic;
320+
///
321+
/// panic::set_hook(Box::new(|panic_info| {
322+
/// if let Some(location) = panic_info.location() {
323+
/// println!("panic occured at column {}", location.column());
324+
/// } else {
325+
/// println!("panic occured but can't get location information...");
326+
/// }
327+
/// }));
328+
///
329+
/// panic!("Normal panic");
330+
/// ```
331+
#[unstable(feature = "panic_col", reason = "recently added", issue = "42939")]
332+
pub fn column(&self) -> u32 {
333+
self.col
334+
}
311335
}
312336

313337
fn default_hook(info: &PanicInfo) {
@@ -329,6 +353,7 @@ fn default_hook(info: &PanicInfo) {
329353

330354
let file = info.location.file;
331355
let line = info.location.line;
356+
let col = info.location.col;
332357

333358
let msg = match info.payload.downcast_ref::<&'static str>() {
334359
Some(s) => *s,
@@ -342,8 +367,8 @@ fn default_hook(info: &PanicInfo) {
342367
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
343368

344369
let write = |err: &mut ::io::Write| {
345-
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}",
346-
name, msg, file, line);
370+
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}:{}",
371+
name, msg, file, line, col);
347372

348373
#[cfg(feature = "backtrace")]
349374
{
@@ -467,8 +492,9 @@ pub fn panicking() -> bool {
467492
#[unwind]
468493
pub extern fn rust_begin_panic(msg: fmt::Arguments,
469494
file: &'static str,
470-
line: u32) -> ! {
471-
begin_panic_fmt(&msg, &(file, line))
495+
line: u32,
496+
col: u32) -> ! {
497+
begin_panic_fmt(&msg, &(file, line, col))
472498
}
473499

474500
/// The entry point for panicking with a formatted message.
@@ -482,7 +508,7 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
482508
issue = "0")]
483509
#[inline(never)] #[cold]
484510
pub fn begin_panic_fmt(msg: &fmt::Arguments,
485-
file_line: &(&'static str, u32)) -> ! {
511+
file_line_col: &(&'static str, u32, u32)) -> ! {
486512
use fmt::Write;
487513

488514
// We do two allocations here, unfortunately. But (a) they're
@@ -492,7 +518,39 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
492518

493519
let mut s = String::new();
494520
let _ = s.write_fmt(*msg);
495-
begin_panic(s, file_line)
521+
begin_panic_new(s, file_line_col)
522+
}
523+
524+
// FIXME: In PR #42938, we have added the column as info passed to the panic
525+
// handling code. For this, we want to break the ABI of begin_panic.
526+
// This is not possible to do directly, as the stage0 compiler is hardcoded
527+
// to emit a call to begin_panic in src/libsyntax/ext/build.rs, only
528+
// with the file and line number being passed, but not the colum number.
529+
// By changing the compiler source, we can only affect behaviour of higher
530+
// stages. We need to perform the switch over two stage0 replacements, using
531+
// a temporary function begin_panic_new while performing the switch:
532+
// 0. Right now, we tell stage1 onward to emit a call to begin_panic_new.
533+
// 1. In the first SNAP, stage0 calls begin_panic_new with the new ABI,
534+
// begin_panic stops being used. Now we can change begin_panic to
535+
// the new ABI, and start emitting calls to begin_panic in higher
536+
// stages again, this time with the new ABI.
537+
// 2. After the second SNAP, stage0 calls begin_panic with the new ABI,
538+
// and we can remove the temporary begin_panic_new function.
539+
540+
/// This is the entry point of panicking for panic!() and assert!().
541+
#[unstable(feature = "libstd_sys_internals",
542+
reason = "used by the panic! macro",
543+
issue = "0")]
544+
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
545+
pub fn begin_panic_new<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u32)) -> ! {
546+
// Note that this should be the only allocation performed in this code path.
547+
// Currently this means that panic!() on OOM will invoke this code path,
548+
// but then again we're not really ready for panic on OOM anyway. If
549+
// we do start doing this, then we should propagate this allocation to
550+
// be performed in the parent of this thread instead of the thread that's
551+
// panicking.
552+
553+
rust_panic_with_hook(Box::new(msg), file_line_col)
496554
}
497555

498556
/// This is the entry point of panicking for panic!() and assert!().
@@ -508,7 +566,10 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> !
508566
// be performed in the parent of this thread instead of the thread that's
509567
// panicking.
510568

511-
rust_panic_with_hook(Box::new(msg), file_line)
569+
let (file, line) = *file_line;
570+
let file_line_col = (file, line, 0);
571+
572+
rust_panic_with_hook(Box::new(msg), &file_line_col)
512573
}
513574

514575
/// Executes the primary logic for a panic, including checking for recursive
@@ -520,8 +581,8 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> !
520581
#[inline(never)]
521582
#[cold]
522583
fn rust_panic_with_hook(msg: Box<Any + Send>,
523-
file_line: &(&'static str, u32)) -> ! {
524-
let (file, line) = *file_line;
584+
file_line_col: &(&'static str, u32, u32)) -> ! {
585+
let (file, line, col) = *file_line_col;
525586

526587
let panics = update_panic_count(1);
527588

@@ -540,8 +601,9 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
540601
let info = PanicInfo {
541602
payload: &*msg,
542603
location: Location {
543-
file: file,
544-
line: line,
604+
file,
605+
line,
606+
col,
545607
},
546608
};
547609
HOOK_LOCK.read();

src/libstd/rt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626

2727
// Reexport some of our utilities which are expected by other crates.
28-
pub use panicking::{begin_panic, begin_panic_fmt, update_panic_count};
28+
pub use panicking::{begin_panic_new, begin_panic, begin_panic_fmt, update_panic_count};
2929

3030
#[cfg(not(test))]
3131
#[lang = "start"]

0 commit comments

Comments
 (0)