[red-knot] Fix Stack overflow in Type::bool (#15843)
## Summary This PR adds `Type::call_bound` method for calls that should follow descriptor protocol calling convention. The PR is intentionally shallow in scope and only fixes #15672 Couple of obvious things that weren't done: * Switch to `call_bound` everywhere it should be used * Address the fact, that red_knot resolves `__bool__ = bool` as a Union, which includes `Type::Dynamic` and hence fails to infer that the truthiness is always false for such a class (I've added a todo comment in mdtests) * Doesn't try to invent a new type for descriptors, although I have a gut feeling it may be more convenient in the end, instead of doing method lookup each time like I did in `call_bound` ## Test Plan * extended mdtests with 2 examples from the issue * cargo neatest run
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# Truthiness
|
||||
|
||||
## Literals
|
||||
|
||||
```py
|
||||
from typing_extensions import Literal, LiteralString
|
||||
from knot_extensions import AlwaysFalsy, AlwaysTruthy
|
||||
@@ -45,3 +47,31 @@ def _(
|
||||
reveal_type(bool(c)) # revealed: bool
|
||||
reveal_type(bool(d)) # revealed: bool
|
||||
```
|
||||
|
||||
## Instances
|
||||
|
||||
Checks that we don't get into a cycle if someone sets their `__bool__` method to the `bool` builtin:
|
||||
|
||||
### __bool__ is bool
|
||||
|
||||
```py
|
||||
class BoolIsBool:
|
||||
__bool__ = bool
|
||||
|
||||
reveal_type(bool(BoolIsBool())) # revealed: bool
|
||||
```
|
||||
|
||||
### Conditional __bool__ method
|
||||
|
||||
```py
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
class Boom:
|
||||
if flag():
|
||||
__bool__ = bool
|
||||
else:
|
||||
__bool__ = int
|
||||
|
||||
reveal_type(bool(Boom())) # revealed: bool
|
||||
```
|
||||
|
||||
@@ -141,15 +141,6 @@ class AlwaysFalse:
|
||||
# revealed: Literal[True]
|
||||
reveal_type(not AlwaysFalse())
|
||||
|
||||
# We don't get into a cycle if someone sets their `__bool__` method to the `bool` builtin:
|
||||
class BoolIsBool:
|
||||
# TODO: The `type[bool]` declaration here is a workaround to avoid running into
|
||||
# https://github.com/astral-sh/ruff/issues/15672
|
||||
__bool__: type[bool] = bool
|
||||
|
||||
# revealed: bool
|
||||
reveal_type(not BoolIsBool())
|
||||
|
||||
# At runtime, no `__bool__` and no `__len__` means truthy, but we can't rely on that, because
|
||||
# a subclass could add a `__bool__` method.
|
||||
class NoBoolMethod: ...
|
||||
|
||||
Reference in New Issue
Block a user