Skip to content

Commit 3c7f11e

Browse files
committed
Handle recursive PEP 613 type aliases
1 parent 92a0ffc commit 3c7f11e

File tree

5 files changed

+92
-47
lines changed

5 files changed

+92
-47
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,6 +2617,19 @@ class D:
26172617
reveal_type(D().x) # revealed: Unknown | tuple[Divergent, Literal[1]]
26182618
```
26192619

2620+
And it also works for homogeneous tuples:
2621+
2622+
```py
2623+
def make_homogeneous_tuple(x: T) -> tuple[T, ...]:
2624+
return (x, x)
2625+
2626+
class E:
2627+
def f(self, other: "E"):
2628+
self.x = make_homogeneous_tuple(other.x)
2629+
2630+
reveal_type(E().x) # revealed: Unknown | tuple[Divergent, ...]
2631+
```
2632+
26202633
## Attributes of standard library modules that aren't yet defined
26212634

26222635
For attributes of stdlib modules that exist in future versions, we can give better diagnostics.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# PEP 613 type aliases
2+
3+
We do not support PEP 613 type aliases yet. For now, just make sure that we don't panic:
4+
5+
```py
6+
from typing import TypeAlias
7+
8+
RecursiveTuple: TypeAlias = tuple[int | "RecursiveTuple", str]
9+
10+
def _(rec: RecursiveTuple):
11+
reveal_type(rec) # revealed: tuple[Divergent, str]
12+
13+
RecursiveHomogeneousTuple: TypeAlias = tuple[int | "RecursiveHomogeneousTuple", ...]
14+
15+
def _(rec: RecursiveHomogeneousTuple):
16+
reveal_type(rec) # revealed: tuple[Divergent, ...]
17+
```

crates/ty_python_semantic/src/types/infer/builder/type_expression.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use crate::types::visitor::any_over_type;
1414
use crate::types::{
1515
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
1616
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
17-
TypeAliasType, TypeContext, TypeIsType, UnionBuilder, UnionType, todo_type,
17+
TypeAliasType, TypeContext, TypeIsType, UnionBuilder, UnionType,
18+
exceeds_max_specialization_depth, todo_type,
1819
};
1920

2021
/// Type expressions
@@ -560,8 +561,16 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
560561
ast::Expr::Tuple(elements) => {
561562
if let [element, ellipsis @ ast::Expr::EllipsisLiteral(_)] = &*elements.elts {
562563
self.infer_expression(ellipsis, TypeContext::default());
563-
let result =
564-
TupleType::homogeneous(self.db(), self.infer_type_expression(element));
564+
let element_ty = self.infer_type_expression(element);
565+
566+
let result = TupleType::homogeneous(
567+
self.db(),
568+
if exceeds_max_specialization_depth(self.db(), element_ty) {
569+
Type::divergent()
570+
} else {
571+
element_ty
572+
},
573+
);
565574
self.store_expression_type(tuple_slice, Type::tuple(Some(result)));
566575
return Some(result);
567576
}
@@ -584,7 +593,13 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
584593
// TODO: emit a diagnostic
585594
}
586595
} else {
587-
element_types.push(element_ty);
596+
element_types.push(
597+
if exceeds_max_specialization_depth(self.db(), element_ty) {
598+
Type::divergent()
599+
} else {
600+
element_ty
601+
},
602+
);
588603
}
589604
}
590605

crates/ty_python_semantic/src/types/instance.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ impl<'db> Type<'db> {
7272
{
7373
Type::tuple(TupleType::heterogeneous(
7474
db,
75-
elements.into_iter().map(Into::into).map(|ty| {
76-
if exceeds_max_specialization_depth(db, ty) {
75+
elements.into_iter().map(Into::into).map(|element| {
76+
if exceeds_max_specialization_depth(db, element) {
7777
Type::divergent()
7878
} else {
79-
ty
79+
element
8080
}
8181
}),
8282
))

crates/ty_python_semantic/src/types/visitor.rs

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -388,62 +388,62 @@ mod tests {
388388
fn test_generics_layering_depth() {
389389
let db = setup_db();
390390

391-
let list_of_int =
392-
KnownClass::List.to_specialized_instance(&db, [KnownClass::Int.to_instance(&db)]);
391+
let int = || KnownClass::Int.to_instance(&db);
392+
let list = |element| KnownClass::List.to_specialized_instance(&db, [element]);
393+
let dict = |key, value| KnownClass::Dict.to_specialized_instance(&db, [key, value]);
394+
let set = |element| KnownClass::Set.to_specialized_instance(&db, [element]);
395+
let str = || KnownClass::Str.to_instance(&db);
396+
let bytes = || KnownClass::Bytes.to_instance(&db);
397+
398+
let list_of_int = list(int());
393399
assert_eq!(specialization_depth(&db, list_of_int), 1);
394400

395-
let list_of_list_of_int = KnownClass::List.to_specialized_instance(&db, [list_of_int]);
401+
let list_of_list_of_int = list(list_of_int);
396402
assert_eq!(specialization_depth(&db, list_of_list_of_int), 2);
397403

398-
let list_of_list_of_list_of_int =
399-
KnownClass::List.to_specialized_instance(&db, [list_of_list_of_int]);
404+
let list_of_list_of_list_of_int = list(list_of_list_of_int);
400405
assert_eq!(specialization_depth(&db, list_of_list_of_list_of_int), 3);
401406

402-
let set_of_dict_of_str_and_list_of_int = KnownClass::Set.to_specialized_instance(
403-
&db,
404-
[KnownClass::Dict
405-
.to_specialized_instance(&db, [KnownClass::Str.to_instance(&db), list_of_int])],
406-
);
407+
assert_eq!(specialization_depth(&db, set(dict(str(), list_of_int))), 3);
408+
407409
assert_eq!(
408-
specialization_depth(&db, set_of_dict_of_str_and_list_of_int),
410+
specialization_depth(
411+
&db,
412+
UnionType::from_elements(&db, [list_of_list_of_list_of_int, list_of_list_of_int])
413+
),
409414
3
410415
);
411416

412-
let union_type_1 =
413-
UnionType::from_elements(&db, [list_of_list_of_list_of_int, list_of_list_of_int]);
414-
assert_eq!(specialization_depth(&db, union_type_1), 3);
415-
416-
let union_type_2 =
417-
UnionType::from_elements(&db, [list_of_list_of_int, list_of_list_of_list_of_int]);
418-
assert_eq!(specialization_depth(&db, union_type_2), 3);
417+
assert_eq!(
418+
specialization_depth(
419+
&db,
420+
UnionType::from_elements(&db, [list_of_list_of_int, list_of_list_of_list_of_int])
421+
),
422+
3
423+
);
419424

420-
let tuple_of_tuple_of_int = Type::heterogeneous_tuple(
421-
&db,
422-
[Type::heterogeneous_tuple(
425+
assert_eq!(
426+
specialization_depth(
423427
&db,
424-
[KnownClass::Int.to_instance(&db)],
425-
)],
428+
Type::heterogeneous_tuple(&db, [Type::heterogeneous_tuple(&db, [int()])])
429+
),
430+
2
426431
);
427-
assert_eq!(specialization_depth(&db, tuple_of_tuple_of_int), 2);
428432

429-
let tuple_of_list_of_int_and_str = KnownClass::Tuple
430-
.to_specialized_instance(&db, [list_of_int, KnownClass::Str.to_instance(&db)]);
431-
assert_eq!(specialization_depth(&db, tuple_of_list_of_int_and_str), 1);
433+
assert_eq!(
434+
specialization_depth(&db, Type::heterogeneous_tuple(&db, [list_of_int, str()])),
435+
2
436+
);
432437

433-
let list_of_union_of_lists = KnownClass::List.to_specialized_instance(
434-
&db,
435-
[UnionType::from_elements(
438+
assert_eq!(
439+
specialization_depth(
436440
&db,
437-
[
438-
KnownClass::List
439-
.to_specialized_instance(&db, [KnownClass::Int.to_instance(&db)]),
440-
KnownClass::List
441-
.to_specialized_instance(&db, [KnownClass::Str.to_instance(&db)]),
442-
KnownClass::List
443-
.to_specialized_instance(&db, [KnownClass::Bytes.to_instance(&db)]),
444-
],
445-
)],
441+
list(UnionType::from_elements(
442+
&db,
443+
[list(int()), list(str()), list(bytes())]
444+
))
445+
),
446+
2
446447
);
447-
assert_eq!(specialization_depth(&db, list_of_union_of_lists), 2);
448448
}
449449
}

0 commit comments

Comments
 (0)