Skip to content

Commit 4060082

Browse files
committed
don't consider self
1 parent ebfb33c commit 4060082

File tree

2 files changed

+140
-22
lines changed

2 files changed

+140
-22
lines changed

crates/ty_python_semantic/resources/mdtest/type_properties/is_subtype_of.md

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1963,6 +1963,10 @@ static_assert(not is_subtype_of(type[A], Callable[[int], A]))
19631963

19641964
### Bound methods
19651965

1966+
The bound `self` or `cls` parameter of a bound method has been curried away, and is no longer
1967+
available as a parameter when calling it. It therefore is not considered when checking subtyping
1968+
involving the bound method.
1969+
19661970
```py
19671971
from typing import Callable
19681972
from ty_extensions import TypeOf, static_assert, is_subtype_of
@@ -1981,12 +1985,131 @@ static_assert(is_subtype_of(TypeOf[a.f], Callable[[int], int]))
19811985
static_assert(is_subtype_of(TypeOf[a.g], Callable[[int], int]))
19821986
static_assert(is_subtype_of(TypeOf[A.g], Callable[[int], int]))
19831987

1984-
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[float], int]))
19851988
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[], int]))
19861989

19871990
static_assert(is_subtype_of(TypeOf[A.f], Callable[[A, int], int]))
19881991
```
19891992

1993+
Return types are in covariant position:
1994+
1995+
```py
1996+
static_assert(is_subtype_of(TypeOf[a.f], Callable[[int], object]))
1997+
static_assert(is_subtype_of(TypeOf[a.g], Callable[[int], object]))
1998+
static_assert(is_subtype_of(TypeOf[A.g], Callable[[int], object]))
1999+
2000+
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[int], bool]))
2001+
static_assert(not is_subtype_of(TypeOf[a.g], Callable[[int], bool]))
2002+
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[int], bool]))
2003+
```
2004+
2005+
Parameter types are in contravariant position:
2006+
2007+
```py
2008+
static_assert(not is_subtype_of(TypeOf[a.f], Callable[[object], int]))
2009+
static_assert(not is_subtype_of(TypeOf[a.g], Callable[[object], int]))
2010+
static_assert(not is_subtype_of(TypeOf[A.g], Callable[[object], int]))
2011+
2012+
static_assert(is_subtype_of(TypeOf[a.f], Callable[[bool], int]))
2013+
static_assert(is_subtype_of(TypeOf[a.g], Callable[[bool], int]))
2014+
static_assert(is_subtype_of(TypeOf[A.g], Callable[[bool], int]))
2015+
```
2016+
2017+
We should get the same results when comparing with a bound method of a different class.
2018+
2019+
```py
2020+
class B:
2021+
def returns_int(self, a: int) -> int:
2022+
return 0
2023+
2024+
def returns_object(self, a: int) -> object:
2025+
return a
2026+
2027+
def returns_bool(self, a: int) -> bool:
2028+
return True
2029+
2030+
def takes_object(self, a: object) -> int:
2031+
return 0
2032+
2033+
def takes_bool(self, a: bool) -> int:
2034+
return 0
2035+
2036+
@classmethod
2037+
def class_returns_int(cls, a: int) -> int:
2038+
return 0
2039+
2040+
@classmethod
2041+
def class_returns_object(cls, a: int) -> object:
2042+
return a
2043+
2044+
@classmethod
2045+
def class_returns_bool(cls, a: int) -> bool:
2046+
return True
2047+
2048+
@classmethod
2049+
def class_takes_object(cls, a: object) -> int:
2050+
return 0
2051+
2052+
@classmethod
2053+
def class_takes_bool(cls, a: bool) -> int:
2054+
return 0
2055+
2056+
b = B()
2057+
2058+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.returns_object]))
2059+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.returns_int]))
2060+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[b.returns_bool]))
2061+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[b.takes_object]))
2062+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.takes_bool]))
2063+
2064+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.class_returns_object]))
2065+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.class_returns_int]))
2066+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[b.class_returns_bool]))
2067+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[b.class_takes_object]))
2068+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[b.class_takes_bool]))
2069+
2070+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[B.class_returns_object]))
2071+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[B.class_returns_int]))
2072+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[B.class_returns_bool]))
2073+
static_assert(not is_subtype_of(TypeOf[a.f], TypeOf[B.class_takes_object]))
2074+
static_assert(is_subtype_of(TypeOf[a.f], TypeOf[B.class_takes_bool]))
2075+
2076+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.returns_object]))
2077+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.returns_int]))
2078+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[b.returns_bool]))
2079+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[b.takes_object]))
2080+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.takes_bool]))
2081+
2082+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.class_returns_object]))
2083+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.class_returns_int]))
2084+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[b.class_returns_bool]))
2085+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[b.class_takes_object]))
2086+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[b.class_takes_bool]))
2087+
2088+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[B.class_returns_object]))
2089+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[B.class_returns_int]))
2090+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[B.class_returns_bool]))
2091+
static_assert(not is_subtype_of(TypeOf[a.g], TypeOf[B.class_takes_object]))
2092+
static_assert(is_subtype_of(TypeOf[a.g], TypeOf[B.class_takes_bool]))
2093+
2094+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.returns_object]))
2095+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.returns_int]))
2096+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[b.returns_bool]))
2097+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[b.takes_object]))
2098+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.takes_bool]))
2099+
2100+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.class_returns_object]))
2101+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.class_returns_int]))
2102+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[b.class_returns_bool]))
2103+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[b.class_takes_object]))
2104+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[b.class_takes_bool]))
2105+
2106+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[B.class_returns_object]))
2107+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[B.class_returns_int]))
2108+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[B.class_returns_bool]))
2109+
static_assert(not is_subtype_of(TypeOf[A.g], TypeOf[B.class_takes_object]))
2110+
static_assert(is_subtype_of(TypeOf[A.g], TypeOf[B.class_takes_bool]))
2111+
```
2112+
19902113
### Overloads
19912114

19922115
#### Subtype overloaded

crates/ty_python_semantic/src/types.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9165,20 +9165,16 @@ impl<'db> BoundMethodType<'db> {
91659165
relation: TypeRelation,
91669166
visitor: &HasRelationToVisitor<'db>,
91679167
) -> ConstraintSet<'db> {
9168-
// A bound method is a typically a subtype of itself. However, we must explicitly verify
9169-
// the subtyping of the underlying function signatures (since they might be specialized
9170-
// differently), and of the bound self parameter (taking care that parameters, including a
9171-
// bound self parameter, are contravariant.)
9172-
self.function(db)
9173-
.has_relation_to_impl(db, other.function(db), relation, visitor)
9174-
.and(db, || {
9175-
other.self_instance(db).has_relation_to_impl(
9176-
db,
9177-
self.self_instance(db),
9178-
relation,
9179-
visitor,
9180-
)
9181-
})
9168+
// Note that we do not consider `self_instance`, and we use `into_callable_type` to compare
9169+
// the signatures of the bound methods _after_ the `self` parameter has been bound. The
9170+
// bound `self` is no longer available as a parameter to callers, and should therefore not
9171+
// influence the result of the check.
9172+
self.into_callable_type(db).has_relation_to_impl(
9173+
db,
9174+
other.into_callable_type(db),
9175+
relation,
9176+
visitor,
9177+
)
91829178
}
91839179

91849180
fn is_equivalent_to_impl(
@@ -9187,13 +9183,12 @@ impl<'db> BoundMethodType<'db> {
91879183
other: Self,
91889184
visitor: &IsEquivalentVisitor<'db>,
91899185
) -> ConstraintSet<'db> {
9190-
self.function(db)
9191-
.is_equivalent_to_impl(db, other.function(db), visitor)
9192-
.and(db, || {
9193-
other
9194-
.self_instance(db)
9195-
.is_equivalent_to_impl(db, self.self_instance(db), visitor)
9196-
})
9186+
// Note that we do not consider `self_instance`, and we use `into_callable_type` to compare
9187+
// the signatures of the bound methods _after_ the `self` parameter has been bound. The
9188+
// bound `self` is no longer available as a parameter to callers, and should therefore not
9189+
// influence the result of the check.
9190+
self.into_callable_type(db)
9191+
.is_equivalent_to_impl(db, other.into_callable_type(db), visitor)
91979192
}
91989193
}
91999194

0 commit comments

Comments
 (0)