-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make PanicInfo::message
infallible
#115561
Conversation
I'm very much against this change as it doesn't make sense that The use of |
let message = | ||
*(&msg as &dyn Any).downcast_ref::<&'static str>().unwrap_or(&"<non-str payload>"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be good if this also attempts to downcast to String
, just like the default panic hook does:
rust/library/std/src/panicking.rs
Line 262 in 8769c26
None => match info.payload().downcast_ref::<String>() { |
Then panic_any(some_string)
, and panic!(format!(..))
in Rust ≤2018, will also work.
(For a String
you'll have to use format_args!("{}", s)
though, rather than new_v1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be done, as we'd be borrowing from msg
while also using it as the payload (and thus moving out of it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. It is possible, since the payload isn't actually consumed until after the PanicInfo is gone (at the end of rust_panic_with_hook), but it'd require refactoring some of the code in this file quite a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is the payload.get()
method call, not the move. BoxMeUp::get
must be mutable in order for the #[panic_handler]
to be able to lazily allocate a String
by formatting the Message
from the PanicInfo
that gets passed to it.
It's all a bit roundabout, but if we're willing to break some use cases that assume there's a String
in the payload for panic!("{}", 42)
, then that does seem fixable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The specific problematic impl is https://github.com/rust-lang/rust/blob/1b9567fcaa842b1028f9913838c4b70952470767/library/std/src/panicking.rs#L585
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is the
payload.get()
method call, not the move.BoxMeUp::get
must be mutable in order for the#[panic_handler]
to be able to lazily allocate aString
by formatting theMessage
from thePanicInfo
that gets passed to it.
Sure, but .get() is always called when constructing the PanicInfo that's passed to the panic hook, so at that point the String will have been allocated and can be borrowed by format_args!().
So, rust_panic_with_hook
could just .get() the payload first and then try downcasting it to &'static str
and String
to create a format_args!("{s}")
to make the PanicInfo
with. (That also avoids formatting message
twice when someone uses .message()
in a panic hook. (Since at that point the message has already been formatted into a String
.))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, a lot of the annoyances here come from PanicInfo (the argument to #[panic_handler]) and PanicInfo (the argument to the panic hook) being the same type. I'm hoping we might be able to do something about that: #115974 That'd make this all a lot easier. :)
let message = | ||
*(&msg as &dyn Any).downcast_ref::<&'static str>().unwrap_or(&"<non-str payload>"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"<non-str payload>"
The default panic hook prints "Box<dyn Any>"
in that case. I'm not particularly attached to that specific message, but we should probably keep those the same.
rust/library/std/src/panicking.rs
Line 264 in 8769c26
None => "Box<dyn Any>", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, even better: the default panic hook should not do any downcasting and instead use .message(), now that it works in all cases.
Dumping some of my notes here from digging through the panic code:
Case 1 results in a Case 2 results in a Case 3 results in a A Case 3 only exists in std; it cannot happen as a result from a panic from no_std code. std's default panic hook tries downcasting the payload to This PR changes case 3, My conclusion from that all: if this PR also makes |
Somewhat related: I think |
Thinking more about that: If Perhaps the first type should just be roughtly Then the first type will have an unconditional |
#yolo, let's see what happens when we try to run this through crater: #115974 :) |
…try> Split core's PanicInfo and std's PanicInfo This experimental PR is based on some thoughts I was having here: rust-lang#115561 (comment) --- `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. This PR is an experiment to see what happens if we make 1 and 2 separate types. To try to avoid breaking existing code, they are still named `core::panic::PanicInfo` and `std::panic::PanicInfo`, in the hope that people write the former when used in `#[panic_handler]` (since then they'd be using `no_std`) and the latter when used in a panic hook (since then they're definitely using `std`). Let's see how much explodes. :)
…try> Split core's PanicInfo and std's PanicInfo This experimental PR is based on some thoughts I was having here: rust-lang#115561 (comment) --- `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. This PR is an experiment to see what happens if we make 1 and 2 separate types. To try to avoid breaking existing code, they are still named `core::panic::PanicInfo` and `std::panic::PanicInfo`, in the hope that people write the former when used in `#[panic_handler]` (since then they'd be using `no_std`) and the latter when used in a panic hook (since then they're definitely using `std`). Let's see how much explodes. :)
Marking this as temporariliy blocked on #115974. If splitting core and std's PanicInfo turns out to be feasible, I think If splitting turns out to be infeasible, I think It might be worth considering using the opaque |
☔ The latest upstream changes (presumably #116013) made this pull request unmergeable. Please resolve the merge conflicts. |
#115974 looks feasible. I addressed the last few review comments on that PR, so hopefully it can move forward soon. It makes |
…manieu Split core's PanicInfo and std's PanicInfo `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. --- This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type. (Based on some thoughts I was having here: rust-lang#115561 (comment)) --- For the release notes: > We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. > > `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. > > The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
…=Amanieu Split core's PanicInfo and std's PanicInfo `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. --- This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type. (Based on some thoughts I was having here: rust-lang#115561 (comment)) --- For the release notes: > We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. > > `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. > > The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
…=Amanieu Split core's PanicInfo and std's PanicInfo `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. --- This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type. (Based on some thoughts I was having here: rust-lang#115561 (comment)) --- For the release notes: > We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. > > `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. > > The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
…=Amanieu Split core's PanicInfo and std's PanicInfo `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. --- This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type. (Based on some thoughts I was having here: rust-lang#115561 (comment)) --- For the release notes: > We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. > > `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. > > The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
Rollup merge of rust-lang#115974 - m-ou-se:panicinfo-and-panicinfo, r=Amanieu Split core's PanicInfo and std's PanicInfo `PanicInfo` is used in two ways: 1. As argument to the `#[panic_handler]` in `no_std` context. 2. As argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in `std` context. In situation 1, the `PanicInfo` always has a *message* (of type `fmt::Arguments`), but never a *payload* (of type `&dyn Any`). In situation 2, the `PanicInfo` always has a *payload* (which is often a `String`), but not always a *message*. Having these as the same type is annoying. It means we can't add `.message()` to the first one without also finding a way to properly support it on the second one. (Which is what rust-lang#66745 is blocked on.) It also means that, because the implementation is in `core`, the implementation cannot make use of the `String` type (which doesn't exist in `core`): https://github.com/rust-lang/rust/blob/0692db1a9082380e027f354912229dfd6af37e78/library/core/src/panic/panic_info.rs#L171-L172 This also means that we cannot easily add a useful method like `PanicInfo::payload_as_str() -> Option<&str>` that works for both `&'static str` and `String` payloads. I don't see any good reasons for these to be the same type, other than historical reasons. --- This PR is makes 1 and 2 separate types. To try to avoid breaking existing code and reduce churn, the first one is still named `core::panic::PanicInfo`, and `std::panic::PanicInfo` is a new (deprecated) alias to `PanicHookInfo`. The crater run showed this as a viable option, since people write `core::` when defining a `#[panic_handler]` (because they're in `no_std`) and `std::` when writing a panic hook (since then they're definitely using `std`). On top of that, many definitions of a panic hook don't specify a type at all: they are written as a closure with an inferred argument type. (Based on some thoughts I was having here: rust-lang#115561 (comment)) --- For the release notes: > We have renamed `std::panic::PanicInfo` to `std::panic::PanicHookInfo`. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0. > > `core::panic::PanicInfo` will remain unchanged, however, as this is now a *different type*. > > The reason is that these types have different roles: `std::panic::PanicHookInfo` is the argument to the [panic hook](https://doc.rust-lang.org/stable/std/panic/fn.set_hook.html) in std context (where panics can have an arbitrary payload), while `core::panic::PanicInfo` is the argument to the [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) in no_std context (where panics always carry a formatted *message*). Separating these types allows us to add more useful methods to these types, such as `std::panic::PanicHookInfo::payload_as_str()` and `core::panic::PanicInfo::message()`.
#115974 has been merged! We now have an infallible core::panic::PanicInfo::message! Closing this. |
addresses the remaining T-libs-api concern in #66745
I picked
<non-str payload>
as the fallback messager? libs-api