gleam-lang/gleam · error

no unconditional patterns left

Error message

no unconditional patterns left

What it means

Panic from `pattern.to_runtime_check_kind().expect("no unconditional patterns left")`. `to_runtime_check_kind` returns None for patterns that still contain unprocessed sub-checks (e.g. guards or nested patterns that cannot be a single unconditional check); this code assumed every pattern reaching it had been fully reduced to one runtime check kind.

Solutions

  1. Extend `to_runtime_check_kind` to handle the new pattern shape, or ensure the pattern is fully simplified before reaching this code.
  2. Check the caller's fast paths (bit-array/list handling above this line) cover every compound pattern kind.
  3. Add a test with the pattern that panics and fix the reduction pass so it never forwards unsimplified patterns.

Example fix

// before
let kind = pattern.to_runtime_check_kind().expect("no unconditional patterns left");
// after
let kind = pattern.to_runtime_check_kind().unwrap_or_else(|| {
    panic!("pattern {:?} did not reduce to a runtime check kind", pattern)
});
Defensive patterns

Strategy: validation

Validate before calling

// Only call when the pattern is a simple unconditional pattern:
if pattern.to_runtime_check_kind().is_none() { simplify_pattern(pattern)?; }

Type guard

fn is_unconditional(pattern: &Pattern) -> bool {
    pattern.to_runtime_check_kind().is_some()
}

Try / catch

std::panic::catch_unwind(|| compile_pattern(p)).unwrap_or_else(|_| Err(Error::UnsupportedPattern(p.clone())))

Prevention

When it happens

Trigger: Compiling a case branch whose pattern is not yet a simple unconditional pattern — e.g. a pattern with a guard, or a nested/compound pattern that the earlier reduction step failed to split — reaching `compile_pattern`/`add_branch` and calling `to_runtime_check_kind`.

Common situations: Compiler developers hit this when adding new pattern forms (e.g. new literal or bit-array segment types) without teaching `to_runtime_check_kind` (or the reduction pass) about them.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14). Data as JSON: /api/errors/c24aecfacdf0caaf. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/exhaustiveness.rs:3122

        pattern_check: PatternCheck,
        mut branch: Branch,
        branch_mode: &BranchMode,
        compiler: &mut Compiler<'_>,
    ) {
        let pattern = compiler.pattern(pattern_check.pattern).clone();

        // Bit array patterns are split in a different way that requires special
        // handling. Instead of reasoning on overlapping checks what we do it we
        // always split the decision tree in two distinct paths based on one of
        // the bit array pattern's tests.
        if let Pattern::BitArray { tests } = pattern {
            self.add_checked_bit_array_branch(pattern_check, tests, branch, compiler);
            return;
        }

        let kind = pattern
            .to_runtime_check_kind()
            .expect("no unconditional patterns left");

        let CheckOverlaps {
            overlapping,
            needs_new_choice,
        } = self.check_overlaps(&kind);

        // The branch is relevant to every existing choice its check overlaps
        // with, so we add it (together with any newly discovered checks) to all
        // of those paths. For a string prefix this also includes any longer
        // prefixes and exact literals that start with it. When one of those
        // matched but its branch then failed, control falls through to this
        // shorter prefix.
        for index in overlapping.iter() {
            let (overlapping_check, branches) = self
                .choices
                .get_mut(*index)
                .expect("check to already be a choice");

View on GitHub (pinned to 49f8762da5)