Repository navigation
Crate metadata and crate hash depend on the host CPU #164379
Description
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);
}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 -lI 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
- #162202: the same query with the parallel front end, where the
DefIndexes of these associated types vary between runs. Here the IDs are the same on both hosts; only their order differs. - #113990: crate hash depending on the host tuple.
- Earlier cases of metadata encoded in hash order that differed across hosts: #159718 (doc links, from #159677) and #161450 (syntax contexts, from #129094).
- #162555 and #162809 fix parallel compiler: async fns not reproducible #162202 by making these
DefIds deterministic. They don't change the order in which this map is written, so this issue remains:small.rshas noimpl Traitat all (every entry has an empty list) and still differs between hosts. Remap local def ids for deterministic metadata encoding #162809 touches the same metadata files a fix here would. - The table was added in #143783.
@rustbot modify labels: +regression-from-stable-to-beta -regression-untriaged +A-reproducibility +A-metadata
Activity
bjorn3 commentedon Oct 16, 2026
cc @Daniel-B-Smith
susitsm commentedon Oct 16, 2026
I think this will be solved when we solve #163878, with a fix being something like #164046 but for all encoded
HashMap-s, not justHashMap<DefId, V>. Basically, we should never encodeHashMap-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
IndexMapis not a great long term solution, just a ticking time bomb until aHashMapis accidentally reintroduced into one of the metadata fields. I'm waiting for #162809 to submit a PR fixing all such cases by modifying theEncodeimplementation forHashMapand allowingEncoder-s to implement it themselves.tiny-ac commentedon Oct 16, 2026
Thanks both! Makes sense, happy to test the fix once it's ready
apiraino commentedon Oct 16, 2026
Assigning priority (discussion on Zulip).
@rustbot label -I-prioritize +P-medium
Daniel-B-Smith commentedon Oct 16, 2026
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.