mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ty] Remove hack in protocol satisfiability check (#20568)
## Summary This removes a hack in the protocol satisfiability check that was previously needed to work around missing assignability-modeling of inferable type variables. Assignability of type variables is not implemented fully, but some recent changes allow us to remove that hack with limited impact on the ecosystem (and the test suite). The change in the typing conformance test is favorable. ## Test Plan * Adapted Markdown tests * Made sure that this change works in combination with https://github.com/astral-sh/ruff/pull/20517
This commit is contained in:
parent
9f3cffc65c
commit
efbb80f747
2 changed files with 38 additions and 29 deletions
|
@ -1928,21 +1928,23 @@ from typing_extensions import TypeVar, Self, Protocol
|
||||||
from ty_extensions import is_equivalent_to, static_assert, is_assignable_to, is_subtype_of
|
from ty_extensions import is_equivalent_to, static_assert, is_assignable_to, is_subtype_of
|
||||||
|
|
||||||
class NewStyleClassScoped[T](Protocol):
|
class NewStyleClassScoped[T](Protocol):
|
||||||
def method(self, input: T) -> None: ...
|
def method(self: Self, input: T) -> None: ...
|
||||||
|
|
||||||
S = TypeVar("S")
|
S = TypeVar("S")
|
||||||
|
|
||||||
class LegacyClassScoped(Protocol[S]):
|
class LegacyClassScoped(Protocol[S]):
|
||||||
def method(self, input: S) -> None: ...
|
def method(self: Self, input: S) -> None: ...
|
||||||
|
|
||||||
static_assert(is_equivalent_to(NewStyleClassScoped, LegacyClassScoped))
|
# TODO: these should pass
|
||||||
static_assert(is_equivalent_to(NewStyleClassScoped[int], LegacyClassScoped[int]))
|
static_assert(is_equivalent_to(NewStyleClassScoped, LegacyClassScoped)) # error: [static-assert-error]
|
||||||
|
static_assert(is_equivalent_to(NewStyleClassScoped[int], LegacyClassScoped[int])) # error: [static-assert-error]
|
||||||
|
|
||||||
class NominalGeneric[T]:
|
class NominalGeneric[T]:
|
||||||
def method(self, input: T) -> None: ...
|
def method(self, input: T) -> None: ...
|
||||||
|
|
||||||
def _[T](x: T) -> T:
|
def _[T](x: T) -> T:
|
||||||
static_assert(is_equivalent_to(NewStyleClassScoped[T], LegacyClassScoped[T]))
|
# TODO: should pass
|
||||||
|
static_assert(is_equivalent_to(NewStyleClassScoped[T], LegacyClassScoped[T])) # error: [static-assert-error]
|
||||||
static_assert(is_subtype_of(NominalGeneric[T], NewStyleClassScoped[T]))
|
static_assert(is_subtype_of(NominalGeneric[T], NewStyleClassScoped[T]))
|
||||||
static_assert(is_subtype_of(NominalGeneric[T], LegacyClassScoped[T]))
|
static_assert(is_subtype_of(NominalGeneric[T], LegacyClassScoped[T]))
|
||||||
return x
|
return x
|
||||||
|
@ -2016,17 +2018,27 @@ class NominalReturningSelfNotGeneric:
|
||||||
# TODO: should pass
|
# TODO: should pass
|
||||||
static_assert(is_equivalent_to(LegacyFunctionScoped, NewStyleFunctionScoped)) # error: [static-assert-error]
|
static_assert(is_equivalent_to(LegacyFunctionScoped, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||||
|
|
||||||
static_assert(is_subtype_of(NominalNewStyle, NewStyleFunctionScoped))
|
static_assert(is_assignable_to(NominalNewStyle, NewStyleFunctionScoped))
|
||||||
static_assert(is_subtype_of(NominalNewStyle, LegacyFunctionScoped))
|
static_assert(is_assignable_to(NominalNewStyle, LegacyFunctionScoped))
|
||||||
|
# TODO: should pass
|
||||||
|
static_assert(is_subtype_of(NominalNewStyle, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||||
|
# TODO: should pass
|
||||||
|
static_assert(is_subtype_of(NominalNewStyle, LegacyFunctionScoped)) # error: [static-assert-error]
|
||||||
static_assert(not is_assignable_to(NominalNewStyle, UsesSelf))
|
static_assert(not is_assignable_to(NominalNewStyle, UsesSelf))
|
||||||
|
|
||||||
static_assert(is_subtype_of(NominalLegacy, NewStyleFunctionScoped))
|
static_assert(is_assignable_to(NominalLegacy, NewStyleFunctionScoped))
|
||||||
static_assert(is_subtype_of(NominalLegacy, LegacyFunctionScoped))
|
static_assert(is_assignable_to(NominalLegacy, LegacyFunctionScoped))
|
||||||
|
# TODO: should pass
|
||||||
|
static_assert(is_subtype_of(NominalLegacy, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||||
|
# TODO: should pass
|
||||||
|
static_assert(is_subtype_of(NominalLegacy, LegacyFunctionScoped)) # error: [static-assert-error]
|
||||||
static_assert(not is_assignable_to(NominalLegacy, UsesSelf))
|
static_assert(not is_assignable_to(NominalLegacy, UsesSelf))
|
||||||
|
|
||||||
static_assert(not is_assignable_to(NominalWithSelf, NewStyleFunctionScoped))
|
static_assert(not is_assignable_to(NominalWithSelf, NewStyleFunctionScoped))
|
||||||
static_assert(not is_assignable_to(NominalWithSelf, LegacyFunctionScoped))
|
static_assert(not is_assignable_to(NominalWithSelf, LegacyFunctionScoped))
|
||||||
static_assert(is_subtype_of(NominalWithSelf, UsesSelf))
|
static_assert(is_assignable_to(NominalWithSelf, UsesSelf))
|
||||||
|
# TODO: should pass
|
||||||
|
static_assert(is_subtype_of(NominalWithSelf, UsesSelf)) # error: [static-assert-error]
|
||||||
|
|
||||||
# TODO: these should pass
|
# TODO: these should pass
|
||||||
static_assert(not is_assignable_to(NominalNotGeneric, NewStyleFunctionScoped)) # error: [static-assert-error]
|
static_assert(not is_assignable_to(NominalNotGeneric, NewStyleFunctionScoped)) # error: [static-assert-error]
|
||||||
|
@ -2035,8 +2047,23 @@ static_assert(not is_assignable_to(NominalNotGeneric, UsesSelf))
|
||||||
|
|
||||||
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, NewStyleFunctionScoped))
|
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, NewStyleFunctionScoped))
|
||||||
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, LegacyFunctionScoped))
|
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, LegacyFunctionScoped))
|
||||||
|
|
||||||
# TODO: should pass
|
# TODO: should pass
|
||||||
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, UsesSelf)) # error: [static-assert-error]
|
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, UsesSelf)) # error: [static-assert-error]
|
||||||
|
|
||||||
|
# These test cases are taken from the typing conformance suite:
|
||||||
|
class ShapeProtocolImplicitSelf(Protocol):
|
||||||
|
def set_scale(self, scale: float) -> Self: ...
|
||||||
|
|
||||||
|
class ShapeProtocolExplicitSelf(Protocol):
|
||||||
|
def set_scale(self: Self, scale: float) -> Self: ...
|
||||||
|
|
||||||
|
class BadReturnType:
|
||||||
|
def set_scale(self, scale: float) -> int:
|
||||||
|
return 42
|
||||||
|
|
||||||
|
static_assert(not is_assignable_to(BadReturnType, ShapeProtocolImplicitSelf))
|
||||||
|
static_assert(not is_assignable_to(BadReturnType, ShapeProtocolExplicitSelf))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Subtyping of protocols with `@classmethod` or `@staticmethod` members
|
## Subtyping of protocols with `@classmethod` or `@staticmethod` members
|
||||||
|
|
|
@ -23,7 +23,6 @@ use crate::{
|
||||||
diagnostic::report_undeclared_protocol_member,
|
diagnostic::report_undeclared_protocol_member,
|
||||||
signatures::{Parameter, Parameters},
|
signatures::{Parameter, Parameters},
|
||||||
todo_type,
|
todo_type,
|
||||||
visitor::any_over_type,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -571,24 +570,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
|
||||||
attribute_type
|
attribute_type
|
||||||
};
|
};
|
||||||
|
|
||||||
let proto_member_as_bound_method = method.bind_self(db);
|
attribute_type.has_relation_to_impl(db, method.bind_self(db), relation, visitor)
|
||||||
|
|
||||||
if any_over_type(
|
|
||||||
db,
|
|
||||||
proto_member_as_bound_method,
|
|
||||||
&|t| matches!(t, Type::TypeVar(_)),
|
|
||||||
true,
|
|
||||||
) {
|
|
||||||
// TODO: proper validation for generic methods on protocols
|
|
||||||
return ConstraintSet::from(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_type.has_relation_to_impl(
|
|
||||||
db,
|
|
||||||
proto_member_as_bound_method,
|
|
||||||
relation,
|
|
||||||
visitor,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// TODO: consider the types of the attribute on `other` for property members
|
// TODO: consider the types of the attribute on `other` for property members
|
||||||
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
|
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue