Repository: rust
Issue: Tracking Issue for ```#[deprecated_safe]``` attribute
Labels: T-lang,C-tracking-issue,S-tracking-impl-incomplete,F-deprecated_safe

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.

```rust
#[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 rust-lang/lang-team#147)
- [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide])
- [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide])

[stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr
[doc-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#documentation-prs

### 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

* Proof of concept: https://github.com/skippy10110/rust/commits/rustc_deprecated_safe
* #94982
* #95025