Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

float and complex forcibly change invariant types to covariant #18257

Open
jorenham opened this issue Dec 6, 2024 · 1 comment
Open

float and complex forcibly change invariant types to covariant #18257

jorenham opened this issue Dec 6, 2024 · 1 comment
Labels
bug mypy got something wrong

Comments

@jorenham
Copy link

jorenham commented Dec 6, 2024

The following example illustrates the issue, and how fixing it will give rise to an important use-case.

mypy playground

from typing import Protocol


class Just[T](Protocol):
    """
    Taken from `optype.typing.Just`.
    https://github.com/jorenham/optype?tab=readme-ov-file#just-types
    """
    @property  # type: ignore[override]
    def __class__(self, /) -> type[T]: ...
    @__class__.setter
    def __class__(self, t: type[T], /) -> None: ...


def assert_object(x: Just[object], /) -> object:
    # free sentinels anyone?
    assert type(x) is object
    return x

def assert_int(x: Just[int], /) -> int:
    # resolves python/mypy#8363
    assert type(x) is int
    return x

def assert_float(x: Just[float], /) -> float:
    # `int` promotion workaround?
    assert type(x) is float
    return x
    
    
assert_object(object())  # ok: <accepted>
assert_object(0.0)       # ok: incompatible type "float"
assert_object(-1)        # ok: incompatible type "int"
assert_object(not 1)     # ok: incompatible type "bool"

assert_int(object())     # ok: incompatible type "object"
assert_int(22 / 7)       # ok: incompatible type "float"
assert_int(0x1337)       # ok: <accepted>
assert_int(0 > -0)       # ok: incompatible type "bool"  (just let that sink in(t)...)

assert_float(object())   # ok: incompatible type "object"
assert_float(3.14)       # ok: <accepted>
assert_float(-1)         # !?: <accepted>
assert_float(False)      # !?: <accepted>

# ... `float` forcibly changes `~T@Just` to `+T@Just`?

# Some notes:
#
# * same issue with `builtins.complex`
# * user-defined types such as `class A: ...` don't have this problem
# * pyright (1.1.390) doesn't have this issue

huh, what's going on with the syntax highlighting?

@jorenham jorenham added the bug mypy got something wrong label Dec 6, 2024
@jorenham jorenham changed the title float ignores invariance when used as type argument float ignores (in)variance when used as type argument Dec 6, 2024
@jorenham
Copy link
Author

jorenham commented Dec 7, 2024

@KotlinIsland tracked down the responsible code

mypy/mypy/subtypes.py

Lines 502 to 508 in ec4ccb0

if not self.subtype_context.ignore_promotions:
for base in left.type.mro:
if base._promote and any(
self._is_subtype(p, self.right) for p in base._promote
):
type_state.record_subtype_cache_entry(self._subtype_kind, left, right)
return True

@jorenham jorenham changed the title float ignores (in)variance when used as type argument float and complex forcibly change invariant types to covariant Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

1 participant