Skip to content

Crate metadata and crate hash depend on the host CPU #164379

Description

@tiny-ac

I've been working on a PoC to share build caches between machines. Compiling the same crate for the same target gave different .rmeta bytes when running on an x86_64 compared to an aarch64 host in specific cases.

Since version 1.100 (which includes #154724) this will start affecting crate hashes (since they will be computed from metadata encoding). Thus, affecting other dependent crates (their hashes will be affected as well).

Code

I tried this code:
53 unit structs that shift the item indexes, then a trait whose methods have 0 to 2 associated types between them.

small.rs (101 lines)
pub struct S0;
pub struct S1;
pub struct S2;
pub struct S3;
pub struct S4;
pub struct S5;
pub struct S6;
pub struct S7;
pub struct S8;
pub struct S9;
pub struct S10;
pub struct S11;
pub struct S12;
pub struct S13;
pub struct S14;
pub struct S15;
pub struct S16;
pub struct S17;
pub struct S18;
pub struct S19;
pub struct S20;
pub struct S21;
pub struct S22;
pub struct S23;
pub struct S24;
pub struct S25;
pub struct S26;
pub struct S27;
pub struct S28;
pub struct S29;
pub struct S30;
pub struct S31;
pub struct S32;
pub struct S33;
pub struct S34;
pub struct S35;
pub struct S36;
pub struct S37;
pub struct S38;
pub struct S39;
pub struct S40;
pub struct S41;
pub struct S42;
pub struct S43;
pub struct S44;
pub struct S45;
pub struct S46;
pub struct S47;
pub struct S48;
pub struct S49;
pub struct S50;
pub struct S51;
pub struct S52;
pub trait Many {
    type T0;
    fn m0(&self);
    type T1;
    type T2;
    fn m1(&self);
    type T3;
    type T4;
    fn m2(&self);
    type T5;
    type T6;
    fn m3(&self);
    type T7;
    fn m4(&self);
    type T8;
    type T9;
    fn m5(&self);
    type T10;
    type T11;
    fn m6(&self);
    type T12;
    type T13;
    fn m7(&self);
    type T14;
    fn m8(&self);
    fn m9(&self);
    type T15;
    fn m10(&self);
    fn m11(&self);
    type T16;
    fn m12(&self);
    type T17;
    type T18;
    fn m13(&self);
    type T19;
    type T20;
    fn m14(&self);
    type T21;
    fn m15(&self);
    type T22;
    type T23;
    fn m16(&self);
    type T24;
    fn m17(&self);
    type T25;
    type T26;
    fn m18(&self);
}
compiled for the same target with the aarch64 and the x86_64 builds of the same rustc (on an Apple-silicon Mac, the x86_64 rustc runs under Rosetta):
for h in aarch64 x86_64; do
  rustc +beta-$h-apple-darwin --crate-type lib --edition 2021 --crate-name many \
    --emit metadata -C metadata=fixed --target aarch64-apple-darwin -o $h.rmeta small.rs
  RUSTC_BOOTSTRAP=1 rustc +beta-$h-apple-darwin -Z ls=root $h.rmeta | grep '^hash'
done
cmp -l aarch64.rmeta x86_64.rmeta | wc -l

I expected to see this happen: identical .rmeta files and the same crate hash, since the inputs and the target are the same.

Instead, this happened: 44 bytes differ, and the crate hash differs (7c51198e62cf8a840f2052930eabec9a on aarch64, 6e023909af23af05c69b5b2a41958563 on x86_64)

Version it worked on

It most recently worked on: Rust 1.99.0. The .rmeta bytes already differ there (28 bytes for small.rs), but the crate hash is the same on both hosts (.rmeta files already differ on that version).

Version with regression

rustc --version --verbose:

rustc 1.100.0-beta.5 (8ea38001b 2026-10-13)
binary: rustc
commit-hash: 8ea38001b9da1d6683ac5ebbc993cac89d3a09df
commit-date: 2026-10-13
host: aarch64-apple-darwin
release: 1.100.0-beta.5
LLVM version: 23.1.3

Cause

associated_types_for_impl_traits_in_trait_or_impl returns a DefIdMap<Vec<DefId>> that is stored in the metadata as is (table, encoder), so its entries are written in the map's iteration order. The keys, their hashes and the insertion order are the same on both hosts, but when a key's slot is taken, hashbrown looks for a free one 16 slots at a time on x86_64 and 8 at a time on aarch64, so a colliding entry can land in a different slot.

Using an insertion-ordered FxIndexMap, as #159718 did for DocLinkResMap, makes the files identical on both hosts (tested with patched builds of nightly e9bbadf68).

collect_return_position_impl_trait_in_trait_tys is stored the same way and should probably get the same change.

Related

@rustbot modify labels: +regression-from-stable-to-beta -regression-untriaged +A-reproducibility +A-metadata

Activity

  1. added
    C-bugCategory: This is a bug.
    regression-untriagedUntriaged performance or correctness regression.
    on Oct 15, 2026
  2. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    A-metadataArea: Crate metadata
    A-reproducibilityArea: Reproducible / deterministic builds
    and removed
    regression-untriagedUntriaged performance or correctness regression.
    on Oct 15, 2026
  3. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Oct 16, 2026
  4. bjorn3 commented on Oct 16, 2026

    @bjorn3
    Member
  5. susitsm commented on Oct 16, 2026

    @susitsm
    Contributor

    I think this will be solved when we solve #163878, with a fix being something like #164046 but for all encoded HashMap-s, not just HashMap<DefId, V>. Basically, we should never encode HashMap-s in iteration order with encoders where being deterministic matters.

    I don't know how urgent it is to fix this, but replacing these fields one-by-one with IndexMap is not a great long term solution, just a ticking time bomb until a HashMap is accidentally reintroduced into one of the metadata fields. I'm waiting for #162809 to submit a PR fixing all such cases by modifying the Encode implementation for HashMap and allowing Encoder-s to implement it themselves.

  6. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 16, 2026
  7. tiny-ac commented on Oct 16, 2026

    @tiny-ac
    Author

    Thanks both! Makes sense, happy to test the fix once it's ready

  8. apiraino commented on Oct 16, 2026

    @apiraino
    Contributor

    Assigning priority (discussion on Zulip).

    @rustbot label -I-prioritize +P-medium

  9. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 16, 2026
  10. Daniel-B-Smith commented on Oct 16, 2026

    @Daniel-B-Smith
    Contributor

    If you want a fix in the interim while waiting for the proper fix, I wrote up #164443 using @susitsm's idea. I'm also fine to dump that PR and wait for the better fix.

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-metadataArea: Crate metadataA-reproducibilityArea: Reproducible / deterministic buildsC-bugCategory: This is a bug.P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions