Skip to content

Commit 25b21a1

Browse files
committed
Auto merge of rust-lang#93179 - Urgau:unreachable-2021, r=m-ou-se,oli-obk
Fix invalid special casing of the unreachable! macro This pull-request fix an invalid special casing of the `unreachable!` macro in the same way the `panic!` macro was solved, by adding two new internal only macros `unreachable_2015` and `unreachable_2021` edition dependent and turn `unreachable!` into a built-in macro that do dispatching. This logic is stolen from the `panic!` macro. ~~This pull-request also adds an internal feature `format_args_capture_non_literal` that allows capturing arguments from formatted string that expanded from macros. The original RFC rust-lang#2795 mentioned this as a future possibility. This feature is [required](rust-lang#92137 (comment)) because of concatenation that needs to be done inside the macro:~~ ```rust $crate::concat!("internal error: entered unreachable code: ", $fmt) ``` **In summary** the new behavior for the `unreachable!` macro with this pr is: Edition 2021: ```rust let x = 5; unreachable!("x is {x}"); ``` ``` internal error: entered unreachable code: x is 5 ``` Edition <= 2018: ```rust let x = 5; unreachable!("x is {x}"); ``` ``` internal error: entered unreachable code: x is {x} ``` Also note that the change in this PR are **insta-stable** and **breaking changes** but this a considered as being a [bug](rust-lang#92137 (comment)). If someone could start a perf run and then a crater run this would be appreciated. Fixes rust-lang#92137
2 parents 7b43cfc + a4b93eb commit 25b21a1

22 files changed

+300
-54
lines changed

compiler/rustc_builtin_macros/src/assert.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::panic::use_panic_2021;
1+
use crate::edition_panic::use_panic_2021;
22
use rustc_ast::ptr::P;
33
use rustc_ast::token;
44
use rustc_ast::tokenstream::{DelimSpan, TokenStream};

compiler/rustc_builtin_macros/src/panic.rs renamed to compiler/rustc_builtin_macros/src/edition_panic.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,29 @@ pub fn expand_panic<'cx>(
2020
sp: Span,
2121
tts: TokenStream,
2222
) -> Box<dyn MacResult + 'cx> {
23-
let panic = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
23+
let mac = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
24+
expand(mac, cx, sp, tts)
25+
}
2426

27+
// This expands to either
28+
// - `$crate::panic::unreachable_2015!(...)` or
29+
// - `$crate::panic::unreachable_2021!(...)`
30+
// depending on the edition.
31+
pub fn expand_unreachable<'cx>(
32+
cx: &'cx mut ExtCtxt<'_>,
33+
sp: Span,
34+
tts: TokenStream,
35+
) -> Box<dyn MacResult + 'cx> {
36+
let mac = if use_panic_2021(sp) { sym::unreachable_2021 } else { sym::unreachable_2015 };
37+
expand(mac, cx, sp, tts)
38+
}
39+
40+
fn expand<'cx>(
41+
mac: rustc_span::Symbol,
42+
cx: &'cx mut ExtCtxt<'_>,
43+
sp: Span,
44+
tts: TokenStream,
45+
) -> Box<dyn MacResult + 'cx> {
2546
let sp = cx.with_call_site_ctxt(sp);
2647

2748
MacEager::expr(
@@ -31,7 +52,7 @@ pub fn expand_panic<'cx>(
3152
path: Path {
3253
span: sp,
3354
segments: cx
34-
.std_path(&[sym::panic, panic])
55+
.std_path(&[sym::panic, mac])
3556
.into_iter()
3657
.map(|ident| PathSegment::from_ident(ident))
3758
.collect(),

compiler/rustc_builtin_macros/src/lib.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ mod concat_bytes;
3232
mod concat_idents;
3333
mod derive;
3434
mod deriving;
35+
mod edition_panic;
3536
mod env;
3637
mod format;
3738
mod format_foreign;
3839
mod global_allocator;
3940
mod log_syntax;
40-
mod panic;
4141
mod source_util;
4242
mod test;
4343
mod trace_macros;
@@ -83,8 +83,9 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
8383
log_syntax: log_syntax::expand_log_syntax,
8484
module_path: source_util::expand_mod,
8585
option_env: env::expand_option_env,
86-
core_panic: panic::expand_panic,
87-
std_panic: panic::expand_panic,
86+
core_panic: edition_panic::expand_panic,
87+
std_panic: edition_panic::expand_panic,
88+
unreachable: edition_panic::expand_unreachable,
8889
stringify: source_util::expand_stringify,
8990
trace_macros: trace_macros::expand_trace_macros,
9091
}

compiler/rustc_hir/src/lang_items.rs

-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ language_item_table! {
277277
Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
278278
PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
279279
PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
280-
PanicStr, sym::panic_str, panic_str, Target::Fn, GenericRequirement::None;
281280
ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
282281
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
283282
PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;

compiler/rustc_lint/src/non_fmt_panic.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
4949
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
5050
if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
5151
if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
52+
let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
53+
5254
if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
5355
|| Some(def_id) == cx.tcx.lang_items().panic_fn()
54-
|| Some(def_id) == cx.tcx.lang_items().panic_str()
56+
|| f_diagnostic_name == Some(sym::panic_str)
5557
{
5658
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
5759
if matches!(
@@ -61,6 +63,22 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
6163
check_panic(cx, f, arg);
6264
}
6365
}
66+
} else if f_diagnostic_name == Some(sym::unreachable_display) {
67+
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
68+
if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) {
69+
check_panic(
70+
cx,
71+
f,
72+
// This is safe because we checked above that the callee is indeed
73+
// unreachable_display
74+
match &arg.kind {
75+
// Get the borrowed arg not the borrow
76+
hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
77+
_ => bug!("call to unreachable_display without borrow"),
78+
},
79+
);
80+
}
81+
}
6482
}
6583
}
6684
}
@@ -85,8 +103,8 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
85103
return;
86104
}
87105

88-
// Find the span of the argument to `panic!()`, before expansion in the
89-
// case of `panic!(some_macro!())`.
106+
// Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the
107+
// case of `panic!(some_macro!())` or `unreachable!(some_macro!())`.
90108
// We don't use source_callsite(), because this `panic!(..)` might itself
91109
// be expanded from another macro, in which case we want to stop at that
92110
// expansion.
@@ -319,6 +337,7 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span,
319337
| sym::std_panic_macro
320338
| sym::assert_macro
321339
| sym::debug_assert_macro
340+
| sym::unreachable_macro
322341
) {
323342
break;
324343
}

compiler/rustc_span/src/symbol.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1437,7 +1437,12 @@ symbols! {
14371437
unmarked_api,
14381438
unpin,
14391439
unreachable,
1440+
unreachable_2015,
1441+
unreachable_2015_macro,
1442+
unreachable_2021,
1443+
unreachable_2021_macro,
14401444
unreachable_code,
1445+
unreachable_display,
14411446
unreachable_macro,
14421447
unrestricted_attribute_tokens,
14431448
unsafe_block_in_unsafe_fn,

library/core/src/macros/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,22 @@ macro_rules! writeln {
594594
/// unreachable!("The loop should always return");
595595
/// }
596596
/// ```
597+
#[cfg(not(bootstrap))]
598+
#[macro_export]
599+
#[rustc_builtin_macro(unreachable)]
600+
#[allow_internal_unstable(edition_panic)]
601+
#[stable(feature = "rust1", since = "1.0.0")]
602+
#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")]
603+
macro_rules! unreachable {
604+
// Expands to either `$crate::panic::unreachable_2015` or `$crate::panic::unreachable_2021`
605+
// depending on the edition of the caller.
606+
($($arg:tt)*) => {
607+
/* compiler built-in */
608+
};
609+
}
610+
611+
/// unreachable!() macro
612+
#[cfg(bootstrap)]
597613
#[macro_export]
598614
#[stable(feature = "rust1", since = "1.0.0")]
599615
#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")]

library/core/src/panic.rs

+33
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,39 @@ pub macro panic_2021 {
5858
),
5959
}
6060

61+
#[doc(hidden)]
62+
#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
63+
#[allow_internal_unstable(core_panic)]
64+
#[rustc_diagnostic_item = "unreachable_2015_macro"]
65+
#[rustc_macro_transparency = "semitransparent"]
66+
pub macro unreachable_2015 {
67+
() => (
68+
$crate::panicking::panic("internal error: entered unreachable code")
69+
),
70+
// Use of `unreachable_display` for non_fmt_panic lint.
71+
// NOTE: the message ("internal error ...") is embeded directly in unreachable_display
72+
($msg:expr $(,)?) => (
73+
$crate::panicking::unreachable_display(&$msg)
74+
),
75+
($fmt:expr, $($arg:tt)*) => (
76+
$crate::panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
77+
),
78+
}
79+
80+
#[doc(hidden)]
81+
#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
82+
#[allow_internal_unstable(core_panic)]
83+
#[rustc_diagnostic_item = "unreachable_2021_macro"]
84+
#[rustc_macro_transparency = "semitransparent"]
85+
pub macro unreachable_2021 {
86+
() => (
87+
$crate::panicking::panic("internal error: entered unreachable code")
88+
),
89+
($($t:tt)+) => (
90+
$crate::panic!("internal error: entered unreachable code: {}", $crate::format_args!($($t)+))
91+
),
92+
}
93+
6194
/// An internal trait used by libstd to pass data from libstd to `panic_unwind`
6295
/// and other panic runtimes. Not intended to be stabilized any time soon, do
6396
/// not use.

library/core/src/panicking.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,20 @@ pub const fn panic(expr: &'static str) -> ! {
5050

5151
#[inline]
5252
#[track_caller]
53-
#[lang = "panic_str"] // needed for `non-fmt-panics` lint
53+
#[rustc_diagnostic_item = "panic_str"]
5454
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
5555
pub const fn panic_str(expr: &str) -> ! {
5656
panic_display(&expr);
5757
}
5858

59+
#[cfg(not(bootstrap))]
60+
#[inline]
61+
#[track_caller]
62+
#[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint
63+
pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! {
64+
panic_fmt(format_args!("internal error: entered unreachable code: {}", *x));
65+
}
66+
5967
#[inline]
6068
#[track_caller]
6169
#[lang = "panic_display"] // needed for const-evaluated panics

src/test/mir-opt/issue_76432.test.SimplifyComparisonIntegral.diff

+5-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
let mut _19: *const T; // in scope 0 at $DIR/issue_76432.rs:9:54: 9:68
2222
let mut _20: *const T; // in scope 0 at $DIR/issue_76432.rs:9:70: 9:84
2323
let mut _21: *const T; // in scope 0 at $DIR/issue_76432.rs:9:70: 9:84
24-
let mut _22: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
24+
let mut _22: !; // in scope 0 at $SRC_DIR/core/src/panic.rs:LL:COL
2525
let mut _23: &[T; 3]; // in scope 0 at $DIR/issue_76432.rs:7:19: 7:29
2626
scope 1 {
2727
debug v => _2; // in scope 1 at $DIR/issue_76432.rs:7:9: 7:10
@@ -66,16 +66,16 @@
6666
}
6767

6868
bb1: {
69-
StorageLive(_22); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
70-
core::panicking::panic(const "internal error: entered unreachable code"); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
69+
StorageLive(_22); // scope 1 at $SRC_DIR/core/src/panic.rs:LL:COL
70+
core::panicking::panic(const "internal error: entered unreachable code"); // scope 1 at $SRC_DIR/core/src/panic.rs:LL:COL
7171
// mir::Constant
72-
// + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
72+
// + span: $SRC_DIR/core/src/panic.rs:LL:COL
7373
// + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value(Scalar(<ZST>)) }
7474
// ty::Const
7575
// + ty: &str
7676
// + val: Value(Slice { data: Allocation { bytes: [105, 110, 116, 101, 114, 110, 97, 108, 32, 101, 114, 114, 111, 114, 58, 32, 101, 110, 116, 101, 114, 101, 100, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 32, 99, 111, 100, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1099511627775], len: Size { raw: 40 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 40 })
7777
// mir::Constant
78-
// + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
78+
// + span: $SRC_DIR/core/src/panic.rs:LL:COL
7979
// + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [105, 110, 116, 101, 114, 110, 97, 108, 32, 101, 114, 114, 111, 114, 58, 32, 101, 110, 116, 101, 114, 101, 100, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 32, 99, 111, 100, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1099511627775], len: Size { raw: 40 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 40 }) }
8080
}
8181

src/test/ui/consts/const-eval/const_panic.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
2020
LL | const Y: () = std::unreachable!();
2121
| ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:12:15
2222
|
23-
= note: this error originates in the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
23+
= note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
2424

2525
error[E0080]: evaluation of constant value failed
2626
--> $DIR/const_panic.rs:15:15
@@ -68,7 +68,7 @@ error[E0080]: evaluation of constant value failed
6868
LL | const Y_CORE: () = core::unreachable!();
6969
| ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:30:20
7070
|
71-
= note: this error originates in the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
71+
= note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
7272

7373
error[E0080]: evaluation of constant value failed
7474
--> $DIR/const_panic.rs:33:20

src/test/ui/consts/const-eval/const_panic_2021.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
2020
LL | const C: () = std::unreachable!();
2121
| ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:12:15
2222
|
23-
= note: this error originates in the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
23+
= note: this error originates in the macro `$crate::panic::unreachable_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
2424

2525
error[E0080]: evaluation of constant value failed
2626
--> $DIR/const_panic_2021.rs:15:15
@@ -60,7 +60,7 @@ error[E0080]: evaluation of constant value failed
6060
LL | const C_CORE: () = core::unreachable!();
6161
| ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:27:20
6262
|
63-
= note: this error originates in the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
63+
= note: this error originates in the macro `$crate::panic::unreachable_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
6464

6565
error[E0080]: evaluation of constant value failed
6666
--> $DIR/const_panic_2021.rs:30:20

src/test/ui/consts/const-eval/const_panic_libcore_bin.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ error[E0080]: evaluation of constant value failed
1212
LL | const Y: () = unreachable!();
1313
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_libcore_bin.rs:11:15
1414
|
15-
= note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
= note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
1616

1717
error[E0080]: evaluation of constant value failed
1818
--> $DIR/const_panic_libcore_bin.rs:14:15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: format argument must be a string literal
2+
--> $DIR/unreachable-arg.rs:15:18
3+
|
4+
LL | unreachable!(a);
5+
| ^
6+
|
7+
help: you might be missing a string literal to format with
8+
|
9+
LL | unreachable!("{}", a);
10+
| +++++
11+
12+
error: aborting due to previous error
13+

src/test/ui/macros/unreachable-arg.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// ignore-emscripten no processes
2+
3+
// revisions: edition_2015 edition_2021
4+
// [edition_2015]edition:2015
5+
// [edition_2021]edition:2021
6+
// [edition_2015]run-fail
7+
// [edition_2021]check-fail
8+
// [edition_2015]error-pattern:internal error: entered unreachable code: hello
9+
// [edition_2021]error-pattern:format argument must be a string literal
10+
11+
#![allow(non_fmt_panics)]
12+
13+
fn main() {
14+
let a = "hello";
15+
unreachable!(a);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-fail
2+
// ignore-emscripten no processes
3+
4+
// revisions: edition_2015 edition_2021
5+
// [edition_2015]edition:2015
6+
// [edition_2021]edition:2021
7+
// [edition_2015]error-pattern:internal error: entered unreachable code: x is {x}
8+
// [edition_2021]error-pattern:internal error: entered unreachable code: x is 5
9+
10+
#![allow(non_fmt_panics)]
11+
12+
fn main() {
13+
let x = 5;
14+
unreachable!("x is {x}");
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: there is no argument named `x`
2+
--> $DIR/unreachable-format-args.rs:13:5
3+
|
4+
LL | unreachable!("x is {x} and y is {y}", y = 0);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: did you intend to capture a variable `x` from the surrounding scope?
8+
= note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
9+
= note: this error originates in the macro `$crate::concat` (in Nightly builds, run with -Z macro-backtrace for more info)
10+
11+
error: aborting due to previous error
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// ignore-emscripten no processes
2+
3+
// revisions: edition_2015 edition_2021
4+
// [edition_2015]edition:2015
5+
// [edition_2021]edition:2021
6+
// [edition_2015]check-fail
7+
// [edition_2021]run-fail
8+
// [edition_2015]error-pattern:there is no argument named `x`
9+
// [edition_2021]error-pattern:internal error: entered unreachable code: x is 5 and y is 0
10+
11+
fn main() {
12+
let x = 5;
13+
unreachable!("x is {x} and y is {y}", y = 0);
14+
}

0 commit comments

Comments
 (0)