Skip to content

dict.setdefault on an unpinned dict always makes the dict nullable (overload evaluation premature pinning) #2668

@yangdanny97

Description

@yangdanny97

Describe the Bug

available_clients = {}
def process_device(source, device):
    available_clients.setdefault(device.machineIdentifier, {"device": device}) # makes the value type dict | None
    available_clients[device.machineIdentifier].setdefault("source", source) # error!

The standard library type annotation on MutableMapping.setdefault weird here

class MutableMapping:
    # This overload should be allowed only if the value type is compatible with None.
    #
    # Keep the following methods in line with MutableMapping.setdefault, modulo positional-only differences:
    # -- collections.OrderedDict.setdefault
    # -- collections.ChainMap.setdefault
    # -- weakref.WeakKeyDictionary.setdefault
    @overload
    def setdefault(self: MutableMapping[_KT, _T | None], key: _KT, default: None = None, /) -> _T | None: ...
    @overload
    def setdefault(self, key: _KT, default: _VT, /) -> _VT: ...

I think maybe overload evaluation is pinning the type too early based on the first overload, even when it was not selected.

Sandbox Link

https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeSIqAbqtKtrAPoDGUEM6ALnAAQC8PYAF8AOlhhgexAE65m8OI0wwqEeQAo4uAK7T5AGh7LV8gJSIxPKz2q0o9Jq3Zc4hODE7KwqbVE7rjNRhCAFtUZgALCHQYAEllLghIGGlDYBEQQPkMxCMVIKFTS2tbOgYYFjYObgBtLOCwyOi4hM4k9mkAXTcPLx8-dQytXWyQQ2G9GFMxkDJpCShSQk5cEKgKAGIeAAVSebBFnjQsPHweZlx0SABzXVQ2y8IxLYBlGBgeCM5OYjhEAHp-nMFktcNJrv8OP9MHI4P8LlcILdpPcIJd-jwwGCbDQyrBzpcbncHugeLhiCTXGIyJwIpcALRUFJwNGkgQZADMhAAjAAmDJiEBCfSUZhtJkAMWgMAoxxwBBI5CEQA

(Only applicable for extension issues) IDE Information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions