Skip to content

compat(nightly-2025-07-05): IndexedVal goes private#147

Open
cds-amal wants to merge 3 commits intostack/pr5-cifrom
stack/pr7-nightly-2025-07-05
Open

compat(nightly-2025-07-05): IndexedVal goes private#147
cds-amal wants to merge 3 commits intostack/pr5-cifrom
stack/pr7-nightly-2025-07-05

Conversation

@cds-amal
Copy link
Collaborator

Nightly 2025-07-05 makes the IndexedVal trait pub(crate), which means external code (that's us) can no longer call to_index() or to_val() on types like Ty, Span, AllocId, and VariantIdx. These calls are scattered across the codebase: the printer uses them for deterministic sorting, the graph renderer uses them for index labels, and the receipts system needs them for interned-index detection.

The fix is a compat shim (src/compat/indexed_val.rs) that provides free functions to_index and to_val working on both sides of the breakpoint:

  • Old nightlies (pre-2025-07-05): delegate to the trait methods directly. Business as usual.
  • New nightlies: to_index uses a minimal serde Serializer that extracts the inner usize from the newtype's #[derive(Serialize)] implementation. to_val goes the other direction via transmute_copy with a compile-time size assertion. These types are all single-field newtypes around usize, so the layout is guaranteed; the size check is belt-and-suspenders.

The serde trick deserves a moment's pause: rather than reaching into rustc internals or duplicating private type definitions, we exploit the fact that Ty(42) serializes as 42 through serde's derive machinery. A Serializer that only implements serialize_newtype_struct and serialize_u64 can recover the inner value with zero unsafe code on the extraction side. The transmute_copy for reconstruction is the one unavoidable unsafe block, and it's constrained to types that pass the size assertion. @dkcumming, please fact check me here.

All call sites (collect.rs, mir_visitor.rs, types.rs, mk_graph/context.rs, mk_graph/index.rs, mk_graph/util.rs) are updated to use the free functions instead of the trait methods.

A note on the boilerplate. You'll notice indexed_val.rs is ~217 lines, and about 100 of those are trivially mechanical Err(UsizeExtractError) stubs: the serde Serializer trait requires ~30 methods, and we only care about two (serialize_newtype_struct and serialize_u64). A natural question: couldn't a macro factor those out?

Turns out there are three options, and none of them clearly win. (1) A local reject_all! macro that stamps out fn $name($args) -> Result<..> { Err(...) } for each unused method: saves ~80 lines, but anyone reading the code now has to mentally expand the macro to verify nothing interesting is hiding in there. For a file that's meant to be "read once and trust," that's a real cost. (2) serde's own forward_to_serialize_any!: designed for exactly this pattern, but it requires implementing serialize_any, which our vendored rustc serde may or may not expose (it's behind #[doc(hidden)] in some versions; fragile across nightlies). (3) Leave it as is: the boilerplate is annoying to write once but costs nothing to maintain (serde hasn't changed its required Serializer methods in years), and the two methods that matter are clearly visible at the top of the impl block. I went with option 3.

Test plan

  • cargo build passes with nightly-2025-07-05
  • cargo clippy -- -Dwarnings passes
  • make integration-test passes (golden files for nightly-2025-07-05 included)
  • cargo build still passes with nightly-2025-03-01 (backward compat)

@cds-amal cds-amal force-pushed the stack/pr7-nightly-2025-07-05 branch from 16efe7e to ae12130 Compare March 11, 2026 19:40
@cds-amal cds-amal force-pushed the stack/pr7-nightly-2025-07-05 branch from ae12130 to 9e0b227 Compare March 13, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant