You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Inconsistent erroring for wider argument type in operator override when using overloads (false negative) #22304
When overriding an overridden binary operator (e.g. __mul__, __add__) inside a subclass, mypy enforces that the subclass's operator argument is not a wider type than the superclass's because of operator's "double dispatch" behavior. This is desired behavior I think.
However, mypy does not catch some cases that are equivalent to cases that it errors on, specifically non-overloads, and so is inconsistent.
Expected: One and Two and Three all have their __mul__ method marked as an error, and are consistent.
(Alternatively, maybe all pass and have no errors.)
Actual Behavior
Only Two has its __mul__ method marked as an error, even though both One and Two effectively have the same method signature, and Three is also broader.
main.py:15: error: Signature of "__mul__" incompatible with supertype "Base" [override]
main.py:15: note: Superclass:
main.py:15: note: def __mul__(self, float, /) -> Two
main.py:15: note: Subclass:
main.py:15: note: @overload
main.py:15: note: def __mul__(self, float, /) -> Two
main.py:15: note: @overload
main.py:15: note: def __mul__(self, str, /) -> Two
main.py:15: note: Overloaded operator methods can't have wider argument types in overrides
Found 1 error in 1 file (checked 1 source file)
changed the title [-]Inconsistent erroring for wider argument type in operator override when using overloads[/-][+]Inconsistent erroring for wider argument type in operator override when using overloads (false negative)[/+]on Oct 26, 2026
Thanks for the clear repro — confirmed on mypy 1.18.2, same output as your 2.4.0 run: only Two errors, One and Three are silent.
Root cause — in mypy/checker.py::check_override, the "operator methods can't extend the domain" rule is gated on the override being an overload:
elifisinstance(override, Overloaded) andself.is_forward_op_method(name): # line 2501
For One, override is a plain CallableType, so that branch is skipped. The generic LSP check just above it (is_subtype(override, original), line 2499) passes, because widening a parameter is contravariance-correct for an ordinary method — which is exactly why the operator rule exists as a separate constraint (double dispatch: the reverse method on the supertype can be selected for operands the subclass widened into). So the operator-specific rule is currently only enforced on the Overloaded path.
The helper it uses, get_op_other_domain (line 2454), already handles CallableType as well as Overloaded, so dropping the isinstance(override, Overloaded) condition would put all three classes on the same footing and report One and Three too — the "all error" outcome you listed first.
Before I write that patch I'd like your call on the direction, because it isn't risk-free: every existing override of a forward operator method with a wider argument type that isn't written as an overload would start erroring. That pattern is reasonably common in numeric/tensor-style code, so it's a user-visible behavior change, and mypy's own self-check would likely surface some of them. The other consistent reading is that the overload-only rule is the intended scope and One/Three should stay silent.
Happy to implement the first option — plus cases in the override test data — if you confirm that's the direction you want.
Activity
[-]Inconsistent erroring for wider argument type in operator override when using overloads[/-][+]Inconsistent erroring for wider argument type in operator override when using overloads (false negative)[/+]inchang-ing commentedon Oct 28, 2026
Thanks for the clear repro — confirmed on mypy 1.18.2, same output as your 2.4.0 run: only
Twoerrors,OneandThreeare silent.Root cause — in
mypy/checker.py::check_override, the "operator methods can't extend the domain" rule is gated on the override being an overload:For
One,overrideis a plainCallableType, so that branch is skipped. The generic LSP check just above it (is_subtype(override, original), line 2499) passes, because widening a parameter is contravariance-correct for an ordinary method — which is exactly why the operator rule exists as a separate constraint (double dispatch: the reverse method on the supertype can be selected for operands the subclass widened into). So the operator-specific rule is currently only enforced on theOverloadedpath.The helper it uses,
get_op_other_domain(line 2454), already handlesCallableTypeas well asOverloaded, so dropping theisinstance(override, Overloaded)condition would put all three classes on the same footing and reportOneandThreetoo — the "all error" outcome you listed first.Before I write that patch I'd like your call on the direction, because it isn't risk-free: every existing override of a forward operator method with a wider argument type that isn't written as an overload would start erroring. That pattern is reasonably common in numeric/tensor-style code, so it's a user-visible behavior change, and mypy's own self-check would likely surface some of them. The other consistent reading is that the overload-only rule is the intended scope and
One/Threeshould stay silent.Happy to implement the first option — plus cases in the override test data — if you confirm that's the direction you want.