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 type variable clash in nested positions and in attributes #14095

Merged
merged 5 commits into from
Nov 16, 2022

Conversation

ilevkivskyi
Copy link
Member

Addresses the non-crash part of #10244 (and similar situations).

The freshen_function_type_vars() use in checkmember.py was inconsistent:

  • It needs to be applied to attributes too, not just methods
  • It needs to be a visitor, since generic callable can appear in a nested position

The downsides are ~2% performance regression, and people will see more large ids in reveal_type() (since refreshing functions uses a global unique counter). But since this is a correctness issue that can cause really bizarre error messages, I think it is totally worth it.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@ilevkivskyi
Copy link
Member Author

Btw, I think we can improve performance by having a version of ExpandTypeVisitor that would update the expansion mapping "on the fly", but this may be tricky to implement. So I would propose to not do premature optimization.

@github-actions

This comment has been minimized.

Copy link
Collaborator

@Michael0x2a Michael0x2a left a comment

Choose a reason for hiding this comment

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

Bit surprised the overhead is as high as 2%, but I agree correctness trumps perf.

I have some questions about mypy semantics (see inline comments), but I do think recursively processing types is strictly more correct then what we're doing now, so :stamp: on those grounds.

T = TypeVar("T")
R = TypeVar("R")
class C(Generic[R]):
x: Callable[[T], R]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is having this something we actually want to support? (Allowing "T" to exist as a "free" typevar)

I was always under the impression that having free typevars like this was effectively undefined behavior and not something we really want to encourage users to do.

So, an alternative approach here might be to report an error here then replace 'T' with either 'object', 'never', or 'any' for the purposes of subsequent type analysis.

Copy link
Member Author

Choose a reason for hiding this comment

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

Couple things here:

  • In general, things like Callable[[T], str] are fine (e.g. for a function with unused argument), the opposite like Callable[[str], T] are wrong/undefined (and mypy actually gives an error for such types when defining a function).
  • The bug also happens for other situations as well, e.g. if I type x: Callable[[T], Tuple[T, R]] and adjust the callsite, I just use the simplest case for a test. I will update the test to make it more clear.

@@ -1654,7 +1654,7 @@ class C:
def bar(self) -> Self: ...
foo: Callable[[S, Self], Tuple[Self, S]]

reveal_type(C().foo) # N: Revealed type is "def [S] (S`-1, __main__.C) -> Tuple[__main__.C, S`-1]"
reveal_type(C().foo) # N: Revealed type is "def [S] (S`1, __main__.C) -> Tuple[__main__.C, S`1]"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if I remember the exact rules, but shouldn't S continue to use a negative ID, since it's a method-scoped typevar instead of a class-scoped one?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but we didn't follow this convention since when we started using freshen_function_type_vars() in checkmember ~5 years ago. This is because unification variables have positive ids. But yeah, at some point we should try to clean this up.

@github-actions
Copy link
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@ilevkivskyi ilevkivskyi merged commit 48c4a47 into python:master Nov 16, 2022
@ilevkivskyi ilevkivskyi deleted the fix-typevar-clash branch November 16, 2022 21:39
JukkaL added a commit that referenced this pull request Dec 20, 2022
Only perform type variable freshening if it's needed, i.e. there is a
nested generic callable, since it's fairly expensive. Make the check for
generic callables fast by creating a specialized type query visitor base
class for queries with bool results. The visitor tries hard to avoid
memory allocation in typical cases, since allocation is slow.

This addresses at least some of the performance regression in #14095.
This improved self-check performance by about 3% when compiled with
mypyc (-O2).

The new visitor class can potentially help with other type queries as
well. I'll explore it in follow-up PRs.
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.

2 participants