Repository: python/mypy

Issue:
Join doesn't treat correctly variance of generics
Currently, joins for the instances of same type are calculated using this code:
```python
        if is_subtype(t, s) or is_subtype(s, t):
            args = []
            for i in range(len(t.args)):
                args.append(join_types(t.args[i], s.args[i]))
            return Instance(t.type, args)
```
This has two problems:
* For a covariant generic `G`, the join of `G[int]` and `G[str]`, is currently just `object`, not `G[object]` as it (I believe) should be.
* This behaviour is wrong for contravariant types, in this case there should be a meet of arguments: so that `join(C[int], C[float])` should be `C[int]` for contravariant `C`, while currently it is `C[float]`.

A correct behaviour would to check for every type argument position:
* if the class is covariant in it, calculate join of args on the left and right
* if the class is contravariant in it, calculate meet of args
* if the class is invariant in it, check that the type on the left and right are the same
* if the last one didn't hold, fall back to `object`.
* verify bounds and values restrictions for new args, if some are not satisfied, fall back to `object`.
* (optional) if one of the meets in third bullet produced an `UninhabitedType`, fall back to `object` as well.


Discussion:
An additional important step is to verify bounds and values before creating a new instance. I edited the description.
https://github.com/python/mypy/issues/4170 provides a real-world example of this problem.

Resolving patch:
diff --git a/mypy/join.py b/mypy/join.py
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -1,7 +1,7 @@
 """Calculation of the least upper bound types (joins)."""
 
 from mypy.backports import OrderedDict
-from typing import List, Optional
+from typing import List, Optional, Tuple
 
 from mypy.types import (
     Type, AnyType, NoneType, TypeVisitor, Instance, UnboundType, TypeVarType, CallableType,
@@ -14,9 +14,96 @@
     is_subtype, is_equivalent, is_subtype_ignoring_tvars, is_proper_subtype,
     is_protocol_implementation, find_member
 )
-from mypy.nodes import ARG_NAMED, ARG_NAMED_OPT
+from mypy.nodes import ARG_NAMED, ARG_NAMED_OPT, INVARIANT, COVARIANT, CONTRAVARIANT
 import mypy.typeops
 from mypy import state
+from mypy import meet
+
+
+class InstanceJoiner:
+    def __init__(self) -> None:
+        self.seen_instances = []  # type: List[Tuple[Instance, Instance]]
+
+    def join_instances(self, t: Instance, s: Instance) -> ProperType:
+        if (t, s) in self.seen_instances or (s, t) in self.seen_instances:
+            return object_from_instance(t)
+
+        self.seen_instances.append((t, s))
+
+        """Calculate the join of two instance types."""
+        if t.type == s.type:
+            # Simplest case: join two types with the same base type (but
+            # potentially different arguments).
+
+            # Combine type arguments.
+            args = []  # type: List[Type]
+            # N.B: We use zip instead of indexing because the lengths might have
+            # mismatches during daemon reprocessing.
+            for ta, sa, type_var in zip(t.args, s.args, t.type.defn.type_vars):
+                ta_proper = get_proper_type(ta)
+                sa_proper = get_proper_type(sa)
+                new_type = None  # type: Optional[Type]
+                if isinstance(ta_proper, AnyType):
+                    new_type = AnyType(TypeOfAny.from_another_any, ta_proper)
+                elif isinstance(sa_proper, AnyType):
+                    new_type = AnyType(TypeOfAny.from_another_any, sa_proper)
+                elif type_var.variance == COVARIANT:
+                    new_type = join_types(ta, sa, self)
+                    if len(type_var.values) != 0 and new_type not in type_var.values:
+                        self.seen_instances.pop()
+                        return object_from_instance(t)
+                    if not is_subtype(new_type, type_var.upper_bound):
+                        self.seen_instances.pop()
+                        return object_from_instance(t)
+                elif type_var.variance == CONTRAVARIANT:
+                    new_type = meet.meet_types(ta, sa)
+                    if len(type_var.values) != 0 and new_type not in type_var.values:
+                        self.seen_instances.pop()
+                        return object_from_instance(t)
+                    # No need to check subtype, as ta and sa already have to be subtypes of
+                    # upper_bound
+                elif type_var.variance == INVARIANT:
+                    new_type = join_types(ta, sa)
+                    if not is_equivalent(ta, sa):
+                        self.seen_instances.pop()
+                        return object_from_instance(t)
+                assert new_type is not None
+                args.append(new_type)
+            result = Instance(t.type, args)  # type: ProperType
+        elif t.type.bases and is_subtype_ignoring_tvars(t, s):
+            result = self.join_instances_via_supertype(t, s)
+        else:
+            # Now t is not a subtype of s, and t != s. Now s could be a subtype
+            # of t; alternatively, we need to find a common supertype. This works
+            # in of the both cases.
+            result = self.join_instances_via_supertype(s, t)
+
+        self.seen_instances.pop()
+        return result
+
+    def join_instances_via_supertype(self, t: Instance, s: Instance) -> ProperType:
+        # Give preference to joins via duck typing relationship, so that
+        # join(int, float) == float, for example.
+        if t.type._promote and is_subtype(t.type._promote, s):
+            return join_types(t.type._promote, s, self)
+        elif s.type._promote and is_subtype(s.type._promote, t):
+            return join_types(t, s.type._promote, self)
+        # Compute the "best" supertype of t when joined with s.
+        # The definition of "best" may evolve; for now it is the one with
+        # the longest MRO.  Ties are broken by using the earlier base.
+        best = None  # type: Optional[ProperType]
+        for base in t.type.bases:
+            mapped = map_instance_to_supertype(t, base.type)
+            res = self.join_instances(mapped, s)
+            if best is None or is_better(res, best):
+                best = res
+        assert best is not None
+        promote = get_proper_type(t.type._promote)
+        if isinstance(promote, Instance):
+            res = self.join_instances(promote, s)
+            if is_better(res, best):
+                best = res
+        return best
 
 
 def join_simple(declaration: Optional[Type], s: Type, t: Type) -> ProperType:
@@ -69,7 +156,7 @@ def trivial_join(s: Type, t: Type) -> ProperType:
         return object_or_any_from_type(get_proper_type(t))
 
 
-def join_types(s: Type, t: Type) -> ProperType:
+def join_types(s: Type, t: Type, instance_joiner: Optional[InstanceJoiner] = None) -> ProperType:
     """Return the least upper bound of s and t.
 
     For example, the join of 'int' and 'object' is 'object'.
@@ -110,7 +197,7 @@ def join_types(s: Type, t: Type) -> ProperType:
         return AnyType(TypeOfAny.from_error)
 
     # Use a visitor to handle non-trivial cases.
-    return t.accept(TypeJoinVisitor(s))
+    return t.accept(TypeJoinVisitor(s, instance_joiner))
 
 
 class TypeJoinVisitor(TypeVisitor[ProperType]):
@@ -120,8 +207,9 @@ class TypeJoinVisitor(TypeVisitor[ProperType]):
       s: The other (left) type operand.
     """
 
-    def __init__(self, s: ProperType) -> None:
+    def __init__(self, s: ProperType, instance_joiner: Optional[InstanceJoiner] = None) -> None:
         self.s = s
+        self.instance_joiner = instance_joiner
 
     def visit_unbound_type(self, t: UnboundType) -> ProperType:
         return AnyType(TypeOfAny.special_form)
@@ -163,7 +251,9 @@ def visit_type_var(self, t: TypeVarType) -> ProperType:
 
     def visit_instance(self, t: Instance) -> ProperType:
         if isinstance(self.s, Instance):
-            nominal = join_instances(t, self.s)
+            if self.instance_joiner is None:
+                self.instance_joiner = InstanceJoiner()
+            nominal = self.instance_joiner.join_instances(t, self.s)
             structural = None  # type: Optional[Instance]
             if t.type.is_protocol and is_protocol_implementation(self.s, t):
                 structural = t
@@ -282,8 +372,10 @@ def visit_tuple_type(self, t: TupleType) -> ProperType:
         # * Joining with any Sequence also returns a Sequence:
         #   Tuple[int, bool] + List[bool] becomes Sequence[int]
         if isinstance(self.s, TupleType) and self.s.length() == t.length():
-            fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
-                                      mypy.typeops.tuple_fallback(t))
+            if self.instance_joiner is None:
+                self.instance_joiner = InstanceJoiner()
+            fallback = self.instance_joiner.join_instances(mypy.typeops.tuple_fallback(self.s),
+                                                       mypy.typeops.tuple_fallback(t))
             assert isinstance(fallback, Instance)
             if self.s.length() == t.length():
                 items = []  # type: List[Type]
@@ -364,56 +456,6 @@ def default(self, typ: Type) -> ProperType:
             return AnyType(TypeOfAny.special_form)
 
 
-def join_instances(t: Instance, s: Instance) -> ProperType:
-    """Calculate the join of two instance types."""
-    if t.type == s.type:
-        # Simplest case: join two types with the same base type (but
-        # potentially different arguments).
-        if is_subtype(t, s) or is_subtype(s, t):
-            # Compatible; combine type arguments.
-            args = []  # type: List[Type]
-            # N.B: We use zip instead of indexing because the lengths might have
-            # mismatches during daemon reprocessing.
-            for ta, sa in zip(t.args, s.args):
-                args.append(join_types(ta, sa))
-            return Instance(t.type, args)
-        else:
-            # Incompatible; return trivial result object.
-            return object_from_instance(t)
-    elif t.type.bases and is_subtype_ignoring_tvars(t, s):
-        return join_instances_via_supertype(t, s)
-    else:
-        # Now t is not a subtype of s, and t != s. Now s could be a subtype
-        # of t; alternatively, we need to find a common supertype. This works
-        # in of the both cases.
-        return join_instances_via_supertype(s, t)
-
-
-def join_instances_via_supertype(t: Instance, s: Instance) -> ProperType:
-    # Give preference to joins via duck typing relationship, so that
-    # join(int, float) == float, for example.
-    if t.type._promote and is_subtype(t.type._promote, s):
-        return join_types(t.type._promote, s)
-    elif s.type._promote and is_subtype(s.type._promote, t):
-        return join_types(t, s.type._promote)
-    # Compute the "best" supertype of t when joined with s.
-    # The definition of "best" may evolve; for now it is the one with
-    # the longest MRO.  Ties are broken by using the earlier base.
-    best = None  # type: Optional[ProperType]
-    for base in t.type.bases:
-        mapped = map_instance_to_supertype(t, base.type)
-        res = join_instances(mapped, s)
-        if best is None or is_better(res, best):
-            best = res
-    assert best is not None
-    promote = get_proper_type(t.type._promote)
-    if isinstance(promote, Instance):
-        res = join_instances(promote, s)
-        if is_better(res, best):
-            best = res
-    return best
-
-
 def is_better(t: Type, s: Type) -> bool:
     # Given two possible results from join_instances_via_supertype(),
     # indicate whether t is the better one.
diff --git a/mypy/meet.py b/mypy/meet.py
--- a/mypy/meet.py
+++ b/mypy/meet.py
@@ -1,9 +1,6 @@
 from mypy.backports import OrderedDict
 from typing import List, Optional, Tuple, Callable
 
-from mypy.join import (
-    is_similar_callables, combine_similar_callables, join_type_list, unpack_callback_protocol
-)
 from mypy.types import (
     Type, AnyType, TypeVisitor, UnboundType, NoneType, TypeVarType, Instance, CallableType,
     TupleType, TypedDictType, ErasedType, UnionType, PartialType, DeletedType,
@@ -15,6 +12,7 @@
 from mypy.maptype import map_instance_to_supertype
 from mypy.typeops import tuple_fallback, make_simplified_union, is_recursive_pair
 from mypy import state
+from mypy import join
 
 # TODO Describe this module.
 
@@ -518,7 +516,7 @@ def visit_instance(self, t: Instance) -> ProperType:
                     else:
                         return NoneType()
         elif isinstance(self.s, FunctionLike) and t.type.is_protocol:
-            call = unpack_callback_protocol(t)
+            call = join.unpack_callback_protocol(t)
             if call:
                 return meet_types(call, self.s)
         elif isinstance(self.s, FunctionLike) and self.s.is_type_obj() and t.type.is_metaclass():
@@ -536,9 +534,9 @@ def visit_instance(self, t: Instance) -> ProperType:
         return self.default(self.s)
 
     def visit_callable_type(self, t: CallableType) -> ProperType:
-        if isinstance(self.s, CallableType) and is_similar_callables(t, self.s):
+        if isinstance(self.s, CallableType) and join.is_similar_callables(t, self.s):
             if is_equivalent(t, self.s):
-                return combine_similar_callables(t, self.s)
+                return join.combine_similar_callables(t, self.s)
             result = meet_similar_callables(t, self.s)
             # We set the from_type_type flag to suppress error when a collection of
             # concrete class objects gets inferred as their common abstract superclass.
@@ -556,7 +554,7 @@ def visit_callable_type(self, t: CallableType) -> ProperType:
                 return TypeType.make_normalized(res)
             return self.default(self.s)
         elif isinstance(self.s, Instance) and self.s.type.is_protocol:
-            call = unpack_callback_protocol(self.s)
+            call = join.unpack_callback_protocol(self.s)
             if call:
                 return meet_types(t, call)
         return self.default(self.s)
@@ -575,7 +573,7 @@ def visit_overloaded(self, t: Overloaded) -> ProperType:
             else:
                 return meet_types(t.fallback, s.fallback)
         elif isinstance(self.s, Instance) and self.s.type.is_protocol:
-            call = unpack_callback_protocol(self.s)
+            call = join.unpack_callback_protocol(self.s)
             if call:
                 return meet_types(t, call)
         return meet_types(t.fallback, s)
@@ -611,7 +609,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> ProperType:
                     assert t_item_type is not None
                     item_list.append((item_name, t_item_type))
             items = OrderedDict(item_list)
-            mapping_value_type = join_type_list(list(items.values()))
+            mapping_value_type = join.join_type_list(list(items.values()))
             fallback = self.s.create_anonymous_fallback(value_type=mapping_value_type)
             required_keys = t.required_keys | self.s.required_keys
             return TypedDictType(items, required_keys, fallback)


Test patch:
diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py
--- a/mypy/test/testtypes.py
+++ b/mypy/test/testtypes.py
@@ -510,7 +510,9 @@ def callable(self, vars: List[str], *a: Type) -> CallableType:
 
 class JoinSuite(Suite):
     def setUp(self) -> None:
-        self.fx = TypeFixture()
+        self.fx = TypeFixture(INVARIANT)
+        self.fx_co = TypeFixture(COVARIANT)
+        self.fx_contra = TypeFixture(CONTRAVARIANT)
 
     def test_trivial_cases(self) -> None:
         for simple in self.fx.a, self.fx.o, self.fx.b:
@@ -630,11 +632,6 @@ def test_other_mixed_types(self) -> None:
                     self.assert_join(t1, t2, self.fx.o)
 
     def test_simple_generics(self) -> None:
-        self.assert_join(self.fx.ga, self.fx.ga, self.fx.ga)
-        self.assert_join(self.fx.ga, self.fx.gb, self.fx.ga)
-        self.assert_join(self.fx.ga, self.fx.gd, self.fx.o)
-        self.assert_join(self.fx.ga, self.fx.g2a, self.fx.o)
-
         self.assert_join(self.fx.ga, self.fx.nonet, self.fx.ga)
         self.assert_join(self.fx.ga, self.fx.anyt, self.fx.anyt)
 
@@ -642,23 +639,43 @@ def test_simple_generics(self) -> None:
                   self.callable(self.fx.a, self.fx.b)]:
             self.assert_join(t, self.fx.ga, self.fx.o)
 
+    def test_generics_invariant(self) -> None:
+        self.assert_join(self.fx.ga, self.fx.ga, self.fx.ga)
+        self.assert_join(self.fx.ga, self.fx.gb, self.fx.o)
+        self.assert_join(self.fx.ga, self.fx.gd, self.fx.o)
+        self.assert_join(self.fx.ga, self.fx.g2a, self.fx.o)
+
+    def test_generics_covariant(self) -> None:
+        self.assert_join(self.fx_co.ga, self.fx_co.ga, self.fx_co.ga)
+        self.assert_join(self.fx_co.ga, self.fx_co.gb, self.fx_co.ga)
+        self.assert_join(self.fx_co.ga, self.fx_co.gd, self.fx_co.go)
+        self.assert_join(self.fx_co.ga, self.fx_co.g2a, self.fx_co.o)
+
+    def test_generics_contravariant(self) -> None:
+        self.assert_join(self.fx_contra.ga, self.fx_contra.ga, self.fx_contra.ga)
+        self.assert_join(self.fx_contra.ga, self.fx_contra.gb, self.fx_contra.gb)
+        self.assert_join(self.fx_contra.ga, self.fx_contra.gd, self.fx_contra.gn)
+        self.assert_join(self.fx_contra.ga, self.fx_contra.g2a, self.fx_contra.o)
+
     def test_generics_with_multiple_args(self) -> None:
-        self.assert_join(self.fx.hab, self.fx.hab, self.fx.hab)
-        self.assert_join(self.fx.hab, self.fx.hbb, self.fx.hab)
-        self.assert_join(self.fx.had, self.fx.haa, self.fx.o)
+        self.assert_join(self.fx_co.hab, self.fx_co.hab, self.fx_co.hab)
+        self.assert_join(self.fx_co.hab, self.fx_co.hbb, self.fx_co.hab)
+        self.assert_join(self.fx_co.had, self.fx_co.haa, self.fx_co.hao)
 
     def test_generics_with_inheritance(self) -> None:
-        self.assert_join(self.fx.gsab, self.fx.gb, self.fx.gb)
-        self.assert_join(self.fx.gsba, self.fx.gb, self.fx.ga)
-        self.assert_join(self.fx.gsab, self.fx.gd, self.fx.o)
+        self.assert_join(self.fx_co.gsab, self.fx_co.gb, self.fx_co.gb)
+        self.assert_join(self.fx_co.gsba, self.fx_co.gb, self.fx_co.ga)
+        self.assert_join(self.fx_co.gsab, self.fx_co.gd, self.fx_co.go)
 
     def test_generics_with_inheritance_and_shared_supertype(self) -> None:
-        self.assert_join(self.fx.gsba, self.fx.gs2a, self.fx.ga)
-        self.assert_join(self.fx.gsab, self.fx.gs2a, self.fx.ga)
-        self.assert_join(self.fx.gsab, self.fx.gs2d, self.fx.o)
+        self.assert_join(self.fx_co.gsba, self.fx_co.gs2a, self.fx_co.ga)
+        self.assert_join(self.fx_co.gsab, self.fx_co.gs2a, self.fx_co.ga)
+        self.assert_join(self.fx_co.gsab, self.fx_co.gs2d, self.fx_co.go)
 
     def test_generic_types_and_any(self) -> None:
         self.assert_join(self.fx.gdyn, self.fx.ga, self.fx.gdyn)
+        self.assert_join(self.fx_co.gdyn, self.fx_co.ga, self.fx_co.gdyn)
+        self.assert_join(self.fx_contra.gdyn, self.fx_contra.ga, self.fx_contra.gdyn)
 
     def test_callables_with_any(self) -> None:
         self.assert_join(self.callable(self.fx.a, self.fx.a, self.fx.anyt,
diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py
--- a/mypy/test/typefixture.py
+++ b/mypy/test/typefixture.py
@@ -129,6 +129,7 @@ def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type,
         self.gtf2 = Instance(self.gi, [self.tf2])    # G[T`-2]
         self.gs = Instance(self.gi, [self.s])        # G[S]
         self.gdyn = Instance(self.gi, [self.anyt])    # G[Any]
+        self.gn = Instance(self.gi, [NoneType()])    # G[None]
 
         self.g2a = Instance(self.g2i, [self.a])      # G2[A]
 
@@ -145,6 +146,7 @@ def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type,
         self.hbb = Instance(self.hi, [self.b, self.b])    # H[B, B]
         self.hts = Instance(self.hi, [self.t, self.s])    # H[T, S]
         self.had = Instance(self.hi, [self.a, self.d])    # H[A, D]
+        self.hao = Instance(self.hi, [self.a, self.o])    # H[A, object]
 
         self.lsta = Instance(self.std_listi, [self.a])  # List[A]
         self.lstb = Instance(self.std_listi, [self.b])  # List[B]
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -5436,7 +5436,7 @@ main:8:6: error: Type argument "builtins.int" of "G" must be a subtype of "built
 [case testExtremeForwardReferencing]
 from typing import TypeVar, Generic
 
-T = TypeVar('T')
+T = TypeVar('T', covariant=True)
 class B(Generic[T]): ...
 
 y: A
diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test
--- a/test-data/unit/check-generics.test
+++ b/test-data/unit/check-generics.test
@@ -2430,3 +2430,50 @@ class C: ...
 def test() -> Optional[S]:
     reveal_type(A().gen(C()))  # N: Revealed type is "__main__.C*"
     return None
+
+[case testGenericJoinCovariant]
+from typing import Generic, TypeVar, List
+
+T = TypeVar("T", covariant=True)
+
+class Container(Generic[T]): ...
+class Base: ...
+class A(Base): ...
+class B(Base): ...
+
+a: A
+b: B
+
+a_c: Container[A]
+b_c: Container[B]
+
+reveal_type([a, b])  # N: Revealed type is "builtins.list[__main__.Base*]"
+reveal_type([a_c, b_c])  # N: Revealed type is "builtins.list[__main__.Container*[__main__.Base]]"
+[builtins fixtures/list.pyi]
+
+[case testGenericJoinContravariant]
+from typing import Generic, TypeVar, List
+
+T = TypeVar("T", contravariant=True)
+
+class Container(Generic[T]): ...
+class A: ...
+class B(A): ...
+
+a_c: Container[A]
+b_c: Container[B]
+
+reveal_type([a_c, b_c])  # N: Revealed type is "builtins.list[__main__.Container*[__main__.B]]"
+[builtins fixtures/list.pyi]
+
+[case testGenericJoinRecursiveTypes]
+from typing import Sequence, TypeVar
+
+class A(Sequence[A]): ...
+class B(Sequence[B]): ...
+
+a: A
+b: B
+
+reveal_type([a, b])  # N: Revealed type is "builtins.list[typing.Sequence*[builtins.object]]"
+[builtins fixtures/list.pyi]
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -791,10 +791,10 @@ AnyStr = TypeVar('AnyStr', bytes, str)
 def f(x: Union[AnyStr, int], *a: AnyStr) -> None: pass
 f('foo')
 f('foo', 'bar')
-f('foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "object"
+f('foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "Sequence[object]"
 f(1)
 f(1, 'foo')
-f(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "object"
+f(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "f" cannot be "Sequence[object]"
 [builtins fixtures/primitives.pyi]
 
 
diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test
--- a/test-data/unit/check-newsemanal.test
+++ b/test-data/unit/check-newsemanal.test
@@ -2002,7 +2002,7 @@ class G(Generic[T]): pass
 
 t: Tuple[G[B], G[C]] # E: Type argument "__main__.B" of "G" must be a subtype of "__main__.A" \
                      # E: Type argument "__main__.C" of "G" must be a subtype of "__main__.A"
-reveal_type(t.__iter__) # N: Revealed type is "def () -> typing.Iterator[__main__.G*[__main__.B]]"
+reveal_type(t.__iter__) # N: Revealed type is "def () -> typing.Iterator[builtins.object*]"
 [builtins fixtures/tuple.pyi]
 
 [case testNewAnalyzerClassKeywordsForward]
diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test
--- a/test-data/unit/check-overloading.test
+++ b/test-data/unit/check-overloading.test
@@ -1315,10 +1315,10 @@ def g(x: int, *a: AnyStr) -> None: pass
 
 g('foo')
 g('foo', 'bar')
-g('foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "object"
+g('foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "Sequence[object]"
 g(1)
 g(1, 'foo')
-g(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "object"
+g(1, 'foo', b'bar') # E: Value of type variable "AnyStr" of "g" cannot be "Sequence[object]"
 [builtins fixtures/primitives.pyi]
 
 [case testOverloadOverlapWithTypeVarsWithValuesOrdering]
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
--- a/test-data/unit/pythoneval.test
+++ b/test-data/unit/pythoneval.test
@@ -1005,7 +1005,7 @@ re.subn(bpat, b'', b'')[0] + b''
 re.subn(bre, lambda m: b'', b'')[0] + b''
 re.subn(bpat, lambda m: b'', b'')[0] + b''
 [out]
-_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "object"
+_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "Sequence[object]"
 _program.py:9: error: Cannot infer type argument 1 of "search"
 
 [case testReModuleString]
@@ -1029,7 +1029,7 @@ re.subn(spat, '', '')[0] + ''
 re.subn(sre, lambda m: '', '')[0] + ''
 re.subn(spat, lambda m: '', '')[0] + ''
 [out]
-_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "object"
+_program.py:7: error: Value of type variable "AnyStr" of "search" cannot be "Sequence[object]"
 _program.py:9: error: Cannot infer type argument 1 of "search"
 
 [case testListSetitemTuple]