rust-lang/rust · critical

parent should be Delimited

Error message

parent should be Delimited

What it means

Panic inside TokenStream's cursor (inlined_next) when unwinding from a nested Delimited stream back to its parent: the parent frame on the stack is required to be a TokenTree::Delimited entry, because that is the only construct that pushes a child stream onto the cursor. Reaching a non-Delimited parent means the cursor stack discipline was corrupted — an internal compiler bug in token-stream traversal.

Source

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

                    &TokenTree::Token(token, spacing) => {
                        debug_assert!(!token.kind.is_delim());
                        let res = (token, spacing);
                        self.curr.bump();
                        return res;
                    }
                    &TokenTree::Delimited(sp, spacing, delim, ref tts) => {
                        let trees = TokenTreeCursor::new(tts.clone());
                        self.stack.push(mem::replace(&mut self.curr, trees));
                        if !delim.skip() {
                            return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open);
                        }
                        // No open delimiter to return; continue on to the next iteration.
                    }
                };
            } else if let Some(parent) = self.stack.pop() {
                // We have exhausted this token stream. Move back to its parent token stream.
                let Some(&TokenTree::Delimited(span, spacing, delim, _)) = parent.curr() else {
                    panic!("parent should be Delimited")
                };
                self.curr = parent;
                self.curr.bump(); // move past the `Delimited`
                if !delim.skip() {
                    return (Token::new(delim.as_close_token_kind(), span.close), spacing.close);
                }
                // No close delimiter to return; continue on to the next iteration.
            } else {
                // We have exhausted the outermost token stream. The use of
                // `Spacing::Alone` is arbitrary and immaterial, because the
                // `Eof` token's spacing is never used.
                return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
            }
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report as an ICE on the rust-lang/rust tracker with a minimal repro and the rustc version.
  2. If using internal token APIs to build a stream manually, ensure every child stream you push corresponds to a TokenTree::Delimited entry.
  3. Roll back to a known-good nightly to confirm it is a recent regression.
  4. Run under a debug rustc build to catch the corruption earlier in the cursor stack.
Defensive patterns

Strategy: validation

Validate before calling

// The cursor panics if, after exhausting a child stream, the parent's
// current tree is not a Delimited node. Validate cursor stack integrity
// before calling next()/bump() across a nesting boundary.
fn parent_is_delimited(cursor: &TokenTreeCursor) -> bool {
    cursor.stack.iter().all(|parent| {
        matches!(parent.curr(), Some(TokenTree::Delimited(..)))
    })
}
// Only descend/ascend when the invariant holds:
if !parent_is_delimited(&cursor) { return Err("token stream stack is corrupt: parent is not Delimited"); }

Type guard

fn is_delimited(tt: &TokenTree) -> bool {
    matches!(tt, TokenTree::Delimited(..))
}

Try / catch

let item = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cursor.next()));
match item {
    Ok((tok, spacing)) => (tok, spacing),
    Err(_) => return Err("TokenTreeCursor.next() panicked: parent should be Delimited (corrupted stream)"),
}

Prevention

When it happens

Trigger: Triggered only if the cursor's internal stack is in an inconsistent state, e.g. an explicitly constructed TokenStream whose nested Delimited frames do not match the push/pop discipline, or a proc-macro that builds a TokenStream by hand via internal APIs and feeds it to code that iterates with inlined_next. Cannot occur from streams produced by the normal parser.

Common situations: Custom tooling or fuzzers that synthesize TokenStream internals directly; rustc development regressions; hand-rolled token manipulation in nightly-only crates. End users compiling ordinary crates will not hit this.

Related errors


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