You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Aliases for generic class applications get type Any #2349
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.)
fromtypingimportTypeVar, GenericT=TypeVar('T')
classC(Generic[T]): passreveal_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.)
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.
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.
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.
@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.
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.
Activity
gvanrossum commentedon Oct 27, 2016
Ouch!
rwbarton commentedon Oct 27, 2016
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 commentedon Nov 8, 2016
@gvanrossum @rwbarton This issue could be closed now. It was fixed by #2378
ilevkivskyi commentedon Nov 8, 2016
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.TypeTypein 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 commentedon 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 specialCallabletype withis_type_obj=Truefor the type of a bare class likeC, which is still the inferred type anyways.)Consider these three program fragments, where
Cis a generic class:The first produces
C[builtins.int*]of course. The second producesC[builtins.int*], but only after your commit 57ff82b. But the third always producedC[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 commentedon Nov 8, 2016
@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
xwould beAny.Probably you wanted to say that
c = C[int]always worked in type context likex: corx = None # type: c. However, this is a simple example, it required some effort to allowVec = 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
TypeTypeorTypeApplicationExpr, I am rather in favour of this. I just wanted to say that this will require some time and effort.rwbarton commentedon 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.