{"id":"a5e9e007c8b4888e","repo":"rust-lang/rust","slug":"expected-to-be-able-to-unify-goal-projection-with","errorCode":null,"errorMessage":"expected to be able to unify goal projection with dyn's projection","messagePattern":"expected to be able to unify goal projection with dyn's projection","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs","lineNumber":1029,"sourceCode":"            .iter()\n            .filter(|source_projection| self.projection_may_match(**source_projection, alias_term));\n        let Some(replacement) = matching_projections.next() else {\n            // This shouldn't happen.\n            panic!(\"could not replace {alias_term:?} with term from from {:?}\", self.self_ty);\n        };\n        // FIXME: This *may* have issues with duplicated projections.\n        if matching_projections.next().is_some() {\n            // If there's more than one projection that we can unify here, then we\n            // need to stall until inference constrains things so that there's only\n            // one choice.\n            return Err(Ambiguous);\n        }\n\n        let replacement = self.ecx.instantiate_binder_with_infer(*replacement);\n        self.nested.extend(\n            self.ecx\n                .eq_and_get_goals(self.param_env, alias_term, replacement.projection_term)\n                .expect(\"expected to be able to unify goal projection with dyn's projection\"),\n        );\n\n        Ok(Some(replacement.term))\n    }\n}\n\n/// Marker for bailing with ambiguity.\npub(crate) struct Ambiguous;\n\nimpl<D, I> FallibleTypeFolder<I> for ReplaceProjectionWith<'_, '_, I, D>\nwhere\n    D: SolverDelegate<Interner = I>,\n    I: Interner,\n{\n    type Error = Ambiguous;\n\n    fn cx(&self) -> I {\n        self.ecx.cx()","sourceCodeStart":1011,"sourceCodeEnd":1047,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs#L1011-L1047","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: dyn with associated-type projection that next-solver cannot unify\nfn coerce<'a>(x: &'a dyn Trait<Assoc = u32>) -> &'a dyn Trait { x }\n\n// after: avoid projection replacement via explicit where-clause\nfn coerce<'a, T: ?Sized + Trait<Assoc = u32> + 'a>(x: &'a T) -> &'a dyn Trait where 'a: 'a { x }","handlingStrategy":"type-guard","validationCode":"// Before projecting an associated item through a value, ensure it isn't an opaque `dyn`.\nfn concrete_not_dyn<T: ?Sized>(_v: &T) where T: Sized {} // caller passes concrete types only","typeGuard":"// Guard: only allow projections on concrete types; reject `dyn Trait` at the call site.\nfn is_dyn_trait_object<T: ?Sized>() -> bool {\n    std::mem::size_of::<*mut T>() == std::mem::size_of::<usize>()\n        && !std::mem::size_of::<T>().is_power_of_two_on_concrete()\n}\n// Prefer an explicit associated-type bound so projection is decidable:\n// fn use_it<T: Trait<Assoc = Concrete>>(x: &T) { let _ = x.assoc(); }","tryCatchPattern":null,"preventionTips":["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."],"tags":["rustc-ice","next-solver","dyn","associated-types","nightly","compiler-internal"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}