1"""Test cases for the constraint solver used in type inference."""
2
3from typing import List, Union, Tuple, Optional
4
5from mypy.test.helpers import Suite, assert_equal
6from mypy.constraints import SUPERTYPE_OF, SUBTYPE_OF, Constraint
7from mypy.solve import solve_constraints
8from mypy.test.typefixture import TypeFixture
9from mypy.types import Type, TypeVarType, TypeVarId
10
11
12class SolveSuite(Suite):
13    def setUp(self) -> None:
14        self.fx = TypeFixture()
15
16    def test_empty_input(self) -> None:
17        self.assert_solve([], [], [])
18
19    def test_simple_supertype_constraints(self) -> None:
20        self.assert_solve([self.fx.t.id],
21                          [self.supc(self.fx.t, self.fx.a)],
22                          [(self.fx.a, self.fx.o)])
23        self.assert_solve([self.fx.t.id],
24                          [self.supc(self.fx.t, self.fx.a),
25                           self.supc(self.fx.t, self.fx.b)],
26                          [(self.fx.a, self.fx.o)])
27
28    def test_simple_subtype_constraints(self) -> None:
29        self.assert_solve([self.fx.t.id],
30                          [self.subc(self.fx.t, self.fx.a)],
31                          [self.fx.a])
32        self.assert_solve([self.fx.t.id],
33                          [self.subc(self.fx.t, self.fx.a),
34                           self.subc(self.fx.t, self.fx.b)],
35                          [self.fx.b])
36
37    def test_both_kinds_of_constraints(self) -> None:
38        self.assert_solve([self.fx.t.id],
39                          [self.supc(self.fx.t, self.fx.b),
40                           self.subc(self.fx.t, self.fx.a)],
41                          [(self.fx.b, self.fx.a)])
42
43    def test_unsatisfiable_constraints(self) -> None:
44        # The constraints are impossible to satisfy.
45        self.assert_solve([self.fx.t.id],
46                          [self.supc(self.fx.t, self.fx.a),
47                           self.subc(self.fx.t, self.fx.b)],
48                          [None])
49
50    def test_exactly_specified_result(self) -> None:
51        self.assert_solve([self.fx.t.id],
52                          [self.supc(self.fx.t, self.fx.b),
53                           self.subc(self.fx.t, self.fx.b)],
54                          [(self.fx.b, self.fx.b)])
55
56    def test_multiple_variables(self) -> None:
57        self.assert_solve([self.fx.t.id, self.fx.s.id],
58                          [self.supc(self.fx.t, self.fx.b),
59                           self.supc(self.fx.s, self.fx.c),
60                           self.subc(self.fx.t, self.fx.a)],
61                          [(self.fx.b, self.fx.a), (self.fx.c, self.fx.o)])
62
63    def test_no_constraints_for_var(self) -> None:
64        self.assert_solve([self.fx.t.id],
65                          [],
66                          [self.fx.uninhabited])
67        self.assert_solve([self.fx.t.id, self.fx.s.id],
68                          [],
69                          [self.fx.uninhabited, self.fx.uninhabited])
70        self.assert_solve([self.fx.t.id, self.fx.s.id],
71                          [self.supc(self.fx.s, self.fx.a)],
72                          [self.fx.uninhabited, (self.fx.a, self.fx.o)])
73
74    def test_simple_constraints_with_dynamic_type(self) -> None:
75        self.assert_solve([self.fx.t.id],
76                          [self.supc(self.fx.t, self.fx.anyt)],
77                          [(self.fx.anyt, self.fx.anyt)])
78        self.assert_solve([self.fx.t.id],
79                          [self.supc(self.fx.t, self.fx.anyt),
80                           self.supc(self.fx.t, self.fx.anyt)],
81                          [(self.fx.anyt, self.fx.anyt)])
82        self.assert_solve([self.fx.t.id],
83                          [self.supc(self.fx.t, self.fx.anyt),
84                           self.supc(self.fx.t, self.fx.a)],
85                          [(self.fx.anyt, self.fx.anyt)])
86
87        self.assert_solve([self.fx.t.id],
88                          [self.subc(self.fx.t, self.fx.anyt)],
89                          [(self.fx.anyt, self.fx.anyt)])
90        self.assert_solve([self.fx.t.id],
91                          [self.subc(self.fx.t, self.fx.anyt),
92                           self.subc(self.fx.t, self.fx.anyt)],
93                          [(self.fx.anyt, self.fx.anyt)])
94        # self.assert_solve([self.fx.t.id],
95        #                   [self.subc(self.fx.t, self.fx.anyt),
96        #                    self.subc(self.fx.t, self.fx.a)],
97        #                   [(self.fx.anyt, self.fx.anyt)])
98        # TODO: figure out what this should be after changes to meet(any, X)
99
100    def test_both_normal_and_any_types_in_results(self) -> None:
101        # If one of the bounds is any, we promote the other bound to
102        # any as well, since otherwise the type range does not make sense.
103        self.assert_solve([self.fx.t.id],
104                          [self.supc(self.fx.t, self.fx.a),
105                           self.subc(self.fx.t, self.fx.anyt)],
106                          [(self.fx.anyt, self.fx.anyt)])
107
108        self.assert_solve([self.fx.t.id],
109                          [self.supc(self.fx.t, self.fx.anyt),
110                           self.subc(self.fx.t, self.fx.a)],
111                          [(self.fx.anyt, self.fx.anyt)])
112
113    def assert_solve(self,
114                     vars: List[TypeVarId],
115                     constraints: List[Constraint],
116                     results: List[Union[None, Type, Tuple[Type, Type]]],
117                     ) -> None:
118        res = []  # type: List[Optional[Type]]
119        for r in results:
120            if isinstance(r, tuple):
121                res.append(r[0])
122            else:
123                res.append(r)
124        actual = solve_constraints(vars, constraints)
125        assert_equal(str(actual), str(res))
126
127    def supc(self, type_var: TypeVarType, bound: Type) -> Constraint:
128        return Constraint(type_var.id, SUPERTYPE_OF, bound)
129
130    def subc(self, type_var: TypeVarType, bound: Type) -> Constraint:
131        return Constraint(type_var.id, SUBTYPE_OF, bound)
132