Skip to content

unreachable!("{}") works on Rust 2021 #92137

Closed
@m-ou-se

Description

@m-ou-se
Member

The behavior of unreachable does not depend the edition of the crate that invoked the macro. That's fine for todo!() and unimplemented!() due to the way they are implemented, but in #92068, it was discovered that unreachable!() behaves subtly different, due to the way it has a special one-argument case. Looks like missed this when working on the 2021 panic macros. :(

Activity

added
A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)
C-bugCategory: This is a bug.
on Dec 20, 2021
self-assigned this
on Dec 20, 2021
changed the title [-]unimplemented!("{}") works on Rust 2021[/-] [+]unreachable!("{}") works on Rust 2021[/+] on Dec 20, 2021
m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

todo!() and unimplemented!() are implemented like this:

macro_rules! todo {
() => ($crate::panic!("not yet implemented"));
($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+)));
}

They never accepted todo!("{}") or todo!(123) since their arguments are always directly passed to format_args!(). So, they didn't have to change for Rust 2021.

unreachable!() however, is implemented differently:

macro_rules! unreachable {
() => ({
$crate::panic!("internal error: entered unreachable code")
});
($msg:expr $(,)?) => ({
$crate::unreachable!("{}", $msg)
});
($fmt:expr, $($arg:tt)*) => ({
$crate::panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
});
}

It doesn't just forward to panic!() or format_args!(), but instead it does its own concat shenanigans, and has special handling of the single argument case.

m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

So then the question is: Do we consider this a bug in Rust 2021 and fix it in Rust 1.59 for that edition? Or leave this in and only fix it in a later edition (or never)?

There's quite a few cases on crates.io of Rust 2015/2018 code depending on this behavior, but Rust 2021 is very new at this point. Adding this change/fix to Rust 2021 probably doesn't break anyone, yet.

m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

Behavior:

unreachable!();          // panic message: "internal error: entered unreachable code"
unreachable!(123);       // panic message: "internal error: entered unreachable code: 123"
unreachable!("{}");      // panic message: "internal error: entered unreachable code: {}"
unreachable!("{}", 123); // panic message: "internal error: entered unreachable code: 123"
m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

We can't fix unreachable without making it a builtin macro, like panic!(). That's because (unlike e.g. debug_assert), this macro doesn't just forward to assert/panic, but has its own problematic implementation with a special case that shouldn't exist.

jyn514

jyn514 commented on Dec 20, 2021

@jyn514
Member

We can't fix unreachable without making it a builtin macro, like panic!(). That's because (unlike e.g. debug_assert), this macro doesn't just forward to assert/panic, but has its own problematic implementation with a special case that shouldn't exist.

I'm not sure I follow - can't we remove the concat special case and forward directly to panic, like todo does?

m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

I'm not sure I follow - can't we remove the concat special case and forward directly to panic, like todo does?

@jyn514 Then unreachable!(123) would stop working on Rust 2015/2018.

m-ou-se

m-ou-se commented on Dec 20, 2021

@m-ou-se
MemberAuthor

From a quick grep through crates.io, it looks like there's about 15 crates on crates.io depending on the current behavior that would break if we made it work like todo!(). Here's a few examples from the grep results:

unreachable!(message);
unreachable!(&error)
unreachable!(format!("Cannot compute gcd of {:?} and {:?}", x, y))
Err(err) => unreachable!(err),
_ => unreachable!(format!("Unsupported socket family {}!", raw.family)),
l => unreachable!(format!("got unexpected length {0:?}", l)),
x => unreachable!(x),
Error { message } => unreachable!(message),
unreachable!(false);
unreachable!(msg)
unreachable!(&format!("n = {:?}", n)),
_ => unreachable!(ERROR_MSG),
_ => unreachable!(format!("{} is invalid month!", month)),
hash => { unreachable!(hash); }
joshtriplett

joshtriplett commented on Dec 20, 2021

@joshtriplett
Member

Making unreachable built-in to fix this on Rust 2021 seems reasonable to me.

jhpratt

jhpratt commented on Dec 21, 2021

@jhpratt
Member

IMO as long as there's a crater run fixing this is reasonable. It's certainly a bug.

added
I-prioritizeIssue: Indicates that prioritization has been requested for this issue.
on Jan 5, 2022

67 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-edition-2021Area: The 2021 editionA-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)C-bugCategory: This is a bug.P-highHigh priorityT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.disposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.relnotesMarks issues that should be documented in the release notes of the next release.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    Participants

    @nikomatsakis@joshtriplett@m-ou-se@Arnavion@jqnatividad

    Issue actions

      unreachable!("{}") works on Rust 2021 · Issue #92137 · rust-lang/rust