astral-sh/ruff · error
non-terminal BDD should have source_order
Error message
non-terminal BDD should have source_order
What it means
An `OwnedConstraintSet` pairs its BDD root node with a source-order tree. In this routine the set's constraints are transformed with a closure and the set is rebuilt; a non-terminal root must always carry `source_order`. The expect fires when the transformed set kept a non-terminal BDD but its `source_order` is None - a combine path that dropped the source order.
Source
Thrown at crates/ty_python_semantic/src/types/constraints.rs:1089
self.constraint_cache.extend(
compacted
.constraint_indices
.iter_ones()
.zip(compacted.constraints.iter().copied())
.map(|(old_index, constraint)| (constraint, ConstraintId::from_usize(old_index))),
);
self.node_cache.extend(
compacted
.node_indices
.iter_ones()
.zip(compacted.nodes.iter().copied())
.map(|(old_index, node)| (node, NodeId::from_usize(old_index))),
);
self.source_order_cache.extend(
compacted
.source_orders
.iter()
.copied()
.enumerate()
.map(|(index, source_order)| (source_order, SourceOrderId::from_usize(index))),
);
}
// This is a separate method from `ensure_overlay_identity_caches` because it requires a `db`.
fn ensure_overlay_typevar_identity_cache(&mut self, db: &'db dyn Db) {
let Some(compacted) = &self.compacted else {
return;
};
if !self.typevar_cache.is_empty() {
return;
}
self.typevar_cache.extend(
compacted
.typevars
.iter_enumerated()View on GitHub (pinned to 15f3fe6b15)
Solutions
- Audit every operation that constructs `OwnedConstraintSet` to thread `source_order` for non-terminal roots; only terminal roots may set it to None.
- Reproduce with the smallest inference case (few typevars, few constraints) and run the ty_python_semantic constraint tests.
- Use `debug_assert!` in constructors to catch None-source-order/non-terminal combinations at creation time.
- Report upstream with the reproducer if stock ty panics.
Defensive patterns
Strategy: try-catch
Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
owned_set.map(db, f)
}))
.unwrap_or_else(|_| OwnedConstraintSet::terminal()); // degrade to trivially-true set Prevention
- Contributors: never construct an OwnedConstraintSet with a non-terminal node and None source_order; assert it in the constructor.
- Keep ordering-sensitive mdtests in any PR touching constraint combination.
- Users: pin a known-good ty release for CI so a solver regression cannot block merges.
When it happens
Trigger: Calling this map/compact routine on an owned constraint set where the closure `f` returns a constraint set whose node is non-terminal while `source_order` is None, e.g. a quantifier/combination operation that threads the BDD but forgets the source-order sidecar.
Common situations: Modifying ty's constraint solver (new combine/quantify operations on `OwnedConstraintSet`); typically surfaces in inference mdtests with multiple typevars and ordering-sensitive fixtures.
Related errors
- node should be non-terminal
- storage-free owned constraint sets must have terminal roots
- every TDD constraint should have a source order
- non-terminal constraint set should have a source_order
- Expected `NamedTuple` definition r.h.s. to be a call express
AI-assisted analysis of astral-sh/ruff@15f3fe6b15 (2026-08-20).
Data as JSON: /api/errors/b914ef2ffbc11c3e.
Report an issue: GitHub.