Skip to content

Tracking Issue for #[deprecated_safe] attribute #94978

Open

Description

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

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

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

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: A tracking issue for an RFC or an unstable feature.F-deprecated_safe`#![feature(deprecated_safe)]`S-tracking-impl-incompleteStatus: The implementation is incomplete.T-langRelevant to the language team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    • Status

      Rejected/Not lang

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions