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

  1. 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).
  2. 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.
  3. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/7087eb6e7598d024.json. Report an issue: GitHub.