-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Generic type aliases #2378
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
Generic type aliases #2378
Changes from 1 commit
5dc0090
f6d862b
b0790d4
7ddd6ab
633e3d1
07baa51
56b7ae7
cbcb2c0
5c92b12
13e1fff
ee1c25c
2edcf48
64fc96e
8c64111
c106204
feb9413
d402a68
8cea156
382c53e
863c576
544ff66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1376,12 +1376,15 @@ def visit_type_application(self, tapp: TypeApplication) -> Type: | |
return AnyType() | ||
|
||
def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type: | ||
""" Get type of a type alias (could be generic) in a runtime expression.""" | ||
item = alias.type | ||
if isinstance(item, (Instance, TupleType, UnionType, CallableType)): | ||
item = self.replace_tvars_any(item) | ||
if isinstance(item, Instance): | ||
tp = type_object_type(item.type, self.named_type) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like a comment here explaining that we'll generally get a callable type (or overloaded) with .is_type_obj() true, representing the class's constructor. This would explain that below we really just check for callable and overloaded. |
||
else: | ||
# TODO: Better error reporting, need to find line for | ||
# unsubscribed generic aliases | ||
if alias.line > 0: | ||
self.chk.fail('Invalid type alias in runtime expression: {}' | ||
.format(item), alias) | ||
|
@@ -1393,6 +1396,10 @@ def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type: | |
return AnyType() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not an error because we can only get here when there already was a previous error, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gvanrossum Yes, I think so. At least I don't see a way to get there without previously having an error. |
||
|
||
def replace_tvars_any(self, tp: Type) -> Type: | ||
""" Replace all unbound type variables with Any if an alias is used in | ||
a runtime expression. Basically, this function finishes what could not be done | ||
in similar funtion from typeanal.py. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mind naming that similar function by name? (Also, typo -- funtion.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gvanrossum Yes, sure :-) |
||
""" | ||
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if this might be a job for TypeTranslator... Or perhaps refactor the code that extracts the type variables from a type into a helper function? (I see you have the exact same idiom in three places now. If in the future we add a new parameterized Type subclass we'd be in a bit of pain finding all places where this is used. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gvanrossum I refactored this to two helpers |
||
return tp | ||
typ_args = (tp.args if isinstance(tp, Instance) else | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,6 +158,8 @@ def visit_unbound_type(self, t: UnboundType) -> Type: | |
if exp_len == 0 and act_len == 0: | ||
return override | ||
if act_len != exp_len: | ||
# TODO: Detect wrong type variable numer for unused aliases | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: numer. |
||
# (although it could be difficult at this stage, see comment below) | ||
self.fail('Bad number of arguments for type alias, expected: %s, given: %s' | ||
% (exp_len, act_len), t) | ||
return t | ||
|
@@ -203,6 +205,9 @@ def visit_unbound_type(self, t: UnboundType) -> Type: | |
return AnyType() | ||
|
||
def get_type_var_names(self, tp: Type) -> List[str]: | ||
""" Get all type variable names that are present in a generic type alias | ||
in order of textual appearance (recursively, if needed). | ||
""" | ||
tvars = [] # type: List[str] | ||
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)): | ||
return tvars | ||
|
@@ -235,6 +240,9 @@ def get_tvar_name(self, t: Type) -> Optional[str]: | |
return None | ||
|
||
def replace_alias_tvars(self, tp: Type, vars: List[str], subs: List[Type]) -> Type: | ||
""" Replace type variables in a generic type alias tp with substitutions subs. | ||
Length of subs should be already checked. | ||
""" | ||
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)) or not subs: | ||
return tp | ||
typ_args = (tp.args if isinstance(tp, Instance) else | ||
|
@@ -244,9 +252,12 @@ def replace_alias_tvars(self, tp: Type, vars: List[str], subs: List[Type]) -> Ty | |
for i, arg in enumerate(typ_args): | ||
tvar = self.get_tvar_name(arg) | ||
if tvar and tvar in vars: | ||
# Perform actual substitution... | ||
new_args[i] = subs[vars.index(tvar)] | ||
else: | ||
# ...recursively, if needed. | ||
new_args[i] = self.replace_alias_tvars(arg, vars, subs) | ||
# Create a copy with type vars replaced. | ||
if isinstance(tp, Instance): | ||
return Instance(tp.type, new_args, tp.line) | ||
if isinstance(tp, TupleType): | ||
|
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.
No space before first word of sentence.