Skip to content

Miscompilation caused by incorrectly-deduced readonly on virtual call #137646

Description

@johnbwang

I tried the following code. It requires a binary crate and a library crate.

main.rs

fn main() {
    let mut struct_c = lib::StructC::new();
    let struct_a = lib::StructA { a: 1, b: 2, c: 3 };
    lib::run(&mut struct_c, struct_a);
}

main's Cargo.toml

[package]
name = "compilerbug"
version = "0.1.0"
edition = "2024"

[dependencies]
lib = { path = "./lib" }

[profile.dev]
opt-level = 1
incremental = false

lib.rs

#[derive(Clone, Copy)]
pub struct StructA {
    pub a: i32,
    pub b: i32,
    pub c: i32,
}

pub trait TraitA {}

pub struct StructB {}

impl StructB {
    pub fn new() -> Self {
        Self {}
    }
}

impl TraitA for StructB {}

pub trait TraitB {
    fn get_trait_a(&self, struct_a: StructA) -> Option<Box<dyn TraitA>> {
        Some(Box::new(StructB::new()))
    }
}

pub struct StructC {}

impl StructC {
    pub fn new() -> Self {
        Self {}
    }
}

impl TraitB for StructC {
    fn get_trait_a(&self, mut struct_a: StructA) -> Option<Box<dyn TraitA>> {
        struct_a.a = i32::MAX;
        Some(Box::new(StructB::new()))
    }
}

#[inline(never)]
fn print_struct_a(struct_a: StructA) {
    println!("{}", struct_a.a);
}

#[inline(never)]
pub fn run(trait_b: &mut dyn TraitB, struct_a: StructA) {
    print_struct_a(struct_a);
    trait_b.get_trait_a(struct_a);
    print_struct_a(struct_a);

    // Uncommenting the following will fix the code.
    // println!("{}", struct_a.a);
}

lib's Cargo.toml

[package]
name = "lib"
version = "0.1.0"
edition = "2024"

Since struct_a conforms to Copy, passing struct_a to get_trait_a() should make a copy. Instead, when modifying struct_a inside of get_trait_a(), it modifies the original. When printing the struct a second time, the struct has been modified. To repro the bug, it requires opt-level >= 1 and incremental = false.

I expected the output in my command line to be:

1
1

Instead, this happened:

1
2147483647

Meta

rustc --version --verbose:

