@@ -109,6 +109,13 @@ def solve_constraints(
109
109
else :
110
110
candidate = AnyType (TypeOfAny .special_form )
111
111
res .append (candidate )
112
+
113
+ if not free_vars :
114
+ # Most of the validation for solutions is done in applytype.py, but here we can
115
+ # quickly test solutions w.r.t. to upper bounds, and use the latter (if possible),
116
+ # if solutions are actually not valid (due to poor inference context).
117
+ res = pre_validate_solutions (res , original_vars , constraints )
118
+
112
119
return res , free_vars
113
120
114
121
@@ -487,3 +494,31 @@ def check_linear(scc: set[TypeVarId], lowers: Bounds, uppers: Bounds) -> bool:
487
494
def get_vars (target : Type , vars : list [TypeVarId ]) -> set [TypeVarId ]:
488
495
"""Find type variables for which we are solving in a target type."""
489
496
return {tv .id for tv in get_all_type_vars (target )} & set (vars )
497
+
498
+
499
+ def pre_validate_solutions (
500
+ solutions : list [Type | None ],
501
+ original_vars : Sequence [TypeVarLikeType ],
502
+ constraints : list [Constraint ],
503
+ ) -> list [Type | None ]:
504
+ """Check is each solution satisfies the upper bound of the corresponding type variable.
505
+
506
+ If it doesn't satisfy the bound, check if bound itself satisfies all constraints, and
507
+ if yes, use it instead as a fallback solution.
508
+ """
509
+ new_solutions : list [Type | None ] = []
510
+ for t , s in zip (original_vars , solutions ):
511
+ if s is not None and not is_subtype (s , t .upper_bound ):
512
+ bound_satisfies_all = True
513
+ for c in constraints :
514
+ if c .op == SUBTYPE_OF and not is_subtype (t .upper_bound , c .target ):
515
+ bound_satisfies_all = False
516
+ break
517
+ if c .op == SUPERTYPE_OF and not is_subtype (c .target , t .upper_bound ):
518
+ bound_satisfies_all = False
519
+ break
520
+ if bound_satisfies_all :
521
+ new_solutions .append (t .upper_bound )
522
+ continue
523
+ new_solutions .append (s )
524
+ return new_solutions
0 commit comments