Skip to content

On Windows a static address changes when copied into another static across a module boundary #127518

Closed as duplicate

Description

@Alexendoo

Technically not the address of the static itself, but it is a confusing interaction

I tried this code:

mod m {
    pub static IN_MOD: &str = &"s";
    pub static IN_MOD_2: &str = IN_MOD;
}

static OUTSIDE: &str = m::IN_MOD;

fn main() {
    println!("{:p}", m::IN_MOD);
    println!("{:p}", m::IN_MOD_2);
    println!("{:p}", OUTSIDE);
}

I expected to see this happen:

The same address printed 3 times as it is on Linux

Instead, this happened:

0x7ff6dba2a370
0x7ff6dba2a370
0x7ff6dba2a3c8

In --release mode the three addresses are the same

Meta

Stable behaves the same

rustc --version --verbose:

rustc 1.81.0-nightly (35b658fb1 2024-07-08)
binary: rustc
commit-hash: 35b658fb1071d752d092751d9f17a9fa7b154ec4
commit-date: 2024-07-08
host: x86_64-pc-windows-msvc
release: 1.81.0-nightly
LLVM version: 18.1.7

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 9, 2024
  2. workingjubilee commented on Jul 9, 2024

    @workingjubilee
    Member

    ...that's so weird I'm not even entirely sure it's a bug.

  3. bjorn3 commented on Jul 10, 2024

    @bjorn3
    Member
  4. RalfJung commented on Jul 10, 2024

    @RalfJung
    Member
  5. RalfJung commented on Jul 10, 2024

    @RalfJung
    Member

    But also since we fixed this for Linux, I assume there's some specific Windows interaction here. And all I know about linking on Windows is that it is a lot more complicated than on Linux, and I already don't really understand linking on Linux...

    Cc @ChrisDenton @mati865

  6. oli-obk commented on Jul 10, 2024

    @oli-obk
    Contributor

    Not sure we're protecting against this on linux either. It just works by accident. the address of string literals is not stable. this won't be an issue on windows either if you use [u8] and let x = [42]; &x for IN_MOD's initializer, as that creates an anonymous nested static

  7. RalfJung commented on Jul 10, 2024

    @RalfJung
    Member

    Ah the nested static stuff doesn't fire for string literals?

    Makes sense. In that case this is not-a-bug. If the issue also crops up without the use of string/byte literals, that would be a different case.

  8. Alexendoo commented on Jul 10, 2024

    @Alexendoo
    MemberAuthor

    It also occurs with structs:

    struct Foo {
        a: usize,
    }
    
    mod m {
        pub static IN_MOD: &super::Foo = &super::Foo { a: 1 };
        pub static IN_MOD_2: &super::Foo = IN_MOD;
    }
    
    static OUTSIDE: &Foo = m::IN_MOD;
    
    fn main() {
        println!("{:p}", m::IN_MOD);
        println!("{:p}", m::IN_MOD_2);
        println!("{:p}", OUTSIDE);
    }
    // 0x7ff6916da398
    // 0x7ff6916da398
    // 0x7ff6916da360

    I originally ran into this with declare_lint as it produces a &'static Lint, it's why there's this workaround in Clippy

    /// Double reference to maintain pointer equality
    lint: &'static &'static Lint,

  9. RalfJung commented on Jul 10, 2024

    @RalfJung
    Member

    Yeah &super::Foo { a: 1 } is implicitly promoted here. The intended way to write this is

    mod m {
        pub static FOO: super::Foo = super::Foo { a: 1 };
        pub static IN_MOD: &super::Foo = &FOO;
        pub static IN_MOD_2: &super::Foo = IN_MOD;
    }
  10. oli-obk commented on Jul 10, 2024

    @oli-obk
    Contributor

    or

    mod m {
        pub static IN_MOD: &super::Foo = {
            let x = super::Foo { a: 1 };
            &x
        };
    }
  11. Alexendoo commented on Jul 10, 2024

    @Alexendoo
    MemberAuthor

    The let version gives a lifetime error

    Regarding promotion am I right in thinking that

        pub static IN_MOD: &super::Foo = &super::Foo { a: 1 };

    turns into something like the following?

        pub static IN_MOD: &super::Foo = {
            const PROMOTED: super::Foo = super::Foo { a: 1 };
            &PROMOTED
        };
  12. oli-obk commented on Jul 10, 2024

    @oli-obk
    Contributor

    oopos, need to use &{x} to move out of the x 💀

  13. RalfJung commented on Jul 10, 2024

    @RalfJung
    Member

    turns into something like the following?

    AFAIK it's more like

        pub static IN_MOD: &super::Foo = {
            const PROMOTED: &super::Foo = &super::Foo { a: 1 };
            PROMOTED
        };

    References in constants have no pointer identity guarantees.

    Ofc that does not explain how the const itself is accepted so maybe it should be

    const PROMOTED: &super::Foo = &const { super::Foo { a: 1 } };

    But it's hard to express this entirely syntactically. @oli-obk do we have a MIR body for the pointed-to think or only the reference? I thought it was only for the reference.

  14. oli-obk commented on Jul 10, 2024

    @oli-obk
    Contributor

    I thought it was only for the reference.

    yea, that's right. We used to do it differently, but that had its own problems.

  15. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 10, 2024
  16. workingjubilee commented on Jun 19, 2025

    @workingjubilee
    Member

    Based on the previous conversation I am closing this as it is non-intuitive but also not-a-bug.

  17. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-opsemRelevant to the opsem team
    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
    on Jun 19, 2025
  18. RalfJung commented on Jun 20, 2025

    @RalfJung
    Member

    I think we do eventually want to fix this, but that is tracked at #128775.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.O-windowsOperating system: WindowsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions