Repository: python/mypy

Issue:
Literal[True, False] false positives within tuples
This code generates unexpected errors:

```py
from typing import NamedTuple, Literal, List, Tuple

C = NamedTuple("C", [("x", Literal[True, False])])

T = Tuple[Literal[True, False]]

# Incompatible types in assignment (expression has type "List[C]", variable has type "List[C]")
x: List[C] = [C(True)]

t: T

# Incompatible types in assignment (expression has type "List[Tuple[bool]]", 
# variable has type "List[Tuple[Union[Literal[True], Literal[False]]]]")
y: List[T] = [t]
```

The issue was introduced in #10389.

Marking as high priority since this is a regression.

cc @ethan-leba 


Discussion:

Resolving patch:
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -125,7 +125,10 @@ def _is_subtype(left: Type, right: Type,
         # of a union of all enum items as literal types. Only do it if
         # the previous check didn't succeed, since recombining can be
         # expensive.
-        if not is_subtype_of_item and isinstance(left, Instance) and left.type.is_enum:
+        # `bool` is a special case, because `bool` is `Literal[True, False]`.
+        if (not is_subtype_of_item
+                and isinstance(left, Instance)
+                and (left.type.is_enum or left.type.fullname == 'builtins.bool')):
             right = UnionType(mypy.typeops.try_contracting_literals_in_union(right.items))
             is_subtype_of_item = any(is_subtype(orig_left, item,
                                                 ignore_type_params=ignore_type_params,
diff --git a/mypy/typeops.py b/mypy/typeops.py
--- a/mypy/typeops.py
+++ b/mypy/typeops.py
@@ -759,8 +759,10 @@ def try_contracting_literals_in_union(types: Sequence[Type]) -> List[ProperType]
     Will replace the first instance of the literal with the sum type and
     remove all others.
 
-    if we call `try_contracting_union(Literal[Color.RED, Color.BLUE, Color.YELLOW])`,
+    If we call `try_contracting_union(Literal[Color.RED, Color.BLUE, Color.YELLOW])`,
     this function will return Color.
+
+    We also treat `Literal[True, False]` as `bool`.
     """
     proper_types = [get_proper_type(typ) for typ in types]
     sum_types: Dict[str, Tuple[Set[Any], List[int]]] = {}


Test patch:
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -1060,3 +1060,24 @@ def bad3() -> NamedTuple:
 
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-namedtuple.pyi]
+
+[case testBoolInTuplesRegression]
+# https://github.com/python/mypy/issues/11701
+from typing import NamedTuple, Literal, List, Tuple
+
+C = NamedTuple("C", [("x", Literal[True, False])])
+
+T = Tuple[Literal[True, False]]
+
+# Was error here:
+# Incompatible types in assignment (expression has type "List[C]", variable has type "List[C]")
+x: List[C] = [C(True)]
+
+t: T
+
+# Was error here:
+# Incompatible types in assignment (expression has type "List[Tuple[bool]]",
+# variable has type "List[Tuple[Union[Literal[True], Literal[False]]]]")
+y: List[T] = [t]
+[builtins fixtures/tuple.pyi]
+[typing fixtures/typing-namedtuple.pyi]