{"record":{"id":"b7055abb22b35d48","repo":"gleam-lang/gleam","slug":"check-to-already-be-a-choice","errorCode":null,"errorMessage":"check to already be a choice","messagePattern":"check to already be a choice","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-core/src/exhaustiveness.rs","lineNumber":3139,"sourceCode":"            .to_runtime_check_kind()\n            .expect(\"no unconditional patterns left\");\n\n        let CheckOverlaps {\n            overlapping,\n            needs_new_choice,\n        } = self.check_overlaps(&kind);\n\n        // The branch is relevant to every existing choice its check overlaps\n        // with, so we add it (together with any newly discovered checks) to all\n        // of those paths. For a string prefix this also includes any longer\n        // prefixes and exact literals that start with it. When one of those\n        // matched but its branch then failed, control falls through to this\n        // shorter prefix.\n        for index in overlapping.iter() {\n            let (overlapping_check, branches) = self\n                .choices\n                .get_mut(*index)\n                .expect(\"check to already be a choice\");\n\n            let mut branch = branch.clone();\n            for new_check in compiler.new_checks(&pattern_check.var, &pattern, overlapping_check) {\n                branch.add_check(new_check);\n            }\n            branches.push_back(branch);\n        }\n\n        // Unless the check is fully subsumed by an existing choice, we also add\n        // it as a new path we might have to go down to in the decision tree.\n        // A shorter prefix still needs its own choice even when it overlaps\n        // earlier longer prefixes, so it can match values outside those.\n        if needs_new_choice {\n            self.save_index_of_new_choice(kind.clone());\n\n            let check = compiler.fresh_runtime_check(kind, branch_mode);\n            for new_check in compiler.new_checks(&pattern_check.var, &pattern, &check) {\n                branch.add_check(new_check);","sourceCodeStart":3121,"sourceCodeEnd":3157,"githubUrl":"https://github.com/gleam-lang/gleam/blob/49f8762da595eb3c8f576656a4200a142ca188c9/compiler-core/src/exhaustiveness.rs#L3121-L3157","documentation":"Panic from `self.choices.get_mut(*index).expect(\"check to already be a choice\")` while folding a branch into overlapping earlier choices. The `overlapping` indices come from `check_overlaps`, which is documented to only report indices that already exist in `self.choices`; if an index is missing, the overlap bookkeeping is broken.","triggerScenarios":"Compiling a pattern that `check_overlaps` reported as overlapping some existing choice, then `self.choices.get_mut(index)` failing — e.g. choices were removed/reordered between overlap computation and this loop, or `check_overlaps` returned an index into the wrong vec.","commonSituations":"Hit by Gleam compiler contributors after changes to overlap detection, guard fall-through handling, or when choices are pruned before the merge step.","solutions":["Recompute overlaps immediately before the merge loop so indices cannot go stale.","Check any code between `check_overlaps` and this loop that mutates `self.choices` (push/pop/retain).","Soften to a skip-with-warning (`continue` when lookup fails) while debugging, then fix the root cause in `check_overlaps`."],"exampleFix":"// before\nlet (overlapping_check, branches) = self.choices.get_mut(*index).expect(\"check to already be a choice\");\n// after\nlet Some((overlapping_check, branches)) = self.choices.get_mut(*index) else {\n    panic!(\"overlap index {} not in {} choices\", index, self.choices.len())\n};","handlingStrategy":"validation","validationCode":"for index in &overlapping {\n    assert!((*index as usize) < self.choices.len(), \"stale overlap index\");\n}","typeGuard":"fn choice_at(choices: &[(RuntimeCheck, Branches)], i: usize) -> Option<&(RuntimeCheck, Branches)> {\n    choices.get(i)\n}","tryCatchPattern":"std::panic::catch_unwind(|| merge_overlaps(overlapping)).unwrap_or_else(|_| recompute_overlaps_and_retry())","preventionTips":["Compute overlaps immediately before merging; never cache indices across mutations.","Audit any code that pushes/pops self.choices between check_overlaps and the merge loop.","Use retain/filter APIs carefully since they invalidate positional indices."],"tags":["rust","compiler","panics","exhaustiveness","overlaps"],"backgroundTag":"index-out-of-bounds","analyzedSha":"49f8762da595eb3c8f576656a4200a142ca188c9","analyzedAt":"2026-09-14T11:14:59.388Z","contentChangedAt":"2026-09-14T11:14:59.388Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}