rust-lang/rust · critical

no such thing as an opaque const

Error message

no such thing as an opaque const

What it means

Opaque types (`impl Trait`, async-fn returns, RPIT, TAIT) are always types, never constants. In `normalize_opaque_type` (opaque_types.rs:23), `goal.predicate.term.as_type().expect("no such thing as an opaque const")` fires if the term on the RHS of the projection goal is a const term. An opaque-type projection goal with a const RHS is meaningless, so reaching the `expect` means the solver constructed a malformed opaque-type goal — a compiler bug.

Source

Thrown at compiler/rustc_next_trait_solver/src/solve/project_goals/opaque_types.rs:23

use rustc_type_ir::solve::{GoalSource, QueryResultOrRerunNonErased, RerunReason};
use rustc_type_ir::{self as ty, Interner, MayBeErased, TypingMode, fold_regions};

use crate::delegate::SolverDelegate;
use crate::solve::{Certainty, EvalCtxt, Goal};

impl<D, I> EvalCtxt<'_, D>
where
    D: SolverDelegate<Interner = I>,
    I: Interner,
{
    #[tracing::instrument(skip(self))]
    pub(super) fn normalize_opaque_type(
        &mut self,
        goal: Goal<I, ty::ProjectionPredicate<I>>,
    ) -> QueryResultOrRerunNonErased<I> {
        let cx = self.cx();
        let opaque_ty = goal.predicate.projection_term;
        let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
        let def_id = opaque_ty.expect_opaque_ty_def_id();

        match self.typing_mode() {
            TypingMode::Coherence => {
                // An impossible opaque type bound is the only way this goal will fail
                // e.g. assigning `impl Copy := NotCopy`
                self.add_item_bounds_for_hidden_type(
                    def_id,
                    opaque_ty.args,
                    goal.param_env,
                    expected,
                )?;
                // Trying to normalize an opaque type during coherence is always ambiguous.
                // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`.
                // This allows us to return the nested goals to the parent `AliasRelate` goal.
                // This can then allow nested goals to fail after we've constrained the `term`.
                self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous))?;
                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report at https://github.com/rust-lang/rust/issues with the panic `no such thing as an opaque const` and a minimal `impl Trait` / async repro.
  2. Move the opaque type out of const positions (don't use `impl Trait` as/for an associated const).
  3. Disable `-Znext-solver`.
  4. `rustup update nightly`.

Example fix

// before — opaque in a const-ish position
const C: impl Trait = async {}.await; // nonsensical, leaks const term
// after — opaque as a type only
async fn f() -> impl Trait { /* ... */ }
Defensive patterns

Strategy: type-guard

Validate before calling

// There is no such thing as an 'opaque const' — opaque types (impl Trait)
// are only valid in type position, not in const-value position.
// BAD:  const X: = impl Trait;  (nonsensical)
// GOOD: const X: i32 = 5;
//       type O = impl Trait;   (opaque in type position)
fn ensure_not_opaque_const<T>() -> bool { false }

Type guard

// Reject any construction that places `impl Trait` in a const/value slot.
// Only use `impl Trait` as a return type, argument type, or type alias:
trait Trait {}
fn returns_opaque() -> impl Trait { struct Z; impl Trait for Z {} Z }
// const OPAQUE: impl Trait = ...; // <- never valid, would panic solver

Prevention

When it happens

Trigger: An opaque-type projection goal is built whose `term` is a const (not a type), e.g. when an opaque appears in an associated-const position or const-eval context, under `-Znext-solver`.

Common situations: Nightly users putting `impl Trait` / async-fn opaque returns into associated-const or const-generic positions, or after a rustc update that mishandles opaque terms across the type/const boundary.

Related errors


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