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
- Report as an ICE on the rust-lang/rust tracker with a minimal repro and the rustc version.
- If using internal token APIs to build a stream manually, ensure every child stream you push corresponds to a TokenTree::Delimited entry.
- Roll back to a known-good nightly to confirm it is a recent regression.
- 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
- Do not splice hand-built TokenTree sequences into a Delimited group without matching open/close delimiters — a missing closer desynchronizes the cursor stack.
- When flattening or rewriting token streams, always rebuild via TokenStream::new / Delimited so the nesting invariants hold; never mutate the inner Vec in place.
- If you interleave TokenTreeCursor with manual stack pushes, assert that every pushed parent's current tree is Delimited before ascending.
- Round-trip test any stream transformation through TokenStream -> ast -> TokenStream to catch nesting corruption early.
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
- missing tokens for node: {:?}
- unsupported integer: {self:?}
- unsupported float: {self:?}
- `homogeneous_aggregate` should not be called for scalable ve
- aggregates can't have `FieldsShape::Primitive`
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/c1dd8547c724ee2d.json.
Report an issue: GitHub.