Skip to content

Commit

Permalink
[red-knot] Statically known branches (#15019)
Browse files Browse the repository at this point in the history
## Summary

This changeset adds support for precise type-inference and
boundness-handling of definitions inside control-flow branches with
statically-known conditions, i.e. test-expressions whose truthiness we
can unambiguously infer as *always false* or *always true*.

This branch also includes:
- `sys.platform` support
- statically-known branches handling for Boolean expressions and while
  loops
- new `target-version` requirements in some Markdown tests which were
  now required due to the understanding of `sys.version_info` branches.

closes #12700 
closes #15034 

## Performance

### `tomllib`, -7%, needs to resolve one additional module (sys)

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/tomllib` | 22.2 ± 1.3 | 19.1 |
25.6 | 1.00 |
| `./red_knot_feature --project /home/shark/tomllib` | 23.8 ± 1.6 | 20.8
| 28.6 | 1.07 ± 0.09 |

### `black`, -6%

| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `./red_knot_main --project /home/shark/black` | 129.3 ± 5.1 | 119.0 |
137.8 | 1.00 |
| `./red_knot_feature --project /home/shark/black` | 136.5 ± 6.8 | 123.8
| 147.5 | 1.06 ± 0.07 |

## Test Plan

- New Markdown tests for the main feature in
  `statically-known-branches.md`
- New Markdown tests for `sys.platform`
- Adapted tests for `EllipsisType`, `Never`, etc
  • Loading branch information
sharkdp authored Dec 21, 2024
1 parent d3f51cf commit 000948a
Show file tree
Hide file tree
Showing 39 changed files with 3,187 additions and 632 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ def f():

## `typing.Never`

`typing.Never` is only available in Python 3.11 and later:
`typing.Never` is only available in Python 3.11 and later.

### Python 3.11

```toml
[environment]
Expand All @@ -57,8 +59,17 @@ python-version = "3.11"
```py
from typing import Never

x: Never
reveal_type(Never) # revealed: typing.Never
```

def f():
reveal_type(x) # revealed: Never
### Python 3.10

```toml
[environment]
python-version = "3.10"
```

```py
# error: [unresolved-import]
from typing import Never
```
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ b: tuple[int] = (42,)
c: tuple[str, int] = ("42", 42)
d: tuple[tuple[str, str], tuple[int, int]] = (("foo", "foo"), (42, 42))
e: tuple[str, ...] = ()
# TODO: we should not emit this error
# error: [call-possibly-unbound-method] "Method `__class_getitem__` of type `Literal[tuple]` is possibly unbound"
f: tuple[str, *tuple[int, ...], bytes] = ("42", b"42")
g: tuple[str, Unpack[tuple[int, ...]], bytes] = ("42", b"42")
h: tuple[list[int], list[int]] = ([], [])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,10 @@ def _(flag: bool):

```py
if True or (x := 1):
# TODO: infer that the second arm is never executed, and raise `unresolved-reference`.
# error: [possibly-unresolved-reference]
reveal_type(x) # revealed: Literal[1]
# error: [unresolved-reference]
reveal_type(x) # revealed: Unknown

if True and (x := 1):
# TODO: infer that the second arm is always executed, do not raise a diagnostic
# error: [possibly-unresolved-reference]
reveal_type(x) # revealed: Literal[1]
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ def _(flag: bool):
def __call__(self) -> int: ...

a = NonCallable()
# error: "Object of type `Literal[__call__] | Literal[1]` is not callable (due to union element `Literal[1]`)"
reveal_type(a()) # revealed: int | Unknown
# error: "Object of type `Literal[1] | Literal[__call__]` is not callable (due to union element `Literal[1]`)"
reveal_type(a()) # revealed: Unknown | int
```
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def _(flag: bool):
reveal_type(1 if flag else 2) # revealed: Literal[1, 2]
```

## Statically known branches
## Statically known conditions in if-expressions

```py
reveal_type(1 if True else 2) # revealed: Literal[1]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
# Ellipsis literals

## Simple
## Python 3.9

```toml
[environment]
python-version = "3.9"
```

```py
reveal_type(...) # revealed: ellipsis
```

## Python 3.10

```toml
[environment]
python-version = "3.10"
```

```py
reveal_type(...) # revealed: EllipsisType | ellipsis
reveal_type(...) # revealed: EllipsisType
```
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,14 @@ def _(t: type[object]):

### Handling of `None`

`types.NoneType` is only available in Python 3.10 and later:

```toml
[environment]
python-version = "3.10"
```

```py
# TODO: this error should ideally go away once we (1) understand `sys.version_info` branches,
# and (2) set the target Python version for this test to 3.10.
# error: [possibly-unbound-import] "Member `NoneType` of module `types` is possibly unbound"
from types import NoneType

def _(flag: bool):
Expand Down
Loading

0 comments on commit 000948a

Please sign in to comment.