Skip to content

Commit efbb80f

Browse files
authored
[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 #20517
1 parent 9f3cffc commit efbb80f

File tree

2 files changed

+38
-29
lines changed

2 files changed

+38
-29
lines changed

crates/ty_python_semantic/resources/mdtest/protocols.md

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,21 +1928,23 @@ from typing_extensions import TypeVar, Self, Protocol
19281928
from ty_extensions import is_equivalent_to, static_assert, is_assignable_to, is_subtype_of
19291929

19301930
class NewStyleClassScoped[T](Protocol):
1931-
def method(self, input: T) -> None: ...
1931+
def method(self: Self, input: T) -> None: ...
19321932

19331933
S = TypeVar("S")
19341934

19351935
class LegacyClassScoped(Protocol[S]):
1936-
def method(self, input: S) -> None: ...
1936+
def method(self: Self, input: S) -> None: ...
19371937

1938-
static_assert(is_equivalent_to(NewStyleClassScoped, LegacyClassScoped))
1939-
static_assert(is_equivalent_to(NewStyleClassScoped[int], LegacyClassScoped[int]))
1938+
# TODO: these should pass
1939+
static_assert(is_equivalent_to(NewStyleClassScoped, LegacyClassScoped)) # error: [static-assert-error]
1940+
static_assert(is_equivalent_to(NewStyleClassScoped[int], LegacyClassScoped[int])) # error: [static-assert-error]
19401941

19411942
class NominalGeneric[T]:
19421943
def method(self, input: T) -> None: ...
19431944

19441945
def _[T](x: T) -> T:
1945-
static_assert(is_equivalent_to(NewStyleClassScoped[T], LegacyClassScoped[T]))
1946+
# TODO: should pass
1947+
static_assert(is_equivalent_to(NewStyleClassScoped[T], LegacyClassScoped[T])) # error: [static-assert-error]
19461948
static_assert(is_subtype_of(NominalGeneric[T], NewStyleClassScoped[T]))
19471949
static_assert(is_subtype_of(NominalGeneric[T], LegacyClassScoped[T]))
19481950
return x
@@ -2016,17 +2018,27 @@ class NominalReturningSelfNotGeneric:
20162018
# TODO: should pass
20172019
static_assert(is_equivalent_to(LegacyFunctionScoped, NewStyleFunctionScoped)) # error: [static-assert-error]
20182020

2019-
static_assert(is_subtype_of(NominalNewStyle, NewStyleFunctionScoped))
2020-
static_assert(is_subtype_of(NominalNewStyle, LegacyFunctionScoped))
2021+
static_assert(is_assignable_to(NominalNewStyle, NewStyleFunctionScoped))
2022+
static_assert(is_assignable_to(NominalNewStyle, LegacyFunctionScoped))
2023+
# TODO: should pass
2024+
static_assert(is_subtype_of(NominalNewStyle, NewStyleFunctionScoped)) # error: [static-assert-error]
2025+
# TODO: should pass
2026+
static_assert(is_subtype_of(NominalNewStyle, LegacyFunctionScoped)) # error: [static-assert-error]
20212027
static_assert(not is_assignable_to(NominalNewStyle, UsesSelf))
20222028

2023-
static_assert(is_subtype_of(NominalLegacy, NewStyleFunctionScoped))
2024-
static_assert(is_subtype_of(NominalLegacy, LegacyFunctionScoped))
2029+
static_assert(is_assignable_to(NominalLegacy, NewStyleFunctionScoped))
2030+
static_assert(is_assignable_to(NominalLegacy, LegacyFunctionScoped))
2031+
# TODO: should pass
2032+
static_assert(is_subtype_of(NominalLegacy, NewStyleFunctionScoped)) # error: [static-assert-error]
2033+
# TODO: should pass
2034+
static_assert(is_subtype_of(NominalLegacy, LegacyFunctionScoped)) # error: [static-assert-error]
20252035
static_assert(not is_assignable_to(NominalLegacy, UsesSelf))
20262036

20272037
static_assert(not is_assignable_to(NominalWithSelf, NewStyleFunctionScoped))
20282038
static_assert(not is_assignable_to(NominalWithSelf, LegacyFunctionScoped))
2029-
static_assert(is_subtype_of(NominalWithSelf, UsesSelf))
2039+
static_assert(is_assignable_to(NominalWithSelf, UsesSelf))
2040+
# TODO: should pass
2041+
static_assert(is_subtype_of(NominalWithSelf, UsesSelf)) # error: [static-assert-error]
20302042

20312043
# TODO: these should pass
20322044
static_assert(not is_assignable_to(NominalNotGeneric, NewStyleFunctionScoped)) # error: [static-assert-error]
@@ -2035,8 +2047,23 @@ static_assert(not is_assignable_to(NominalNotGeneric, UsesSelf))
20352047

20362048
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, NewStyleFunctionScoped))
20372049
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, LegacyFunctionScoped))
2050+
20382051
# TODO: should pass
20392052
static_assert(not is_assignable_to(NominalReturningSelfNotGeneric, UsesSelf)) # error: [static-assert-error]
2053+
2054+
# These test cases are taken from the typing conformance suite:
2055+
class ShapeProtocolImplicitSelf(Protocol):
2056+
def set_scale(self, scale: float) -> Self: ...
2057+
2058+
class ShapeProtocolExplicitSelf(Protocol):
2059+
def set_scale(self: Self, scale: float) -> Self: ...
2060+
2061+
class BadReturnType:
2062+
def set_scale(self, scale: float) -> int:
2063+
return 42
2064+
2065+
static_assert(not is_assignable_to(BadReturnType, ShapeProtocolImplicitSelf))
2066+
static_assert(not is_assignable_to(BadReturnType, ShapeProtocolExplicitSelf))
20402067
```
20412068

20422069
## Subtyping of protocols with `@classmethod` or `@staticmethod` members

crates/ty_python_semantic/src/types/protocol_class.rs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use crate::{
2323
diagnostic::report_undeclared_protocol_member,
2424
signatures::{Parameter, Parameters},
2525
todo_type,
26-
visitor::any_over_type,
2726
},
2827
};
2928

@@ -571,24 +570,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> {
571570
attribute_type
572571
};
573572

574-
let proto_member_as_bound_method = method.bind_self(db);
575-
576-
if any_over_type(
577-
db,
578-
proto_member_as_bound_method,
579-
&|t| matches!(t, Type::TypeVar(_)),
580-
true,
581-
) {
582-
// TODO: proper validation for generic methods on protocols
583-
return ConstraintSet::from(true);
584-
}
585-
586-
attribute_type.has_relation_to_impl(
587-
db,
588-
proto_member_as_bound_method,
589-
relation,
590-
visitor,
591-
)
573+
attribute_type.has_relation_to_impl(db, method.bind_self(db), relation, visitor)
592574
}
593575
// TODO: consider the types of the attribute on `other` for property members
594576
ProtocolMemberKind::Property(_) => ConstraintSet::from(matches!(

0 commit comments

Comments
 (0)