78 lines
2.8 KiB
Python
78 lines
2.8 KiB
Python
"""Type inference constraint solving"""
|
|
|
|
from typing import List, Dict, Optional
|
|
from collections import defaultdict
|
|
|
|
from mypy.types import Type, AnyType, UninhabitedType, TypeVarId, TypeOfAny, get_proper_type
|
|
from mypy.constraints import Constraint, SUPERTYPE_OF
|
|
from mypy.join import join_types
|
|
from mypy.meet import meet_types
|
|
from mypy.subtypes import is_subtype
|
|
|
|
|
|
def solve_constraints(vars: List[TypeVarId], constraints: List[Constraint],
|
|
strict: bool = True) -> List[Optional[Type]]:
|
|
"""Solve type constraints.
|
|
|
|
Return the best type(s) for type variables; each type can be None if the value of the variable
|
|
could not be solved.
|
|
|
|
If a variable has no constraints, if strict=True then arbitrarily
|
|
pick NoneType as the value of the type variable. If strict=False,
|
|
pick AnyType.
|
|
"""
|
|
# Collect a list of constraints for each type variable.
|
|
cmap: Dict[TypeVarId, List[Constraint]] = defaultdict(list)
|
|
for con in constraints:
|
|
cmap[con.type_var].append(con)
|
|
|
|
res: List[Optional[Type]] = []
|
|
|
|
# Solve each type variable separately.
|
|
for tvar in vars:
|
|
bottom: Optional[Type] = None
|
|
top: Optional[Type] = None
|
|
candidate: Optional[Type] = None
|
|
|
|
# Process each constraint separately, and calculate the lower and upper
|
|
# bounds based on constraints. Note that we assume that the constraint
|
|
# targets do not have constraint references.
|
|
for c in cmap.get(tvar, []):
|
|
if c.op == SUPERTYPE_OF:
|
|
if bottom is None:
|
|
bottom = c.target
|
|
else:
|
|
bottom = join_types(bottom, c.target)
|
|
else:
|
|
if top is None:
|
|
top = c.target
|
|
else:
|
|
top = meet_types(top, c.target)
|
|
|
|
top = get_proper_type(top)
|
|
bottom = get_proper_type(bottom)
|
|
if isinstance(top, AnyType) or isinstance(bottom, AnyType):
|
|
source_any = top if isinstance(top, AnyType) else bottom
|
|
assert isinstance(source_any, AnyType)
|
|
res.append(AnyType(TypeOfAny.from_another_any, source_any=source_any))
|
|
continue
|
|
elif bottom is None:
|
|
if top:
|
|
candidate = top
|
|
else:
|
|
# No constraints for type variable -- 'UninhabitedType' is the most specific type.
|
|
if strict:
|
|
candidate = UninhabitedType()
|
|
candidate.ambiguous = True
|
|
else:
|
|
candidate = AnyType(TypeOfAny.special_form)
|
|
elif top is None:
|
|
candidate = bottom
|
|
elif is_subtype(bottom, top):
|
|
candidate = bottom
|
|
else:
|
|
candidate = None
|
|
res.append(candidate)
|
|
|
|
return res
|