Change error conditions

This commit is contained in:
Charlie Marsh 2025-12-13 17:09:24 -05:00
parent 99655b54c3
commit d1ceb75960
2 changed files with 52 additions and 5 deletions

View File

@ -362,6 +362,45 @@ def _(x: Sequence[int], y: object):
reveal_type(item) # revealed: int reveal_type(item) # revealed: int
``` ```
## Intersection where some elements are not iterable
When iterating over an intersection type, we should only fail if all positive elements fail to
iterate. If some elements are iterable and some are not, we should iterate over the iterable ones
and intersect their element types.
```py
from ty_extensions import Intersection
class NotIterable:
pass
def _(x: Intersection[list[int], NotIterable]):
# `list[int]` is iterable (yielding `int`), but `NotIterable` is not.
# We should still be able to iterate over the intersection.
for item in x:
reveal_type(item) # revealed: int
```
## Intersection where all elements are not iterable
When iterating over an intersection type where all positive elements are not iterable, we should
fail to iterate.
```py
from ty_extensions import Intersection
class NotIterable1:
pass
class NotIterable2:
pass
def _(x: Intersection[NotIterable1, NotIterable2]):
# error: [not-iterable]
for item in x:
reveal_type(item) # revealed: Unknown
```
## Possibly-not-callable `__iter__` method ## Possibly-not-callable `__iter__` method
```py ```py

View File

@ -6615,11 +6615,19 @@ impl<'db> Type<'db> {
Type::Intersection(intersection) => { Type::Intersection(intersection) => {
// For intersections, we iterate over each positive element and intersect // For intersections, we iterate over each positive element and intersect
// the resulting element types. Negative elements don't affect iteration. // the resulting element types. Negative elements don't affect iteration.
let mut elements_iter = intersection.positive_elements_or_object(db); // We only fail if all elements fail to iterate; as long as at least one
let first_element_spec = elements_iter.next()?.try_iterate_with_mode(db, EvaluationMode::Sync).ok()?; // element can be iterated over, we can produce a result.
let mut builder = TupleSpecBuilder::from(&*first_element_spec); let mut specs_iter = intersection
for element in elements_iter { .positive_elements_or_object(db)
builder = builder.intersect(db, &*element.try_iterate_with_mode(db, EvaluationMode::Sync).ok()?); .filter_map(|element| {
element
.try_iterate_with_mode(db, EvaluationMode::Sync)
.ok()
});
let first_spec = specs_iter.next()?;
let mut builder = TupleSpecBuilder::from(&*first_spec);
for spec in specs_iter {
builder = builder.intersect(db, &spec);
} }
Some(Cow::Owned(builder.build())) Some(Cow::Owned(builder.build()))
} }