Description
openedon Mar 15, 2022
This is a tracking issue for the lang-team MCP "Add #[deprecated_safe] attribute..." (rust-lang/lang-team#147).
The feature gate for the issue is #![feature(deprecated_safe)]
.
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Public API
Apply #[deprecated_safe]
to a pre-existing function or trait that is currently safe but needs to be marked unsafe, which is normally a breaking change. Marking the function or trait as unsafe and applying #[deprecated_safe]
will cause a "safeness deprecation" warning to be emitted anywhere that a compiler error would have been, maintaining backwards compatibility. While this is unsound, the intended use is to address a pre-existing unsoundness in a backwards compatible fashion using deprecations.
#[deprecated_safe(since = "TBD", note = "reason")]
unsafe fn previously_safe_fn() {}
// the following is allowed without erroring, despite the lack of an unsafe block
// warning here
previously_safe_fn();
unsafe {
// no warning
previously_safe_fn();
}
// the following is allowed, despite the fn pointer being safe
// warning here
let safe_fn_ptr: fn() = previously_safe_fn;
// no warning
let unsafe_fn_ptr: unsafe fn() = previously_safe_fn;
// the following is allowed, despite unsafe fn's not being soundly coercible to closures
// warning here
let fn_impl: Box<dyn Fn()> = Box::new(previously_safe_fn);
#[deprecated_safe(since = "TBD", note = "reason")]
unsafe trait PreviouslySafeTrait {}
// the following is allowed, despite the lack of unsafe on the impl block
// warning here
impl PreviouslySafeTrait for u32 {}
// no warning
unsafe impl PreviouslySafeTrait for u64 {}
trait PreviouslySafeTraitFunction {
#[deprecated_safe(since = "TBD", note = "reason")]
unsafe fn previously_safe_fn();
}
impl PreviouslySafeTraitFunction for u32 {
// the following is allowed, despite the lack of unsafe on the impl'd fn
// warning here
fn previously_safe_fn() {}
}
impl PreviouslySafeTraitFunction for u64 {
// no warning
unsafe fn previously_safe_fn() {}
}
// (within libstd only)
// the following will error if used from edition >= 2024 code without unsafe,
// the deprecated_safe escape hatch no longer applies
#[deprecated_safe(since = "TBD", note = "reason", unsafe_edition = "2024")]
unsafe fn previously_safe_fn() {}
Steps
- Implement the MCP (cc Add #[deprecated_safe] attribute to allow functions be be marked unsafe in a backwards compatible fashion lang-team#147)
- Adjust documentation (see instructions on rustc-dev-guide)
- Stabilization PR (see instructions on rustc-dev-guide)
Unresolved Questions
- Are call sites, fn() and Fn() coercions, and impl blocks the only places that would break when adding unsafe? Do other cases need to be handled?
- If this supports becoming an error in a future edition, that won't be autofixable (I (skippy) assume), since an
unsafe {}
block couldn't be added automatically. Is a non-autofixable edition change like that acceptable? - Is it in fact appropriate to make this a publicly available stable attribute, rather than restricting its use to libstd? Libraries can make breaking changes, while rust cannot.
Implementation history
Metadata
Assignees
Labels
Type
Projects
Status
Rejected/Not lang