Skip to content
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
7 changes: 7 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,13 @@ def analyze_ordinary_member_access(self, e: MemberExpr,
e.name, original_type, e, is_lvalue, False, False,
self.named_type, self.not_ready_callback, self.msg,
original_type=original_type, chk=self.chk)
if isinstance(member_type, CallableType):
for v in member_type.variables:
v.id.meta_level = 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we just switch the meta level, I think that we can get an id conflict between type variables in the 0 level if we happen to be in a generic function which has some additional type variables in scope. Not sure what's the best way to work around this. What about serializing the meta levels of type variables as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we can get an id conflict between type variables in the 0 level if we happen to be in a generic function which has some additional type variables in scope

I was also thinking about such possibility, but I didn't manage to trigger this error so I thought maybe I was wrong. Now you are also saying this and I am worried again. I need to think more about this tomorrow, maybe there is a way to exclude such possibility.

What about serializing the meta levels of type variables as well?

I think that serialazing type variables with meta_level == 1 is not a good idea. As I understand, this means that they are in process of being inferred, while serialazing is used for completely analysed modules, or am I wrong?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Actually, your original approach actually seems to be fine, since we are should always freshen the type variables before using them in type inference, so I think that there can be no conflict.

if isinstance(member_type, Overloaded):
for it in member_type.items():
for v in it.variables:
v.id.meta_level = 0
if is_lvalue:
return member_type
else:
Expand Down
19 changes: 19 additions & 0 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,25 @@ main:2: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=typing.Mapp
main:3: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=ntcrash.C.A@4)'
main:4: error: Revealed type is 'def () -> ntcrash.C.A@4'

[case testGenericMethodRestoreMetaLevel]
from typing import Dict

d = {} # type: Dict[str, int]
g = d.get # This should not crash: see https://github.com/python/mypy/issues/2804
[builtins fixtures/dict.pyi]

[case testGenericMethodRestoreMetaLevel2]
from typing import TypeVar

T = TypeVar('T')

class D:
def m(self, x: T) -> T:
return x

g = D().m # This should not crash: see https://github.com/python/mypy/issues/2804
[builtins fixtures/dict.pyi]

[case testIncrementalPerFileFlags]
# flags: --config-file tmp/mypy.ini
import a
Expand Down
6 changes: 5 additions & 1 deletion test-data/unit/fixtures/dict.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Builtins stub used in dictionary-related test cases.

from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload
from typing import TypeVar, Generic, Iterable, Iterator, Mapping, Tuple, overload, Optional, Union

T = TypeVar('T')
KT = TypeVar('KT')
Expand All @@ -19,6 +19,10 @@ class dict(Iterable[KT], Mapping[KT, VT], Generic[KT, VT]):
def __setitem__(self, k: KT, v: VT) -> None: pass
def __iter__(self) -> Iterator[KT]: pass
def update(self, a: Mapping[KT, VT]) -> None: pass
@overload
def get(self, k: KT) -> Optional[VT]: pass
@overload
def get(self, k: KT, default: Union[KT, T]) -> Union[VT, T]: pass

class int: # for convenience
def __add__(self, x: int) -> int: pass
Expand Down