| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- """Type inference constraint solving"""
- from __future__ import annotations
- from collections import defaultdict
- from mypy.constraints import SUPERTYPE_OF, Constraint
- from mypy.join import join_types
- from mypy.meet import meet_types
- from mypy.subtypes import is_subtype
- from mypy.types import (
- AnyType,
- ProperType,
- Type,
- TypeOfAny,
- TypeVarId,
- UninhabitedType,
- UnionType,
- get_proper_type,
- )
- from mypy.typestate import type_state
- def solve_constraints(
- vars: list[TypeVarId], constraints: list[Constraint], strict: bool = True
- ) -> list[Type | None]:
- """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[Type | None] = []
- # Solve each type variable separately.
- for tvar in vars:
- bottom: Type | None = None
- top: Type | None = None
- candidate: Type | None = 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:
- if type_state.infer_unions:
- # This deviates from the general mypy semantics because
- # recursive types are union-heavy in 95% of cases.
- bottom = UnionType.make_union([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)
- p_top = get_proper_type(top)
- p_bottom = get_proper_type(bottom)
- if isinstance(p_top, AnyType) or isinstance(p_bottom, AnyType):
- source_any = top if isinstance(p_top, AnyType) else bottom
- assert isinstance(source_any, ProperType) and 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
|