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
- 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.
- If underflow is legitimately possible, switch to `a.bytes().checked_sub(b.bytes()).map(Size::from_bytes)` and handle the `None` case explicitly.
- 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
- Never subtract a larger Size from a smaller one; assert/document the ordering invariant at the call site.
- Compute offsets via align_to_round_up style helpers that guarantee monotonic increase.
- When iterating field offsets, sort ascending before differencing.
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
- Size::bits: {bytes} bytes in bits doesn't fit in u64
- Size::add: {} + {} doesn't fit in u64
- Size::mul: {} * {} doesn't fit in u64
- cannot change the valid range of a union
- Layout of fields should be Arbitrary for variants
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/687fcf2551aa7386.json.
Report an issue: GitHub.