Skip to content

Commit

Permalink
Merge branch 'main' into binary-arithmetic
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Oct 18, 2024
2 parents ea515bd + 6d7da7b commit 8df9440
Show file tree
Hide file tree
Showing 61 changed files with 731 additions and 557 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:

macos-x86_64:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: macos-12
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ reveal_type(y) # revealed: Literal[1]
## Violates own annotation

```py
x: int = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"
x: int = 'foo' # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`"

```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class C:
if flag:
x = 2

reveal_type(C.x) # revealed: Literal[2]
reveal_type(C.y) # revealed: Literal[1]
reveal_type(C.x) # revealed: Literal[2]
reveal_type(C.y) # revealed: Literal[1]
```
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ else:
class C:
x = 2

y = C.x
reveal_type(y) # revealed: Literal[1, 2]
reveal_type(C.x) # revealed: Literal[1, 2]
```
67 changes: 35 additions & 32 deletions crates/red_knot_python_semantic/resources/mdtest/binary/integers.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
## Binary operations on integers
# Binary operations on integers

## Basic Arithmetic

```py
a = 2 + 1
b = a - 4
c = a * b
d = c // 3
e = c / 3
f = 5 % 3

reveal_type(a) # revealed: Literal[3]
reveal_type(b) # revealed: Literal[-1]
reveal_type(c) # revealed: Literal[-3]
reveal_type(d) # revealed: Literal[-1]
reveal_type(e) # revealed: float
reveal_type(f) # revealed: Literal[2]
reveal_type(2 + 1) # revealed: Literal[3]
reveal_type(3 - 4) # revealed: Literal[-1]
reveal_type(3 * -1) # revealed: Literal[-3]
reveal_type(-3 // 3) # revealed: Literal[-1]
reveal_type(-3 / 3) # revealed: float
reveal_type(5 % 3) # revealed: Literal[2]
```

## Division by Zero

```py
class MyInt(int):
def __truediv__(self, other):
return 100
This error is really outside the current Python type system, because e.g. `int.__truediv__` and
friends are not annotated to indicate that it's an error, and we don't even have a facility to
permit such an annotation. So arguably divide-by-zero should be a lint error rather than a type
checker error. But we choose to go ahead and error in the cases that are very likely to be an error:
dividing something typed as `int` or `float` by something known to be `Literal[0]`.

def returns_int() -> int:
return MyInt(3)
This isn't _definitely_ an error, because the object typed as `int` or `float` could be an instance
of a custom subclass which overrides division behavior to handle zero without error. But if this
unusual case occurs, the error can be avoided by explicitly typing the dividend as that safe custom
subclass; we only emit the error if the LHS type is exactly `int` or `float`, not if its a subclass.

# TODO: `a` should be `int` and `e` should be `float` once we support inference.
```py
a = 1 / 0 # error: "Cannot divide object of type `Literal[1]` by zero"
b = 2 // 0 # error: "Cannot floor divide object of type `Literal[2]` by zero"
c = 3 % 0 # error: "Cannot reduce object of type `Literal[3]` modulo zero"
# even `int` type could be a subclass of `int` with custom behavior; no error
d = returns_int() / 0
# this could be flagged as an error, if we had an ExactFloat or ExactInstance
# type, but given only a `float` type we can't issue an error for the same
# reason: could be a custom float subclass
e = 1.0 / 0

reveal_type(a) # revealed: float

b = 2 // 0 # error: "Cannot floor divide object of type `Literal[2]` by zero"
reveal_type(b) # revealed: int

c = 3 % 0 # error: "Cannot reduce object of type `Literal[3]` modulo zero"
reveal_type(c) # revealed: int
reveal_type(d) # revealed: float
reveal_type(e) # revealed: float

# error: "Cannot divide object of type `int` by zero"
# revealed: float
reveal_type(int() / 0)

# error: "Cannot divide object of type `float` by zero"
# revealed: float
reveal_type(1.0 / 0)

class MyInt(int): pass

# No error for a subclass of int
# revealed: float
reveal_type(MyInt(3) / 0)
```
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ class Multiplier:
return number * self.factor

a = Multiplier(2.0)(3.0)
reveal_type(a) # revealed: float

class Unit: ...

b = Unit()(3.0) # error: "Object of type `Unit` is not callable"

reveal_type(a) # revealed: float
reveal_type(b) # revealed: Unknown
b = Unit()(3.0) # error: "Object of type `Unit` is not callable"
reveal_type(b) # revealed: Unknown
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
```py
class Foo: ...

x = Foo()
reveal_type(x) # revealed: Foo
reveal_type(Foo()) # revealed: Foo
```
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
def get_int() -> int:
return 42

x = get_int()
reveal_type(x) # revealed: int
reveal_type(get_int()) # revealed: int
```

## Async
Expand All @@ -16,10 +15,8 @@ reveal_type(x) # revealed: int
async def get_int_async() -> int:
return 42

x = get_int_async()

# TODO: we don't yet support `types.CoroutineType`, should be generic `Coroutine[Any, Any, int]`
reveal_type(x) # revealed: @Todo
reveal_type(get_int_async()) # revealed: @Todo
```

## Decorated
Expand All @@ -37,10 +34,8 @@ def decorator(func) -> Callable[[], int]:
def bar() -> str:
return 'bar'

x = bar()

# TODO: should reveal `int`, as the decorator replaces `bar` with `foo`
reveal_type(x) # revealed: @Todo
reveal_type(bar()) # revealed: @Todo
```

## Invalid callable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ else:
def f() -> str:
return 'foo'

x = f()
reveal_type(x) # revealed: int | str
reveal_type(f()) # revealed: int | str
```

## Calling with an unknown union

```py
from nonexistent import f # error: [unresolved-import] "Cannot resolve import `nonexistent`"
from nonexistent import f # error: [unresolved-import] "Cannot resolve import `nonexistent`"

if flag:
def f() -> int:
return 1

x = f()
reveal_type(x) # revealed: Unknown | int
reveal_type(f()) # revealed: Unknown | int
```

## Non-callable elements in a union
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
### Comparison: Byte literals
# Comparison: Byte literals

These tests assert that we infer precise `Literal` types for comparisons between objects
inferred as having `Literal` bytes types:

```py
reveal_type(b"abc" == b"abc") # revealed: Literal[True]
reveal_type(b"abc" == b"ab") # revealed: Literal[False]
reveal_type(b"abc" == b"ab") # revealed: Literal[False]

reveal_type(b"abc" != b"abc") # revealed: Literal[False]
reveal_type(b"abc" != b"ab") # revealed: Literal[True]
reveal_type(b"abc" != b"ab") # revealed: Literal[True]

reveal_type(b"abc" < b"abd") # revealed: Literal[True]
reveal_type(b"abc" < b"abb") # revealed: Literal[False]
reveal_type(b"abc" < b"abd") # revealed: Literal[True]
reveal_type(b"abc" < b"abb") # revealed: Literal[False]

reveal_type(b"abc" <= b"abc") # revealed: Literal[True]
reveal_type(b"abc" <= b"abb") # revealed: Literal[False]

reveal_type(b"abc" > b"abd") # revealed: Literal[False]
reveal_type(b"abc" > b"abb") # revealed: Literal[True]
reveal_type(b"abc" > b"abd") # revealed: Literal[False]
reveal_type(b"abc" > b"abb") # revealed: Literal[True]

reveal_type(b"abc" >= b"abc") # revealed: Literal[True]
reveal_type(b"abc" >= b"abd") # revealed: Literal[False]

reveal_type(b"" in b"") # revealed: Literal[True]
reveal_type(b"" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"") # revealed: Literal[False]
reveal_type(b"ab" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"abc") # revealed: Literal[True]
reveal_type(b"d" in b"abc") # revealed: Literal[False]
reveal_type(b"ac" in b"abc") # revealed: Literal[False]
reveal_type(b"" in b"") # revealed: Literal[True]
reveal_type(b"" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"") # revealed: Literal[False]
reveal_type(b"ab" in b"abc") # revealed: Literal[True]
reveal_type(b"abc" in b"abc") # revealed: Literal[True]
reveal_type(b"d" in b"abc") # revealed: Literal[False]
reveal_type(b"ac" in b"abc") # revealed: Literal[False]
reveal_type(b"\x81\x82" in b"\x80\x81\x82") # revealed: Literal[True]
reveal_type(b"\x82\x83" in b"\x80\x81\x82") # revealed: Literal[False]

reveal_type(b"ab" not in b"abc") # revealed: Literal[False]
reveal_type(b"ac" not in b"abc") # revealed: Literal[True]
reveal_type(b"ab" not in b"abc") # revealed: Literal[False]
reveal_type(b"ac" not in b"abc") # revealed: Literal[True]

reveal_type(b"abc" is b"abc") # revealed: bool
reveal_type(b"abc" is b"ab") # revealed: Literal[False]
reveal_type(b"abc" is b"abc") # revealed: bool
reveal_type(b"abc" is b"ab") # revealed: Literal[False]

reveal_type(b"abc" is not b"abc") # revealed: bool
reveal_type(b"abc" is not b"ab") # revealed: Literal[True]
reveal_type(b"abc" is not b"ab") # revealed: Literal[True]
```
Original file line number Diff line number Diff line change
@@ -1,41 +1,27 @@
# Comparing integers
# Comparison: Integers

## Integer literals

```py
a = 1 == 1 == True
b = 1 == 1 == 2 == 4
c = False < True <= 2 < 3 != 6
d = 1 < 1
e = 1 > 1
f = 1 is 1
g = 1 is not 1
h = 1 is 2
i = 1 is not 7
j = 1 <= "" and 0 < 1

reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[False]
reveal_type(c) # revealed: Literal[True]
reveal_type(d) # revealed: Literal[False]
reveal_type(e) # revealed: Literal[False]
reveal_type(f) # revealed: bool
reveal_type(g) # revealed: bool
reveal_type(h) # revealed: Literal[False]
reveal_type(i) # revealed: Literal[True]
reveal_type(j) # revealed: @Todo | Literal[True]
reveal_type(1 == 1 == True) # revealed: Literal[True]
reveal_type(1 == 1 == 2 == 4) # revealed: Literal[False]
reveal_type(False < True <= 2 < 3 != 6) # revealed: Literal[True]
reveal_type(1 < 1) # revealed: Literal[False]
reveal_type(1 > 1) # revealed: Literal[False]
reveal_type(1 is 1) # revealed: bool
reveal_type(1 is not 1) # revealed: bool
reveal_type(1 is 2) # revealed: Literal[False]
reveal_type(1 is not 7) # revealed: Literal[True]
reveal_type(1 <= "" and 0 < 1) # revealed: @Todo | Literal[True]
```

## Integer instance

```py
# TODO: implement lookup of `__eq__` on typeshed `int` stub.
def int_instance() -> int: ...
a = 1 == int_instance()
b = 9 < int_instance()
c = int_instance() < int_instance()

reveal_type(a) # revealed: @Todo
reveal_type(b) # revealed: bool
reveal_type(c) # revealed: bool
reveal_type(1 == int_instance()) # revealed: @Todo
reveal_type(9 < int_instance()) # revealed: bool
reveal_type(int_instance() < int_instance()) # revealed: bool
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Non boolean returns
# Comparison: Non boolean returns

Walking through examples:

Expand Down Expand Up @@ -27,11 +27,12 @@ class B:
class C:
def __lt__(self, other) -> C: ...

a = A() < B() < C()
b = 0 < 1 < A() < 3
c = 10 < 0 < A() < B() < C()
x = A() < B() < C()
reveal_type(x) # revealed: A | B

reveal_type(a) # revealed: A | B
reveal_type(b) # revealed: bool | A
reveal_type(c) # revealed: Literal[False]
y = 0 < 1 < A() < 3
reveal_type(y) # revealed: bool | A

z = 10 < 0 < A() < B() < C()
reveal_type(z) # revealed: Literal[False]
```
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
# Comparing strings
# Comparison: Strings

## String literals

```py
def str_instance() -> str: ...
a = "abc" == "abc"
b = "ab_cd" <= "ab_ce"
c = "abc" in "ab cd"
d = "" not in "hello"
e = "--" is "--"
f = "A" is "B"
g = "--" is not "--"
h = "A" is not "B"
i = str_instance() < "..."
# ensure we're not comparing the interned salsa symbols, which compare by order of declaration.
j = "ab" < "ab_cd"

reveal_type(a) # revealed: Literal[True]
reveal_type(b) # revealed: Literal[True]
reveal_type(c) # revealed: Literal[False]
reveal_type(d) # revealed: Literal[False]
reveal_type(e) # revealed: bool
reveal_type(f) # revealed: Literal[False]
reveal_type(g) # revealed: bool
reveal_type(h) # revealed: Literal[True]
reveal_type(i) # revealed: bool
reveal_type(j) # revealed: Literal[True]
reveal_type("abc" == "abc") # revealed: Literal[True]
reveal_type("ab_cd" <= "ab_ce") # revealed: Literal[True]
reveal_type("abc" in "ab cd") # revealed: Literal[False]
reveal_type("" not in "hello") # revealed: Literal[False]
reveal_type("--" is "--") # revealed: bool
reveal_type("A" is "B") # revealed: Literal[False]
reveal_type("--" is not "--") # revealed: bool
reveal_type("A" is not "B") # revealed: Literal[True]
reveal_type(str_instance() < "...") # revealed: bool

# ensure we're not comparing the interned salsa symbols, which compare by order of declaration.
reveal_type("ab" < "ab_cd") # revealed: Literal[True]
```
Loading

0 comments on commit 8df9440

Please sign in to comment.