Skip to content

[ICE]: StyledBuffer::replace panics with --diagnostic-width <= 10 #154258

Closed

Description

@furkanmamuk

Code

pub fn f() { let mut foo_bar = 0; }

Meta

rustc --version --verbose:

rustc 1.94.0 (4a4ef493e 2026-03-02)
binary: rustc
commit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db
commit-date: 2026-03-02
host: x86_64-unknown-linux-gnu
release: 1.94.0
LLVM version: 21.1.8

Error output

$ rustc --edition=2021 --crate-type lib --diagnostic-width=0 ice.rs

thread 'rustc' panicked at library/alloc/src/vec/mod.rs:2873:36:
slice index starts at 13 but ends at 11
...
  12: <annotate_snippets::renderer::styled_buffer::StyledBuffer>::replace
  13: annotate_snippets::renderer::render::render
  14: <rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter>::emit_messages_default
...
  21: rustc_middle::lint::lint_level::lint_level_impl
  22: rustc_borrowck::borrowck_check_region_constraints

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new
note: rustc 1.94.0 (4a4ef493e 2026-03-02) running on x86_64-unknown-linux-gnu
Full backtrace
 0: <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt
 1: core::fmt::write
 2: std::io::Write::write_fmt
 3: std::panicking::default_hook::{closure}
 4: std::panicking::default_hook
 5: std::panicking::update_hook::<Box<rustc_driver_impl::install_ice_hook::{closure#1}>>::{closure#0}
 6: std::panicking::panic_with_hook
 7: std::panicking::panic_handler::{closure}
 8: std::sys::backtrace::__rust_end_short_backtrace
 9: __rustc::rust_begin_unwind
10: core::panicking::panic_fmt
11: core::slice::index::slice_index_fail
12: <annotate_snippets::renderer::styled_buffer::StyledBuffer>::replace
13: annotate_snippets::renderer::render::render
14: <rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter>::emit_messages_default
15: <AnnotateSnippetEmitter as rustc_errors::emitter::Emitter>::emit_diagnostic
16: <rustc_errors::DiagCtxtInner>::emit_diagnostic::{closure#3}
17: rustc_interface::callbacks::track_diagnostic::<Option<ErrorGuaranteed>>
18: <rustc_errors::DiagCtxtInner>::emit_diagnostic
19: <rustc_errors::DiagCtxtHandle>::emit_diagnostic
20: <() as rustc_errors::diagnostic::EmissionGuarantee>::emit_producing_guarantee
21: rustc_middle::lint::lint_level::lint_level_impl
22: rustc_borrowck::borrowck_check_region_constraints
23: <rustc_borrowck::root_cx::BorrowCheckRootCtxt>::do_mir_borrowck

Trigger conditions

  • Any unused_mut warning where the variable name is 7+ characters (the lint's underline spans 12+ display columns)
  • --diagnostic-width is 10 or less (including 0, the auto-detected value when there is no TTY)

This is hit in practice by non-interactive environments: CI without a PTY, wsl -e invocations, piped output through tools that don't allocate a terminal, etc.

The crash also produces a secondary delayed_bug about OpaqueTypeKey / ProvisionalHiddenType, but this is collateral damage from the panic aborting the borrow checker -- not an independent type system bug.

Regression

Regression in 1.94.0 (works on 1.93.0). Nightly bisect (regressed nightly-2025-11-23) points to #148984 which upgraded annotate-snippets from 0.11.5 to 0.12.9. The vulnerable StyledBuffer::replace method was introduced in the 0.12.x series.
Update: Regression is 1.91.0 -> 1.92.0, not 1.93.0 -> 1.94.0. See this comment for details.

searched nightlies: nightly-2025-11-01 to nightly-2026-01-20
regressed nightly:  nightly-2025-11-23

Root cause

The bug is in annotate-snippets 0.12.10-0.12.13 (still present on main), not in rustc itself. The span-trimming logic in annotate_snippets::renderer::render computes an inverted range when term_width is 0, and StyledBuffer::replace panics on it.

Filed upstream: rust-lang/annotate-snippets-rs#391

Workarounds

RUSTFLAGS="--diagnostic-width=80" cargo build   # explicit width
RUSTFLAGS="-A unused-mut" cargo build            # suppress the lint
rustup override set 1.93.0                       # pin toolchain

Reproducer repo

https://github.com/furkanmamuk/rustc-ice-diagnostic-width-panic

Activity

  1. added
    I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    C-bugCategory: This is a bug.
    on Mar 23, 2026
  2. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Mar 23, 2026
  3. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Mar 23, 2026
  4. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Mar 23, 2026
  5. furkanmamuk commented on Mar 23, 2026

    @furkanmamuk
    Author
  6. jieyouxu commented on Mar 23, 2026

    @jieyouxu
    Member

    @furkanmamuk how do you reproduce the ICE with cargo? I can reproduce it with rustc bare, but not in conjunction with cargo (absent tiny terminal width).

  7. jieyouxu commented on Mar 23, 2026

    @jieyouxu
    Member

    Regression in 1.94.0 (works on 1.93.0). Nightly bisect (regressed nightly-2025-11-23) points to #148984 which upgraded annotate-snippets from 0.11.5 to 0.12.9.

    Can you also elaborate on your bisection (how did you do this?) The linked PR landed on 1.93.0 not 1.94.0?

  8. added
    S-needs-infoStatus: The issue lacks details necessary to triage or act on it.
    on Mar 23, 2026
  9. furkanmamuk commented on Mar 23, 2026

    @furkanmamuk
    Author

    @jieyouxu

    There's no way to trigger it without a small terminal width, diagnostic-width <= 10 is a hard requirement for the bug. It only shows up in environments where width auto-detects to 0 (WSL2 non-interactive, CI without PTY, piped output with no TTY).

    To reproduce with cargo:

    CARGO_TERM_WIDTH=0 cargo check or RUSTFLAGS="--diagnostic-width=0" cargo check

    The original discovery was on WSL2 invoked non-interactively (wsl -e bash -c ...), where /dev/tty reports size 1x1 -- cargo auto-detects width as 0 and passes --diagnostic-width=0 to rustc.


    My original bisection attribution was wrong. What actually happened:

    • 1.93.0 already bundles annotate-snippets 0.12.10 in rustc_errors, but uses the old emitter by default -- the vulnerable StyledBuffer::replace codepath is never hit.
    • #148188 (merged Nov 6, 2025) switched to annotate-snippets as the default emitter on nightly -- this is what the nightly bisect
      (regressed nightly-2025-11-23) actually found.
    • #150032 (merged Dec 17, 2025) enabled it on stable -- this is why 1.94.0 ICEs but 1.93.0 doesn't.

    The bug has been latent in annotate-snippets 0.12.x since it was introduced, but only became reachable in rustc when the emitter switch landed.

  10. added
    P-lowLow priority
    and removed
    S-needs-infoStatus: The issue lacks details necessary to triage or act on it.
    on Mar 23, 2026
  11. jieyouxu commented on Mar 23, 2026

    @jieyouxu
    Member

    I must ask: was this report AI-generated/LLM-generated?

  12. furkanmamuk commented on Mar 23, 2026

    @furkanmamuk
    Author

    @jieyouxu No, investigation is mine. I used an ai assistant (claude) to help structure and format the final report, but every test and finding came from actual debugging sessions. I built it while debugging a compiler error that was blocking my POSIX terminal, which I hit with wsl invocations while testing first.

  13. jieyouxu commented on Mar 23, 2026

    @jieyouxu
    Member

    searched nightlies: from nightly-2025-09-13 to nightly-2026-03-23
    regressed nightly: nightly-2025-10-13
    searched commit range: be0ade2...2300c2a
    regressed commit: ff6dc92

    bisected with cargo-bisect-rustc v0.6.11

    Host triple: x86_64-unknown-linux-gnu
    Reproduce with:

    cargo bisect-rustc --start=1.91.0 --without-cargo --regress=ice --script=rustc -- repro.rs --crate-type=lib --diagnostic-width=0
  14. furkanmamuk commented on Mar 23, 2026

    @furkanmamuk
    Author

    Correction on regression range, and more findings - This goes deeper than originally reported

    Updated version bisect:

    Version ICE at --diagnostic-width=0? Crash location
    1.85.0-1.91.0 No --
    1.92.0 Yes rustc_errors::styled_buffer::StyledBuffer::replace via HumanEmitter
    1.93.0 Yes rustc_errors::styled_buffer::StyledBuffer::replace via HumanEmitter
    1.94.0 Yes annotate_snippets::renderer::styled_buffer::StyledBuffer::replace via AnnotateSnippetEmitter

    CI matrix confirming the version bisect and width threshold

    Nightly bisect (1.91 -> 1.92):
    searched nightlies: from nightly-2025-08-01 to nightly-2025-10-30
    regressed nightly: nightly-2025-10-13
    regressed commit: ff6dc928

    The regressing commit is the auto-merge of #142390("Perform unused assignment and unused variables lints on MIR", merged Oct 12, 2025).
    This changed how unused_mut annotations are structured, causing them to hit the pre-existing bug in StyledBuffer::replace at small terminal widths.

    StyledBuffer::replace has never guarded against inverted ranges. Before #142390, the annotation geometry didn't trigger the bad path at width=0. After #142390, it does.

    In 1.94.0, the default emitter switched to annotate-snippets (#150032), which has its own copy of the same bug, so the crash moved from rustc_errors::StyledBuffer to annotate_snippets::StyledBuffer.

    Fix status:

  15. added
    D-diagnostic-infraDiagnostics: Issues that affect all diagnostics, or relate to the diagnostic machinery itself.
    on Mar 23, 2026
  16. grindvoll commented on Mar 24, 2026

    @grindvoll

    I hit the same (or at least related) problem today with 1.94 - I noticed a crash in the output from the rust-analyzer plugin in VsCode. rust-analyzer plugin uses --diagnostic-width=0, and after narrowing down, it ends up with the same conclusion as described here - a bug in StyledBuffer::replace.

    This is another code snippet that reveals the panic:

    echo "use aaaaaaaaaaa;" | rustc --diagnostic-width=0 -

    In this case, the unknown import error is the trigger. The length of the import (aaaaaaaaaaa) must be 11 or 12 for the panic to happen.

    Regression info:

    The example in this comment generates a panic in all rust versions >= 1.87.

  17. added a commit that references this issue on Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsC-bugCategory: This is a bug.D-diagnostic-infraDiagnostics: Issues that affect all diagnostics, or relate to the diagnostic machinery itself.I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️P-lowLow priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions