rust-lang/rust · critical
expected to be able to unify goal projection with dyn's proj
Error message
expected to be able to unify goal projection with dyn's projection
What it means
This `expect` at structural_traits.rs:1029 sits in `try_eagerly_replace_alias`, the machinery that rewrites a projection alias on a `dyn Trait` self type by unifying the goal's alias term against a projection bound drawn from the dyn's principal. The expectation is that once `projection_may_match` succeeded earlier in the function, the inference `eq_and_get_goals` must succeed. Failure means the projection bound that passed the cheap `may_match` structural check still failed full unification — an internal inconsistency in the next-solver's dyn coercion path.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs:1029
.iter()
.filter(|source_projection| self.projection_may_match(**source_projection, alias_term));
let Some(replacement) = matching_projections.next() else {
// This shouldn't happen.
panic!("could not replace {alias_term:?} with term from from {:?}", self.self_ty);
};
// FIXME: This *may* have issues with duplicated projections.
if matching_projections.next().is_some() {
// If there's more than one projection that we can unify here, then we
// need to stall until inference constrains things so that there's only
// one choice.
return Err(Ambiguous);
}
let replacement = self.ecx.instantiate_binder_with_infer(*replacement);
self.nested.extend(
self.ecx
.eq_and_get_goals(self.param_env, alias_term, replacement.projection_term)
.expect("expected to be able to unify goal projection with dyn's projection"),
);
Ok(Some(replacement.term))
}
}
/// Marker for bailing with ambiguity.
pub(crate) struct Ambiguous;
impl<D, I> FallibleTypeFolder<I> for ReplaceProjectionWith<'_, '_, I, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
type Error = Ambiguous;
fn cx(&self) -> I {
self.ecx.cx()View on GitHub (pinned to 22057b88b0)
Solutions
- Confirm whether `-Znext-solver` is on; the panic is specific to that solver path, so removing the flag avoids it.
- Reduce the dyn coercion / associated-type bound to a minimal case and report at https://github.com/rust-lang/rust (label next-solver).
- Bisect the nightly toolchain (`cargo bisect-rustc`) to identify the regressing commit.
- Temporarily replace the `dyn Trait<Assoc=…>` usage with a concrete type or a where-clause bound to sidestep the projection replacement.
Example fix
// before: dyn with associated-type projection that next-solver cannot unify
fn coerce<'a>(x: &'a dyn Trait<Assoc = u32>) -> &'a dyn Trait { x }
// after: avoid projection replacement via explicit where-clause
fn coerce<'a, T: ?Sized + Trait<Assoc = u32> + 'a>(x: &'a T) -> &'a dyn Trait where 'a: 'a { x } Defensive patterns
Strategy: type-guard
Validate before calling
// Before projecting an associated item through a value, ensure it isn't an opaque `dyn`.
fn concrete_not_dyn<T: ?Sized>(_v: &T) where T: Sized {} // caller passes concrete types only Type guard
// Guard: only allow projections on concrete types; reject `dyn Trait` at the call site.
fn is_dyn_trait_object<T: ?Sized>() -> bool {
std::mem::size_of::<*mut T>() == std::mem::size_of::<usize>()
&& !std::mem::size_of::<T>().is_power_of_two_on_concrete()
}
// Prefer an explicit associated-type bound so projection is decidable:
// fn use_it<T: Trait<Assoc = Concrete>>(x: &T) { let _ = x.assoc(); } Prevention
- Never call associated functions/types through a bare `dyn Trait` whose associated type is unconstrained; specify `dyn Trait<Assoc = T>` or box a concrete type.
- When you need dyn dispatch plus projection, introduce a wrapper that stores the projected result instead of recomputing it through the dyn.
- Keep the next trait solver off for crates that lean heavily on dyn objects until it stabilizes.
- Add `where Self: Sized` to methods that project associated types, so dyn calls are rejected at the type-checker instead of ICE-ing the solver.
When it happens
Trigger: Reached when the next trait solver (`-Znext-solver`) evaluates a `dyn Trait + Associated<Type>` whose projection bound structurally matches a goal projection but cannot be equated under the param-env, e.g. during unsizing/coercion of a dyn with associated-type bounds or when relating two dyn types.
Common situations: Nightly with `-Znext-solver` enabled; using `dyn Trait<Assoc = T>` with generic associated types; combining `feature(associated_type_defaults)` or `feature(min_specialization)` with dyn coercions; bugs introduced by a next-solver refactor.
Related errors
- Failed to get parent for {child:?}
- 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/a5e9e007c8b4888e.json.
Report an issue: GitHub.