Skip to content

Commit ad56164

Browse files
authored
Ensure we always infer a valid fallback type for lambda callables (#13576)
Fixes #9234 This diff fixes a bug in `infer_lambda_type_using_context` where it blindly trusted and reused whatever fallback the context callable was using. This causes mypy to crash in the case where the context was a dynamic constructor. This is because... 1. The constructor has a fallback of `builtins.type` 2. The Callable object `infer_lambda_type_using_context` returns uses this fallback as-is. 3. The join of the LHS and RHS of the ternary ends up being a `def (Any) -> Any` with a fallback of `builtins.type`. See: https://github.com/python/mypy/blob/7ffaf230a3984faaf848fe314cf275b854a0cdb0/mypy/join.py#L578 4. Later, we call `CallableType.is_type_obj()` and `CallableType.type_object()`. The former ends up succeeding due to the fallback, but the latter fails an assert because the return type is Any, not an Instance: https://github.com/python/mypy/blob/7ffaf230a3984faaf848fe314cf275b854a0cdb0/mypy/types.py#L1771 I opted to fix this by modifying `infer_lambda_type_using_context` so it overrides the fallback to always be `builtins.function` -- I don't think it makes sense for it to be anything else.
1 parent 71e19e8 commit ad56164

File tree

2 files changed

+19
-0
lines changed

2 files changed

+19
-0
lines changed

mypy/checkexpr.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4229,6 +4229,10 @@ def infer_lambda_type_using_context(
42294229
callable_ctx = get_proper_type(replace_meta_vars(ctx, ErasedType()))
42304230
assert isinstance(callable_ctx, CallableType)
42314231

4232+
# The callable_ctx may have a fallback of builtins.type if the context
4233+
# is a constructor -- but this fallback doesn't make sense for lambdas.
4234+
callable_ctx = callable_ctx.copy_modified(fallback=self.named_type("builtins.function"))
4235+
42324236
if callable_ctx.type_guard is not None:
42334237
# Lambda's return type cannot be treated as a `TypeGuard`,
42344238
# because it is implicit. And `TypeGuard`s must be explicit.

test-data/unit/check-inference.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,21 @@ class A:
12761276
def h(x: Callable[[], int]) -> None:
12771277
pass
12781278

1279+
[case testLambdaJoinWithDynamicConstructor]
1280+
from typing import Any, Union
1281+
1282+
class Wrapper:
1283+
def __init__(self, x: Any) -> None: ...
1284+
1285+
def f(cond: bool) -> Any:
1286+
f = Wrapper if cond else lambda x: x
1287+
reveal_type(f) # N: Revealed type is "def (x: Any) -> Any"
1288+
return f(3)
1289+
1290+
def g(cond: bool) -> Any:
1291+
f = lambda x: x if cond else Wrapper
1292+
reveal_type(f) # N: Revealed type is "def (x: Any) -> Any"
1293+
return f(3)
12791294

12801295
-- Boolean operators
12811296
-- -----------------

0 commit comments

Comments
 (0)