From 59fb462587b06ff7a8a20963c44d07ea7217e577 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 4 Nov 2025 12:20:48 -0500 Subject: [PATCH 1/3] simplify unions containing multiple type variables during inference --- .../resources/mdtest/generics/pep695/functions.md | 12 ++++++++++++ crates/ty_python_semantic/src/types/generics.rs | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index a5e62f686623d..48b6f7d4a5e6f 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -474,6 +474,18 @@ def g(x: str): f(prefix=x, suffix=".tar.gz") ``` +If the type variables is present multiple times in the union, we choose the correct union element to +infer against based on the argument type: + +```py +def h[T](x: list[T] | dict[T, T]) -> T | None: + ... + +def _(x: list[int], y: dict[int, int]): + reveal_type(h(x)) # revealed: int | None + reveal_type(h(y)) # revealed: int | None +``` + ## Nested functions see typevars bound in outer function ```py diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 444c5badd6622..992e664401762 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1397,11 +1397,13 @@ impl<'db> SpecializationBuilder<'db> { return Ok(()); } - // Remove the union elements that are not related to `formal`. + // Remove the union elements from `actual` that are not related to `formal`, and vice + // versa. // // For example, if `formal` is `list[T]` and `actual` is `list[int] | None`, we want to specialize `T` - // to `int`. + // to `int`, and so ignore the `None`. let actual = actual.filter_disjoint_elements(self.db, formal, self.inferable); + let formal = formal.filter_disjoint_elements(self.db, actual, self.inferable); match (formal, actual) { // TODO: We haven't implemented a full unification solver yet. If typevars appear in From c14501323b0f1237662cb5deb29ad984ca541053 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 4 Nov 2025 12:24:45 -0500 Subject: [PATCH 2/3] pre-commit --- .../resources/mdtest/generics/pep695/functions.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index 48b6f7d4a5e6f..e4d2ca098b14e 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -478,9 +478,7 @@ If the type variables is present multiple times in the union, we choose the corr infer against based on the argument type: ```py -def h[T](x: list[T] | dict[T, T]) -> T | None: - ... - +def h[T](x: list[T] | dict[T, T]) -> T | None: ... def _(x: list[int], y: dict[int, int]): reveal_type(h(x)) # revealed: int | None reveal_type(h(y)) # revealed: int | None From afa859eb41f7b48823f01dfa480831f9a64cb07a Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Wed, 5 Nov 2025 09:58:25 -0500 Subject: [PATCH 3/3] fix typo Co-authored-by: Carl Meyer --- .../resources/mdtest/generics/pep695/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index e4d2ca098b14e..5db84cfd5ab36 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -474,7 +474,7 @@ def g(x: str): f(prefix=x, suffix=".tar.gz") ``` -If the type variables is present multiple times in the union, we choose the correct union element to +If the type variable is present multiple times in the union, we choose the correct union element to infer against based on the argument type: ```py