Skip to content

Commit e6120a6

Browse files
committed
avoid inferring intersection types for call arguments
1 parent 37ef0b4 commit e6120a6

6 files changed

Lines changed: 375 additions & 170 deletions

File tree

crates/ty_python_semantic/resources/mdtest/bidirectional.md

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ when generics are involved, the type of an outer expression can sometimes be use
88
inner expressions. Bidirectional type inference is a mechanism that propagates such "expected types"
99
to the inference of inner expressions.
1010

11-
## Propagating target type annotation
12-
1311
```toml
1412
[environment]
1513
python-version = "3.12"
1614
```
1715

16+
## Propagating target type annotation
17+
1818
```py
1919
from typing import Literal
2020

@@ -80,11 +80,6 @@ def _() -> TD:
8080

8181
## Propagating return type annotation
8282

83-
```toml
84-
[environment]
85-
python-version = "3.12"
86-
```
87-
8883
```py
8984
from typing import overload, Callable
9085

@@ -192,11 +187,6 @@ def f() -> list[Literal[1]]:
192187

193188
## Instance attributes
194189

195-
```toml
196-
[environment]
197-
python-version = "3.12"
198-
```
199-
200190
Both meta and class/instance attribute annotations are used as type context:
201191

202192
```py
@@ -240,13 +230,99 @@ def _(xy: X | Y):
240230
xy.x = reveal_type([1]) # revealed: list[int]
241231
```
242232

243-
## Class constructor parameters
233+
## Overloads
244234

245-
```toml
246-
[environment]
247-
python-version = "3.12"
235+
The type context of all matching overloads is considered during argument inference:
236+
237+
```py
238+
from typing import overload, TypedDict
239+
240+
def int_or_str() -> int | str:
241+
raise NotImplementedError
242+
243+
@overload
244+
def f1(x: list[int | None], y: int) -> int: ...
245+
@overload
246+
def f1(x: list[int | str], y: str) -> str: ...
247+
def f1(x, y) -> int | str:
248+
raise NotImplementedError
249+
250+
# TODO: We should reveal `list[int]` here.
251+
x1 = f1(reveal_type([1]), 1) # revealed: list[int]
252+
reveal_type(x1) # revealed: int
253+
254+
x2 = f1(reveal_type([1]), int_or_str()) # revealed: list[int]
255+
reveal_type(x2) # revealed: int | str
256+
257+
@overload
258+
def f2[T](x: T, y: int) -> T: ...
259+
@overload
260+
def f2(x: list[int | str], y: str) -> object: ...
261+
def f2(x, y) -> object: ...
262+
263+
x3 = f2(reveal_type([1]), 1) # revealed: list[int]
264+
reveal_type(x3) # revealed: list[int]
265+
266+
class TD(TypedDict):
267+
x: list[int | str]
268+
269+
class TD2(TypedDict):
270+
x: list[int | None]
271+
272+
@overload
273+
def f3(x: TD, y: int) -> int: ...
274+
@overload
275+
def f3(x: TD2, y: str) -> str: ...
276+
def f3(x, y) -> object: ...
277+
278+
# TODO: We should reveal `TD2` here.
279+
x4 = f3(reveal_type({"x": [1]}), "1") # revealed: dict[str, list[int]]
280+
reveal_type(x4) # revealed: str
281+
282+
x5 = f3(reveal_type({"x": [1]}), int_or_str()) # revealed: dict[str, list[int]]
283+
reveal_type(x5) # revealed: int | str
284+
285+
@overload
286+
def f4[T](_: list[T]) -> list[T]: ...
287+
@overload
288+
def f4(_: list[str]) -> list[str]: ...
289+
def f4(_: object): ...
290+
291+
x6 = f4(reveal_type([])) # revealed: list[Unknown]
292+
reveal_type(x6) # revealed: list[Unknown]
293+
294+
@overload
295+
def f5(_: list[int | str]) -> int: ...
296+
@overload
297+
def f5(_: set[int | str]) -> str: ...
298+
def f5(_) -> object:
299+
raise NotImplementedError
300+
301+
def list_or_set[T](x: T) -> list[T] | set[T]:
302+
raise NotImplementedError
303+
304+
# TODO: We should reveal `list[int | str] | set[int | str]` here.
305+
x7 = f5(reveal_type(list_or_set(1))) # revealed: list[int] | set[int]
306+
reveal_type(x7) # revealed: int | str
307+
308+
@overload
309+
def f6(_: list[int | None]) -> int: ...
310+
@overload
311+
def f6(_: set[int | str]) -> str: ...
312+
def f6(_) -> object:
313+
raise NotImplementedError
314+
315+
def list_or_set2[T, U](x: T, y: U) -> list[T] | set[U]:
316+
raise NotImplementedError
317+
318+
# TODO: We should not error here.
319+
# error: [no-matching-overload]
320+
x8 = f6(reveal_type(list_or_set2(1, 1))) # revealed: list[int] | set[int]
321+
reveal_type(x8) # revealed: Unknown
248322
```
249323

324+
## Class constructor parameters
325+
250326
The parameters of both `__init__` and `__new__` are used as type context sources for constructor
251327
calls:
252328

@@ -269,11 +345,6 @@ A(f([]))
269345

270346
## Conditional expressions
271347

272-
```toml
273-
[environment]
274-
python-version = "3.12"
275-
```
276-
277348
The type context is propagated through both branches of conditional expressions:
278349

279350
```py
@@ -290,11 +361,6 @@ def _(flag: bool):
290361

291362
## Dunder Calls
292363

293-
```toml
294-
[environment]
295-
python-version = "3.12"
296-
```
297-
298364
The key and value parameters types are used as type context for `__setitem__` dunder calls:
299365

300366
```py
@@ -387,11 +453,6 @@ def _(x: Intersection[X, Y]):
387453

388454
## Multi-inference diagnostics
389455

390-
```toml
391-
[environment]
392-
python-version = "3.12"
393-
```
394-
395456
Diagnostics unrelated to the type-context are only reported once:
396457

397458
```py

0 commit comments

Comments
 (0)