rust-lang/rust · critical

Size::sub: {} - {} would result in negative size

Error message

Size::sub: {} - {} would result in negative size

What it means

Thrown by the `Sub` impl for `Size` (compiler/rustc_abi/src/lib.rs:1000) when the right operand is larger than the left, i.e. `self.bytes() - other.bytes()` would underflow. `Size` is an unsigned byte count with no notion of negative values, so subtraction that goes below zero is a programmer error.

Source

Thrown at compiler/rustc_abi/src/lib.rs:1000

// Panicking addition, subtraction and multiplication for convenience.
// Avoid during layout computation, return `LayoutError` instead.

impl Add for Size {
    type Output = Size;
    #[inline]
    fn add(self, other: Size) -> Size {
        Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| {
            panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes())
        }))
    }
}

impl Sub for Size {
    type Output = Size;
    #[inline]
    fn sub(self, other: Size) -> Size {
        Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| {
            panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes())
        }))
    }
}

impl Mul<Size> for u64 {
    type Output = Size;
    #[inline]
    fn mul(self, size: Size) -> Size {
        size * self
    }
}

impl Mul<u64> for Size {
    type Output = Size;
    #[inline]
    fn mul(self, count: u64) -> Size {
        match self.bytes().checked_mul(count) {
            Some(bytes) => Size::from_bytes(bytes),

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Establish the precondition with `debug_assert!(a.bytes() >= b.bytes())` and fix the caller if it ever fires, since a negative size is always a logic error.
  2. If underflow is legitimately possible, switch to `a.bytes().checked_sub(b.bytes()).map(Size::from_bytes)` and handle the `None` case explicitly.
  3. Verify the operands are not swapped (the most common cause is `small - large` instead of `large - small`).

Example fix

// before
let padding = next_offset - prev_end;

// after
let padding = next_offset
    .bytes()
    .checked_sub(prev_end.bytes())
    .map(Size::from_bytes)
    .expect("fields ordered: next_offset >= prev_end");
Defensive patterns

Strategy: validation

Validate before calling

// Size::sub panics when result would be negative (a < b).
fn safe_sub(a: rustc_abi::Size, b: rustc_abi::Size) -> Option<rustc_abi::Size> {
    a.bytes().checked_sub(b.bytes()).map(rustc_abi::Size::from_bytes)
}

Type guard

// Ordering guard, not a type guard.
fn is_subtractable(a: rustc_abi::Size, b: rustc_abi::Size) -> bool {
    a.bytes() >= b.bytes()
}

Try / catch

// Do not catch; subtraction underflow indicates wrong offset ordering upstream.
// Return a Result/Option from the calling code via checked_sub.

Prevention

When it happens

Trigger: Computing `a - b` via the `-` operator where `b.bytes() > a.bytes()`. Typical in offset arithmetic — e.g. subtracting a field offset from a base offset that was assumed larger but is actually smaller, or subtracting a stride from `Size::ZERO`.

Common situations: Wrong ordering assumption in struct/enum layout (e.g. computing padding as `field_end - next_offset` when fields are reordered), or a downstream tool computing relative offsets from an uninitialized/zero base. Surfaces in codegen and in consumers like miri and rust-analyzer that replay layouts.

Related errors


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