Skip to content
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

Streamline some elements of variadic types support #15924

Merged
merged 8 commits into from
Aug 23, 2023

Conversation

ilevkivskyi
Copy link
Member

@ilevkivskyi ilevkivskyi commented Aug 21, 2023

Fixes #13981
Fixes #15241
Fixes #15495
Fixes #15633
Fixes #15667
Fixes #15897
Fixes #15929

OK, I started following the plan outlined in #15879. In this PR I focused mostly on "kinematics". Here are some notes (in random order):

  • I decided to normalize TupleType and Instance items in semanal_typeargs.py (not in the type constructors, like for unions). It looks like a simpler way to normalize for now. After this, we can rely on the fact that only non-trivial (more below on what is trivial) variadic items in a type list is either *Ts or *tuple[X, ...]. A single top-level TupleType can appear in an unpack only as type of *args.
  • Callables turned out to be tricky. There is certain tight coupling between FuncDef.type and FuncDef.arguments that makes it hard to normalize prefix to be expressed as individual arguments at definition. I faced exactly the same problem when I implemented ** unpacking for TypedDicts. So we have two choices: either handle prefixes everywhere, or use normalization helper in relevant code. I propose to go with the latter (it worked well for ** unpacking).
  • I decided to switch Unpack to be disallowed by default in typeanal.py, only very specific potions are allowed now. Although this required plumbing allow_unpack all the way from semanal.py, conceptually it is simple. This is similar to how ParamSpec is handled.
  • This PR fixes all currently open crash issues (some intentionally, some accidentally) plus a bunch of TODOs I found in the tests (but not all).
  • I decided to simplify TypeAliasExpr (and made it simple reference to the SymbolNode, like e.g. TypedDictExpr and NamedTupleExpr). This is not strictly necessary for this PR, but it makes some parts of it a bit simpler, and I wanted to do it for long time.

Here is a more detailed plan of what I am leaving for future PRs (in rough order of priority):

  • Close non-crash open issues (it looks like there are only three, and all seem to be straightforward)
  • Handle trivial items in UnpackType gracefully. These are <nothing> and Any (and also potentially object). They can appear e.g. after a user error. Currently they can cause assert crashes. (Not sure what is the best way to do this).
  • Go over current places where Unpack is handled, and verify both possible variadic items are handled.
  • Audit variadic Instance constrains and subtyping (the latter is probably OK, but the former may be broken).
  • Audit Callable and Tuple subtyping for variadic-related edge cases (constraints seem OK for these).
  • Figure out story about map_instance_to_supertype() (if no changes are needed, add tests for subclassing).
  • Clear most remaining TODOs.
  • Go once more over the large scale picture and check whether we have some important parts missing (or unhandled interactions between those).
  • Verify various "advanced" typing features work well with TypeVarTuples (and add some support if missing but looks important).
  • Enable this feature by default.

I hope to finish these in next few weeks.

@github-actions

This comment has been minimized.

@AlexWaygood
Copy link
Member

This PR also fixes the just-opened #15929

@github-actions
Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@ilevkivskyi
Copy link
Member Author

@JukkaL @jhance I already have the next PR ready, it would be great if you can review this today.

@ilevkivskyi ilevkivskyi merged commit 6f650cf into python:master Aug 23, 2023
17 checks passed
@ilevkivskyi ilevkivskyi deleted the streamline-typevar-tuple branch August 23, 2023 19:26
ilevkivskyi added a commit that referenced this pull request Sep 7, 2023
This PR closes the first part of support for `TypeVarTuple`: the
"static" analysis of types (of course everything is static in mypy, but
some parts are more static): `semanal`/`typeanal`, `expand_type()`,
`map_instance_to_supertype()`, `erase_type()` (things that precede
and/or form foundation for type inference and subtyping). This one was
quite tricky, supporting unpacks of forward references required some
thinking.

What is included in this PR:
* Moving argument count validation from `semanal_typeargs` to
`typeanal`. In one of previous PRs I mentioned that `get_proper_type()`
may be called during semantic analysis causing troubles if we have
invalid aliases. So we need to move validation to early stage. For
instances, this is not required, but I strongly prefer keeping instances
and aliases similar. And ideally at some point we can combine the logic,
since it gets more and more similar. At some point we may want to
prohibit using `get_proper_type()` during semantic analysis, but I don't
want to block `TypeVarTuple` support on this, since this may be a
significant refactoring.
* Fixing `map_instance_to_supertype()` and `erase_type()`. These two are
straightforward, we either use `expand_type()` logic directly (by
calling it), or following the same logic.
* Few simplifications in `expandtype` and `typeops` following previous
normalizations of representation, unless there is a flaw in my logic,
removed branches should be all dead code.
* Allow (only fixed) unpacks in argument lists for non-variadic types.
They were prohibited for no good reason.
* (Somewhat limited) support for forward references in unpacks. As I
mentioned this one is tricky because of how forward references are
represented. Usually they follow either a life cycle like: `Any` ->
`<known type>`, or `<Any>` -> `<placeholder>` -> `<known type>` (second
one is relatively rare and usually only appears for potentially
recursive things like base classes or type alias targets). It looks like
`<placeholder>` can never appear as a _valid_ unpack target, I don't
have a proof for this, but I was not able to trigger this, so I am not
handling it (possible downside is that there may be extra errors about
invalid argument count for invalid unpack targets). If I am wrong and
this can happen in some valid cases, we can add handling for unpacks of
placeholders later. Currently, the handling for `Any` stage of forward
references is following: if we detect it, we simply create a dummy valid
alias or instance. This logic should work for the same reason having
plain `Any` worked in the first place (and why all tests pass if we
delete `visit_placeholder_type()`): because (almost) each time we
analyze a type, it is either already complete, or we analyze it _from
scratch_, i.e. we call `expr_to_unanalyzed_type()`, then
`visit_unbound_type()` etc. We almost never store "partially analyzed"
types (there are guards against incomplete references and placeholders
in annotations), and when we do, it is done in a controlled way that
guarantees a type will be re-analyzed again. Since this is such a tricky
subject, I didn't add any complex logic to support more tricky use cases
(like multiple forward references to fixed unpacks in single list). I
propose that we release this, and then see what kind of bug reports we
will get.
* Additional validation for type arguments position to ensure that
`TypeVarTuple`s are never split. Total count is not enough to ban case
where we have type variables `[T, *Ts, S, U]` and arguments `[int, int,
*Us, int]`. We need to explicitly ensure that actual suffix and prefix
are longer or equal to formal ones. Such splitting would be very hard to
support, and is explicitly banned by the PEP.
* Few minor cleanups.

Some random comments:
* It is tricky to preserve valid parts of type arguments, if there is an
argument count error involving an unpack. So after such error I simply
set all arguments to `Any` (or `*tuple[Any, ...]` when needed).
* I know there is some code duplication. I tried to factor it away, but
it turned out non-trivial. I may do some de-duplication pass after
everything is done, and it is easier to see the big picture.
* Type applications (i.e. when we have `A[int, int]` in runtime context)
are wild west currently. I decided to postpone variadic support for them
to a separate PR, because there is already some support (we will just
need to handle edge cases and more error conditions) and I wanted
minimize size of this PR.
* Something I wanted to mention in one of previous PRs but forgot: Long
time ago I proposed to normalize away type aliases inside `Unpack`, but
I abandoned this idea, it doesn't really give us any benefits.

As I said, this is the last PR for the "static part", in the next PR I
will work on fixing subtyping and inference for variadic instances. And
then will continue with remaining items I mentioned in my master plan in
#15924

Fixes #15978

---------

Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants