rust-lang/rust · critical

unexpected infer {a_ty:?} {b_ty:?}

Error message

unexpected infer {a_ty:?} {b_ty:?}

What it means

In `consider_structural_builtin_unsize_candidates` (trait_goals.rs:836), after structurally normalizing `b_ty`, the code panics with `unexpected infer {a_ty:?} {b_ty:?}` if `a_ty` (the source of the unsize goal) is still a `TyVar`. The source type of an `Unsize` goal must not be an unbound inference variable by the time structural unsizing candidates are assembled; doing so is a solver-invariant violation.

Source

Thrown at compiler/rustc_next_trait_solver/src/solve/trait_goals.rs:836

        goal: Goal<I, Self>,
    ) -> Result<Vec<Candidate<I>>, RerunNonErased> {
        if goal.predicate.polarity != ty::PredicatePolarity::Positive {
            return Ok(vec![]);
        }

        let result = ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(
            |ecx| -> Result<Vec<Candidate<I>>, NoSolutionOrRerunNonErased> {
                let a_ty = goal.predicate.self_ty();
                // We need to normalize the b_ty since it's matched structurally
                // in the other functions below.
                let b_ty = ecx.structurally_normalize_ty(
                    goal.param_env,
                    goal.predicate.trait_ref.args.type_at(1),
                )?;

                let goal = goal.with(ecx.cx(), (a_ty, b_ty));
                match (a_ty.kind(), b_ty.kind()) {
                    (ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),

                    (_, ty::Infer(ty::TyVar(..))) => {
                        Ok(vec![ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS)?])
                    }

                    // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
                    (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => Ok(ecx
                        .consider_builtin_dyn_upcast_candidates(
                            goal, a_data, a_region, b_data, b_region,
                        )),

                    // `T` -> `dyn Trait` unsizing.
                    (_, ty::Dynamic(b_region, b_data)) => Ok(vec![
                        ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data)?,
                    ]),

                    // `[T; N]` -> `[T]` unsizing
                    (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report at https://github.com/rust-lang/rust/issues with the two types from the panic.
  2. Disable `-Znext-solver`.
  3. Annotate the source type of the coercion so it isn't a bare inference variable (e.g. `let b: Box<dyn Trait> = Box::new(concrete_value());`).
  4. `rustup update nightly` and bisect.

Example fix

// before — source of unsizing left inferred
let b: Box<dyn Trait> = make_it(); // make_it returns an unknown type
// after — pin the source type
let b: Box<dyn Trait> = Box::new(make_it::<Concrete>());
Defensive patterns

Strategy: validation

Validate before calling

// The solver found an inference variable where it expected two concrete
// types to compare. This usually means a type is left unconstrained.
// Validate by adding explicit type annotations so no inference var remains.
trait Cmp<A> {}
fn compare<A, B>() where A: Cmp<B> {} // A, B unconstrained -> infer vars
// Fix: constrain them
fn compare_ok() { compare::<i32, i32>(); }

Type guard

// Force both sides of a trait goal to concrete types via turbofish or
// explicit annotations before the solver runs:
trait SameType {}
impl SameType for () {}
fn assert_same<T, U>() where T: SameType, U: SameType {}
// At call site:
// assert_same::<i32, i32>();
// Never call generic helpers with two unresolved inference vars.

Prevention

When it happens

Trigger: An `Unsize` goal where the source (`a_ty`, the type being unsized) is still an unbound inference variable reaches structural unsizing assembly under `-Znext-solver` — e.g. coercing an unknown generic to `dyn Trait` or to a slice.

Common situations: Nightly users hitting unsizing coercions (trait-object coercion, array→slice, struct unsizing) through generic/async code where the source type hasn't been inferred yet; common with `?Sized` generics, `Box<dyn ...>`, and after rustc updates to unsize candidate assembly.

Related errors


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