rust-lang/rust · critical

reservation impl for trait with assoc item: {:?}

Error message

reservation impl for trait with assoc item: {:?}

What it means

An `unimplemented!` in `consider_impl_candidate` (projection of an associated item through an impl) when the matched impl has polarity `Reservation` (`#[rustc_reservation_impl]`). Reservation impls are reserved for traits that have NO associated items; the solver does not know how to project an associated type/const through one, so it explicitly bails. Encountering it means a reservation impl was placed on a trait that declares an associated item — a configuration the new solver refuses to handle.

Source

Thrown at compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs:276

    ) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
        let cx = ecx.cx();

        let alias_def_id = goal.predicate.alias.expect_projection_def_id();
        let goal_trait_ref = goal.predicate.alias.trait_ref(cx);
        let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
        if !DeepRejectCtxt::relate_rigid_infer(ecx.cx()).args_may_unify(
            goal.predicate.alias.trait_ref(cx).args,
            impl_trait_ref.skip_binder().args,
        ) {
            return Err(NoSolution.into());
        }

        // We have to ignore negative impls when projecting.
        let impl_polarity = cx.impl_polarity(impl_def_id);
        match impl_polarity {
            ty::ImplPolarity::Negative => return Err(NoSolution.into()),
            ty::ImplPolarity::Reservation => {
                unimplemented!("reservation impl for trait with assoc item: {:?}", goal)
            }
            ty::ImplPolarity::Positive => {}
        };

        ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
            let impl_args = ecx.fresh_args_for_item(impl_def_id.into());
            let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args).skip_norm_wip();

            ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;

            let where_clause_bounds = cx
                .clauses_of(impl_def_id.into())
                .iter_instantiated(cx, impl_args)
                .map(Unnormalized::skip_norm_wip)
                .map(|clause| goal.with(cx, clause));
            ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds)?;

            // Bail if the nested goals don't hold here. This is to avoid unnecessarily

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Remove the associated item from the trait, or remove the `#[rustc_reservation_impl]` attribute from the impl — the two are mutually exclusive for projection.
  2. If you are upstreaming a change to a std trait, consult the reservation-impl contract (the attribute exists to reserve a slot without providing the impl) before adding assoc items.
  3. Report as a rustc bug only if the trait genuinely has no associated items and the panic still fires (then it is a solver dispatch bug).

Example fix

// before — reservation impl on a trait WITH an associated item
#[rustc_reservation_impl]
impl Trait for () {}
trait Trait { type Item; }
// after — drop the reservation attribute, or drop the associated item
impl Trait for () { type Item = (); }
trait Trait { type Item; }
Defensive patterns

Strategy: validation

Validate before calling

// A reservation impl (#[rustc_reservation_impl]) is only legal on a
// marker/auto trait that has NO associated items. Verify before relying
// on one.
fn reservation_impl_legal(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
    let assoc = tcx.associated_item_def_ids(trait_def_id);
    assoc.is_empty()
}

Type guard

// True only when the trait is a bare marker with no associated items,
// i.e. safe to attach a reservation impl to.
fn is_marker_trait_without_items(tcx: TyCtxt<'_>, did: DefId) -> bool {
    tcx.associated_item_def_ids(did).is_empty() && tcx.is_auto_trait(did)
}

Try / catch

use std::panic;
match panic::catch_unwind(panic::AssertUnwindSafe(|| solver.normalizes_to(goal))) {
    Ok(v) => v,
    Err(_) => Err("reservation impl requires a trait with no associated items"),
}

Prevention

When it happens

Trigger: A type has an `#[rustc_reservation_impl] impl Trait for ..` annotation AND `Trait` declares an associated type or associated const, and a `NormalizesTo` goal for that associated item is evaluated (e.g. `<T as Trait>::Item` is normalized). Only reachable on nightly/with the next solver since reservation impls are std-internal.

Common situations: Editing `core`/`std`/`alloc` internals where `#[rustc_reservation_impl]` is used (e.g. reserved impls for `Future`, auto traits). Almost never seen in third-party crates; if you see it, you are likely using the attribute incorrectly or added an associated item to a trait that still carries a reservation impl.

Related errors


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