-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Start using TypeAliasType in the semantic analyzer #7923
Conversation
(A side note, I can potentially make fewer but larger PRs, but IMO so far the idea of small PRs worked well for type aliases, so I would prefer to continue this way.) |
There is another important thing to call out, previously unions never contained another unions as items (because constructor flattened them), and some code might implicitly rely on this. IMO we should probably update these places, since maintaining this guarantee may be painful. |
Yet another important thing is that this may break many plugins, so we need to announce this in #6617 when will merge this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, this PR enables type alias types with fairly minor local changes. I'm no longer worried that introducing this is very risky, but I think that we should have at least one full week of internal testing before a public release.
Thank you for writing such detailed plan for the next steps! The plan looks solid to me.
def visit_type_alias_type(self, t: TypeAliasType) -> None: | ||
super().visit_type_alias_type(t) | ||
if t in self.seen_aliases: | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment about this special case.
@@ -27,6 +29,7 @@ def __init__(self, errors: Errors, options: Options, is_typeshed_file: bool) -> | |||
self.scope = Scope() | |||
# Should we also analyze function definitions, or only module top-levels? | |||
self.recurse_into_functions = True | |||
self.seen_aliases = set() # type: Set[TypeAliasType] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment (also explain why we need this).
mypy/types.py
Outdated
@@ -180,6 +180,10 @@ def _expand_once(self) -> Type: | |||
its public wrapper mypy.types.get_proper_type() is preferred. | |||
""" | |||
assert self.alias is not None | |||
if self.alias.no_args: | |||
assert isinstance(self.alias.target, ProperType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this? Add comment.
@@ -253,7 +253,7 @@ class TypeQuery(SyntheticTypeVisitor[T]): | |||
|
|||
def __init__(self, strategy: Callable[[Iterable[T]], T]) -> None: | |||
self.strategy = strategy | |||
self.seen = [] # type: List[Type] | |||
self.seen_aliases = set() # type: Set[TypeAliasType] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whill we ever need to clear this? If the visitor is single-use, mention this in the docstring.
This PR starts using the new
TypeAliasType
in the semantic analyzer. This PR doesn't yet pulls the trigger to enable the recursive types, but it is now essentially one line of code away. This PR:TypeAliasType
instead of eagerly expanding the alias target.TypeAliasExpr
to refer toTypeAlias
(sorry for the noise).get_proper_type()
I found necessary while playing with actual recursive types over the weekend.Here are some strategical comments:
It would probably make sense to test this well locally before a public release.
L = List; x: L[int]
, I preserve this by using eager expansion in this special case, otherwise it would complicate the whole logic significantly. This is also mostly a legacy thing because we have built-in aliases likeList = list
magically added by semantic analyzer.get_proper_type()
is not a best strategy. It saves all the existing special-casing but also introduces a risk for infinite recursion. In particular, "type ops tangle" should ideally always pass on the original alias type. Unfortunately, there is no way to fix/enforce this (without having some severe performance impact). Note it is mostly fine to "carelessly" useget_proper_type()
in the "front end" (likechecker.py
,checkexpr.py
,checkmember.py
etc).Here is my plan for the next five PRs:
SubtypeVisitor
andProperSubtypeVisitor
, there is very large amount of code duplication (there is already an issue for this).sametypes.py
(I am going to open a new issue, see my arguments there).TypeInfo
s, but will be stored asTypeAlias
. Essentially there will be almost no difference betweenA = Tuple[int, int]
andA = NamedTuple('A', [('x', int), ('y', int)])
. This will allow typed dicts and named tuple participate in recursive types.TypeInfo
s so it IMO it really makes sense to make them uniform (and avoid confusions and code duplication in future).5a. Potentially as a follow-up I may add support for generic named tuples and typed dicts, since steps 4 plus 5 will make this almost trivial.