{"id":"afeaaec559b5898d","repo":"rust-lang/rust","slug":"unexpected-infer-a-ty-b-ty","errorCode":null,"errorMessage":"unexpected infer {a_ty:?} {b_ty:?}","messagePattern":"unexpected infer (.+?) (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/trait_goals.rs","lineNumber":836,"sourceCode":"        goal: Goal<I, Self>,\n    ) -> Result<Vec<Candidate<I>>, RerunNonErased> {\n        if goal.predicate.polarity != ty::PredicatePolarity::Positive {\n            return Ok(vec![]);\n        }\n\n        let result = ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(\n            |ecx| -> Result<Vec<Candidate<I>>, NoSolutionOrRerunNonErased> {\n                let a_ty = goal.predicate.self_ty();\n                // We need to normalize the b_ty since it's matched structurally\n                // in the other functions below.\n                let b_ty = ecx.structurally_normalize_ty(\n                    goal.param_env,\n                    goal.predicate.trait_ref.args.type_at(1),\n                )?;\n\n                let goal = goal.with(ecx.cx(), (a_ty, b_ty));\n                match (a_ty.kind(), b_ty.kind()) {\n                    (ty::Infer(ty::TyVar(..)), ..) => panic!(\"unexpected infer {a_ty:?} {b_ty:?}\"),\n\n                    (_, ty::Infer(ty::TyVar(..))) => {\n                        Ok(vec![ecx.forced_ambiguity(MaybeInfo::AMBIGUOUS)?])\n                    }\n\n                    // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.\n                    (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => Ok(ecx\n                        .consider_builtin_dyn_upcast_candidates(\n                            goal, a_data, a_region, b_data, b_region,\n                        )),\n\n                    // `T` -> `dyn Trait` unsizing.\n                    (_, ty::Dynamic(b_region, b_data)) => Ok(vec![\n                        ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data)?,\n                    ]),\n\n                    // `[T; N]` -> `[T]` unsizing\n                    (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {","sourceCodeStart":818,"sourceCodeEnd":854,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs#L818-L854","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Report at https://github.com/rust-lang/rust/issues with the two types from the panic.","Disable `-Znext-solver`.","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());`).","`rustup update nightly` and bisect."],"exampleFix":"// before — source of unsizing left inferred\nlet b: Box<dyn Trait> = make_it(); // make_it returns an unknown type\n// after — pin the source type\nlet b: Box<dyn Trait> = Box::new(make_it::<Concrete>());","handlingStrategy":"validation","validationCode":"// The solver found an inference variable where it expected two concrete\n// types to compare. This usually means a type is left unconstrained.\n// Validate by adding explicit type annotations so no inference var remains.\ntrait Cmp<A> {}\nfn compare<A, B>() where A: Cmp<B> {} // A, B unconstrained -> infer vars\n// Fix: constrain them\nfn compare_ok() { compare::<i32, i32>(); }","typeGuard":"// Force both sides of a trait goal to concrete types via turbofish or\n// explicit annotations before the solver runs:\ntrait SameType {}\nimpl SameType for () {}\nfn assert_same<T, U>() where T: SameType, U: SameType {}\n// At call site:\n// assert_same::<i32, i32>();\n// Never call generic helpers with two unresolved inference vars.","tryCatchPattern":null,"preventionTips":["Add explicit type annotations (turbofish `::<T, U>`) at call sites so both type arguments are concrete.","Avoid leaving two-sided inference variables in trait-goal positions; the solver needs at least one concrete side.","Minimise the use of fully-generic two-sided bounds; specialise at least one side to a known type."],"tags":["rustc","trait-solver","next-solver","ice","unsize","coercion","inference"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}