-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[ty] Return a tuple spec from the iterator protocol #19496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cfb020d
cf8ea56
8494aa0
231d844
6325734
9f57971
6cee766
cb14197
5bebd97
b7c3791
b748d74
5e0820c
fe7a3cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -977,24 +977,14 @@ impl<'db> Bindings<'db> { | |||||||||||||||||||||||||||||||||||||||
// `tuple(range(42))` => `tuple[int, ...]` | ||||||||||||||||||||||||||||||||||||||||
// BUT `tuple((1, 2))` => `tuple[Literal[1], Literal[2]]` rather than `tuple[Literal[1, 2], ...]` | ||||||||||||||||||||||||||||||||||||||||
if let [Some(argument)] = overload.parameter_types() { | ||||||||||||||||||||||||||||||||||||||||
let overridden_return = | ||||||||||||||||||||||||||||||||||||||||
argument.into_tuple().map(Type::Tuple).unwrap_or_else(|| { | ||||||||||||||||||||||||||||||||||||||||
// Some awkward special handling is required here because of the fact | ||||||||||||||||||||||||||||||||||||||||
// that calling `try_iterate()` on `Never` returns `Never`, | ||||||||||||||||||||||||||||||||||||||||
// but `tuple[Never, ...]` eagerly simplifies to `tuple[()]`, | ||||||||||||||||||||||||||||||||||||||||
// which will cause us to emit false positives if we index into the tuple. | ||||||||||||||||||||||||||||||||||||||||
// Using `tuple[Unknown, ...]` avoids these false positives. | ||||||||||||||||||||||||||||||||||||||||
Comment on lines
-982
to
-986
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved this logic over into def _(never: Never):
for x in never:
reveal_type(x) # revealed: Never i.e., the body is never executed, just like when we iterate over the empty tuple: for x in ():
reveal_type(x) # revealed: Never This is consistent with simplifying The interpretation described here is that iterating over To me, the interpretation without this special case seems more intuitive, especially when you consider it in terms of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I added the special case in #18987, not #19469 — it was to tackle false positives I saw in the ecosystem report for sympy on early versions of that PR. If you look at the revisions to the primer report in #18987 (comment), the false positives for sympy are present in the penultimate version of the comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me the thing that seems wrong in this comment is that we simplify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No because that would imply the type is uninhabited. The situation for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that makes sense, but I do wonder if there is any practical benefit to that simplification. It seems like there is a practical cost, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dcreager added that particular simplification so I'll defer to him, but I'm a bit reluctant to give up on it. It feels obviously correct from the perspective of set-theoretic types. But we probably don't need the somewhat unprincipled special case I added here that Doug was asking me about -- a better fix would probably just be to check here whether the node is reachable before emitting the diagnostic: ruff/crates/ty_python_semantic/src/types/infer.rs Lines 8421 to 8439 in b605c3e
I think the machinery @sharkdp added should allow us to query that realtively easily? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think currently in order to query reachability for a node we have to know in advance that that node might need its reachability queried, and proactively store it in semantic indexing. Which has a memory cost if the category of nodes we are adding is large. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another possibility would be to special-case that iterating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If iterating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I've left the |
||||||||||||||||||||||||||||||||||||||||
let specialization = if argument.is_never() { | ||||||||||||||||||||||||||||||||||||||||
Type::unknown() | ||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||
argument.try_iterate(db).expect( | ||||||||||||||||||||||||||||||||||||||||
"try_iterate() should not fail on a type \ | ||||||||||||||||||||||||||||||||||||||||
assignable to `Iterable`", | ||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||
TupleType::homogeneous(db, specialization) | ||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||
overload.set_return_type(overridden_return); | ||||||||||||||||||||||||||||||||||||||||
let tuple_spec = argument.try_iterate(db).expect( | ||||||||||||||||||||||||||||||||||||||||
"try_iterate() should not fail on a type \ | ||||||||||||||||||||||||||||||||||||||||
assignable to `Iterable`", | ||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||
overload.set_return_type(Type::tuple(TupleType::new( | ||||||||||||||||||||||||||||||||||||||||
db, | ||||||||||||||||||||||||||||||||||||||||
tuple_spec.as_ref(), | ||||||||||||||||||||||||||||||||||||||||
))); | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
|
@@ -2097,14 +2087,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { | |||||||||||||||||||||||||||||||||||||||
// elements. For tuples, we don't have to do anything! For other types, we treat it as | ||||||||||||||||||||||||||||||||||||||||
// an iterator, and create a homogeneous tuple of its output type, since we don't know | ||||||||||||||||||||||||||||||||||||||||
// how many elements the iterator will produce. | ||||||||||||||||||||||||||||||||||||||||
// TODO: update `Type::try_iterate` to return this tuple type for us. | ||||||||||||||||||||||||||||||||||||||||
let argument_types = match argument_type { | ||||||||||||||||||||||||||||||||||||||||
Type::Tuple(tuple) => Cow::Borrowed(tuple.tuple(self.db)), | ||||||||||||||||||||||||||||||||||||||||
_ => { | ||||||||||||||||||||||||||||||||||||||||
let element_type = argument_type.iterate(self.db); | ||||||||||||||||||||||||||||||||||||||||
Cow::Owned(Tuple::homogeneous(element_type)) | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||
let argument_types = argument_type.iterate(self.db); | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
// TODO: When we perform argument expansion during overload resolution, we might need | ||||||||||||||||||||||||||||||||||||||||
// to retry both `match_parameters` _and_ `check_types` for each expansion. Currently | ||||||||||||||||||||||||||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.