rust-lang/rust · critical

Attempted to compute stable hash for LazyAttrTokenStream

Error message

Attempted to compute stable hash for LazyAttrTokenStream

What it means

`panic!("Attempted to compute stable hash for LazyAttrTokenStream")` fires from the `StableHash` impl. Stable hashing feeds incremental-compilation fingerprinting; hashing a lazy stream (whose content depends on live parser cursor state) would produce unstable fingerprints and silently break incr-comp. The panic forces the stream to be converted to a concrete `AttrTokenStream` before it participates in any hash.

Source

Thrown at compiler/rustc_ast/src/tokenstream.rs:162

        write!(f, "LazyAttrTokenStream({:?})", self.to_attr_token_stream())
    }
}

impl<S: SpanEncoder> Encodable<S> for LazyAttrTokenStream {
    fn encode(&self, _s: &mut S) {
        panic!("Attempted to encode LazyAttrTokenStream");
    }
}

impl<D: SpanDecoder> Decodable<D> for LazyAttrTokenStream {
    fn decode(_d: &mut D) -> Self {
        panic!("Attempted to decode LazyAttrTokenStream");
    }
}

impl StableHash for LazyAttrTokenStream {
    fn stable_hash<Hcx: StableHashCtxt>(&self, _hcx: &mut Hcx, _hasher: &mut StableHasher) {
        panic!("Attempted to compute stable hash for LazyAttrTokenStream");
    }
}

/// A token range within a `Parser`'s full token stream.
#[derive(Clone, Debug)]
pub struct ParserRange(pub Range<u32>);

/// A token range within an individual AST node's (lazy) token stream, i.e.
/// relative to that node's first token. Distinct from `ParserRange` so the two
/// kinds of range can't be mixed up.
#[derive(Clone, Debug)]
pub struct NodeRange(pub Range<u32>);

/// Indicates a range of tokens that should be replaced by an `AttrsTarget`
/// (replacement) or be replaced by nothing (deletion). This is used in two
/// places during token collection.
///
/// 1. Replacement. During the parsing of an AST node that may have a

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Force the stream to `AttrTokenStream` (`.to_attr_token_stream()`) before storing it in any structure that gets fingerprinted (AST nodes, attributes, query results).
  2. Use `RUST_BACKTRACE=1` to locate the fingerprinting site, then trace back to where the lazy stream should have been forced.
  3. Add a test that hashes the affected node to catch the regression in CI.

Example fix

// before
struct Node { tokens: LazyAttrTokenStream }
// later: stable_hash fails

// after
struct Node { tokens: AttrTokenStream }
// constructed via: lazy.to_attr_token_stream()
Defensive patterns

Strategy: validation

Validate before calling

// Avoid StableHasher over LazyAttrTokenStream.
// Hash the materialized form:
let stream = lazy.to_attr_token_stream();
stream.stable_hash(hcx, hasher);

Prevention

When it happens

Trigger: Any `StableHash::stable_hash` call on a value still typed as `LazyAttrTokenStream` — typically from incr-comp fingerprinting of an AST node, attribute, or macro token result that retains the lazy variant.

Common situations: Refactors that delay forcing of lazy streams past the point where fingerprinting runs. New query inputs that include un-forced `LazyAttrTokenStream`. Incremental compilation ICEs after macro/attribute-related changes.

Related errors


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