Skip to content

Use both sides of type context for generic type inference (part 2) - #22394

Merged
ilevkivskyi merged 4 commits into
python:masterfrom
ilevkivskyi:new-inference-2
Nov 10, 2026
Merged

ilevkivskyi merged 4 commits into
python:masterfrom
ilevkivskyi:new-inference-2

Conversation

@ilevkivskyi

@ilevkivskyi ilevkivskyi commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Ref #22063

Fixes #8478
Fixes #10281

This makes the new logic work in presence of second pass arguments (callables). I also did a little refactoring, and added some documenting comments.

Implementation is generally straightforward. I may add more tests depending on primer and mypy-issues.

@github-actions

This comment has been minimized.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

New error in discord.py looks like a new false positive, but I can't find an easy repro so far.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

OK, I found a repro

[case testTwoSidedInferenceWithParamSpecGeneric]
from typing import Callable, Generic, TypeVar, Union
from typing_extensions import ParamSpec

P = ParamSpec("P")
T = TypeVar("T")
I = TypeVar("I", bound=Union[int, str])

class C(Generic[T]): ...

def thing(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: ...

def test(x: C[I]) -> list[int]: ...
ci: C[int]
f: list[int] = thing(test, ci)
[builtins fixtures/paramspec.pyi]

This is however quite niche, it is related to weakness in how we calculate joins of "generic" Parameters. I will check if there is a simple fix.

@github-actions

This comment has been minimized.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

The change in trio is actually good IMO, I think this is ready for review. @hauntsaninja @JukkaL please take a look if you have time.

@JukkaL JukkaL left a comment

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.

This looks good. The only issue I found is that nested lambdas are slower to type check. Example where this matters:

from typing import Callable, Iterable, Sequence, TypeVar

T = TypeVar("T")
S = TypeVar("S")


def mapl(fn: Callable[[T], S], x: Iterable[T]) -> list[S]:
    return [fn(i) for i in x]


# The outer type context isn't needed to trigger the slowdown (the inner calls get
# context from the enclosing lambda), but it makes the first call two-sided too.
x: Sequence[object] = mapl(
    lambda v1: mapl(
        lambda v2: mapl(
            lambda v3: mapl(
                lambda v4: mapl(
                    lambda v5: mapl(
                        lambda v6: mapl(
                            lambda v7: mapl(lambda v8: mapl(lambda v9: v9, [1]), [1]),
                            [1],
                        ),
                        [1],
                    ),
                    [1],
                ),
                [1],
            ),
            [1],
        ),
        [1],
    ),
    [1],
)

It gets worse as the nesting depth increases, but this is probably not a major issue, as deeply nested lambdas are rare (though I've seen them in some real-world test code).

Comment thread mypy/checkexpr.py Outdated
# * Infer constraints from inner (argument) context (pass 1, non-callable arguments).
# * Solve the combined constraint set (outer + partial inner).
# * Apply the new (more precise) inferred type arguments to initial callable.
# * Re-infer argument types in more precise type cntext

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.

Typo: cntext

Comment thread mypy/checkexpr.py Outdated
if all(solid_inferred_type(a, type_vars_set) for a in inferred_args):
return self.apply_inferred_arguments(initial_callee_type, inferred_args, context)
# Before giving up, check first pass result. In some cases this may help in situations
# where both passes together create a system too tricky for out solver.

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.

Typo: out solver

Comment thread mypy/infer.py Outdated
)
if extra_constraints:
constraints += extra_constraints
# Combining extra constraints may shadow precise constrains by imprecise ones.

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.

Typo: constrains

@ilevkivskyi

Copy link
Copy Markdown
Member Author

@JukkaL Yeah, lambdas are a known problem, because we can't use expression cache inside them. The new inference scheme heavily relies on the expression cache (since its complexity is exponential without the cache). At some point we may need to think about this. One possible idea may be to store a separate stack of contexts for lambdas, and use combined context as a cache key. This will probably fix performance at least in some cases (like deeply nested calls within a single lambda), I didn't think a lot about it however.

@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

egglog-python (https://github.com/egraphs-good/egglog-python)
+ python/egglog/exp/array_api.py:69: error: Unused "type: ignore" comment  [unused-ignore]
+ python/egglog/exp/array_api.py:70: error: Unused "type: ignore" comment  [unused-ignore]

core (https://github.com/home-assistant/core)
- homeassistant/components/minecraft_server/api.py:95: error: Item "None" of "Any | None" has no attribute "timeout"  [union-attr]
+ homeassistant/components/fastdotcom/coordinator.py:34: error: Returning Any from function declared to return "dict[str, float] | None"  [no-any-return]

trio (https://github.com/python-trio/trio)
+ src/trio/_socket.py:1311: error: Unused "type: ignore" comment  [unused-ignore]
+ src/trio/_socket.py:1312: error: Argument 3 to "_nonblocking_helper" of "_SocketType" has incompatible type "*list[object]"; expected "Buffer"  [arg-type]
+ src/trio/_socket.py:1312: error: Argument 3 to "_nonblocking_helper" of "_SocketType" has incompatible type "*list[object]"; expected "tuple[Any, ...] | str | Buffer"  [arg-type]

pywin32 (https://github.com/mhammond/pywin32)
  ./pythonwin/pywin/test/_dbgscript.py: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
- version: 2.5.0+dev.b53c712057b5516f416c7f17fdf0744326d79c6c
+ version: 2.5.0+dev.9a6a30c35571d087d697a78a0fbdaded571e5a57

@ilevkivskyi

Copy link
Copy Markdown
Member Author

Oh interesting, it looks like pywin32 is crashing on master. I will take a look.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

This looks like a regression in the new parser, it now crashes on non-UTF-8 file (containing actual latin-1 symbols) that also has a function definition in it.

@JukkaL since this is completely unrelated to this PR, I will handle it later.

@ilevkivskyi
ilevkivskyi merged commit a0dda1c into python:master Nov 10, 2026
25 checks passed
@ilevkivskyi
ilevkivskyi deleted the new-inference-2 branch November 10, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants