rust-lang / rust-lang/rust

closures are not required to outlive generic arguments mentioned in their body

Open
#153,140 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-closures A-lifetimes A-type-system I-unsound P-low T-types
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

mod side_table {
    use std::sync::atomic::{AtomicPtr, Ordering};
    static STORAGE: AtomicPtr<String> = AtomicPtr::new(std::ptr::null_mut());
    pub fn set<'a: 'a>(x: &'a String) {
        // TODO: assert that this is the first time we've called `set` to avoid
        // overwriting the value.
        STORAGE.store((&raw const *x).cast_mut(), Ordering::Release)
    }
    /// # Safety
    ///
    /// This must only be called after `set` has been called for the lifetime `'a`.
    pub unsafe fn get<'a>() -> &'a String {
        unsafe { &*STORAGE.load(Ordering::Acquire) }
    }
}
fn yikes<'a>(x: &'a String) -> impl FnOnce() + use<> {
    side_table::set::<'a>(x);
    || {
        // SAFETY: We've put a reference in the storage which is
        // valid for the whole lifetime `'a`
        let print_me = unsafe { side_table::get::<'a>() };
        println!("{print_me}");
    }
}

fn main() {
    let closure = {
        let temp = String::from("temporary");
        yikes(&temp)
    };

    closure();
}

I believe the safety comment in the closure is correct and this should be sound. It explicitly checks that get is called with the lifetime 'a.

This is the even more general version of https://github.com/rust-lang/rust/issues/84366 https://github.com/rust-lang/rust/issues/112905

I discussed closure outives requirements with @BoxyUwU recently and there we considered that longterm, we would like the following rules:

  • closures have to outlive all generic parameters mentioned in their signature
  • for any lifetime/type parameter referenced in the body of the closure, we check that this generic parameter is outlived by at least one element of the signature
    • which implies that the closure can only ever be used while these parameters are still valid

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the Rust example from the issue and checking how the compiler handles the closure returned by yikes. Read the linked issues #84366 and #112905, then evaluate the proposed closure outlives rules; done means the compiler no longer accepts the unsound case while enforcing the stated lifetime requirements.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.