From 83f40e7dd46cc54b8442318c349642e1b5ba395e Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Jan 2022 18:46:47 +0300 Subject: [PATCH 1/4] Fix crash on `ErasedType` and `covers_at_runtime`, refs #11913 --- mypy/subtypes.py | 7 +++++++ test-data/unit/check-inference.test | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index cb7f53f0d7cb..7e44f5bb7b47 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1167,6 +1167,13 @@ def restrict_subtype_away(t: Type, s: Type, *, ignore_promotions: bool = False) def covers_at_runtime(item: Type, supertype: Type, ignore_promotions: bool) -> bool: """Will isinstance(item, supertype) always return True at runtime?""" item = get_proper_type(item) + if (isinstance(item, (ErasedType, DeletedType)) + or isinstance(supertype, (ErasedType, DeletedType))): + # It might happen when working with `lambda` arguments. + # Basically, we cannot tell at this point: whether some type will cover + # `lambda`'s argument or not. But, `ErasedType` or `DeletedType` + # will break our code later in `erase_type` visitor. + return False # Since runtime type checks will ignore type arguments, erase the types. supertype = erase_type(supertype) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index adf4a7b60420..4b2720a178d6 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3210,3 +3210,24 @@ def test(seq: List[Union[Iterable, Any]]) -> None: k = [k] reveal_type(k) # N: Revealed type is "builtins.list[Any]" [builtins fixtures/list.pyi] + +[case testErasedTypeRuntimeCoverage] +# https://github.com/python/mypy/issues/11913 +from typing import TypeVar, Type, Generic, Callable, Iterable + +_S = TypeVar('_S') +_T1 = TypeVar('_T1') + +class map(Generic[_S]): + def __init__(self, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> None: ... + +class DataType: ... +T = TypeVar("T", bound=DataType) + +def collection_from_dict_value(model: Type[T]) -> None: + map( + # This line was failing: + lambda i: i if isinstance(i, model) else i, + ["asdf"] + ) +[builtins fixtures/isinstancelist.pyi] From ad5d73ae72eeb631e6f5d186d84b17cfe298bf7d Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Jan 2022 19:19:08 +0300 Subject: [PATCH 2/4] Fixes CI --- mypy/subtypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 7e44f5bb7b47..7a6ed27a9727 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1167,6 +1167,7 @@ def restrict_subtype_away(t: Type, s: Type, *, ignore_promotions: bool = False) def covers_at_runtime(item: Type, supertype: Type, ignore_promotions: bool) -> bool: """Will isinstance(item, supertype) always return True at runtime?""" item = get_proper_type(item) + supertype = get_proper_type(supertype) if (isinstance(item, (ErasedType, DeletedType)) or isinstance(supertype, (ErasedType, DeletedType))): # It might happen when working with `lambda` arguments. From 21ec5d4c7c7ef258a8e79bb7fa71f726aa401c7b Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 6 Jan 2022 20:53:07 +0300 Subject: [PATCH 3/4] Return `ErasedType` from `erase_type` --- mypy/erasetype.py | 3 +-- mypy/subtypes.py | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/mypy/erasetype.py b/mypy/erasetype.py index 8acebbd783d8..3301325eb0a1 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -41,8 +41,7 @@ def visit_uninhabited_type(self, t: UninhabitedType) -> ProperType: return t def visit_erased_type(self, t: ErasedType) -> ProperType: - # Should not get here. - raise RuntimeError() + return t def visit_partial_type(self, t: PartialType) -> ProperType: # Should not get here. diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 7a6ed27a9727..200106000132 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1168,13 +1168,6 @@ def covers_at_runtime(item: Type, supertype: Type, ignore_promotions: bool) -> b """Will isinstance(item, supertype) always return True at runtime?""" item = get_proper_type(item) supertype = get_proper_type(supertype) - if (isinstance(item, (ErasedType, DeletedType)) - or isinstance(supertype, (ErasedType, DeletedType))): - # It might happen when working with `lambda` arguments. - # Basically, we cannot tell at this point: whether some type will cover - # `lambda`'s argument or not. But, `ErasedType` or `DeletedType` - # will break our code later in `erase_type` visitor. - return False # Since runtime type checks will ignore type arguments, erase the types. supertype = erase_type(supertype) From 3c3ecbb259cdb9579e27a2009ff47f3026f8fe29 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Fri, 7 Jan 2022 01:13:24 +0300 Subject: [PATCH 4/4] Update test-data/unit/check-inference.test Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- test-data/unit/check-inference.test | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index feb5c8346511..4de6e4a76f92 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3215,21 +3215,15 @@ def test(seq: List[Union[Iterable, Any]]) -> None: # https://github.com/python/mypy/issues/11913 from typing import TypeVar, Type, Generic, Callable, Iterable -_S = TypeVar('_S') -_T1 = TypeVar('_T1') +class DataType: ... -class map(Generic[_S]): - def __init__(self, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> None: ... +T1 = TypeVar('T1') +T2 = TypeVar("T2", bound=DataType) -class DataType: ... -T = TypeVar("T", bound=DataType) - -def collection_from_dict_value(model: Type[T]) -> None: - map( - # This line was failing: - lambda i: i if isinstance(i, model) else i, - ["asdf"] - ) +def map(__func: T1) -> None: ... + +def collection_from_dict_value(model: Type[T2]) -> None: + map(lambda i: i if isinstance(i, model) else i) [builtins fixtures/isinstancelist.pyi] [case testRegression11705_Strict]