mirror of https://github.com/astral-sh/ruff
codex attempt 1
This commit is contained in:
parent
8bc753b842
commit
8655598901
|
|
@ -43,8 +43,7 @@ def unbounded[T]():
|
||||||
# revealed: None
|
# revealed: None
|
||||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(bool, T, bool) & ConstraintSet.range(Never, T, str)))
|
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(bool, T, bool) & ConstraintSet.range(Never, T, str)))
|
||||||
|
|
||||||
# TODO: revealed: ty_extensions.Specialization[T@unbounded = int]
|
# revealed: ty_extensions.Specialization[T@unbounded = int]
|
||||||
# revealed: ty_extensions.Specialization[T@unbounded = bool]
|
|
||||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, int) | ConstraintSet.range(Never, T, bool)))
|
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, int) | ConstraintSet.range(Never, T, bool)))
|
||||||
# revealed: ty_extensions.Specialization[T@unbounded = Never]
|
# revealed: ty_extensions.Specialization[T@unbounded = Never]
|
||||||
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, int) | ConstraintSet.range(Never, T, str)))
|
reveal_type(generic_context(unbounded).specialize_constrained(ConstraintSet.range(Never, T, int) | ConstraintSet.range(Never, T, str)))
|
||||||
|
|
@ -311,19 +310,18 @@ def constrained_by_gradual_list[T: (list[Base], list[Any])]():
|
||||||
# Same tests as above, but with the typevar constraints in a different order, to make sure the
|
# Same tests as above, but with the typevar constraints in a different order, to make sure the
|
||||||
# results do not depend on our BDD variable ordering.
|
# results do not depend on our BDD variable ordering.
|
||||||
def constrained_by_gradual_list_reverse[T: (list[Any], list[Base])]():
|
def constrained_by_gradual_list_reverse[T: (list[Any], list[Base])]():
|
||||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = Top[list[Any]]]
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.always()))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.always()))
|
||||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[object]]
|
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[object]]
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[object])))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[object])))
|
||||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Any]]
|
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Any]]
|
||||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base] & list[Any]]
|
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Any])))
|
||||||
# revealed: None
|
# revealed: None
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.never()))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.never()))
|
||||||
|
|
||||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(Never, T, list[Base])))
|
||||||
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = list[Base]]
|
# revealed: ty_extensions.Specialization[T@constrained_by_gradual_list_reverse = Top[list[Any]]]
|
||||||
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
reveal_type(generic_context(constrained_by_gradual_list_reverse).specialize_constrained(ConstraintSet.range(list[Base], T, object)))
|
||||||
|
|
||||||
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
# TODO: revealed: ty_extensions.Specialization[T@constrained_by_gradual = list[Any]]
|
||||||
|
|
|
||||||
|
|
@ -552,6 +552,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
||||||
return Node::new_constraint(
|
return Node::new_constraint(
|
||||||
db,
|
db,
|
||||||
ConstrainedTypeVar::new(db, typevar, Type::Never, Type::object()),
|
ConstrainedTypeVar::new(db, typevar, Type::Never, Type::object()),
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
.negate(db);
|
.negate(db);
|
||||||
}
|
}
|
||||||
|
|
@ -603,6 +604,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
||||||
Type::TypeVar(bound),
|
Type::TypeVar(bound),
|
||||||
Type::TypeVar(bound),
|
Type::TypeVar(bound),
|
||||||
),
|
),
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -613,10 +615,12 @@ impl<'db> ConstrainedTypeVar<'db> {
|
||||||
let lower = Node::new_constraint(
|
let lower = Node::new_constraint(
|
||||||
db,
|
db,
|
||||||
ConstrainedTypeVar::new(db, lower, Type::Never, Type::TypeVar(typevar)),
|
ConstrainedTypeVar::new(db, lower, Type::Never, Type::TypeVar(typevar)),
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
let upper = Node::new_constraint(
|
let upper = Node::new_constraint(
|
||||||
db,
|
db,
|
||||||
ConstrainedTypeVar::new(db, upper, Type::TypeVar(typevar), Type::object()),
|
ConstrainedTypeVar::new(db, upper, Type::TypeVar(typevar), Type::object()),
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
lower.and(db, upper)
|
lower.and(db, upper)
|
||||||
}
|
}
|
||||||
|
|
@ -626,6 +630,7 @@ impl<'db> ConstrainedTypeVar<'db> {
|
||||||
let lower = Node::new_constraint(
|
let lower = Node::new_constraint(
|
||||||
db,
|
db,
|
||||||
ConstrainedTypeVar::new(db, lower, Type::Never, Type::TypeVar(typevar)),
|
ConstrainedTypeVar::new(db, lower, Type::Never, Type::TypeVar(typevar)),
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
let upper = if upper.is_object() {
|
let upper = if upper.is_object() {
|
||||||
Node::AlwaysTrue
|
Node::AlwaysTrue
|
||||||
|
|
@ -645,11 +650,12 @@ impl<'db> ConstrainedTypeVar<'db> {
|
||||||
let upper = Node::new_constraint(
|
let upper = Node::new_constraint(
|
||||||
db,
|
db,
|
||||||
ConstrainedTypeVar::new(db, upper, Type::TypeVar(typevar), Type::object()),
|
ConstrainedTypeVar::new(db, upper, Type::TypeVar(typevar), Type::object()),
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
lower.and(db, upper)
|
lower.and(db, upper)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Node::new_constraint(db, ConstrainedTypeVar::new(db, typevar, lower, upper)),
|
_ => Node::new_constraint(db, ConstrainedTypeVar::new(db, typevar, lower, upper), 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -830,6 +836,7 @@ impl<'db> Node<'db> {
|
||||||
constraint: ConstrainedTypeVar<'db>,
|
constraint: ConstrainedTypeVar<'db>,
|
||||||
if_true: Node<'db>,
|
if_true: Node<'db>,
|
||||||
if_false: Node<'db>,
|
if_false: Node<'db>,
|
||||||
|
source_order: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!((if_true.root_constraint(db)).is_none_or(|root_constraint| {
|
debug_assert!((if_true.root_constraint(db)).is_none_or(|root_constraint| {
|
||||||
root_constraint.ordering(db) > constraint.ordering(db)
|
root_constraint.ordering(db) > constraint.ordering(db)
|
||||||
|
|
@ -842,36 +849,60 @@ impl<'db> Node<'db> {
|
||||||
if if_true == Node::AlwaysFalse && if_false == Node::AlwaysFalse {
|
if if_true == Node::AlwaysFalse && if_false == Node::AlwaysFalse {
|
||||||
return Node::AlwaysFalse;
|
return Node::AlwaysFalse;
|
||||||
}
|
}
|
||||||
Self::Interior(InteriorNode::new(db, constraint, if_true, if_false))
|
let max_source_order = source_order
|
||||||
|
.max(if_true.max_source_order(db))
|
||||||
|
.max(if_false.max_source_order(db));
|
||||||
|
Self::Interior(InteriorNode::new(
|
||||||
|
db,
|
||||||
|
constraint,
|
||||||
|
if_true,
|
||||||
|
if_false,
|
||||||
|
source_order,
|
||||||
|
max_source_order,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new BDD node for an individual constraint. (The BDD will evaluate to `true` when
|
/// Creates a new BDD node for an individual constraint. (The BDD will evaluate to `true` when
|
||||||
/// the constraint holds, and to `false` when it does not.)
|
/// the constraint holds, and to `false` when it does not.)
|
||||||
fn new_constraint(db: &'db dyn Db, constraint: ConstrainedTypeVar<'db>) -> Self {
|
fn new_constraint(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
constraint: ConstrainedTypeVar<'db>,
|
||||||
|
source_order: usize,
|
||||||
|
) -> Self {
|
||||||
Self::Interior(InteriorNode::new(
|
Self::Interior(InteriorNode::new(
|
||||||
db,
|
db,
|
||||||
constraint,
|
constraint,
|
||||||
Node::AlwaysTrue,
|
Node::AlwaysTrue,
|
||||||
Node::AlwaysFalse,
|
Node::AlwaysFalse,
|
||||||
|
source_order,
|
||||||
|
source_order,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new BDD node for a positive or negative individual constraint. (For a positive
|
/// Creates a new BDD node for a positive or negative individual constraint. (For a positive
|
||||||
/// constraint, this returns the same BDD node as [`new_constraint`][Self::new_constraint]. For
|
/// constraint, this returns the same BDD node as [`new_constraint`][Self::new_constraint]. For
|
||||||
/// a negative constraint, it returns the negation of that BDD node.)
|
/// a negative constraint, it returns the negation of that BDD node.)
|
||||||
fn new_satisfied_constraint(db: &'db dyn Db, constraint: ConstraintAssignment<'db>) -> Self {
|
fn new_satisfied_constraint(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
constraint: ConstraintAssignment<'db>,
|
||||||
|
source_order: usize,
|
||||||
|
) -> Self {
|
||||||
match constraint {
|
match constraint {
|
||||||
ConstraintAssignment::Positive(constraint) => Self::Interior(InteriorNode::new(
|
ConstraintAssignment::Positive(constraint) => Self::Interior(InteriorNode::new(
|
||||||
db,
|
db,
|
||||||
constraint,
|
constraint,
|
||||||
Node::AlwaysTrue,
|
Node::AlwaysTrue,
|
||||||
Node::AlwaysFalse,
|
Node::AlwaysFalse,
|
||||||
|
source_order,
|
||||||
|
source_order,
|
||||||
)),
|
)),
|
||||||
ConstraintAssignment::Negative(constraint) => Self::Interior(InteriorNode::new(
|
ConstraintAssignment::Negative(constraint) => Self::Interior(InteriorNode::new(
|
||||||
db,
|
db,
|
||||||
constraint,
|
constraint,
|
||||||
Node::AlwaysFalse,
|
Node::AlwaysFalse,
|
||||||
Node::AlwaysTrue,
|
Node::AlwaysTrue,
|
||||||
|
source_order,
|
||||||
|
source_order,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -885,6 +916,56 @@ impl<'db> Node<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn max_source_order(self, db: &'db dyn Db) -> usize {
|
||||||
|
match self {
|
||||||
|
Node::Interior(interior) => interior.max_source_order(db),
|
||||||
|
Node::AlwaysTrue | Node::AlwaysFalse => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_source_order_offset(self, db: &'db dyn Db, offset: usize) -> Self {
|
||||||
|
if offset == 0 {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Node::AlwaysTrue => Node::AlwaysTrue,
|
||||||
|
Node::AlwaysFalse => Node::AlwaysFalse,
|
||||||
|
Node::Interior(interior) => Node::new(
|
||||||
|
db,
|
||||||
|
interior.constraint(db),
|
||||||
|
interior.if_true(db).with_source_order_offset(db, offset),
|
||||||
|
interior.if_false(db).with_source_order_offset(db, offset),
|
||||||
|
interior.source_order(db) + offset,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_order_for(self, db: &'db dyn Db, constraint: ConstrainedTypeVar<'db>) -> usize {
|
||||||
|
fn walk<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
node: Node<'db>,
|
||||||
|
constraint: ConstrainedTypeVar<'db>,
|
||||||
|
best: Option<usize>,
|
||||||
|
) -> Option<usize> {
|
||||||
|
match node {
|
||||||
|
Node::AlwaysTrue | Node::AlwaysFalse => best,
|
||||||
|
Node::Interior(interior) => {
|
||||||
|
let best = if interior.constraint(db) == constraint {
|
||||||
|
Some(best.map_or(interior.source_order(db), |b| {
|
||||||
|
b.min(interior.source_order(db))
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
best
|
||||||
|
};
|
||||||
|
let best = walk(db, interior.if_true(db), constraint, best);
|
||||||
|
walk(db, interior.if_false(db), constraint, best)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(db, self, constraint, None).unwrap_or(1)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether this BDD represent the constant function `true`.
|
/// Returns whether this BDD represent the constant function `true`.
|
||||||
fn is_always_satisfied(self, db: &'db dyn Db) -> bool {
|
fn is_always_satisfied(self, db: &'db dyn Db) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -992,41 +1073,33 @@ impl<'db> Node<'db> {
|
||||||
|
|
||||||
/// Returns the `or` or union of two BDDs.
|
/// Returns the `or` or union of two BDDs.
|
||||||
fn or(self, db: &'db dyn Db, other: Self) -> Self {
|
fn or(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
match (self, other) {
|
let rhs_offset = self.max_source_order(db);
|
||||||
(Node::AlwaysTrue, Node::AlwaysTrue) => Node::AlwaysTrue,
|
self.or_with_rhs_offset(db, other, rhs_offset)
|
||||||
(Node::AlwaysFalse, other) | (other, Node::AlwaysFalse) => other,
|
|
||||||
(Node::AlwaysTrue, Node::Interior(interior))
|
|
||||||
| (Node::Interior(interior), Node::AlwaysTrue) => Node::new(
|
|
||||||
db,
|
|
||||||
interior.constraint(db),
|
|
||||||
Node::AlwaysTrue,
|
|
||||||
Node::AlwaysTrue,
|
|
||||||
),
|
|
||||||
(Node::Interior(a), Node::Interior(b)) => {
|
|
||||||
// OR is commutative, which lets us halve the cache requirements
|
|
||||||
let (a, b) = if b.0 < a.0 { (b, a) } else { (a, b) };
|
|
||||||
a.or(db, b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn or_with_rhs_offset(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Self {
|
||||||
|
match (self, other) {
|
||||||
|
(Node::AlwaysTrue, _) => Node::AlwaysTrue,
|
||||||
|
(_, Node::AlwaysTrue) => Node::AlwaysTrue,
|
||||||
|
(Node::AlwaysFalse, rhs) => rhs.with_source_order_offset(db, rhs_offset),
|
||||||
|
(lhs, Node::AlwaysFalse) => lhs,
|
||||||
|
(Node::Interior(lhs), Node::Interior(rhs)) => lhs.or(db, rhs, rhs_offset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `and` or intersection of two BDDs.
|
/// Returns the `and` or intersection of two BDDs.
|
||||||
fn and(self, db: &'db dyn Db, other: Self) -> Self {
|
fn and(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
match (self, other) {
|
let rhs_offset = self.max_source_order(db);
|
||||||
(Node::AlwaysFalse, Node::AlwaysFalse) => Node::AlwaysFalse,
|
self.and_with_rhs_offset(db, other, rhs_offset)
|
||||||
(Node::AlwaysTrue, other) | (other, Node::AlwaysTrue) => other,
|
|
||||||
(Node::AlwaysFalse, Node::Interior(interior))
|
|
||||||
| (Node::Interior(interior), Node::AlwaysFalse) => Node::new(
|
|
||||||
db,
|
|
||||||
interior.constraint(db),
|
|
||||||
Node::AlwaysFalse,
|
|
||||||
Node::AlwaysFalse,
|
|
||||||
),
|
|
||||||
(Node::Interior(a), Node::Interior(b)) => {
|
|
||||||
// AND is commutative, which lets us halve the cache requirements
|
|
||||||
let (a, b) = if b.0 < a.0 { (b, a) } else { (a, b) };
|
|
||||||
a.and(db, b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn and_with_rhs_offset(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Self {
|
||||||
|
match (self, other) {
|
||||||
|
(Node::AlwaysFalse, _) => Node::AlwaysFalse,
|
||||||
|
(_, Node::AlwaysFalse) => Node::AlwaysFalse,
|
||||||
|
(Node::AlwaysTrue, rhs) => rhs.with_source_order_offset(db, rhs_offset),
|
||||||
|
(lhs, Node::AlwaysTrue) => lhs,
|
||||||
|
(Node::Interior(lhs), Node::Interior(rhs)) => lhs.and(db, rhs, rhs_offset),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1038,6 +1111,11 @@ impl<'db> Node<'db> {
|
||||||
/// Returns a new BDD that evaluates to `true` when both input BDDs evaluate to the same
|
/// Returns a new BDD that evaluates to `true` when both input BDDs evaluate to the same
|
||||||
/// result.
|
/// result.
|
||||||
fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
fn iff(self, db: &'db dyn Db, other: Self) -> Self {
|
||||||
|
let rhs_offset = self.max_source_order(db);
|
||||||
|
self.iff_with_rhs_offset(db, other, rhs_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iff_with_rhs_offset(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Self {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Node::AlwaysFalse, Node::AlwaysFalse) | (Node::AlwaysTrue, Node::AlwaysTrue) => {
|
(Node::AlwaysFalse, Node::AlwaysFalse) | (Node::AlwaysTrue, Node::AlwaysTrue) => {
|
||||||
Node::AlwaysTrue
|
Node::AlwaysTrue
|
||||||
|
|
@ -1048,20 +1126,22 @@ impl<'db> Node<'db> {
|
||||||
(Node::AlwaysTrue | Node::AlwaysFalse, Node::Interior(interior)) => Node::new(
|
(Node::AlwaysTrue | Node::AlwaysFalse, Node::Interior(interior)) => Node::new(
|
||||||
db,
|
db,
|
||||||
interior.constraint(db),
|
interior.constraint(db),
|
||||||
self.iff(db, interior.if_true(db)),
|
self.iff_with_rhs_offset(db, interior.if_true(db), rhs_offset),
|
||||||
self.iff(db, interior.if_false(db)),
|
self.iff_with_rhs_offset(db, interior.if_false(db), rhs_offset),
|
||||||
|
interior.source_order(db) + rhs_offset,
|
||||||
),
|
),
|
||||||
(Node::Interior(interior), Node::AlwaysTrue | Node::AlwaysFalse) => Node::new(
|
(Node::Interior(interior), Node::AlwaysTrue | Node::AlwaysFalse) => Node::new(
|
||||||
db,
|
db,
|
||||||
interior.constraint(db),
|
interior.constraint(db),
|
||||||
interior.if_true(db).iff(db, other),
|
interior
|
||||||
interior.if_false(db).iff(db, other),
|
.if_true(db)
|
||||||
|
.iff_with_rhs_offset(db, other, rhs_offset),
|
||||||
|
interior
|
||||||
|
.if_false(db)
|
||||||
|
.iff_with_rhs_offset(db, other, rhs_offset),
|
||||||
|
interior.source_order(db),
|
||||||
),
|
),
|
||||||
(Node::Interior(a), Node::Interior(b)) => {
|
(Node::Interior(a), Node::Interior(b)) => a.iff(db, b, rhs_offset),
|
||||||
// IFF is commutative, which lets us halve the cache requirements
|
|
||||||
let (a, b) = if b.0 < a.0 { (b, a) } else { (a, b) };
|
|
||||||
a.iff(db, b)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1205,11 +1285,14 @@ impl<'db> Node<'db> {
|
||||||
should_remove: &mut dyn FnMut(ConstrainedTypeVar<'db>) -> bool,
|
should_remove: &mut dyn FnMut(ConstrainedTypeVar<'db>) -> bool,
|
||||||
map: &SequentMap<'db>,
|
map: &SequentMap<'db>,
|
||||||
path: &mut PathAssignments<'db>,
|
path: &mut PathAssignments<'db>,
|
||||||
|
max_source_order: &mut usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Node::AlwaysTrue => Node::AlwaysTrue,
|
Node::AlwaysTrue => Node::AlwaysTrue,
|
||||||
Node::AlwaysFalse => Node::AlwaysFalse,
|
Node::AlwaysFalse => Node::AlwaysFalse,
|
||||||
Node::Interior(interior) => interior.abstract_one_inner(db, should_remove, map, path),
|
Node::Interior(interior) => {
|
||||||
|
interior.abstract_one_inner(db, should_remove, map, path, max_source_order)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1377,8 +1460,13 @@ impl<'db> Node<'db> {
|
||||||
// false
|
// false
|
||||||
//
|
//
|
||||||
// (Note that the `else` branch shouldn't be reachable, but we have to provide something!)
|
// (Note that the `else` branch shouldn't be reachable, but we have to provide something!)
|
||||||
let left_node = Node::new_satisfied_constraint(db, left);
|
let left_node =
|
||||||
let right_node = Node::new_satisfied_constraint(db, right);
|
Node::new_satisfied_constraint(db, left, self.source_order_for(db, left.constraint()));
|
||||||
|
let right_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
right,
|
||||||
|
self.source_order_for(db, right.constraint()),
|
||||||
|
);
|
||||||
let right_result = right_node.ite(db, Node::AlwaysFalse, when_left_but_not_right);
|
let right_result = right_node.ite(db, Node::AlwaysFalse, when_left_but_not_right);
|
||||||
let left_result = left_node.ite(db, right_result, when_not_left);
|
let left_result = left_node.ite(db, right_result, when_not_left);
|
||||||
let result = replacement.ite(db, when_left_and_right, left_result);
|
let result = replacement.ite(db, when_left_and_right, left_result);
|
||||||
|
|
@ -1441,8 +1529,13 @@ impl<'db> Node<'db> {
|
||||||
// Lastly, verify that the result is consistent with the input. (It must produce the same
|
// Lastly, verify that the result is consistent with the input. (It must produce the same
|
||||||
// results when `left ∨ right`.) If it doesn't, the substitution isn't valid, and we should
|
// results when `left ∨ right`.) If it doesn't, the substitution isn't valid, and we should
|
||||||
// return the original BDD unmodified.
|
// return the original BDD unmodified.
|
||||||
let left_node = Node::new_satisfied_constraint(db, left);
|
let left_node =
|
||||||
let right_node = Node::new_satisfied_constraint(db, right);
|
Node::new_satisfied_constraint(db, left, self.source_order_for(db, left.constraint()));
|
||||||
|
let right_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
right,
|
||||||
|
self.source_order_for(db, right.constraint()),
|
||||||
|
);
|
||||||
let validity = replacement.iff(db, left_node.or(db, right_node));
|
let validity = replacement.iff(db, left_node.or(db, right_node));
|
||||||
let constrained_original = self.and(db, validity);
|
let constrained_original = self.and(db, validity);
|
||||||
let constrained_replacement = result.and(db, validity);
|
let constrained_replacement = result.and(db, validity);
|
||||||
|
|
@ -1632,6 +1725,8 @@ struct InteriorNode<'db> {
|
||||||
constraint: ConstrainedTypeVar<'db>,
|
constraint: ConstrainedTypeVar<'db>,
|
||||||
if_true: Node<'db>,
|
if_true: Node<'db>,
|
||||||
if_false: Node<'db>,
|
if_false: Node<'db>,
|
||||||
|
source_order: usize,
|
||||||
|
max_source_order: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
|
|
@ -1646,83 +1741,105 @@ impl<'db> InteriorNode<'db> {
|
||||||
self.constraint(db),
|
self.constraint(db),
|
||||||
self.if_true(db).negate(db),
|
self.if_true(db).negate(db),
|
||||||
self.if_false(db).negate(db),
|
self.if_false(db).negate(db),
|
||||||
|
self.source_order(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
||||||
fn or(self, db: &'db dyn Db, other: Self) -> Node<'db> {
|
fn or(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Node<'db> {
|
||||||
let self_constraint = self.constraint(db);
|
let self_constraint = self.constraint(db);
|
||||||
let other_constraint = other.constraint(db);
|
let other_constraint = other.constraint(db);
|
||||||
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
||||||
Ordering::Equal => Node::new(
|
Ordering::Equal => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).or(db, other.if_true(db)),
|
self.if_true(db)
|
||||||
self.if_false(db).or(db, other.if_false(db)),
|
.or_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.or_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Less => Node::new(
|
Ordering::Less => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).or(db, Node::Interior(other)),
|
self.if_true(db)
|
||||||
self.if_false(db).or(db, Node::Interior(other)),
|
.or_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.or_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Greater => Node::new(
|
Ordering::Greater => Node::new(
|
||||||
db,
|
db,
|
||||||
other_constraint,
|
other_constraint,
|
||||||
Node::Interior(self).or(db, other.if_true(db)),
|
Node::Interior(self).or_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
Node::Interior(self).or(db, other.if_false(db)),
|
Node::Interior(self).or_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
other.source_order(db) + rhs_offset,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
||||||
fn and(self, db: &'db dyn Db, other: Self) -> Node<'db> {
|
fn and(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Node<'db> {
|
||||||
let self_constraint = self.constraint(db);
|
let self_constraint = self.constraint(db);
|
||||||
let other_constraint = other.constraint(db);
|
let other_constraint = other.constraint(db);
|
||||||
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
||||||
Ordering::Equal => Node::new(
|
Ordering::Equal => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).and(db, other.if_true(db)),
|
self.if_true(db)
|
||||||
self.if_false(db).and(db, other.if_false(db)),
|
.and_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.and_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Less => Node::new(
|
Ordering::Less => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).and(db, Node::Interior(other)),
|
self.if_true(db)
|
||||||
self.if_false(db).and(db, Node::Interior(other)),
|
.and_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.and_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Greater => Node::new(
|
Ordering::Greater => Node::new(
|
||||||
db,
|
db,
|
||||||
other_constraint,
|
other_constraint,
|
||||||
Node::Interior(self).and(db, other.if_true(db)),
|
Node::Interior(self).and_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
Node::Interior(self).and(db, other.if_false(db)),
|
Node::Interior(self).and_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
other.source_order(db) + rhs_offset,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
|
||||||
fn iff(self, db: &'db dyn Db, other: Self) -> Node<'db> {
|
fn iff(self, db: &'db dyn Db, other: Self, rhs_offset: usize) -> Node<'db> {
|
||||||
let self_constraint = self.constraint(db);
|
let self_constraint = self.constraint(db);
|
||||||
let other_constraint = other.constraint(db);
|
let other_constraint = other.constraint(db);
|
||||||
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
match (self_constraint.ordering(db)).cmp(&other_constraint.ordering(db)) {
|
||||||
Ordering::Equal => Node::new(
|
Ordering::Equal => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).iff(db, other.if_true(db)),
|
self.if_true(db)
|
||||||
self.if_false(db).iff(db, other.if_false(db)),
|
.iff_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.iff_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Less => Node::new(
|
Ordering::Less => Node::new(
|
||||||
db,
|
db,
|
||||||
self_constraint,
|
self_constraint,
|
||||||
self.if_true(db).iff(db, Node::Interior(other)),
|
self.if_true(db)
|
||||||
self.if_false(db).iff(db, Node::Interior(other)),
|
.iff_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.if_false(db)
|
||||||
|
.iff_with_rhs_offset(db, Node::Interior(other), rhs_offset),
|
||||||
|
self.source_order(db),
|
||||||
),
|
),
|
||||||
Ordering::Greater => Node::new(
|
Ordering::Greater => Node::new(
|
||||||
db,
|
db,
|
||||||
other_constraint,
|
other_constraint,
|
||||||
Node::Interior(self).iff(db, other.if_true(db)),
|
Node::Interior(self).iff_with_rhs_offset(db, other.if_true(db), rhs_offset),
|
||||||
Node::Interior(self).iff(db, other.if_false(db)),
|
Node::Interior(self).iff_with_rhs_offset(db, other.if_false(db), rhs_offset),
|
||||||
|
other.source_order(db) + rhs_offset,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1731,6 +1848,7 @@ impl<'db> InteriorNode<'db> {
|
||||||
fn exists_one(self, db: &'db dyn Db, bound_typevar: BoundTypeVarIdentity<'db>) -> Node<'db> {
|
fn exists_one(self, db: &'db dyn Db, bound_typevar: BoundTypeVarIdentity<'db>) -> Node<'db> {
|
||||||
let map = self.sequent_map(db);
|
let map = self.sequent_map(db);
|
||||||
let mut path = PathAssignments::default();
|
let mut path = PathAssignments::default();
|
||||||
|
let mut max_source_order = self.max_source_order(db);
|
||||||
self.abstract_one_inner(
|
self.abstract_one_inner(
|
||||||
db,
|
db,
|
||||||
// Remove any node that constrains `bound_typevar`, or that has a lower/upper bound of
|
// Remove any node that constrains `bound_typevar`, or that has a lower/upper bound of
|
||||||
|
|
@ -1753,6 +1871,7 @@ impl<'db> InteriorNode<'db> {
|
||||||
},
|
},
|
||||||
map,
|
map,
|
||||||
&mut path,
|
&mut path,
|
||||||
|
&mut max_source_order,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1760,6 +1879,7 @@ impl<'db> InteriorNode<'db> {
|
||||||
fn retain_one(self, db: &'db dyn Db, bound_typevar: BoundTypeVarIdentity<'db>) -> Node<'db> {
|
fn retain_one(self, db: &'db dyn Db, bound_typevar: BoundTypeVarIdentity<'db>) -> Node<'db> {
|
||||||
let map = self.sequent_map(db);
|
let map = self.sequent_map(db);
|
||||||
let mut path = PathAssignments::default();
|
let mut path = PathAssignments::default();
|
||||||
|
let mut max_source_order = self.max_source_order(db);
|
||||||
self.abstract_one_inner(
|
self.abstract_one_inner(
|
||||||
db,
|
db,
|
||||||
// Remove any node that constrains some other typevar than `bound_typevar`, and any
|
// Remove any node that constrains some other typevar than `bound_typevar`, and any
|
||||||
|
|
@ -1779,6 +1899,7 @@ impl<'db> InteriorNode<'db> {
|
||||||
},
|
},
|
||||||
map,
|
map,
|
||||||
&mut path,
|
&mut path,
|
||||||
|
&mut max_source_order,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1788,7 +1909,9 @@ impl<'db> InteriorNode<'db> {
|
||||||
should_remove: &mut dyn FnMut(ConstrainedTypeVar<'db>) -> bool,
|
should_remove: &mut dyn FnMut(ConstrainedTypeVar<'db>) -> bool,
|
||||||
map: &SequentMap<'db>,
|
map: &SequentMap<'db>,
|
||||||
path: &mut PathAssignments<'db>,
|
path: &mut PathAssignments<'db>,
|
||||||
|
max_source_order: &mut usize,
|
||||||
) -> Node<'db> {
|
) -> Node<'db> {
|
||||||
|
*max_source_order = (*max_source_order).max(self.max_source_order(db));
|
||||||
let self_constraint = self.constraint(db);
|
let self_constraint = self.constraint(db);
|
||||||
if should_remove(self_constraint) {
|
if should_remove(self_constraint) {
|
||||||
// If we should remove constraints involving this typevar, then we replace this node
|
// If we should remove constraints involving this typevar, then we replace this node
|
||||||
|
|
@ -1800,9 +1923,13 @@ impl<'db> InteriorNode<'db> {
|
||||||
// corresponding branch.
|
// corresponding branch.
|
||||||
let if_true = path
|
let if_true = path
|
||||||
.walk_edge(db, map, self_constraint.when_true(), |path, new_range| {
|
.walk_edge(db, map, self_constraint.when_true(), |path, new_range| {
|
||||||
let branch = self
|
let branch = self.if_true(db).abstract_one_inner(
|
||||||
.if_true(db)
|
db,
|
||||||
.abstract_one_inner(db, should_remove, map, path);
|
should_remove,
|
||||||
|
map,
|
||||||
|
path,
|
||||||
|
max_source_order,
|
||||||
|
);
|
||||||
path.assignments[new_range]
|
path.assignments[new_range]
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|assignment| {
|
.filter(|assignment| {
|
||||||
|
|
@ -1811,15 +1938,23 @@ impl<'db> InteriorNode<'db> {
|
||||||
!should_remove(assignment.constraint())
|
!should_remove(assignment.constraint())
|
||||||
})
|
})
|
||||||
.fold(branch, |branch, assignment| {
|
.fold(branch, |branch, assignment| {
|
||||||
branch.and(db, Node::new_satisfied_constraint(db, *assignment))
|
*max_source_order += 1;
|
||||||
|
branch.and(
|
||||||
|
db,
|
||||||
|
Node::new_satisfied_constraint(db, *assignment, *max_source_order),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::AlwaysFalse);
|
.unwrap_or(Node::AlwaysFalse);
|
||||||
let if_false = path
|
let if_false = path
|
||||||
.walk_edge(db, map, self_constraint.when_false(), |path, new_range| {
|
.walk_edge(db, map, self_constraint.when_false(), |path, new_range| {
|
||||||
let branch = self
|
let branch = self.if_false(db).abstract_one_inner(
|
||||||
.if_false(db)
|
db,
|
||||||
.abstract_one_inner(db, should_remove, map, path);
|
should_remove,
|
||||||
|
map,
|
||||||
|
path,
|
||||||
|
max_source_order,
|
||||||
|
);
|
||||||
path.assignments[new_range]
|
path.assignments[new_range]
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|assignment| {
|
.filter(|assignment| {
|
||||||
|
|
@ -1828,7 +1963,11 @@ impl<'db> InteriorNode<'db> {
|
||||||
!should_remove(assignment.constraint())
|
!should_remove(assignment.constraint())
|
||||||
})
|
})
|
||||||
.fold(branch, |branch, assignment| {
|
.fold(branch, |branch, assignment| {
|
||||||
branch.and(db, Node::new_satisfied_constraint(db, *assignment))
|
*max_source_order += 1;
|
||||||
|
branch.and(
|
||||||
|
db,
|
||||||
|
Node::new_satisfied_constraint(db, *assignment, *max_source_order),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::AlwaysFalse);
|
.unwrap_or(Node::AlwaysFalse);
|
||||||
|
|
@ -1837,20 +1976,31 @@ impl<'db> InteriorNode<'db> {
|
||||||
// Otherwise, we abstract the if_false/if_true edges recursively.
|
// Otherwise, we abstract the if_false/if_true edges recursively.
|
||||||
let if_true = path
|
let if_true = path
|
||||||
.walk_edge(db, map, self_constraint.when_true(), |path, _| {
|
.walk_edge(db, map, self_constraint.when_true(), |path, _| {
|
||||||
self.if_true(db)
|
self.if_true(db).abstract_one_inner(
|
||||||
.abstract_one_inner(db, should_remove, map, path)
|
db,
|
||||||
|
should_remove,
|
||||||
|
map,
|
||||||
|
path,
|
||||||
|
max_source_order,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::AlwaysFalse);
|
.unwrap_or(Node::AlwaysFalse);
|
||||||
let if_false = path
|
let if_false = path
|
||||||
.walk_edge(db, map, self_constraint.when_false(), |path, _| {
|
.walk_edge(db, map, self_constraint.when_false(), |path, _| {
|
||||||
self.if_false(db)
|
self.if_false(db).abstract_one_inner(
|
||||||
.abstract_one_inner(db, should_remove, map, path)
|
db,
|
||||||
|
should_remove,
|
||||||
|
map,
|
||||||
|
path,
|
||||||
|
max_source_order,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.unwrap_or(Node::AlwaysFalse);
|
.unwrap_or(Node::AlwaysFalse);
|
||||||
// NB: We cannot use `Node::new` here, because the recursive calls might introduce new
|
// NB: We cannot use `Node::new` here, because the recursive calls might introduce new
|
||||||
// derived constraints into the result, and those constraints might appear before this
|
// derived constraints into the result, and those constraints might appear before this
|
||||||
// one in the BDD ordering.
|
// one in the BDD ordering.
|
||||||
Node::new_constraint(db, self_constraint).ite(db, if_true, if_false)
|
Node::new_constraint(db, self_constraint, self.source_order(db))
|
||||||
|
.ite(db, if_true, if_false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1878,7 +2028,13 @@ impl<'db> InteriorNode<'db> {
|
||||||
let (if_true, found_in_true) = self.if_true(db).restrict_one(db, assignment);
|
let (if_true, found_in_true) = self.if_true(db).restrict_one(db, assignment);
|
||||||
let (if_false, found_in_false) = self.if_false(db).restrict_one(db, assignment);
|
let (if_false, found_in_false) = self.if_false(db).restrict_one(db, assignment);
|
||||||
(
|
(
|
||||||
Node::new(db, self_constraint, if_true, if_false),
|
Node::new(
|
||||||
|
db,
|
||||||
|
self_constraint,
|
||||||
|
if_true,
|
||||||
|
if_false,
|
||||||
|
self.source_order(db),
|
||||||
|
),
|
||||||
found_in_true || found_in_false,
|
found_in_true || found_in_false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -2003,11 +2159,18 @@ impl<'db> InteriorNode<'db> {
|
||||||
if seen_constraints.contains(&new_constraint) {
|
if seen_constraints.contains(&new_constraint) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let new_node = Node::new_constraint(db, new_constraint);
|
let derived_source_order = simplified.max_source_order(db) + 1;
|
||||||
let positive_left_node =
|
let new_node = Node::new_constraint(db, new_constraint, derived_source_order);
|
||||||
Node::new_satisfied_constraint(db, left_constraint.when_true());
|
let positive_left_node = Node::new_satisfied_constraint(
|
||||||
let positive_right_node =
|
db,
|
||||||
Node::new_satisfied_constraint(db, right_constraint.when_true());
|
left_constraint.when_true(),
|
||||||
|
simplified.source_order_for(db, left_constraint),
|
||||||
|
);
|
||||||
|
let positive_right_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
right_constraint.when_true(),
|
||||||
|
simplified.source_order_for(db, right_constraint),
|
||||||
|
);
|
||||||
let lhs = positive_left_node.and(db, positive_right_node);
|
let lhs = positive_left_node.and(db, positive_right_node);
|
||||||
let intersection = new_node.ite(db, lhs, Node::AlwaysFalse);
|
let intersection = new_node.ite(db, lhs, Node::AlwaysFalse);
|
||||||
simplified = simplified.and(db, intersection);
|
simplified = simplified.and(db, intersection);
|
||||||
|
|
@ -2037,10 +2200,16 @@ impl<'db> InteriorNode<'db> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if let Some((larger_constraint, smaller_constraint)) = larger_smaller {
|
if let Some((larger_constraint, smaller_constraint)) = larger_smaller {
|
||||||
let positive_larger_node =
|
let positive_larger_node = Node::new_satisfied_constraint(
|
||||||
Node::new_satisfied_constraint(db, larger_constraint.when_true());
|
db,
|
||||||
let negative_larger_node =
|
larger_constraint.when_true(),
|
||||||
Node::new_satisfied_constraint(db, larger_constraint.when_false());
|
simplified.source_order_for(db, larger_constraint),
|
||||||
|
);
|
||||||
|
let negative_larger_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
larger_constraint.when_false(),
|
||||||
|
simplified.source_order_for(db, larger_constraint),
|
||||||
|
);
|
||||||
|
|
||||||
// larger ∨ smaller = larger
|
// larger ∨ smaller = larger
|
||||||
simplified = simplified.substitute_union(
|
simplified = simplified.substitute_union(
|
||||||
|
|
@ -2094,20 +2263,39 @@ impl<'db> InteriorNode<'db> {
|
||||||
.map(|seen| (seen, intersection_constraint)),
|
.map(|seen| (seen, intersection_constraint)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let positive_intersection_node =
|
let derived_source_order = simplified.max_source_order(db) + 1;
|
||||||
Node::new_satisfied_constraint(db, intersection_constraint.when_true());
|
let positive_intersection_node = Node::new_satisfied_constraint(
|
||||||
let negative_intersection_node =
|
db,
|
||||||
Node::new_satisfied_constraint(db, intersection_constraint.when_false());
|
intersection_constraint.when_true(),
|
||||||
|
derived_source_order,
|
||||||
|
);
|
||||||
|
let negative_intersection_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
intersection_constraint.when_false(),
|
||||||
|
derived_source_order,
|
||||||
|
);
|
||||||
|
|
||||||
let positive_left_node =
|
let positive_left_node = Node::new_satisfied_constraint(
|
||||||
Node::new_satisfied_constraint(db, left_constraint.when_true());
|
db,
|
||||||
let negative_left_node =
|
left_constraint.when_true(),
|
||||||
Node::new_satisfied_constraint(db, left_constraint.when_false());
|
simplified.source_order_for(db, left_constraint),
|
||||||
|
);
|
||||||
|
let negative_left_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
left_constraint.when_false(),
|
||||||
|
simplified.source_order_for(db, left_constraint),
|
||||||
|
);
|
||||||
|
|
||||||
let positive_right_node =
|
let positive_right_node = Node::new_satisfied_constraint(
|
||||||
Node::new_satisfied_constraint(db, right_constraint.when_true());
|
db,
|
||||||
let negative_right_node =
|
right_constraint.when_true(),
|
||||||
Node::new_satisfied_constraint(db, right_constraint.when_false());
|
simplified.source_order_for(db, right_constraint),
|
||||||
|
);
|
||||||
|
let negative_right_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
right_constraint.when_false(),
|
||||||
|
simplified.source_order_for(db, right_constraint),
|
||||||
|
);
|
||||||
|
|
||||||
// left ∧ right = intersection
|
// left ∧ right = intersection
|
||||||
simplified = simplified.substitute_intersection(
|
simplified = simplified.substitute_intersection(
|
||||||
|
|
@ -2172,10 +2360,16 @@ impl<'db> InteriorNode<'db> {
|
||||||
// All of the below hold because we just proved that the intersection of left
|
// All of the below hold because we just proved that the intersection of left
|
||||||
// and right is empty.
|
// and right is empty.
|
||||||
|
|
||||||
let positive_left_node =
|
let positive_left_node = Node::new_satisfied_constraint(
|
||||||
Node::new_satisfied_constraint(db, left_constraint.when_true());
|
db,
|
||||||
let positive_right_node =
|
left_constraint.when_true(),
|
||||||
Node::new_satisfied_constraint(db, right_constraint.when_true());
|
simplified.source_order_for(db, left_constraint),
|
||||||
|
);
|
||||||
|
let positive_right_node = Node::new_satisfied_constraint(
|
||||||
|
db,
|
||||||
|
right_constraint.when_true(),
|
||||||
|
simplified.source_order_for(db, right_constraint),
|
||||||
|
);
|
||||||
|
|
||||||
// left ∧ right = false
|
// left ∧ right = false
|
||||||
simplified = simplified.substitute_intersection(
|
simplified = simplified.substitute_intersection(
|
||||||
|
|
@ -3482,26 +3676,14 @@ mod tests {
|
||||||
fn test_display_graph_output() {
|
fn test_display_graph_output() {
|
||||||
let expected = indoc! {r#"
|
let expected = indoc! {r#"
|
||||||
(T = str)
|
(T = str)
|
||||||
┡━₁ (T = bool)
|
┡━₁ (U = str)
|
||||||
│ ┡━₁ (U = str)
|
│ ┡━₁ always
|
||||||
│ │ ┡━₁ (U = bool)
|
|
||||||
│ │ │ ┡━₁ always
|
|
||||||
│ │ │ └─₀ always
|
|
||||||
│ │ └─₀ (U = bool)
|
|
||||||
│ │ ┡━₁ always
|
|
||||||
│ │ └─₀ never
|
|
||||||
│ └─₀ (U = str)
|
|
||||||
│ ┡━₁ (U = bool)
|
|
||||||
│ │ ┡━₁ always
|
|
||||||
│ │ └─₀ always
|
|
||||||
│ └─₀ (U = bool)
|
│ └─₀ (U = bool)
|
||||||
│ ┡━₁ always
|
│ ┡━₁ always
|
||||||
│ └─₀ never
|
│ └─₀ never
|
||||||
└─₀ (T = bool)
|
└─₀ (T = bool)
|
||||||
┡━₁ (U = str)
|
┡━₁ (U = str)
|
||||||
│ ┡━₁ (U = bool)
|
│ ┡━₁ always
|
||||||
│ │ ┡━₁ always
|
|
||||||
│ │ └─₀ always
|
|
||||||
│ └─₀ (U = bool)
|
│ └─₀ (U = bool)
|
||||||
│ ┡━₁ always
|
│ ┡━₁ always
|
||||||
│ └─₀ never
|
│ └─₀ never
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue