gleam-lang/gleam · error

bit array compilation with no choice

Error message

bit array compilation with no choice

What it means

Panic after compiling a bit-array pivot test: the code assumes `self.choices` has exactly the one choice pushed for the successful test, so `self.choices.get_mut(0)` must succeed. If choices is empty here, the pivot-test push above did not happen or choices were cleared.

Solutions

  1. Verify every path that reaches this code pushed the `(RuntimeCheck::BitArray { .. }, ...)` choice first.
  2. Check early returns above (e.g. already-compiled patterns) do not bypass the pivot push while later code still assumes choice 0 exists.
  3. Use `let Some((_, branches)) = self.choices.get_mut(0)` with a descriptive panic to ease debugging.

Example fix

// before
let (_, if_true_branches) = self.choices.get_mut(0).expect("bit array compilation with no choice");
// after
let Some((_, if_true_branches)) = self.choices.get_mut(0) else {
    panic!("choices empty when adding bit array branch")
};
Defensive patterns

Strategy: validation

Validate before calling

assert!(!self.choices.is_empty(), "pivot test must be pushed before branch compilation");

Type guard

fn first_choice(choices: &Choices) -> Option<&(RuntimeCheck, Branches)> { choices.first() }

Try / catch

std::panic::catch_unwind(|| add_bit_array_branch(branch)).map_err(|_| Bug::BitArrayNoChoice)

Prevention

When it happens

Trigger: Compiling the if-true side of a bit-array decision node when `self.choices` is empty — e.g. the pivot test push was skipped, or a previous branch of the compiler removed choices before this point.

Common situations: Hit by Gleam compiler contributors while refactoring the bit-array path of the exhaustiveness compiler, or when early-return paths (like the `add_checked_bit_array_branch` fast path) leave `self.choices` empty.

Related errors


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

Appendix: source

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

                        BitArrayMatchedValue::LiteralFloat(_)
                        | BitArrayMatchedValue::LiteralInt { .. }
                        | BitArrayMatchedValue::LiteralString { .. }
                        | BitArrayMatchedValue::Discard(_) => {}
                    }
                }

                false
            });

            let variable = &pattern_check.var;
            branch.add_check(variable.is(compiler.bit_array_pattern(tests)));

            // We know that there's always going to be a single choice for the
            // successful check, so we get that and add the branch to it.
            let (_, if_true_branches) = self
                .choices
                .get_mut(0)
                .expect("bit array compilation with no choice");
            if_true_branches.push_back(branch);
        }

        // Same goes for the if_false branch: knowing the pivot test has failed
        // we only want to keep those branches with a pattern that still have a
        // chance to match.
        if !pattern_fails_if_test_fails {
            // The main difference with the if true case is that we have no way
            // of pruning the number of needed tests, so we add this check back
            // exactly as it is.
            branch.add_check(pattern_check);
            self.fallback.push_back(branch);
        }
    }

    fn save_index_of_new_choice(&mut self, kind: RuntimeCheckKind) {
        let index = self.choices.len();
        match kind {

View on GitHub (pinned to 49f8762da5)