Skip to content

Always create correct instances #3305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2339,20 +2339,26 @@ def named_type(self, name: str) -> Instance:
"""
# Assume that the name refers to a type.
sym = self.lookup_qualified(name)
return Instance(cast(TypeInfo, sym.node), [])
node = sym.node
assert isinstance(node, TypeInfo)
return Instance(node, [AnyType()] * len(node.defn.type_vars))

def named_generic_type(self, name: str, args: List[Type]) -> Instance:
"""Return an instance with the given name and type arguments.

Assume that the number of arguments is correct. Assume that
the name refers to a compatible generic type.
"""
return Instance(self.lookup_typeinfo(name), args)
info = self.lookup_typeinfo(name)
# TODO: assert len(args) == len(info.defn.type_vars)
return Instance(info, args)

def lookup_typeinfo(self, fullname: str) -> TypeInfo:
# Assume that the name refers to a class.
sym = self.lookup_qualified(fullname)
return cast(TypeInfo, sym.node)
node = sym.node
assert isinstance(node, TypeInfo)
return node

def type_type(self) -> Instance:
"""Return instance type 'type'."""
Expand Down
35 changes: 25 additions & 10 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1063,15 +1063,23 @@ def class_type(self, info: TypeInfo) -> Type:

def named_type(self, qualified_name: str, args: List[Type] = None) -> Instance:
sym = self.lookup_qualified(qualified_name, None)
assert isinstance(sym.node, TypeInfo)
return Instance(sym.node, args or [])
node = sym.node
assert isinstance(node, TypeInfo)
if args:
# TODO: assert len(args) == len(node.defn.type_vars)
return Instance(node, args)
return Instance(node, [AnyType()] * len(node.defn.type_vars))

def named_type_or_none(self, qualified_name: str, args: List[Type] = None) -> Instance:
sym = self.lookup_fully_qualified_or_none(qualified_name)
if not sym:
return None
assert isinstance(sym.node, TypeInfo)
return Instance(sym.node, args or [])
node = sym.node
assert isinstance(node, TypeInfo)
if args:
# TODO: assert len(args) == len(node.defn.type_vars)
return Instance(node, args)
return Instance(node, [AnyType()] * len(node.defn.type_vars))

def is_typeddict(self, expr: Expression) -> bool:
return (isinstance(expr, RefExpr) and isinstance(expr.node, TypeInfo) and
Expand Down Expand Up @@ -2040,7 +2048,9 @@ def build_namedtuple_typeinfo(self, name: str, items: List[str], types: List[Typ
# Actual signature should return OrderedDict[str, Union[types]]
ordereddictype = (self.named_type_or_none('builtins.dict', [strtype, AnyType()])
or self.object_type())
fallback = self.named_type('__builtins__.tuple', types)
# 'builtins.tuple' has only one type parameter, the corresponding type argument
# in the fallback instance is a join of all item types.
fallback = self.named_type('__builtins__.tuple', [join.join_type_list(types)])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use type joins safely during semantic analysis, since MROs may be incomplete :-(

One potential way to fix this would be to postpone all joins to a new, fourth semantic analysis pass. That sounds risky though, so for the 0.510 release we could just use Any here as the fallback as a temporary workaround.

This has happened so many times that I've been considering asserting during joins and other type operations that need MROs that they aren't called during semantic analysis, at least when running tests.

# Note: actual signature should accept an invariant version of Iterable[UnionType[types]].
# but it can't be expressed. 'new' and 'len' should be callable types.
iterable_type = self.named_type_or_none('typing.Iterable', [AnyType()])
Expand Down Expand Up @@ -3113,9 +3123,10 @@ def lookup_qualified(self, name: str, ctx: Context) -> SymbolTableNode:
return n

def builtin_type(self, fully_qualified_name: str) -> Instance:
node = self.lookup_fully_qualified(fully_qualified_name)
assert isinstance(node.node, TypeInfo)
return Instance(node.node, [])
sym = self.lookup_fully_qualified(fully_qualified_name)
node = sym.node
assert isinstance(node, TypeInfo)
return Instance(node, [AnyType()] * len(node.defn.type_vars))

def lookup_fully_qualified(self, name: str) -> SymbolTableNode:
"""Lookup a fully qualified name.
Expand Down Expand Up @@ -3673,8 +3684,12 @@ def fail_blocker(self, msg: str, ctx: Context) -> None:
def builtin_type(self, name: str, args: List[Type] = None) -> Instance:
names = self.modules['builtins']
sym = names.names[name]
assert isinstance(sym.node, TypeInfo)
return Instance(sym.node, args or [])
node = sym.node
assert isinstance(node, TypeInfo)
if args:
# TODO: assert len(args) == len(node.defn.type_vars)
return Instance(node, args)
return Instance(node, [AnyType()] * len(node.defn.type_vars))


def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike:
Expand Down
18 changes: 9 additions & 9 deletions test-data/unit/check-async-await.test
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ async def f() -> int:
x = await g()
return x
[out]
main:7: error: Incompatible types in await (actual type Generator[int, None, str], expected type "Awaitable")
main:7: error: Incompatible types in await (actual type Generator[int, None, str], expected type Awaitable[Any])

[case testAwaitIteratorError]

Expand All @@ -95,7 +95,7 @@ async def f() -> int:
x = await g()
return x
[out]
main:6: error: Incompatible types in await (actual type Iterator[Any], expected type "Awaitable")
main:6: error: Incompatible types in await (actual type Iterator[Any], expected type Awaitable[Any])

[case testAwaitArgumentError]

Expand All @@ -106,7 +106,7 @@ async def f() -> int:
return x
[builtins fixtures/async_await.pyi]
[out]
main:5: error: Incompatible types in await (actual type "int", expected type "Awaitable")
main:5: error: Incompatible types in await (actual type "int", expected type Awaitable[Any])

[case testAwaitResultError]

Expand Down Expand Up @@ -271,7 +271,7 @@ class C:
def __aenter__(self) -> int: pass
async def __aexit__(self, x, y, z) -> None: pass
async def f() -> None:
async with C() as x: # E: Incompatible types in "async with" for __aenter__ (actual type "int", expected type "Awaitable")
async with C() as x: # E: Incompatible types in "async with" for __aenter__ (actual type "int", expected type Awaitable[Any])
pass
[builtins fixtures/async_await.pyi]
[out]
Expand All @@ -293,7 +293,7 @@ class C:
async def __aenter__(self) -> int: pass
def __aexit__(self, x, y, z) -> int: pass
async def f() -> None:
async with C() as x: # E: Incompatible types in "async with" for __aexit__ (actual type "int", expected type "Awaitable")
async with C() as x: # E: Incompatible types in "async with" for __aexit__ (actual type "int", expected type Awaitable[Any])
pass
[builtins fixtures/async_await.pyi]
[out]
Expand Down Expand Up @@ -615,11 +615,11 @@ def plain_host_generator() -> Generator[str, None, None]:

async def plain_host_coroutine() -> None:
x = 0
x = await plain_generator() # E: Incompatible types in await (actual type Generator[str, None, int], expected type "Awaitable")
x = await plain_generator() # E: Incompatible types in await (actual type Generator[str, None, int], expected type Awaitable[Any])
x = await plain_coroutine()
x = await decorated_generator()
x = await decorated_coroutine()
x = await other_iterator() # E: Incompatible types in await (actual type "It", expected type "Awaitable")
x = await other_iterator() # E: Incompatible types in await (actual type "It", expected type Awaitable[Any])
x = await other_coroutine()

@coroutine
Expand All @@ -636,11 +636,11 @@ def decorated_host_generator() -> Generator[str, None, None]:
@coroutine
async def decorated_host_coroutine() -> None:
x = 0
x = await plain_generator() # E: Incompatible types in await (actual type Generator[str, None, int], expected type "Awaitable")
x = await plain_generator() # E: Incompatible types in await (actual type Generator[str, None, int], expected type Awaitable[Any])
x = await plain_coroutine()
x = await decorated_generator()
x = await decorated_coroutine()
x = await other_iterator() # E: Incompatible types in await (actual type "It", expected type "Awaitable")
x = await other_iterator() # E: Incompatible types in await (actual type "It", expected type Awaitable[Any])
x = await other_coroutine()

[builtins fixtures/async_await.pyi]
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/fixtures/list.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class list(Iterable[T], Generic[T]):
def append(self, x: T) -> None: pass
def extend(self, x: Iterable[T]) -> None: pass

class tuple: pass
class tuple(Generic[T]): pass
class function: pass
class int: pass
class float: pass
Expand Down