Skip to content

Commit 4761f2d

Browse files
committed
woot woot
1 parent 6dead2e commit 4761f2d

File tree

3 files changed

+26
-35
lines changed

3 files changed

+26
-35
lines changed

crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,7 @@ def test_seq(x: Sequence[T]) -> Sequence[T]:
436436
def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: tuple[()]):
437437
reveal_type(test_seq(t1)) # revealed: Sequence[int | float | complex | list[int]]
438438
reveal_type(test_seq(t2)) # revealed: Sequence[int | str]
439-
440-
# TODO: this should be `Sequence[Never]`
441-
reveal_type(test_seq(t3)) # revealed: Sequence[Unknown]
439+
reveal_type(test_seq(t3)) # revealed: Sequence[Never]
442440
```
443441

444442
### `__init__` is itself generic

crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,7 @@ def test_seq[T](x: Sequence[T]) -> Sequence[T]:
375375
def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: tuple[()]):
376376
reveal_type(test_seq(t1)) # revealed: Sequence[int | float | complex | list[int]]
377377
reveal_type(test_seq(t2)) # revealed: Sequence[int | str]
378-
379-
# TODO: this should be `Sequence[Never]`
380-
reveal_type(test_seq(t3)) # revealed: Sequence[Unknown]
378+
reveal_type(test_seq(t3)) # revealed: Sequence[Never]
381379
```
382380

383381
### `__init__` is itself generic

crates/ty_python_semantic/src/types/generics.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,31 +1372,6 @@ impl<'db> SpecializationBuilder<'db> {
13721372
return Ok(());
13731373
}
13741374

1375-
// If the actual type is a subtype of the formal type, then return without adding any new
1376-
// type mappings. (Note that if the formal type contains any typevars, this check will
1377-
// fail, since no non-typevar types are assignable to a typevar. Also note that we are
1378-
// checking _subtyping_, not _assignability_, so that we do specialize typevars to dynamic
1379-
// argument types; and we have a special case for `Never`, which is a subtype of all types,
1380-
// but which we also do want as a specialization candidate.)
1381-
//
1382-
// In particular, this handles a case like
1383-
//
1384-
// ```py
1385-
// def f[T](t: T | None): ...
1386-
//
1387-
// f(None)
1388-
// ```
1389-
//
1390-
// without specializing `T` to `None`.
1391-
if !matches!(formal, Type::ProtocolInstance(_))
1392-
&& !actual.is_never()
1393-
&& actual
1394-
.when_subtype_of(self.db, formal, self.inferable)
1395-
.is_always_satisfied(self.db)
1396-
{
1397-
return Ok(());
1398-
}
1399-
14001375
// Remove the union elements from `actual` that are not related to `formal`, and vice
14011376
// versa.
14021377
//
@@ -1452,10 +1427,30 @@ impl<'db> SpecializationBuilder<'db> {
14521427
self.add_type_mapping(*formal_bound_typevar, remaining_actual);
14531428
}
14541429
(Type::Union(formal), _) => {
1455-
// Second, if the formal is a union, and precisely one union element _is_ a typevar (not
1456-
// _contains_ a typevar), then we add a mapping between that typevar and the actual
1457-
// type. (Note that we've already handled above the case where the actual is
1458-
// assignable to any _non-typevar_ union element.)
1430+
// Second, if the formal is a union, and precisely one union element is assignable
1431+
// from the actual type, then we don't add any type mapping. This handles a case like
1432+
//
1433+
// ```py
1434+
// def f[T](t: T | None): ...
1435+
//
1436+
// f(None)
1437+
// ```
1438+
//
1439+
// without specializing `T` to `None`.
1440+
//
1441+
// Otherwise, if precisely one union element _is_ a typevar (not _contains_ a
1442+
// typevar), then we add a mapping between that typevar and the actual type.
1443+
if !actual.is_never() {
1444+
let assignable_elements = (formal.elements(self.db).iter()).filter(|ty| {
1445+
actual
1446+
.when_subtype_of(self.db, **ty, self.inferable)
1447+
.is_always_satisfied(self.db)
1448+
});
1449+
if assignable_elements.exactly_one().is_ok() {
1450+
return Ok(());
1451+
}
1452+
}
1453+
14591454
let bound_typevars =
14601455
(formal.elements(self.db).iter()).filter_map(|ty| ty.as_typevar());
14611456
if let Ok(bound_typevar) = bound_typevars.exactly_one() {

0 commit comments

Comments
 (0)