[ty] Fix protocol methods whose receiver is annotated with the protocol
Summary
Since #27301, ty rejects valid implementations of a protocol method whose self is annotated with the protocol itself:
from typing import Protocol
class HasMethod(Protocol):
def method(self: "HasMethod", value: int, /) -> object: ...
class Implementation:
def method(self, value: int) -> None: ...
x: HasMethod = Implementation() # error[invalid-assignment]
A callback protocol with __call__(self: "Callback", ...) rejects every callable except a plain function: bound methods, classmethods, lambdas, callable instances and functools.partial objects.
Binding the protocol's method to the receiver Implementation checks Implementation against the receiver annotation HasMethod, which needs the same binding again. #27301 made that binding the cached protocol_apply_self_with_receiver query, so the cycle is now detected there instead of in the assignability queries, which assume a recursive relation holds. The query's initial value, CallableType::bottom, is permissive when it stands for an implementation's method, but it rejects everything when it stands for the protocol's requirement. The fixed point therefore settles on "not assignable".
This seeds the query with the binding that assumes the receiver satisfies its annotation: the same Self substitution, without receiver constraints. That matches how the assignability queries treat recursive relations: the relation holds unless another part of the comparison fails.
Closes astral-sh/ty#4746
#27227 and #28671 also change how explicit receivers are bound in protocol comparisons and edit the same functions.
Test Plan
Added mdtests for a class implementing a method with a protocol-typed receiver, for callback protocols (bound method, classmethod, lambda, callable instance, partial, and an incompatible function), and for a specialized generic receiver. They fail on main and pass with this change. The existing ty_python_semantic and ty tests pass unchanged.