rust-lang/rust · critical
Failed to get parent for {child:?}
Error message
Failed to get parent for {child:?} What it means
This panic fires in specialization_graph.rs:45 inside `Graph::parent`, which looks up the immediate parent of an impl in the per-trait specialization tree (`DefIdMap<DefId> parent`). The map is populated during coherence graph construction, so a missing entry means an impl DefId was queried for its parent before (or without) being registered into the graph — an internal compiler invariant violation, not user code. It is reached through specialization/coherence machinery (e.g. extracting default items, overlap checks).
Source
Thrown at compiler/rustc_middle/src/traits/specialization_graph.rs:45
pub struct Graph {
/// All impls have a parent; the "root" impls have as their parent the `def_id`
/// of the trait.
pub parent: DefIdMap<DefId>,
/// The "root" impls are found by looking up the trait's def_id.
pub children: DefIdMap<Children>,
}
impl Graph {
pub fn new() -> Graph {
Graph { parent: Default::default(), children: Default::default() }
}
/// The parent of a given impl, which is the `DefId` of the trait when the
/// impl is a "specialization root".
#[track_caller]
pub fn parent(&self, child: DefId) -> DefId {
*self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {child:?}"))
}
}
/// What kind of overlap check are we doing -- this exists just for testing and feature-gating
/// purposes.
#[derive(Copy, Clone, PartialEq, Eq, Hash, StableHash, Debug, TyEncodable, TyDecodable)]
pub enum OverlapMode {
/// The 1.0 rules (either types fail to unify, or where clauses are not implemented for crate-local types)
Stable,
/// Feature-gated test: Stable, *or* there is an explicit negative impl that rules out one of the where-clauses.
WithNegative,
/// Just check for negative impls, not for "where clause not implemented": used for testing.
Strict,
}
impl OverlapMode {
pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode {
let with_negative_coherence = tcx.features().with_negative_coherence();View on GitHub (pinned to 22057b88b0)
Solutions
- Minimize the specialization graph to the smallest reproducing impl set and file an issue at https://github.com/rust-lang/rust with `-Ztoggle-features` / `--self-profile` output and the ICE backtrace.
- Try compiling with a stable toolchain (specialization is nightly-only) to confirm it is feature-gated machinery.
- Reduce the crate with `cargo bugoxide` / `creduce` on the `.rs` triggering coherence and bisect across nightly commits to find the regression.
- As a workaround, flatten the specialization (avoid a `default impl` chain that relies on parent traversal) or split the trait so no specialization parent lookup is required.
Example fix
// before: specialization chain forcing parent traversal
impl<T> default Trait for T { /* ... */ }
impl Trait for Vec<u8> { /* ... */ } // ICE: parent lookup for Vec<u8>
// after: avoid default impl, use direct blanket impl
impl<T> Trait for T { /* ... */ }
impl Trait for Vec<u8> { /* ... */ } Defensive patterns
Strategy: validation
Validate before calling
// Before relying on specialization, verify the specialization graph is well-formed.
// Static check: reject overlapping impls unless `min_specialization` is active and sound.
fn assert_no_specialization_cycles(impls: &[&str]) -> Result<(), String> {
// User-level guard: avoid the feature entirely on stable toolchains.
if !cfg!(feature = "min_specialization") && impls.iter().any(|i| i.contains("default")) {
return Err("specialization requires nightly + min_specialization; refusing to build graph".into());
}
Ok(())
} Type guard
// Narrow a trait ref to a non-specialized concrete impl before resolving its parent.
fn is_concrete_specializable(t: &impl std::any::TypeInfo) -> bool {
// Only allow types whose impl is the sole, non-default one.
!t.is_trait_object() && t.implements_single_impl()
} Prevention
- Avoid `min_specialization` on nightly unless you control every impl in the graph; prefer marker-struct based specialization which stays sound on stable.
- Pin a known-good stable rustc for CI; nightly specialization_graph churn is the leading trigger for parent-lookup ICEs.
- Never query a specialization parent at runtime from proc-macro/tool code without first confirming the impl is reachable in the current crate graph.
- Run `cargo +stable check` as a gate; if stable accepts the impl set but nightly ICEs, file the bug rather than shipping.
When it happens
Trigger: Hit when the compiler calls `Graph::parent(child)` (specialization_graph.rs:44) for an impl `DefId` that was never inserted into the `parent` map during `Graph` construction — typically via specialization queries (`tcx.impls_in_trait`, default-item resolution, or overlap mode `WithNegative`/`Strict`).
Common situations: Using `min_specialization`/`specialization` with a complex impl hierarchy on nightly; adding a new blanket impl that interacts badly with existing specializations; regressions after a rustc upgrade that changes coherence ordering; cross-crate specialization where the child crate is compiled with a different feature set than the defining crate.
Related errors
- expected to be able to unify goal projection with dyn's proj
- Sized/MetaSized is never const
- AsyncFnKindHelper is not const
- Tuple trait is not const
- Pointee is not const
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/dc7aceca8cf54ee1.json.
Report an issue: GitHub.