astral-sh/ruff · error

typevar should be interned before ordering

Error message

typevar should be interned before ordering

What it means

Constraint-set storage interns every typevar through `intern_typevar` before it can be ordered; `typevar_id` only performs a lookup in `typevar_cache` keyed by typevar identity. The expect fires when ordering is requested for a `BoundTypeVarInstance` that was never interned in this builder - a typevar from another storage/overlay, or one added without going through the interning path.

Source

Thrown at crates/ty_python_semantic/src/types/constraints.rs:1398

                    support.insert(typevar);
                }
                walk_type_with_recursion_guard(db, ty, self, &self.recursion_guard);
            }
        }

        InternMentionedTypevars {
            env,
            storage: RefCell::new(self),
            support: RefCell::new(support),
            recursion_guard: TypeCollector::default(),
        }
        .visit_type(db, ty);
    }

    /// Interns all of the typevars mentioned in a constraint in a stable order.
    fn intern_constraint_typevars(
        &mut self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        constraint: Constraint<'db>,
    ) -> Support {
        let mut support = Support::default();
        support.insert(self.intern_typevar(db, constraint.typevar()));
        for bound in constraint.iter_stored_bounds() {
            self.intern_mentioned_typevars_in_type(db, env, bound.ty(), &mut support);
        }
        support
    }

    fn intern_constraint(
        &mut self,
        db: &'db dyn Db,
        env: &ProgramEnvironment<'db>,
        data: Constraint<'db>,
    ) -> ConstraintId {
        let support = self.intern_constraint_typevars(db, env, data);

View on GitHub (pinned to 15f3fe6b15)

Solutions

  1. Ensure every code path that can later order a typevar calls `intern_typevar` (constraints.rs:1261) when adding its constraints.
  2. If ordering foreign typevars is legitimate, make `typevar_id` intern on miss instead of expecting.
  3. Add a regression test that orders a typevar immediately after adding constraints and one after compaction.
  4. Report upstream with the reproducer if stock ty panics.

Example fix

// before
let id = self.typevar_id(db, typevar);

// after: intern first, ordering stays a pure lookup
let id = {
    self.storage.borrow_mut().intern_typevar(db, typevar);
    self.typevar_id(db, typevar)
};
Defensive patterns

Strategy: try-catch

Try / catch

let id = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    builder.typevar_id(db, typevar)
}))
.ok(); // callers must treat None as 'not yet known to this builder'

Prevention

When it happens

Trigger: Calling ordering logic on a typevar whose constraints were never loaded into this builder: mixing storages, using a typevar after compaction split the storage, or a new code path that creates constraints without interning the typevar first.

Common situations: Contributions adding constraint-producing operations that bypass `intern_typevar`; overlay/borrow issues where the cache is ensured (`ensure_overlay_identity_caches`) but never populated; ordering-sensitive mdtests failing after a solver refactor.

Related errors


AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20). Data as JSON: /api/errors/71bf16573b4039e3. Report an issue: GitHub.