Skip to content

Regression: dict | None or dict fallback breaks type inference for nested dicts after PR #19249 #19492

Closed

Description

@KevinRK29

Bug Report

After upgrading mypy past PR #19249, we observed new errors related to dictionary fallback using or expressions when value types are unions of nested dicts and optional types.

This appears to be a regression in type inference, as the same code passed in previous versions of mypy.

To Reproduce

from typing import Union, Optional, Any

P = dict[str, Union[Optional[str], dict[str, Optional[str]]]]

def f(x: P) -> None:
    pass

def g(x: dict[str, Any] | None, s: str | None) -> None:
    f(x or {'x': s})  # ❌ Error in master branch, passes in latest mypy release (1.17.0)

mypy playground Gist: https://mypy-play.net/?mypy=master&python=3.12&gist=c4a5711c815d342425983850913896fe

Actual Behavior

main.py:9: error: Argument 1 to "f" has incompatible type "dict[str, Any] | dict[str, str | None]"; expected "dict[str, str | dict[str, str | None] | None]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: mypy 1.18.0+dev.abb1cc787455501e43f344c8af2de7d0d262e5ef
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.11

Activity

  1. sterliakov commented on Jul 26, 2025

    @sterliakov
    Collaborator

    Thanks for bisecting!

    This only used to work because of Any, {'x': s} was inferred as dict[str, Any] to match the LHS (so not an inference regression, strictly speaking, but still somewhat annoying).

  2. sterliakov commented on Jul 26, 2025

    @sterliakov
    Collaborator

    OK, the context is applied correctly. The problem is that we're failing to apply our "ignore trivial (only Any) constraints when or-ing them" fallback when constraints for multiple targets coexist.

    We're trying to or the following constraints:

    [
        [1 <: builtins.str, 1 :> builtins.str, 2 <: Any, 2 :> Any], 
        None, 
        [1 <: builtins.str, 1 :> builtins.str, 2 <: builtins.str | None | builtins.dict[builtins.str, builtins.str | None], 2 :> builtins.str | None | builtins.dict[builtins.str, builtins.str | None]],
    ]
    

    None is ignored. We can or subsets of those constraints for every variable, but can't or whole groups.

    mypy/mypy/constraints.py

    Lines 539 to 544 in d528707

    # All options have same structure. In this case we can merge-in trivial
    # options (i.e. those that only have Any) and try again.
    # TODO: More generally, if a given (variable, direction) pair appears in
    # every option, combine the bounds with meet/join always, not just for Any.
    trivial_options = select_trivial(valid_options)
    if trivial_options and len(trivial_options) < len(valid_options):

    I'll try to tackle that TODO sooner or later (it should be enough to resolve this ticket), but feel free to help:)

  3. JukkaL commented on Aug 19, 2025

    @JukkaL
    Collaborator

    @ilevkivskyi Would you have time to have a look at this, since this is potentially blocking the next release?

  4. ilevkivskyi commented on Aug 19, 2025

    @ilevkivskyi
    Member

    I can't give any promises, but I will try to look at this in the next few days.

  5. sterliakov commented on Aug 20, 2025

    @sterliakov
    Collaborator

    Okay, let's see the primer first, but it looks like I was overcomplicating things again.

  6. ilevkivskyi commented on Aug 21, 2025

    @ilevkivskyi
    Member

    @sterliakov Is my help still needed here, or you are working on this? (I see there are some problems in the PR for type variables with values.)

  7. sterliakov commented on Aug 22, 2025

    @sterliakov
    Collaborator

    @ilevkivskyi I'm looking into them, yes:)

  8. added a commit that references this issue on Aug 28, 2025
    e633140
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-type-contextType context / bidirectional inference

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions