@@ -64,7 +64,8 @@ use rustc_hash::FxHashSet;
6464use salsa:: plumbing:: AsId ;
6565
6666use crate :: Db ;
67- use crate :: types:: { BoundTypeVarIdentity , IntersectionType , Type , UnionType } ;
67+ use crate :: types:: generics:: InferableTypeVars ;
68+ use crate :: types:: { BoundTypeVarIdentity , IntersectionType , Type , TypeRelation , UnionType } ;
6869
6970/// An extension trait for building constraint sets from [`Option`] values.
7071pub ( crate ) trait OptionConstraintsExtension < T > {
@@ -185,6 +186,22 @@ impl<'db> ConstraintSet<'db> {
185186 self . node . is_always_satisfied ( )
186187 }
187188
189+ fn type_implies (
190+ self ,
191+ db : & ' db dyn Db ,
192+ lhs : Type < ' db > ,
193+ rhs : Type < ' db > ,
194+ inferable : InferableTypeVars < ' _ , ' db > ,
195+ ) -> bool {
196+ lhs. has_relation_to (
197+ db,
198+ rhs,
199+ inferable,
200+ TypeRelation :: ConstraintImplication ( self ) ,
201+ )
202+ . is_always_satisfied ( )
203+ }
204+
188205 /// Updates this constraint set to hold the union of itself and another constraint set.
189206 pub ( crate ) fn union ( & mut self , db : & ' db dyn Db , other : Self ) -> Self {
190207 self . node = self . node . or ( db, other. node ) ;
@@ -247,7 +264,7 @@ impl<'db> ConstraintSet<'db> {
247264 }
248265
249266 pub ( crate ) fn display ( self , db : & ' db dyn Db ) -> impl Display {
250- self . node . display ( db)
267+ self . node . display ( db, self )
251268 }
252269}
253270
@@ -321,20 +338,34 @@ impl<'db> ConstrainedTypeVar<'db> {
321338 ///
322339 /// This is used (among other places) to simplify how we display constraint sets, by removing
323340 /// redundant constraints from a clause.
324- fn implies ( self , db : & ' db dyn Db , other : Self ) -> bool {
325- other. contains ( db, self )
341+ fn implies ( self , db : & ' db dyn Db , other : Self , constraints : ConstraintSet < ' db > ) -> bool {
342+ if self . typevar ( db) != other. typevar ( db) {
343+ return false ;
344+ }
345+ constraints. type_implies ( db, other. lower ( db) , self . lower ( db) , InferableTypeVars :: None )
346+ && constraints. type_implies (
347+ db,
348+ self . upper ( db) ,
349+ other. upper ( db) ,
350+ InferableTypeVars :: None ,
351+ )
326352 }
327353
328354 /// Returns the intersection of two range constraints, or `None` if the intersection is empty.
329- fn intersect ( self , db : & ' db dyn Db , other : Self ) -> Option < Self > {
355+ fn intersect (
356+ self ,
357+ db : & ' db dyn Db ,
358+ other : Self ,
359+ constraints : ConstraintSet < ' db > ,
360+ ) -> Option < Self > {
330361 // (s₁ ≤ α ≤ t₁) ∧ (s₂ ≤ α ≤ t₂) = (s₁ ∪ s₂) ≤ α ≤ (t₁ ∩ t₂))
331362 let lower = UnionType :: from_elements ( db, [ self . lower ( db) , other. lower ( db) ] ) . normalized ( db) ;
332363 let upper =
333364 IntersectionType :: from_elements ( db, [ self . upper ( db) , other. upper ( db) ] ) . normalized ( db) ;
334365
335366 // If `lower ≰ upper`, then the intersection is empty, since there is no type that is both
336367 // greater than `lower`, and less than `upper`.
337- if !lower . is_subtype_of ( db, upper) {
368+ if !constraints . type_implies ( db, lower , upper, InferableTypeVars :: None ) {
338369 return None ;
339370 }
340371
@@ -755,10 +786,10 @@ impl<'db> Node<'db> {
755786 }
756787
757788 /// Simplifies a BDD, replacing constraints with simpler or smaller constraints where possible.
758- fn simplify ( self , db : & ' db dyn Db ) -> Self {
789+ fn simplify ( self , db : & ' db dyn Db , constraints : ConstraintSet < ' db > ) -> Self {
759790 match self {
760791 Node :: AlwaysTrue | Node :: AlwaysFalse => self ,
761- Node :: Interior ( interior) => interior. simplify ( db) ,
792+ Node :: Interior ( interior) => interior. simplify ( db, constraints ) ,
762793 }
763794 }
764795
@@ -796,14 +827,15 @@ impl<'db> Node<'db> {
796827 searcher. clauses
797828 }
798829
799- fn display ( self , db : & ' db dyn Db ) -> impl Display {
830+ fn display ( self , db : & ' db dyn Db , constraints : ConstraintSet < ' db > ) -> impl Display {
800831 // To render a BDD in DNF form, you perform a depth-first search of the BDD tree, looking
801832 // for any path that leads to the AlwaysTrue terminal. Each such path represents one of the
802833 // intersection clauses in the DNF form. The path traverses zero or more interior nodes,
803834 // and takes either the true or false edge from each one. That gives you the positive or
804835 // negative individual constraints in the path's clause.
805836 struct DisplayNode < ' db > {
806837 node : Node < ' db > ,
838+ constraints : ConstraintSet < ' db > ,
807839 db : & ' db dyn Db ,
808840 }
809841
@@ -814,15 +846,16 @@ impl<'db> Node<'db> {
814846 Node :: AlwaysFalse => f. write_str ( "never" ) ,
815847 Node :: Interior ( _) => {
816848 let mut clauses = self . node . satisfied_clauses ( self . db ) ;
817- clauses. simplify ( self . db ) ;
849+ clauses. simplify ( self . db , self . constraints ) ;
818850 clauses. display ( self . db ) . fmt ( f)
819851 }
820852 }
821853 }
822854 }
823855
824856 DisplayNode {
825- node : self . simplify ( db) ,
857+ node : self . simplify ( db, constraints) ,
858+ constraints,
826859 db,
827860 }
828861 }
@@ -1033,7 +1066,7 @@ impl<'db> InteriorNode<'db> {
10331066 }
10341067
10351068 #[ salsa:: tracked( heap_size=ruff_memory_usage:: heap_size) ]
1036- fn simplify ( self , db : & ' db dyn Db ) -> Node < ' db > {
1069+ fn simplify ( self , db : & ' db dyn Db , constraints : ConstraintSet < ' db > ) -> Node < ' db > {
10371070 // To simplify a non-terminal BDD, we find all pairs of constraints that are mentioned in
10381071 // the BDD. If any of those pairs can be simplified to some other BDD, we perform a
10391072 // substitution to replace the pair with the simplification.
@@ -1117,7 +1150,7 @@ impl<'db> InteriorNode<'db> {
11171150 // There are some simplifications we can make when the intersection of the two
11181151 // constraints is empty, and others that we can make when the intersection is
11191152 // non-empty.
1120- match left_constraint. intersect ( db, right_constraint) {
1153+ match left_constraint. intersect ( db, right_constraint, constraints ) {
11211154 Some ( intersection_constraint) => {
11221155 // If the intersection is non-empty, we need to create a new constraint to
11231156 // represent that intersection. We also need to add the new constraint to our
@@ -1274,7 +1307,7 @@ impl<'db> ConstraintAssignment<'db> {
12741307 ///
12751308 /// This is used (among other places) to simplify how we display constraint sets, by removing
12761309 /// redundant constraints from a clause.
1277- fn implies ( self , db : & ' db dyn Db , other : Self ) -> bool {
1310+ fn implies ( self , db : & ' db dyn Db , other : Self , constraints : ConstraintSet < ' db > ) -> bool {
12781311 match ( self , other) {
12791312 // For two positive constraints, one range has to fully contain the other; the smaller
12801313 // constraint implies the larger.
@@ -1284,7 +1317,7 @@ impl<'db> ConstraintAssignment<'db> {
12841317 (
12851318 ConstraintAssignment :: Positive ( self_constraint) ,
12861319 ConstraintAssignment :: Positive ( other_constraint) ,
1287- ) => self_constraint. implies ( db, other_constraint) ,
1320+ ) => self_constraint. implies ( db, other_constraint, constraints ) ,
12881321
12891322 // For two negative constraints, one range has to fully contain the other; the ranges
12901323 // represent "holes", though, so the constraint with the larger range implies the one
@@ -1295,7 +1328,7 @@ impl<'db> ConstraintAssignment<'db> {
12951328 (
12961329 ConstraintAssignment :: Negative ( self_constraint) ,
12971330 ConstraintAssignment :: Negative ( other_constraint) ,
1298- ) => other_constraint. implies ( db, self_constraint) ,
1331+ ) => other_constraint. implies ( db, self_constraint, constraints ) ,
12991332
13001333 // For a positive and negative constraint, the ranges have to be disjoint, and the
13011334 // positive range implies the negative range.
@@ -1305,7 +1338,9 @@ impl<'db> ConstraintAssignment<'db> {
13051338 (
13061339 ConstraintAssignment :: Positive ( self_constraint) ,
13071340 ConstraintAssignment :: Negative ( other_constraint) ,
1308- ) => self_constraint. intersect ( db, other_constraint) . is_none ( ) ,
1341+ ) => self_constraint
1342+ . intersect ( db, other_constraint, constraints)
1343+ . is_none ( ) ,
13091344
13101345 // It's theoretically possible for a negative constraint to imply a positive constraint
13111346 // if the positive constraint is always satisfied (`Never ≤ T ≤ object`). But we never
@@ -1391,15 +1426,15 @@ impl<'db> SatisfiedClause<'db> {
13911426 /// want to remove the larger one and keep the smaller one.)
13921427 ///
13931428 /// Returns a boolean that indicates whether any simplifications were made.
1394- fn simplify ( & mut self , db : & ' db dyn Db ) -> bool {
1429+ fn simplify ( & mut self , db : & ' db dyn Db , constraints : ConstraintSet < ' db > ) -> bool {
13951430 let mut changes_made = false ;
13961431 let mut i = 0 ;
13971432 // Loop through each constraint, comparing it with any constraints that appear later in the
13981433 // list.
13991434 ' outer: while i < self . constraints . len ( ) {
14001435 let mut j = i + 1 ;
14011436 while j < self . constraints . len ( ) {
1402- if self . constraints [ j] . implies ( db, self . constraints [ i] ) {
1437+ if self . constraints [ j] . implies ( db, self . constraints [ i] , constraints ) {
14031438 // If constraint `i` is removed, then we don't need to compare it with any
14041439 // later constraints in the list. Note that we continue the outer loop, instead
14051440 // of breaking from the inner loop, so that we don't bump index `i` below.
@@ -1408,7 +1443,7 @@ impl<'db> SatisfiedClause<'db> {
14081443 self . constraints . swap_remove ( i) ;
14091444 changes_made = true ;
14101445 continue ' outer;
1411- } else if self . constraints [ i] . implies ( db, self . constraints [ j] ) {
1446+ } else if self . constraints [ i] . implies ( db, self . constraints [ j] , constraints ) {
14121447 // If constraint `j` is removed, then we can continue the inner loop. We will
14131448 // swap a new element into place at index `j`, and will continue comparing the
14141449 // constraint at index `i` with later constraints.
@@ -1472,11 +1507,11 @@ impl<'db> SatisfiedClauses<'db> {
14721507 /// Simplifies the DNF representation, removing redundancies that do not change the underlying
14731508 /// function. (This is used when displaying a BDD, to make sure that the representation that we
14741509 /// show is as simple as possible while still producing the same results.)
1475- fn simplify ( & mut self , db : & ' db dyn Db ) {
1510+ fn simplify ( & mut self , db : & ' db dyn Db , constraints : ConstraintSet < ' db > ) {
14761511 // First simplify each clause individually, by removing constraints that are implied by
14771512 // other constraints in the clause.
14781513 for clause in & mut self . clauses {
1479- clause. simplify ( db) ;
1514+ clause. simplify ( db, constraints ) ;
14801515 }
14811516
14821517 while self . simplify_one_round ( ) {
0 commit comments