rust-lang/rust · error
cannot change the valid range of a union
Error message
cannot change the valid range of a union
What it means
Thrown by `Scalar::valid_range_mut` (compiler/rustc_abi/src/lib.rs:1607) when invoked on `Scalar::Union`. A union scalar deliberately has no `valid_range` (it allows undef and has no niches — see the field comment at lib.rs:1531-1533), so the range is not stored and cannot be mutated. Mutating it would imply adding a niche to a union, which is semantically invalid.
Source
Thrown at compiler/rustc_abi/src/lib.rs:1607
pub fn to_union(&self) -> Self {
Self::Union { value: self.primitive() }
}
#[inline]
pub fn valid_range(&self, cx: &impl HasDataLayout) -> WrappingRange {
match *self {
Scalar::Initialized { valid_range, .. } => valid_range,
Scalar::Union { value } => WrappingRange::full(value.size(cx)),
}
}
#[inline]
/// Allows the caller to mutate the valid range. This operation will panic if attempted on a
/// union.
pub fn valid_range_mut(&mut self) -> &mut WrappingRange {
match self {
Scalar::Initialized { valid_range, .. } => valid_range,
Scalar::Union { .. } => panic!("cannot change the valid range of a union"),
}
}
/// Returns `true` if all possible numbers are valid, i.e `valid_range` covers the whole
/// layout.
#[inline]
pub fn is_always_valid<C: HasDataLayout>(&self, cx: &C) -> bool {
match *self {
Scalar::Initialized { valid_range, .. } => valid_range.is_full_for(self.size(cx)),
Scalar::Union { .. } => true,
}
}
/// Returns `true` if this type can be left uninit.
#[inline]
pub fn is_uninit_valid(&self) -> bool {
match *self {
Scalar::Initialized { .. } => false,View on GitHub (pinned to 22057b88b0)
Solutions
- Before mutating the range, pattern-match on the `Scalar`: only call `valid_range_mut()` for `Scalar::Initialized { .. }`, and handle `Scalar::Union { .. }` separately (skip niche adjustment).
- If your algorithm requires a mutable range, reconstruct as `Scalar::Initialized { value: union.value, valid_range: WrappingRange::full(...) }` instead of trying to mutate the union form.
- Add an assertion/test exercising union-typed fields in the affected layout pass to catch the assumption early.
Example fix
// before
let range = scalar.valid_range_mut();
*range = new_range;
// after
if let Scalar::Initialized { valid_range, .. } = &mut scalar {
*valid_range = new_range;
} else {
// unions have no niche to narrow
return Ok(layout);
} Defensive patterns
Strategy: type-guard
Validate before calling
// valid_range_mut panics on Scalar::Union. Narrow before mutating.
match scalar {
rustc_abi::Scalar::Initialized { valid_range, .. } => {
// safely mutate valid_range here
}
rustc_abi::Scalar::Union { .. } => {
// a union's range is always full for its size; do not mutate
}
} Type guard
fn is_initialized_scalar(s: &rustc_abi::Scalar) -> bool {
matches!(s, rustc_abi::Scalar::Initialized { .. })
}
// Usage:
// if is_initialized_scalar(&scalar) { scalar.valid_range_mut().start = ...; } Try / catch
// Not a runtime error to catch; the invariant is enforced by the type. // Use the match above and skip mutation in the Union arm.
Prevention
- Read the doc comment on valid_range_mut: 'This operation will panic if attempted on a union.'
- Treat Scalar::Union as immutable-by-construction; only Scalar::Initialized has a mutable valid_range.
- If you need an unconstrained range on an Initialized scalar, set it to WrappingRange::full(size), not mutate a Union.
When it happens
Trigger: Calling `valid_range_mut()` on a `Scalar` produced by `to_union()` or constructed as `Scalar::Union { .. }`. This typically happens in niche/layout adjustment code that did not first check whether the scalar was `Initialized`.
Common situations: A type that was treated as a regular scalar but was lowered as a union (e.g. `MaybeUninit<T>`, `union` types, or `ManuallyDrop`-like wrappers), then passed to code that tries to narrow the valid range for niche optimization. Also seen when generic layout code assumes every `Scalar` is `Initialized`.
Related errors
- Size::bits: {bytes} bytes in bits doesn't fit in u64
- Size::add: {} + {} doesn't fit in u64
- Size::sub: {} - {} would result in negative size
- Size::mul: {} * {} doesn't fit in u64
- FieldsShape::offset: `Primitive`s have no fields
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/7087eb6e7598d024.json.
Report an issue: GitHub.