Skip to content

Aliases for generic class applications get type Any #2349

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

Closed
rwbarton opened this issue Oct 27, 2016 · 7 comments
Closed

Aliases for generic class applications get type Any #2349

rwbarton opened this issue Oct 27, 2016 · 7 comments
Labels
bug mypy got something wrong

Comments

@rwbarton
Copy link
Contributor

As of #2302 mypy accepts use of a type application of a generic class as an expression anywhere. Previously, such applications were only allowed in type aliases (i.e., on the RHS of a simple assignment). But there is still special logic for such aliases, and it creates unexpected Any types. (They were there before too, but now there's clearly no reason for them.)

from typing import TypeVar, Generic
T = TypeVar('T')
class C(Generic[T]): pass

reveal_type(C[int]())
  # before 2302: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
  #            : Revealed type is 'Any'
  #  after 2302: Revealed type is 'index.C[builtins.int*]'

c = C[int]
x = c()
reveal_type(x)
  # both before and after 2302: Revealed type is 'Any'
  # (Even c has type Any.)
@gvanrossum
Copy link
Member

Ouch!

@gvanrossum gvanrossum added the bug mypy got something wrong label Oct 27, 2016
@gvanrossum gvanrossum added this to the 0.4.x milestone Oct 27, 2016
@rwbarton
Copy link
Contributor Author

It would be worth investigating whether special type checking of aliases can be removed completely. Special processing is still needed in semantic analysis so that the alias can be used in a type annotation, but I'm not aware of a situation where the type checker still needs to treat the type alias definition specially.

@ilevkivskyi
Copy link
Member

@gvanrossum @rwbarton This issue could be closed now. It was fixed by #2378

@ilevkivskyi
Copy link
Member

Concerning whether we need special treatment: It looks like the way they are treated now is left from times when there was no Type[...] i.e. TypeType in mypy terminology. I think we could use this now to treat type alias nodes on common ground with other expressions. But I could say that this would be a somewhat significant refactoring.

@rwbarton
Copy link
Contributor Author

rwbarton commented Nov 8, 2016

It's still not obvious to me why any special logic is required at all. (Even before Type[C], since we already had a special Callable type with is_type_obj=True for the type of a bare class like C, which is still the inferred type anyways.)

Consider these three program fragments, where C is a generic class:

x = C[int]()
reveal_type(x)
c = C[int]
x = c()
reveal_type(x)
(c,) = (C[int],)
x = c()
reveal_type(x)

The first produces C[builtins.int*] of course. The second produces C[builtins.int*], but only after your commit 57ff82b. But the third always produced C[builtins.int*], just by side-stepping the type alias logic. Mypy is not smart enough to recognize the (c,) = (C[int],) form as a type alias, but it is smart enough to understand how to type check it. In fact, the reason that the third program succeeds is exactly the same as the reason the first program succeeds.

So, at least in this case, the logic added in 57ff82b seems to be somehow redundant, in the sense that if the type checker did not treat type alias definition RHSs specially at all, it would have already gotten the correct result.

@ilevkivskyi
Copy link
Member

@rwbarton Concerning the third example, I am not sure what do you mean by "always". Before #2302 generic type application was prohibited in runtime context (this has nothing to do with aliases), so that first example and most probably two other would simply give you an error and x would be Any.

Probably you wanted to say that c = C[int] always worked in type context like x: c or x = None # type: c. However, this is a simple example, it required some effort to allow Vec = Iterable[Tuple[T, T]] etc. (see http://mypy.readthedocs.io/en/latest/kinds_of_types.html#type-aliases) and cover many corner cases.

Anyway, my first attempt to allow generic type aliases was actually exactly like you suggested: just re-using type application (this is the thing that makes mypy "smart enough to understand how to type check it"), but I didn't manage to do this. I have nothing against someone refactoring type aliases either using TypeType or TypeApplicationExpr, I am rather in favour of this. I just wanted to say that this will require some time and effort.

@rwbarton
Copy link
Contributor Author

rwbarton commented Nov 8, 2016

I'm not sure exactly when the third fragment started working, but it was many commits before 57ff82b. I am quite sure that I am talking about these three program fragments, and not any other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants