Skip to content

mypyc generated __ne__ uses self type instead of __eq__'s RHS type with strict dunder typing #21568

@rheard

Description

@rheard

Bug Report

While testing fixes for #21566 , I tried to set strict_dunder_typing=True which allowed me to somewhat narrow the issue down, and I've sinced removed this bool. However it did expose a seemingly other issue.

When mypyc implicitly generates __ne__ from a user-defined __eq__ with strict_dunder_typing=True, the generated __ne__ appears to use the type of self as the type of its right-hand-side argument.

I may be misunderstanding the intended behavior of strict_dunder_typing, but my expectation is that the generated __ne__ should mirror the argument type of __eq__'s other parameter. In other words, if __eq__ accepts a particular set of object types, then the generated __ne__ should also accept that same set of object types.

To Reproduce

Add this test case to mypyc/test-data/run-dunders.test:

[case testDundersImplicitNeWithNarrowRhs]
class Accepted:
    pass

class Eq:
    def __eq__(self, other: Accepted) -> bool:  # type: ignore[override]
        return True

def test_implicit_ne_with_narrow_rhs() -> None:
    assert Eq() == Accepted()
    assert not (Eq() != Accepted())

Run the strict dunder tests:

python -m pytest -q mypyc/test/test_run.py -k testDundersImplicitNeWithNarrowRhs

Expected Behavior

Both comparisons should work.

Since __eq__ is defined as:

def __eq__(self, other: Accepted) -> bool:
    return True

the generated __ne__ should accept the same other type and behave as though it were approximately:

def __ne__(self, other: Accepted) -> bool:
    return not self.__eq__(other)

So this should pass:

assert Eq() == Accepted()
assert not (Eq() != Accepted())

Actual Behavior

The == comparison succeeds, but the != comparison fails at runtime because the implicitly generated __ne__ expects the right-hand-side argument to be Eq.

In other words, the generated __ne__ appears to behave as though its RHS argument type came from self, not from __eq__'s other parameter.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions