fix(turbo-rcstr): make TaggedValue usable on wasm (#97577)
### What?
Makes `turbo-rcstr`'s tagged value work on wasm — and on any target
whose pointer is narrower than the
value — **without changing the representation or the inline capacity on
any target**. `RcStr` stays 8
bytes wide everywhere, `MAX_INLINE_LEN` stays 7 everywhere, and `rcstr!`
stays a `const`.
### Why?
`TaggedValue` stored a `NonNull<()>` on 64-bit but a bare `NonZeroU64`
when
`target_pointer_width = "32"`. `rcstr!` expands to a `const`, so both
constructors must be usable
during const evaluation — and with integer storage `new_ptr` needs a
pointer→integer cast, which
const-eval forbids. Every `rcstr!` taking the static path failed to
compile:
```
error[E0080]: unable to turn pointer into integer
```
Concretely, `cargo check -p turbopack-core --target
wasm32-wasip1-threads` reported **4** of these
before this change and **0** after.
### How?
The fix rests on an asymmetry in const evaluation:
| direction | const-eval | why |
|---|---|---|
| pointer → integer | **forbidden** | a pointer is an abstract
(allocation, offset) pair; its numeric address doesn't exist until the
linker assigns one |
| integer → pointer | allowed | the result carries no provenance, which
is fine as long as it is never dereferenced |
64-bit already only needs the legal directions: `new_ptr` is
pointer→pointer, `new_tag` is
integer→pointer. So rather than narrow the value on other targets, they
now store a struct that keeps
a **real pointer field**:
```rust
#[repr(C, align(8))]
struct RawLittle { ptr: NonNull<()>, pad: [u8; PAD] }
```
`new_ptr` stores the pointer as a pointer; `new_tag` transmutes an
integer into the struct. Reading the
value back as an integer (`get_ptr`, `get_value`, `tag_byte`) only
happens at **run time**, where
pointer→integer is perfectly legal.
Three details are load-bearing, and each has a static assertion:
- **`align(8)`** — a pointer field alone would align the struct to 4 on
wasm32, and reading the value
as a `u64` then traps with `RuntimeError: operation does not support
unaligned accesses`.
- **No padding** — the byte payload is sized to the pointer width,
because padding bytes are
uninitialised and would make the whole-value transmute in `new_tag`
invalid.
- **Endian-dependent field order** — the pointer's least-significant
byte must land where the value's
least-significant byte lives (offset 0 little-endian, last byte
big-endian), since that is where the
tag is read from. Both layouts are declared unconditionally so the
big-endian assertion still
compiles (and was verified to fail when deliberately broken) on a
little-endian host.
A union satisfies both constructors too, but unions carry no niche in
rustc, so `Option<RcStr>` would
grow to 16 bytes. The struct keeps it at 8 — also asserted.
### Testing
- `turbo-rcstr`: **11 passed** on host, **10 passed / 1 ignored** on
`wasm32-wasip1-threads`
(the ignored one needs the static-`RcStr` registry, which depends on the
link-section loader hook).
- Round-trips every `rcstr!` length from 0 to 9 on both targets,
covering both sides of the inline
boundary and the static path.
- Each of the new static assertions was verified to **fail** when
deliberately broken, including the
big-endian layout one.
- `MAX_INLINE_LEN == 7` asserted on host and wasm.
- The five `const … = rcstr!(…)` sites in the tree — including two `pub
const`s in `turbopack-core` —
are untouched and still compile as `const`.
- `atom_size_128` still builds; its pre-existing const limitation is
unchanged and out of scope.
- `cargo fmt --check`, and clippy `-D warnings` for the host and for
`wasm32-wasip1-threads`.
Big-endian and 16-bit are compile-checked and assertion-protected, but
not runtime-exercised — no such
target is available here.
<!-- NEXT_JS_LLM -->
<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>