rustc 1.85.0 (4d91de4e4 2025-02-17)
binary: rustc
commit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688
commit-date: 2025-02-17
host: aarch64-apple-darwin
release: 1.85.0
LLVM version: 19.1.7

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Feb 26, 2025
  2. changed the title [-]Large Copy types breaks expected Copy semantics[/-] [+]Large `Copy` types breaks expected `Copy` semantics[/+] on Feb 26, 2025
  3. steffahn commented on Feb 26, 2025

    @steffahn
    Member
    searched toolchains nightly-2023-10-01 through nightly-2023-11-11
    
    
    ********************************************************************************
    Regression in nightly-2023-10-09
    ********************************************************************************
    
    fetching https://static.rust-lang.org/dist/2023-10-08/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-10-08: 40 B / 40 B [==================================] 100.00 % 501.91 KB/s converted 2023-10-08 to 97c81e1b537088f1881c8894ee8579812ed9b6d1
    fetching https://static.rust-lang.org/dist/2023-10-09/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-10-09: 40 B / 40 B [==================================] 100.00 % 348.89 KB/s converted 2023-10-09 to bf9a1c8a193fc373897196321215794c8bebbeec
    looking for regression commit between 2023-10-08 and 2023-10-09
    fetching (via remote github) commits from max(97c81e1b537088f1881c8894ee8579812ed9b6d1, 2023-10-06) to bf9a1c8a193fc373897196321215794c8bebbeec
    ending github query because we found starting sha: 97c81e1b537088f1881c8894ee8579812ed9b6d1
    get_commits_between returning commits, len: 10
      commit[0] 2023-10-07: Auto merge of #116416 - Kobzol:ci-host-llvm-17-0-2, r=Mark-Simulacrum
      commit[1] 2023-10-08: Auto merge of #114623 - Kobzol:opt-dist-gha-summaries, r=Mark-Simulacrum
      commit[2] 2023-10-08: Auto merge of #116450 - Kobzol:automation-try-bors-ci, r=Mark-Simulacrum
      commit[3] 2023-10-08: Auto merge of #116486 - van-ema:master, r=nikic
      commit[4] 2023-10-08: Auto merge of #116487 - tamird:avoid-unwrap-absolute, r=bjorn3
      commit[5] 2023-10-08: Auto merge of #116509 - Enselic:rustc-test-op, r=Mark-Simulacrum
      commit[6] 2023-10-08: Auto merge of #116514 - petrochenkov:nogccld, r=lqd
      commit[7] 2023-10-08: Auto merge of #116183 - cjgillot:debug-dse-always, r=oli-obk
      commit[8] 2023-10-08: Auto merge of #116454 - tmiasko:small-dominators, r=cjgillot
      commit[9] 2023-10-08: Auto merge of #116515 - petrochenkov:nolegflavor, r=lqd
    ERROR: no CI builds available between 97c81e1b537088f1881c8894ee8579812ed9b6d1 and bf9a1c8a193fc373897196321215794c8bebbeec within last 167 days
    

    well… among those that’s probably the LLVM bump then, right?

  4. theemathas commented on Feb 26, 2025

    @theemathas
    Contributor

    Might also be #116454

  5. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    I-miscompileIssue: Correct Rust code lowers to incorrect machine code
    on Feb 26, 2025
  6. steffahn commented on Feb 26, 2025

    @steffahn
    Member

    Interestingly, with the

    // Uncommenting the following will fix the code.
    

    marked section uncommented (and fixed to refer to struct_a by the correct name), it’s also still broken for old Rust versions; regressing at the same point as before (2023-10-09), and actually being “fixed” again only at the following point:

    searched toolchains nightly-2024-06-07 through nightly-2024-07-20
    
    
    ********************************************************************************
    Regression in nightly-2024-06-29
    ********************************************************************************
    
    fetching https://static.rust-lang.org/dist/2024-06-28/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2024-06-28: 40 B / 40 B [====================================] 100.00 % 1.15 MB/s converted 2024-06-28 to 9c3bc805dd9cb84019c124b9a50fdff1e62a7ec9
    fetching https://static.rust-lang.org/dist/2024-06-29/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2024-06-29: 40 B / 40 B [====================================] 100.00 % 1.06 MB/s converted 2024-06-29 to e9e6e2e444c30c23a9c878a88fbc3978c2acad95
    looking for regression commit between 2024-06-28 and 2024-06-29
    fetching (via remote github) commits from max(9c3bc805dd9cb84019c124b9a50fdff1e62a7ec9, 2024-06-26) to e9e6e2e444c30c23a9c878a88fbc3978c2acad95
    ending github query because we found starting sha: 9c3bc805dd9cb84019c124b9a50fdff1e62a7ec9
    get_commits_between returning commits, len: 5
      commit[0] 2024-06-27: Auto merge of #127049 - flip1995:clippy-subtree-update, r=Manishearth
      commit[1] 2024-06-28: Auto merge of #127010 - GuillaumeGomez:update-puppeteer, r=notriddle
      commit[2] 2024-06-28: Auto merge of #127076 - matthiaskrgr:rollup-l01gm36, r=matthiaskrgr
      commit[3] 2024-06-28: Auto merge of #127000 - Oneirical:no-test-for-the-wicked, r=Kobzol
      commit[4] 2024-06-28: Auto merge of #126701 - onur-ozkan:build-lld-if-enabled, r=Kobzol
    ERROR: no CI builds available between 9c3bc805dd9cb84019c124b9a50fdff1e62a7ec9 and e9e6e2e444c30c23a9c878a88fbc3978c2acad95 within last 167 days
    

    I don’t know, is that this one perhaps?

  7. moxian commented on Feb 26, 2025

    @moxian
  8. steffahn commented on Feb 26, 2025

    @steffahn
    Member

    It also isn’t “fixed” by uncommenting the indicated line, if opt-level = 1 is used.

  9. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    on Feb 26, 2025
  10. steffahn commented on Feb 26, 2025

    @steffahn
    Member

    It’s really interesting how it seems to be relevant whether or not the default method body does mutate its argument. E.g.:

    pub trait Trait {
        fn method(&self, mut arr: [i32; 3]) {
            println!("{:p}", &arr[0]);
    
            // uncomment to "fix":
            // println!("{:p}", &mut arr[0]);
        }
    }
    
    impl Trait for () {
        fn method(&self, mut arr: [i32; 3]) {
            arr[0] = 42;
        }
    }
    
    fn inspect_arr(arr: [i32; 3]) {
        assert_eq!(format!("{}", arr[0]), "0");
    }
    
    pub fn run(trait_b: & dyn Trait, arr: [i32; 3]) {
        inspect_arr(arr);
        trait_b.method(arr);
        inspect_arr(arr);
    }
    use name_of_crate::*;
    
    fn main() {
        run(&(), [0, 0, 0]);
    }

    here the println!("{:p}", &mut arr[0]); (or various other things that mutably access or actually mutate arr) do make a difference.

  11. moxian commented on Feb 26, 2025

    @moxian
    Contributor

    With -Zmir-opt-level=0 (thanks @saethlin for the pointer) it reproduces way further back to nightly-2023-07-16:

    Details
    ********************************************************************************
    Regression in nightly-2023-07-16
    ********************************************************************************
    
    fetching https://static.rust-lang.org/dist/2023-07-15/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-07-15: 40 B / 40 B [=======================================================] 100.00 % 62.59 KB/s converted 2023-07-15 to ad963232d9b987d66a6f8e6ec4141f672b8b9900
    fetching https://static.rust-lang.org/dist/2023-07-16/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-07-16: 40 B / 40 B [======================================================] 100.00 % 174.93 KB/s converted 2023-07-16 to 4c8bb79d9f565115637cc6da739f8389e79f3a29
    looking for regression commit between 2023-07-15 and 2023-07-16
    fetching (via remote github) commits from max(ad963232d9b987d66a6f8e6ec4141f672b8b9900, 2023-07-13) to 4c8bb79d9f565115637cc6da739f8389e79f3a29
    ending github query because we found starting sha: ad963232d9b987d66a6f8e6ec4141f672b8b9900
    get_commits_between returning commits, len: 5
      commit[0] 2023-07-14: Auto merge of #113471 - compiler-errors:new-solver-norm-escaping, r=lcnr
      commit[1] 2023-07-15: Auto merge of #113514 - jyn514:more-gha-groups, r=oli-obk
      commit[2] 2023-07-15: Auto merge of #112157 - erikdesjardins:align, r=nikic
      commit[3] 2023-07-15: Auto merge of #113732 - matthiaskrgr:rollup-nm5qy4i, r=matthiaskrgr
      commit[4] 2023-07-15: Auto merge of #113697 - GuillaumeGomez:rm-unneeded-externallocation-handling, r=lqd
    ERROR: no CI builds available between ad963232d9b987d66a6f8e6ec4141f672b8b9900 and 4c8bb79d9f565115637cc6da739f8389e79f3a29 within last 167 days
    

    - #112157 sounds related maybe?

  12. steffahn commented on Feb 26, 2025

    @steffahn
    Member

    That’s funny, my bisection (of my reduced example) with -Zmir-opt-level=0 also reproduces further back but not quite as far: only up to

    searched toolchains nightly-2023-08-20 through nightly-2023-10-01
    
    
    ********************************************************************************
    Regression in nightly-2023-08-27
    ********************************************************************************
    
    fetching https://static.rust-lang.org/dist/2023-08-26/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-08-26: 40 B / 40 B [=======================================================] 100.00 % 566.52 KB/s converted 2023-08-26 to 734a0d0aa0d5cab60f94f6d0c6a014dae12915f1
    fetching https://static.rust-lang.org/dist/2023-08-27/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2023-08-27: 40 B / 40 B [=======================================================] 100.00 % 351.72 KB/s converted 2023-08-27 to 69e97df5ce571a777acd654ec3697ae8d25962ea
    looking for regression commit between 2023-08-26 and 2023-08-27
    fetching (via remote github) commits from max(734a0d0aa0d5cab60f94f6d0c6a014dae12915f1, 2023-08-24) to 69e97df5ce571a777acd654ec3697ae8d25962ea
    ending github query because we found starting sha: 734a0d0aa0d5cab60f94f6d0c6a014dae12915f1
    get_commits_between returning commits, len: 10
      commit[0] 2023-08-25: Auto merge of #115202 - ouz-a:more_smir, r=spastorino
      commit[1] 2023-08-25: Auto merge of #115221 - compiler-errors:walk-path, r=estebank
      commit[2] 2023-08-26: Auto merge of #115211 - spastorino:add-missing-smir-generics-of, r=compiler-errors
      commit[3] 2023-08-26: Auto merge of #115228 - saethlin:is-interrupted, r=thomcc
      commit[4] 2023-08-26: Auto merge of #115236 - scottmcm:less-vector, r=compiler-errors
      commit[5] 2023-08-26: Auto merge of #115232 - wesleywiser:revert_114643, r=tmiasko
      commit[6] 2023-08-26: Auto merge of #115243 - weihanglo:update-cargo, r=weihanglo
      commit[7] 2023-08-26: Auto merge of #115246 - matthiaskrgr:rollup-zdiw9gt, r=matthiaskrgr
      commit[8] 2023-08-26: Auto merge of #115198 - Zoxc:query-panic-wait, r=cjgillot
      commit[9] 2023-08-26: Auto merge of #115224 - spastorino:remove-lub_empty, r=lcnr
    ERROR: no CI builds available between 734a0d0aa0d5cab60f94f6d0c6a014dae12915f1 and 69e97df5ce571a777acd654ec3697ae8d25962ea within last 167 days
    

    oh well… this might be because I use an array…

    most likely that regression point was related to

    because the regression was also mismatching between nightly and stable, suggesting a back-port…

    Ah yes, with a tuple instead of an array I do get nightly-2023-07-16 as well :-)

  13. moxian commented on Feb 26, 2025

    @moxian
    Contributor

    I looked at LLVM IR, and realized I can't really read it myself, but I can at least share it here to save other folks some trouble:
    https://godbolt.org/z/6znK7E3Md (original example, 1.72.0 - 1.73.0 )
    https://godbolt.org/z/bjcEGn8s1 (slightly modified steffahn's [i8;12] reduction, 1.69.0 - 1.70.0)

  14. 11 remaining items

  15. added
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Feb 26, 2025
  16. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Feb 26, 2025
  17. apiraino commented on Feb 26, 2025

    @apiraino
    Contributor

    WG-prioritization assigning priority (Zulip discussion).

    @rustbot label -I-prioritize +P-critical

  18. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Feb 26, 2025
  19. dianqk commented on Feb 26, 2025

    @dianqk
    Member

    #137669 should fix this.

    @saethlin Free to review if you would like.

  20. changed the title [-]Large `Copy` types breaks expected `Copy` semantics[/-] [+]Miscompilation caused by incorrectly-deduced readonly on virtual call[/+] on Feb 26, 2025
  21. added 2 commits that reference this issue on Feb 27, 2025
  22. added a commit that references this issue on Mar 7, 2025
  23. dianqk commented on Mar 12, 2025

    @dianqk
    Member

    #138190 has been merged.

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

Metadata

Metadata

Assignees

Labels

A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-bugCategory: This is a bug.I-miscompileIssue: Correct Rust code lowers to incorrect machine codeI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions