Repository: python/mypy

Issue:
new union syntax doesn't work on `Literal`s
```py
from __future__ import annotations

from typing import Literal

Foo = Literal[1, 2]

Bar = Foo | Literal[3] # error: Unsupported left operand type for | ("object")
```
https://mypy-play.net/?mypy=latest&python=3.10&gist=3a429dfa3e9e3b692aac25887027ab69


Discussion:
This also happens without `__future__` import, python3.9:

```python
from typing import Literal

Foo = Literal[1, 2]
Bar = Foo | Literal[3]
# error: Invalid type alias: expression is not a valid type
# error: Unsupported left operand type for | ("object")
```

Will fix it right now, thanks!
On python3.10 it generates a single error:

```
» mypy out/ex.py --show-traceback
out/ex.py:4: error: Unsupported left operand type for | ("object")
```
On 3.9 without the import the error is correct since `|` doesn't work at runtime in 3.9. But it should not error in 3.10 or with the future import on, or in stringified annotations.

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6019,11 +6019,11 @@ def lookup_qualified(self, name: str) -> SymbolTableNode:
             last = parts[-1]
             if last in n.names:
                 return n.names[last]
-            elif len(parts) == 2 and parts[0] == "builtins":
-                fullname = "builtins." + last
+            elif len(parts) == 2 and parts[0] in ("builtins", "typing"):
+                fullname = ".".join(parts)
                 if fullname in SUGGESTED_TEST_FIXTURES:
-                    suggestion = ", e.g. add '[builtins fixtures/{}]' to your test".format(
-                        SUGGESTED_TEST_FIXTURES[fullname]
+                    suggestion = ", e.g. add '[{} fixtures/{}]' to your test".format(
+                        parts[0], SUGGESTED_TEST_FIXTURES[fullname]
                     )
                 else:
                     suggestion = ""
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -3869,9 +3869,8 @@ class LongName(Generic[T]): ...
         else:
             if alias_definition:
                 return AnyType(TypeOfAny.special_form)
-            # This type is invalid in most runtime contexts, give it an 'object' type.
-            # TODO: Use typing._SpecialForm instead?
-            return self.named_type("builtins.object")
+            # The _SpecialForm type can be used in some runtime contexts (e.g. it may have __or__).
+            return self.named_type("typing._SpecialForm")
 
     def apply_type_arguments_to_callable(
         self, tp: Type, args: Sequence[Type], ctx: Context
@@ -4741,7 +4740,7 @@ def has_member(self, typ: Type, member: str) -> bool:
             typ = typ.fallback
         if isinstance(typ, Instance):
             return typ.type.has_readable_member(member)
-        if isinstance(typ, CallableType) and typ.is_type_obj():
+        if isinstance(typ, FunctionLike) and typ.is_type_obj():
             return typ.fallback.type.has_readable_member(member)
         elif isinstance(typ, AnyType):
             return True
diff --git a/mypy/messages.py b/mypy/messages.py
--- a/mypy/messages.py
+++ b/mypy/messages.py
@@ -132,6 +132,7 @@
     "builtins.isinstance": "isinstancelist.pyi",
     "builtins.property": "property.pyi",
     "builtins.classmethod": "classmethod.pyi",
+    "typing._SpecialForm": "typing-medium.pyi",
 }
 
 
@@ -2253,6 +2254,9 @@ def format_literal_value(typ: LiteralType) -> str:
             if itype.extra_attrs and itype.extra_attrs.mod_name and module_names:
                 return f"{base_str} {itype.extra_attrs.mod_name}"
             return base_str
+        if itype.type.fullname == "typing._SpecialForm":
+            # This is not a real type but used for some typing-related constructs.
+            return "<typing special form>"
         if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames):
             base_str = itype.type.fullname
         else:


Test patch:
diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test
--- a/mypyc/test-data/run-async.test
+++ b/mypyc/test-data/run-async.test
@@ -13,6 +13,9 @@ async def g() -> int:
 async def f() -> int:
     return await g()
 
+[file asyncio/__init__.pyi]
+async def sleep(t: float) -> None: ...
+
 [typing fixtures/typing-full.pyi]
 
 [file driver.py]
diff --git a/test-data/unit/check-ctypes.test b/test-data/unit/check-ctypes.test
--- a/test-data/unit/check-ctypes.test
+++ b/test-data/unit/check-ctypes.test
@@ -20,6 +20,7 @@ a[3] = b"bytes"  # E: No overload variant of "__setitem__" of "Array" matches ar
 for x in a:
     reveal_type(x)  # N: Revealed type is "builtins.int"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesArrayCustomElementType]
 import ctypes
@@ -52,6 +53,7 @@ myu: Union[ctypes.Array[ctypes.c_int], List[str]]
 for myi in myu:
     reveal_type(myi)  # N: Revealed type is "Union[builtins.int, builtins.str]"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesArrayUnionElementType]
 import ctypes
@@ -76,6 +78,7 @@ mya[3] = b"bytes"  # E: No overload variant of "__setitem__" of "Array" matches
 for myx in mya:
     reveal_type(myx)  # N: Revealed type is "Union[__main__.MyCInt, builtins.int]"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesCharArrayAttrs]
 import ctypes
@@ -84,6 +87,7 @@ ca = (ctypes.c_char * 4)(b'a', b'b', b'c', b'\x00')
 reveal_type(ca.value)  # N: Revealed type is "builtins.bytes"
 reveal_type(ca.raw)  # N: Revealed type is "builtins.bytes"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesCharPArrayDoesNotCrash]
 import ctypes
@@ -91,6 +95,7 @@ import ctypes
 # The following line used to crash with "Could not find builtin symbol 'NoneType'"
 ca = (ctypes.c_char_p * 0)()
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesWcharArrayAttrs]
 import ctypes
@@ -99,6 +104,7 @@ wca = (ctypes.c_wchar * 4)('a', 'b', 'c', '\x00')
 reveal_type(wca.value)  # N: Revealed type is "builtins.str"
 wca.raw  # E: Array attribute "raw" is only available with element type "c_char", not "c_wchar"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesCharUnionArrayAttrs]
 import ctypes
@@ -108,6 +114,7 @@ cua: ctypes.Array[Union[ctypes.c_char, ctypes.c_wchar]]
 reveal_type(cua.value)  # N: Revealed type is "Union[builtins.bytes, builtins.str]"
 cua.raw  # E: Array attribute "raw" is only available with element type "c_char", not "Union[c_char, c_wchar]"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesAnyUnionArrayAttrs]
 import ctypes
@@ -117,6 +124,7 @@ caa: ctypes.Array[Union[ctypes.c_char, Any]]
 reveal_type(caa.value)  # N: Revealed type is "Union[builtins.bytes, Any]"
 reveal_type(caa.raw)  # N: Revealed type is "builtins.bytes"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesOtherUnionArrayAttrs]
 import ctypes
@@ -126,6 +134,7 @@ cua: ctypes.Array[Union[ctypes.c_char, ctypes.c_int]]
 cua.value  # E: Array attribute "value" is only available with element type "c_char" or "c_wchar", not "Union[c_char, c_int]"
 cua.raw  # E: Array attribute "raw" is only available with element type "c_char", not "Union[c_char, c_int]"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesAnyArrayAttrs]
 import ctypes
@@ -134,6 +143,7 @@ aa: ctypes.Array[Any]
 reveal_type(aa.value)  # N: Revealed type is "Any"
 reveal_type(aa.raw)  # N: Revealed type is "builtins.bytes"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesOtherArrayAttrs]
 import ctypes
@@ -142,6 +152,7 @@ oa = (ctypes.c_int * 4)(1, 2, 3, 4)
 oa.value  # E: Array attribute "value" is only available with element type "c_char" or "c_wchar", not "c_int"
 oa.raw  # E: Array attribute "raw" is only available with element type "c_char", not "c_int"
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testCtypesArrayConstructorStarargs]
 import ctypes
@@ -154,6 +165,7 @@ reveal_type(intarr4(*int_values))  # N: Revealed type is "ctypes.Array[ctypes.c_
 reveal_type(intarr4(*c_int_values))  # N: Revealed type is "ctypes.Array[ctypes.c_int]"
 reveal_type(intarr6(1, ctypes.c_int(2), *int_values))  # N: Revealed type is "ctypes.Array[ctypes.c_int]"
 reveal_type(intarr6(1, ctypes.c_int(2), *c_int_values))  # N: Revealed type is "ctypes.Array[ctypes.c_int]"
+[typing fixtures/typing-medium.pyi]
 
 float_values = [1.0, 2.0, 3.0, 4.0]
 intarr4(*float_values) # E: Array constructor argument 1 of type "List[float]" is not convertible to the array element type "Iterable[c_int]"
@@ -167,3 +179,4 @@ x = {"a": 1, "b": 2}
 intarr4(**x)
 
 [builtins fixtures/floatdict.pyi]
+[typing fixtures/typing-medium.pyi]
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
--- a/test-data/unit/check-dataclasses.test
+++ b/test-data/unit/check-dataclasses.test
@@ -633,8 +633,9 @@ class Two:
 
 c = Two()
 x = c.S
-reveal_type(x)  # N: Revealed type is "builtins.object"
+reveal_type(x)  # N: Revealed type is "typing._SpecialForm"
 [builtins fixtures/dataclasses.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testDataclassOrdering]
 # flags: --python-version 3.7
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
@@ -1049,20 +1049,20 @@ CA = Callable[[T], int]
 TA = Tuple[T, int]
 UA = Union[T, int]
 
-cs = CA + 1 # E: Unsupported left operand type for + ("object")
+cs = CA + 1 # E: Unsupported left operand type for + ("<typing special form>")
 reveal_type(cs) # N: Revealed type is "Any"
 
-ts = TA() # E: "object" not callable
+ts = TA() # E: "<typing special form>" not callable
 reveal_type(ts) # N: Revealed type is "Any"
 
-us = UA.x # E: "object" has no attribute "x"
+us = UA.x # E: "<typing special form>" has no attribute "x"
 reveal_type(us) # N: Revealed type is "Any"
 
 xx = CA[str] + 1  # E: Type application is only supported for generic classes
 yy = TA[str]()  # E: Type application is only supported for generic classes
 zz = UA[str].x  # E: Type application is only supported for generic classes
 [builtins fixtures/tuple.pyi]
-
+[typing fixtures/typing-medium.pyi]
 [out]
 
 [case testGenericTypeAliasesTypeVarBinding]
diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test
--- a/test-data/unit/check-literal.test
+++ b/test-data/unit/check-literal.test
@@ -1484,16 +1484,17 @@ Alias = Literal[3]
 
 isinstance(3, Literal[3])           # E: Cannot use isinstance() with Literal type
 isinstance(3, Alias)                # E: Cannot use isinstance() with Literal type \
-                                    # E: Argument 2 to "isinstance" has incompatible type "object"; expected "Union[type, Tuple[Any, ...]]"
+                                    # E: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "Union[type, Tuple[Any, ...]]"
 isinstance(3, Renamed[3])           # E: Cannot use isinstance() with Literal type
 isinstance(3, indirect.Literal[3])  # E: Cannot use isinstance() with Literal type
 
 issubclass(int, Literal[3])           # E: Cannot use issubclass() with Literal type
 issubclass(int, Alias)                # E: Cannot use issubclass() with Literal type \
-                                      # E: Argument 2 to "issubclass" has incompatible type "object"; expected "Union[type, Tuple[Any, ...]]"
+                                      # E: Argument 2 to "issubclass" has incompatible type "<typing special form>"; expected "Union[type, Tuple[Any, ...]]"
 issubclass(int, Renamed[3])           # E: Cannot use issubclass() with Literal type
 issubclass(int, indirect.Literal[3])  # E: Cannot use issubclass() with Literal type
 [builtins fixtures/isinstancelist.pyi]
+[typing fixtures/typing-medium.pyi]
 [out]
 
 [case testLiteralErrorsWhenSubclassed]
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1788,3 +1788,19 @@ def f6(a: object) -> None:
         case _ if y is not None:  # E: Name "y" may be undefined
             pass
 [builtins fixtures/tuple.pyi]
+
+[case testTypeAliasWithNewUnionSyntaxAndNoneLeftOperand]
+from typing import overload
+class C:
+    @overload
+    def __init__(self) -> None: pass
+    @overload
+    def __init__(self, x: int) -> None: pass
+    def __init__(self, x=0):
+        pass
+
+class D: pass
+
+X = None | C
+Y = None | D
+[builtins fixtures/type.pyi]
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -821,28 +821,28 @@ c = Child()
 
 reveal_type(NormalImplicit)   # N: Revealed type is "def () -> __main__.Foo"
 reveal_type(NormalExplicit)   # N: Revealed type is "def () -> __main__.Foo"
-reveal_type(SpecialImplicit)  # N: Revealed type is "builtins.object"
-reveal_type(SpecialExplicit)  # N: Revealed type is "builtins.object"
+reveal_type(SpecialImplicit)  # N: Revealed type is "typing._SpecialForm"
+reveal_type(SpecialExplicit)  # N: Revealed type is "typing._SpecialForm"
 
 reveal_type(Parent.NormalImplicit)   # N: Revealed type is "def () -> __main__.Foo"
 reveal_type(Parent.NormalExplicit)   # N: Revealed type is "def () -> __main__.Foo"
-reveal_type(Parent.SpecialImplicit)  # N: Revealed type is "builtins.object"
-reveal_type(Parent.SpecialExplicit)  # N: Revealed type is "builtins.object"
+reveal_type(Parent.SpecialImplicit)  # N: Revealed type is "typing._SpecialForm"
+reveal_type(Parent.SpecialExplicit)  # N: Revealed type is "typing._SpecialForm"
 
 reveal_type(Child.NormalImplicit)   # N: Revealed type is "def () -> __main__.Foo"
 reveal_type(Child.NormalExplicit)   # N: Revealed type is "def () -> __main__.Foo"
-reveal_type(Child.SpecialImplicit)  # N: Revealed type is "builtins.object"
-reveal_type(Child.SpecialExplicit)  # N: Revealed type is "builtins.object"
+reveal_type(Child.SpecialImplicit)  # N: Revealed type is "typing._SpecialForm"
+reveal_type(Child.SpecialExplicit)  # N: Revealed type is "typing._SpecialForm"
 
 reveal_type(p.NormalImplicit)   # N: Revealed type is "def () -> __main__.Foo"
 reveal_type(p.NormalExplicit)   # N: Revealed type is "def () -> __main__.Foo"
-reveal_type(p.SpecialImplicit)  # N: Revealed type is "builtins.object"
-reveal_type(p.SpecialExplicit)  # N: Revealed type is "builtins.object"
+reveal_type(p.SpecialImplicit)  # N: Revealed type is "typing._SpecialForm"
+reveal_type(p.SpecialExplicit)  # N: Revealed type is "typing._SpecialForm"
 
 reveal_type(c.NormalImplicit)   # N: Revealed type is "def () -> __main__.Foo"
 reveal_type(p.NormalExplicit)   # N: Revealed type is "def () -> __main__.Foo"
-reveal_type(c.SpecialImplicit)  # N: Revealed type is "builtins.object"
-reveal_type(c.SpecialExplicit)  # N: Revealed type is "builtins.object"
+reveal_type(c.SpecialImplicit)  # N: Revealed type is "typing._SpecialForm"
+reveal_type(c.SpecialExplicit)  # N: Revealed type is "typing._SpecialForm"
 
 # Use type aliases in a type alias context in a plausible way
 
@@ -895,6 +895,7 @@ reveal_type(weird_child_2)  # N: Revealed type is "def () -> Any"
 reveal_type(weird_child_3)  # N: Revealed type is "def () -> Any"
 reveal_type(weird_child_4)  # N: Revealed type is "def () -> Any"
 [builtins fixtures/tuple.pyi]
+[typing fixtures/typing-medium.pyi]
 
 [case testMalformedTypeAliasRuntimeReassignments]
 from typing import Union
@@ -927,8 +928,8 @@ SpecialExplicit = 4  # E: Cannot assign multiple types to name "SpecialExplicit"
 
 Parent.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
 Parent.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
-Parent.SpecialImplicit = 4
-Parent.SpecialExplicit = 4
+Parent.SpecialImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "<typing special form>")
+Parent.SpecialExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "<typing special form>")
 
 Child.NormalImplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
 Child.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
@@ -945,3 +946,4 @@ c.NormalExplicit = 4  # E: Incompatible types in assignment (expression has type
 c.SpecialImplicit = 4
 c.SpecialExplicit = 4
 [builtins fixtures/tuple.pyi]
+[typing fixtures/typing-medium.pyi]
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
@@ -1660,3 +1660,55 @@ _testNarrowTypeForDictKeys.py:7: note: Revealed type is "builtins.str"
 _testNarrowTypeForDictKeys.py:9: note: Revealed type is "Union[builtins.str, None]"
 _testNarrowTypeForDictKeys.py:14: note: Revealed type is "builtins.str"
 _testNarrowTypeForDictKeys.py:16: note: Revealed type is "Union[builtins.str, None]"
+
+[case testTypeAliasWithNewStyleUnion]
+# flags: --python-version 3.10
+from typing import Literal, Type, TypeAlias
+
+Foo = Literal[1, 2]
+reveal_type(Foo)
+Bar1 = Foo | Literal[3]
+Bar2 = Literal[3] | Foo
+Bar3 = Foo | Foo | Literal[3] | Foo
+
+U1 = int | str
+U2 = U1 | bytes
+U3 = bytes | U1
+
+Opt1 = None | int
+Opt2 = None | float
+Opt3 = int | None
+Opt4 = float | None
+
+A = Type[int] | str
+B: TypeAlias = Type[int] | str
+[out]
+_testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"
+
+[case testTypeAliasWithNewStyleUnionInStub]
+# flags: --python-version 3.7
+import m
+
+[file m.pyi]
+from typing import Type
+from typing_extensions import Literal, TypeAlias
+
+Foo = Literal[1, 2]
+reveal_type(Foo)
+Bar1 = Foo | Literal[3]
+Bar2 = Literal[3] | Foo
+Bar3 = Foo | Foo | Literal[3] | Foo
+
+U1 = int | str
+U2 = U1 | bytes
+U3 = bytes | U1
+
+Opt1 = None | int
+Opt2 = None | float
+Opt3 = int | None
+Opt4 = float | None
+
+A = Type[int] | str
+B: TypeAlias = Type[int] | str
+[out]
+m.pyi:5: note: Revealed type is "typing._SpecialForm"