Some crates use the panic mechanism by design in normal situations, not just for irrecoverable errors. Linking them to the panic_abort
runtime is a logic bug.
Crates may try to prevent this situation with #[cfg(panic = "abort")] compile_error!(...);
, but that is insufficient. cargo rustc -- -C panic=abort
successfully compiles dependency crates with the default panic runtime (supposedly panic_unwind
) while linking the binary crate to the panic_abort
runtime, producing broken executables if the dependency crate depends on unwinding. This works even on stable.
There is currently a hacky way to cause rustc to emit a compile-time error in this case: if the dependency crate ever calls an extern function with an unwinding ABI:
unsafe extern "C-unwind" {
safe fn f();
}
f();
rustc errors out like so:
error: the crate `<dependency crate>` requires panic strategy `unwind` which is incompatible with this crate's strategy of `abort`
However, this is an indirect approach that needs a globally universal identifier (instead of f
) and does not really express the intention well.
It is also possible (and IMO realistic) for some crates to only work correctly under the panic_abort
runtime. For example, a hypothetical replace_with
crate could provide an implementation that would only be sound without unwinding. In this case, however, #[cfg(panic = "unwind")] compile_error!(...);
is a sufficient check, as any crate being compiled with the aborting runtime is sufficient to make the whole executable panic_abort
.
Finally, some crates might want to choose between two implementations (e.g.: raising very rare, but recoverable errors with panics or via Result
s) depending on the panic runtime. Such crates would always work correctly if linked with panic_abort
, but panic_unwind
would be more efficient. The desired effect here is for code behind #[cfg(panic = "unwind")]
to not be linked to the panic_abort
runtime, but for -C panic=abort
to work nevertheless if it's applied to the whole dependency tree rather than just the final executable.
I think that it'd be useful for this to be unified and configured with a single knob. Cargo.toml
is the obvious choice to place it in. There's already a panic
field in [profile.*]
, but the intention here is to configure the required behavior rather a recommended behavior, i.e. semantics rather than optimization settings, so this strikes me as a bad choice. Perhaps package.required_panic_strategy
would work better. That option would accept any of the following four values:
none
(default): No additional requirements, behavior matches current Cargo.unwind
: Fail linkage ifpanic_unwind
isn't linked in. Equivalent to theextern "C-unwind"
trick, used by crates that always need panics.abort
: Fail linkage ifpanic_abort
isn't linked in. Equivalent to#[cfg(panic = "unwind")] compile_error!(...);
, used by crates that assume unwinding is impossible for soundness.same
(bileshed): Fail linkage if the runtime the crate is compiled with doesn't match the runtime that is linked in, used by crates that need to change code in compile-time depending on the panic runtime that is linked in.
Thoughts? Vibes?