rust-lang/rust · critical

failed to unescape char literal

Error message

failed to unescape char literal

What it means

Panic in LitKind::from_token_lit when unescape_char returns Err for a token::Char literal. As with the byte case, the lexer is contractually expected to have validated char escapes beforehand, so this panic signals a violation of that invariant — a malformed char literal reached semantic conversion. An internal-only path that should be unreachable from user source.

Source

Thrown at compiler/rustc_ast/src/util/literal.rs:84

        }

        // For byte/char/string literals, chars and escapes have already been
        // checked in the lexer (in `cook_lexer_literal`). So we can assume all
        // chars and escapes are valid here.
        Ok(match kind {
            token::Bool => {
                assert!(symbol.is_bool_lit());
                LitKind::Bool(symbol == kw::True)
            }
            token::Byte => {
                return unescape_byte(symbol.as_str())
                    .map(LitKind::Byte)
                    .map_err(|_| panic!("failed to unescape byte literal"));
            }
            token::Char => {
                return unescape_char(symbol.as_str())
                    .map(LitKind::Char)
                    .map_err(|_| panic!("failed to unescape char literal"));
            }

            // There are some valid suffixes for integer and float literals,
            // so all the handling is done internally.
            token::Integer => return integer_lit(symbol, suffix),
            token::Float => return float_lit(symbol, suffix),

            token::Str => {
                // If there are no characters requiring special treatment we can
                // reuse the symbol from the token. Otherwise, we must generate a
                // new symbol because the string in the LitKind is different to the
                // string in the token.
                let s = symbol.as_str();
                // Vanilla strings are so common we optimize for the common case where no chars
                // requiring special behaviour are present.
                let symbol = if s.contains('\\') {
                    let mut buf = String::with_capacity(s.len());
                    // Force-inlining here is aggressive but the closure is

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report the ICE to rust-lang/rust with the literal and rustc version hash.
  2. If generating char literal tokens programmatically, ensure symbols are valid char literals or route through the lexer.
  3. Pin to a known-good nightly to confirm the regression window.
  4. Run the failing input through rustc_lexer directly to see whether validation is skipped.
Defensive patterns

Strategy: validation

Validate before calling

// Same shape as byte literals but for '...' char literals.
fn valid_char_literal(sym: &str) -> bool {
    let inner = sym.strip_prefix("'").and_then(|s| s.strip_suffix("'"));
    let Some(inner) = inner else { return false; };
    rustc_lexer::unescape::unescape_char(inner).is_ok()
}
if !valid_char_literal(symbol.as_str()) {
    return Err(format!("invalid char literal: {}", symbol));
}

Type guard

fn is_valid_char_lit(kind: token::LitKind, sym: Symbol) -> bool {
    matches!(kind, token::Char) && rustc_lexer::unescape::unescape_char(sym.as_str()).is_ok()
}

Try / catch

let lit = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| LitKind::from_token_lit(raw_lit)));
match lit {
    Ok(Ok(LitKind::Char(c))) => c,
    Ok(Ok(other)) => other,
    Ok(Err(e)) => return Err(format!("char literal error: {:?}", e)),
    Err(_) => return Err(format!("char literal failed to unescape: {}", raw_lit.symbol)),
}

Prevention

When it happens

Trigger: Reached when from_token_lit is called on a token::Char whose symbol is not a valid char literal (e.g. '\xZZ') without prior lexer validation. Possible via hand-built token streams in proc-macros, fuzzing the parser boundary, or a compiler change that bypasses cook_lexer_literal.

Common situations: Nightly rustc regressions in the lexer; proc-macro tooling that fabricates char literal tokens; fuzz targets. Normal user code with a bad char literal emits a diagnostic at parse time.

Related errors


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