Skip to content

Tracking Issue for const_refs_to_static #119618

Closed

Description

@RalfJung

This is a tracking issue for consts referencing statics, a @rust-lang/wg-const-eval experiment.
The feature gate for the issue is #![feature(const_refs_to_static)].

This feature allow constants to refer to statics. However, reading from a static that is mutable (static mut, or static with interior mutability) leads to an evaluator error. This is done to ensure that it doesn't matter when the constant is evaluated, it will always produce the same result.

Having a reference to a mutable static in the final value leads to a validation error. This is necessary to ensure that converting these references to a valtree (e.g. for pattern matching) will not error. (The conversion would error because valtree conversion must never read from anything mutable.)

The same goes for reading from or having a reference to extern static; those obviously can't be read as we can't know their value at compile time. Mutating any static is also not possible -- we can't have global mutable state shared between const-eval queries.

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.

Steps

Unresolved Questions

  • When a constant refers to or reads from mutable memory, this is currently rejected at type-check time. With this feature it is instead rejected at const-eval time, which can be during monomorphization. However, that's not fundamentally different from all the other ways in which const-eval can fail post-monomorphization.
  • What about the interaction with reference-typed const generics?

Implementation history

This issue has been assigned to @dingxiangfei2009 via this comment.

Activity

  1. added
    T-langRelevant to the language team
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
    on Jan 5, 2024
  2. RalfJung commented on Feb 5, 2024

    @RalfJung
    MemberAuthor

    @ojeda I saw you added this to the Rust-for-Linux wishlist. Could you say a bit more about the intended usecase? Last time this came up, it turned out that you actually need #118447, not consts that point to statics. So I wonder if maybe this time there also is a possible alternative.

  3. Darksonn commented on Feb 8, 2024

    @Darksonn
    Member

    My understanding is that many of the vtables in the kernel have the following shape:

    use core::ptr::addr_of_mut;
    
    struct ThisModule;
    
    trait Module {
        const THIS_MODULE: *mut ThisModule;
    }
    
    struct MyModule;
    
    // Generated by a macro.
    extern "C" {
        static mut THIS_MODULE: ThisModule;
    }
    
    // Generated by a macro.
    impl Module for MyModule {
        const THIS_MODULE: *mut ThisModule = unsafe { addr_of_mut!(THIS_MODULE) };
    }
    
    struct Vtable {
        module: *mut ThisModule,
        foo_fn: fn(*mut ()) -> i32,
    }
    
    pub trait Foo {
        type Mod: Module;
    
        fn foo(&mut self) -> i32;
    }
    
    fn generate_vtable<T: Foo>() -> &'static Vtable {
        &Vtable {
            module: T::Mod::THIS_MODULE,
            foo_fn: |ptr| unsafe { &mut *ptr.cast::<T>() }.foo(),
        }
    }
    error[E0013]: constants cannot refer to statics
      --> src/lib.rs:18:64
       |
    18 |     const THIS_MODULE: *mut ThisModule = unsafe { addr_of_mut!(THIS_MODULE) };
       |                                                                ^^^^^^^^^^^
       |
       = help: consider extracting the value of the `static` to a `const`, and referring to that
    

    We do not need to read or write to THIS_MODULE at const evaluation time. In fact, since it's an extern static, it doesn't really make sense to even talk about doing that.

  4. RalfJung commented on Feb 10, 2024

    @RalfJung
    MemberAuthor

    Your code literally reads the value of THIS_MODULE (an extern static) to create the vtable. That's impossible to do in a const (or static) initializer, and I don't know any way to allow this.

    Or did you mean addr_of_mut!(T::Mod::THIS_MODULE)?

  5. RalfJung commented on Feb 10, 2024

    @RalfJung
    MemberAuthor

    Oh wait, you have multiple items with the same name in different modules. That's very confusing code. I'll have to de-obfuscate it first.^^

  6. RalfJung commented on Feb 11, 2024

    @RalfJung
    MemberAuthor

    I added your testcase in #120932, it then passes with feature(const_mut_refs, const_refs_to_static). :)

  7. added
    B-unstableBlocker: Implemented in the nightly compiler and unstable.
    on Feb 28, 2024
  8. dtolnay commented on Apr 2, 2024

    @dtolnay
    Member

    Potential use case in the standard library:

    pub const fn noop() -> &'static Waker {
    const WAKER: &Waker = &Waker { waker: RawWaker::NOOP };
    WAKER
    }

    By using const_refs_to_static in the standard library and changing WAKER to a static, one can use waker.will_wake(Waker::noop()) or Waker::noop().will_wake(&waker) to determine whether a particular waker is noop. will_wake is based on pointer identity for the waker's data ptr and vtable ptr.

  9. RalfJung commented on Apr 2, 2024

    @RalfJung
    MemberAuthor

    There's no mutability here that I can see, so yeah that would work.

    Basically it's a trade-off between a lower risk of monomorphization-time errors and letting const to more things. With const_refs_to_static you get a mono-time error if you ever read from or (transitively) have a reference to mutable statics (this includes interior mutability) or extern statics.

  10. added a commit that references this issue on May 5, 2024
  11. nikomatsakis commented on Jun 8, 2024

    @nikomatsakis
    Contributor

    What is the current status here -- in particular, the Rust For Linux project makes use of this feature to support creation of vtables (example code) and I am hoping that in the next 6 months we can move their subset to stabilization. @RalfJung or @oli-obk -- can you enlighten me as to what the key blockers and/or questions are?

    EDIT: Oh, that example appears earlier in the thread -- right, I forgot, we have already added a testcase to ensure that it continues to compile.

    EDIT: Created a Zulip thread to discuss.

  12. RalfJung commented on Jun 8, 2024

    @RalfJung
    MemberAuthor

    Oh, that example appears earlier in the thread -- right, I forgot, we have already added a testcase to ensure that it continues to compile.

    Yeah, the feature was added specifically to support that example. ;)

    Main concerns/blockers I can think of:

    • This trades some "early" compile failures for "late" / monomorphization-time failures: when a const has a reference to a static with (interior) mutability, this is now considered an "invalid" const and will fail const validation (similar to e.g. violations of basic type validity invariants). If this is an associated const or an inline const, const validation happens only during monomorphization. Similarly, having a raw pointer to a static with (interior) mutability is allowed, but reading from that raw pointer during const-eval will lead to an error -- and again if this is in an associated/inline const, that error occurs only during monomorphization.
    • Potential open question: instead of considering a constant "invalid" when it has a reference to a static with (interior) mutability, we could allow this and only complain when the constant is used as a pattern, const generic value, or const-eval reads through that reference. However, that would make these errors show up "even later", so I think for now it is better to check this as early as possible.
    • There are subtle interactions with using references in const generics, Cc How should const generics with references work around pointer identity and padding? #120961 @rust-lang/types
    • Also FTR there was never an RFC for this feature, it just got implemented as an experiment.
  13. 3 remaining items

  14. oli-obk commented on Jun 21, 2024

    @oli-obk
    Contributor

    Yes, array lengths are constants, so the feature is required for that

  15. Pzixel commented on Jun 21, 2024

    @Pzixel

    Note that it is currently possible to declare a const fn which mutates static variable. Which leads to a function which is declared as const fn but actually cannot be called in const context. E.g.

    https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=de9d10f90ca407933fd2c10ae85556b6

    If we now replace

    if let Some(y) = nonzero(X) {

    with

    if let Some(y) = const { nonzero(X) } {

    It won't compile. Which means that const modifier on this function does nothing and shouldn't be allowed.

  16. RalfJung commented on Jun 21, 2024

    @RalfJung
    MemberAuthor

    We could detect some simple cases but in general it is your responsibility, as part of writing unsafe code, to ensure this does not happen. Consider that one can take a reference or raw pointer to a static mut and then read through that. It's fine to create the reference, and by the time you read through it we can't know whether it points to a static mut or not.

    The const does something, but it can't check everything. Some things are only checked at "runtime" (i.e. during const-eval), at least with const_refs_to_static.

  17. nikomatsakis commented on Jul 25, 2024

    @nikomatsakis
    Contributor

    Stabilization proposal:

    #128183

  18. nikomatsakis commented on Aug 28, 2024

    @nikomatsakis
    Contributor

    Seeing as #128183 is in FCP, I'm assigning @dingxiangfei2009 to prepare the stabilization PR:

    @rustbot assign @dingxiangfei2009

  19. self-assigned this
    on Aug 28, 2024
  20. added a commit that references this issue on Sep 11, 2024
  21. RalfJung commented on Dec 14, 2024

    @RalfJung
    MemberAuthor

    This is stable and documented, so we can close the tracking issue. :)

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

Metadata

Metadata

Assignees

Labels

A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCF-const_refs_to_static`#![feature(const_refs_to_static)]`T-langRelevant to the language team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions