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

Fix crashes in synthetic types #3322

Merged
merged 14 commits into from
May 24, 2017
Merged

Conversation

ilevkivskyi
Copy link
Member

@ilevkivskyi ilevkivskyi commented May 4, 2017

UPDATE: see #3322 (comment) below.

Fixes #3308

This PR adds better processing of "synthetic" types (NewType, NamedTuple, TypedDict) to the third pass and moves some processing from the second to the third pass. As @JukkaL noticed, this is still not perfect, since some MROs are recomputed in the third pass. I think the right way to fix this is just use unions in this case, the point is that users will practically never see these fallbacks, and this will also allow us to have better type expansions for fallbacks of generic tuple types. @JukkaL what do you think?

Additional fourth pass seems to be an overkill for this. Although, I have a big project in my future plans (probably after we are more or less done with protocols): providing better support for forward references (there are a dozen similar issues about this) and recursive types. Even for this fourth pass seems to be a bit an overkill, and I would rather proceed as Jukka proposed in option (3) in #1701 (comment)

@JukkaL
Copy link
Collaborator

JukkaL commented May 4, 2017

Using unions as fallbacks for typed dicts has the problem that the union could be arbitrarily complex for a typed dict with multiple nested typed dicts, etc. It's actually just the kind of situation where unions may not be a good option. My current thinking is basically like this:

  • If there are generally at most 2 or 3 items, a union is probably okay. Examples: inferring Optional[x]; 1 if cond() else 'x' -> infer Union[int, str] (the latter doesn't work yet though).
  • If there can be a large number of items, a join (which doesn't usually produce unions) would be better. For example, we shouldn't infer unions from list expressions [x, y, ...] since there could be dozens of items, each with a slightly different type.

Also, we use is_subtype checks during the third pass of semantic analysis (in typeanal.py) and these are also probably not good. A simple approach that wouldn't perhaps require a full new pass would be to build a list of deferred lambdas/closures during pass 3 that would perform the final checks/computations without having to do AST traversal. Then pass 4 would just call these closures. However, even this might not be enough -- we may have to perform multiple runs of certain passes, I think. For example, we can't necessarily calculate a join until we have all the fallbacks, and we can't calculate fallbacks until we can do joins, so finding the right order of these operations can be hard. We could perhaps detect incomplete types and defer operations on them to another pass.

@ilevkivskyi ilevkivskyi changed the title Fix crashes in synthetic types [WIP] Fix crashes in synthetic types May 4, 2017
@ilevkivskyi
Copy link
Member Author

ilevkivskyi commented May 4, 2017

I am marking this as work-in-progress until we resolve the question of third/fourth pass, also I have discovered some spurious errors with this PR in --strict-optional mode, because of incompatibility of tuple fallbacks.

@ilevkivskyi
Copy link
Member Author

@JukkaL
I removed the controversial part from this PR, now it only fixes crashes reported in #3308

The rest can be fixed later in a separate PR when we will decide what to do with fourth pass.

@ilevkivskyi ilevkivskyi changed the title [WIP] Fix crashes in synthetic types Fix crashes in synthetic types May 9, 2017
@gvanrossum
Copy link
Member

Can you resolve the merge conflict?

@ilevkivskyi
Copy link
Member Author

Can you resolve the merge conflict?

Resolved.

@ilevkivskyi
Copy link
Member Author

@gvanrossum I have fixed the merge conflict, do you have any comments on this?

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really, all looks fine, just style nits. Thanks!

@@ -731,6 +731,7 @@ class ClassDef(Statement):
info = None # type: TypeInfo # Related TypeInfo
metaclass = '' # type: Optional[str]
decorators = None # type: List[Expression]
analyzed = None # type: Optional[Expression]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need an update to serialize() (perhaps in the comment)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need to serialize this (since it is needed only for semantic analysis). I updated the comment.

mypy/semanal.py Outdated
@@ -3686,6 +3691,11 @@ def visit_class_def(self, tdef: ClassDef) -> None:
if tdef.info.mro:
tdef.info.mro = [] # Force recomputation
calculate_class_mro(tdef, self.fail_blocker)
if tdef.analyzed:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add is not None?

mypy/semanal.py Outdated
if tdef.analyzed:
if isinstance(tdef.analyzed, TypedDictExpr):
self.analyze(tdef.analyzed.info.typeddict_type)
if isinstance(tdef.analyzed, NamedTupleExpr):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably use elif to make it totally clear that these are separate cases. Also, nothing to do for NewType here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We (intentionally) support only functional syntax for NewType, therefore it can't appear in ClassDef analyzed.

from typing import List, NamedTuple
from mypy_extensions import TypedDict

class NM(NamedTuple):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd order the classes and the test expressions the same way.

y1 = NM(x=[])
reveal_type(x) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=__main__.TD)'
reveal_type(x1) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=typing.Mapping[builtins.str, builtins.list[Any]])'
reveal_type(y) # E: Revealed type is 'Tuple[builtins.list[Any], fallback=__main__.NM]'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reveal for y1?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it.

[out]

-- The two tests below will not crash after
-- https://github.com/python/mypy/issues/3319 is fixed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remember to remove the -skip then.

[case testCrashInvalidArgsSyntheticClassSyntax]
from typing import List, NamedTuple
from mypy_extensions import TypedDict

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for fewer (or no) blank lines in tests.

@ilevkivskyi
Copy link
Member Author

ilevkivskyi commented May 24, 2017

@gvanrossum Thanks for review! I fixed all points. (For some reasons Travis is now much slower than AppVeyor.)

@gvanrossum
Copy link
Member

Will merge once tests pass. Perhaps Travis is squeezing the python project because the sprints cause many runs for many repos in the project..
.?

@gvanrossum gvanrossum merged commit ccd95e6 into python:master May 24, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Synthetic types are not processed in third pass
3 participants