rust-lang/rust · critical

unreachable: invalid ExternAbi variant

Error message

unreachable: invalid ExternAbi variant

What it means

`ExternAbi::as_packed` linearly scans `ALL_VARIANTS` to find the calling-ABI variant's index, and this panic is its terminal 'no match' branch. Because every variant is enumerated in `ALL_VARIANTS` by the same macro that defines `as_packed`, a real `ExternAbi` value can never fail to match — the panic is pure defensive scaffolding for memory corruption or a future variant added without updating the list.

Source

Thrown at compiler/rustc_abi/src/extern_abi.rs:159

                }
            }
            // FIXME(FnSigKind): when PartialEq is stably const, use it instead
            const fn internal_const_eq(&self, other: &Self) -> bool {
                match (self, other) {
                    $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*
                    _ => false,
                }
            }
            // ALL_VARIANTS.iter().position(|v| v == self), but const
            pub const fn as_packed(&self) -> u8 {
                let mut index = 0;
                while index < $e_name::ALL_VARIANTS.len() {
                    if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {
                        return index as u8;
                    }
                    index += 1;
                }
                panic!("unreachable: invalid ExternAbi variant");
            }
            pub const fn from_packed(index: u8) -> Self {
                let index = index as usize;
                assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");
                $e_name::ALL_VARIANTS[index]
            }
        }

        impl ::core::str::FromStr for $e_name {
            type Err = AbiFromStrErr;
            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
                match s {
                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
                    _ => Err(AbiFromStrErr::Unknown),
                }
            }
        }
    }

View on GitHub (pinned to 22057b88b0)

Solutions

  1. If you added a new ABI variant, ensure it is listed in the `abi_impls! { ExternAbi = { ... } }` block so both `ALL_VARIANTS` and the matches stay in sync.
  2. If this fires across a compiler version boundary, clear stale incremental/metadata caches and rebuild — `as_packed`/`from_packed` indices are not a stable format.
  3. Reproduce on stock rustc; if it ICEs, file a bug with the `extern "..."` item and rustc commit.
  4. Audit any unsafe code transmuting between `ExternAbi` and a numeric discriminant.

Example fix

// before — new variant declared on the enum but missing from abi_impls!
// (panics when serialized via as_packed)

// after
abi_impls! {
    ExternAbi = {
        // ...existing...
        MyNewAbi =><= "my-new-abi",
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// ExternAbi::as_packed() panics ('unreachable: invalid ExternAbi variant')
// if the value was not produced by a valid variant. The real risk is feeding an
// out-of-range u8 into from_packed() and then round-tripping. Bounds-check first.
use rustc_abi::ExternAbi;
fn valid_abi_packed_index(idx: u8) -> bool {
    (idx as usize) < ExternAbi::ALL_VARIANTS.len()
}
// Prefer parsing a name rather than trusting a raw index:
fn abi_from_name(name: &str) -> Option<ExternAbi> {
    name.parse::<ExternAbi>().ok()
}

Type guard

fn is_known_extern_abi(abi: &ExternAbi) -> bool {
    // ALL_VARIANTS is the authoritative set; this is sound because ExternAbi is
    // non-exhaustive and a future variant added upstream must still round-trip.
    ExternAbi::ALL_VARIANTS.iter().any(|v| v.internal_const_eq(abi))
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| abi.as_packed()));
match result {
    Ok(packed) => { /* store the u8 */ }
    Err(_) => {
        // The abi value was not a member of ALL_VARIANTS (e.g. constructed via
        // unsafe transmute). Refuse to persist it; re-resolve from a name instead.
    }
}

Prevention

When it happens

Trigger: Calling `some_abi.as_packed()` (used to compactly serialize an ABI tag, e.g. in crate metadata or queries) on an `ExternAbi` value not present in the macro-generated `ALL_VARIANTS` list — something only possible via unsafe/transmute corruption, a partially-updated enum, or an exhaustive-match that went stale.

Common situations: A new ABI variant added to the enum but not to the `abi_impls!` list (or vice versa), deserializing an `as_packed` index from mismatched compiler versions, or memory/UB corruption that produced a discriminant outside the enum range (the earlier `assert!` in `from_packed` usually catches that first).

Related errors


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