rust-lang/rust · critical

unexpected self ty `{:?}` when normalizing `<T as Pointee>::

Error message

unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`

What it means

consider_builtin_pointee_candidate (normalizes_to.rs:792) computes the concrete `<T as Pointee>::Metadata` type by matching on self_ty.kind(). Most arms produce a real metadata type (e.g. unit for Sized types, usize for slices/str). The final arm panics for Infer(TyVar|Fresh*), non-rigid Alias (an un-normalized projection), and Bound types. The invariant is that by the time a Pointee projection is evaluated, the self type must be concrete or at least a rigid alias / param / placeholder (which are handled earlier and return early); an unresolved inference variable, a still-normalizing alias, or a bound type should never reach this point.

Source

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

                ),
            },
            ty::Adt(_, _) => Ty::new_unit(cx),

            ty::Tuple(elements) => match elements.last() {
                None => Ty::new_unit(cx),
                Some(tail_ty) => {
                    Ty::new_projection(cx, ty::IsRigid::No, metadata_def_id, [tail_ty])
                }
            },

            ty::UnsafeBinder(_) => {
                // FIXME(unsafe_binder): Figure out how to handle pointee for unsafe binders.
                unimplemented!()
            }

            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
            | ty::Alias(ty::IsRigid::No, _)
            | ty::Bound(..) => panic!(
                "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
                goal.predicate.self_ty()
            ),
        };

        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
            ecx.instantiate_normalizes_to_term(goal, metadata_ty.into())?;
            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
        })
    }

    fn consider_builtin_future_candidate(
        ecx: &mut EvalCtxt<'_, D>,
        goal: Goal<I, Self>,
    ) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
        let self_ty = goal.predicate.self_ty();
        let ty::Coroutine(def_id, args) = self_ty.kind() else {
            return Err(NoSolution.into());

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Add a bound or annotation that makes the self type concrete before metadata is queried, e.g. constrain T: Sized (Metadata = ()) or specify the metadata type explicitly.
  2. Update to the latest nightly; goal-ordering bugs in the next solver's normalization pipeline are fixed frequently.
  3. Disable -Znext-solver to use the legacy solver's metadata handling.
  4. File an ICE with the self_ty value from the panic message and a minimal reproducer involving ptr::from_raw_parts / DynMetadata.

Example fix

// before: <T as Pointee>::Metadata queried with T unconstrained
fn meta<T>() {
    let _: <T as std::pointee::Pointee>::Metadata;
}

// after: constrain T so metadata is statically known
fn meta<T: Sized>() {
    let _: <T as std::pointee::Pointee>::Metadata; // resolves to ()
}
Defensive patterns

Strategy: validation

Validate before calling

// Pointee::Metadata must be normalized against a fully concrete/inferred Self.
// Materialize the type before projecting Metadata:
use core::ptr::Pointee;
type MetaOf<T> = <T as Pointee>::Metadata;       // OK when T is concrete
fn check_str() { let _: MetaOf<str> = (); }      // str -> DynMetadata

Type guard

// Only allow projection when Self is Sized or a known `dyn Trait`/slice:
trait KnownPointee: Pointee {}
impl<T: Sized> KnownPointee for T {}
impl KnownPointee for str {}
impl KnownPointee for [u8] {}
fn meta<T: KnownPointee>() -> <T as Pointee>::Metadata { unimplemented!() }

Prevention

When it happens

Trigger: Code that forces `<T as Pointee>::Metadata` while T is still an unconstrained inference variable, a non-rigid projection that has not finished normalizing, or a bound type — e.g. std::ptr::from_raw_parts or DynMetadata usage on a fully generic T with no Sized/known-metadata bound, under -Znext-solver where an earlier normalization step failed to run or was reordered.

Common situations: Writing generic pointer-metadata helpers over `T` without bounding T; a next-solver goal-ordering regression that skips normalization of a projection before querying its metadata; nightly-only pointer-metadata or unsized-locals experimentation.

Related errors


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