Skip to content

Commit

Permalink
First part of response to comments (bigger things)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilevkivskyi committed Nov 1, 2016
1 parent 2edcf48 commit 64fc96e
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 47 deletions.
29 changes: 7 additions & 22 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
PartialType, DeletedType, UnboundType, UninhabitedType, TypeType,
true_only, false_only, is_named_instance, function_type
true_only, false_only, is_named_instance, function_type,
get_typ_args, set_typ_args

)
from mypy.nodes import (
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr,
Expand Down Expand Up @@ -1378,18 +1380,12 @@ def visit_type_application(self, tapp: TypeApplication) -> Type:
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))
and not alias.runtime):
if not alias.runtime:
item = self.replace_tvars_any(item)
if isinstance(item, Instance):
tp = type_object_type(item.type, self.named_type)
else:
# TODO: Better error reporting: need to find line for
# unsubscribed generic aliases, that are invalid at runtime.
if alias.line > 0:
self.chk.fail('Invalid type alias in runtime expression: {}'
.format(item), alias)
return AnyType()
return alias.fback
if isinstance(tp, CallableType):
if len(tp.variables) != len(item.args):
self.msg.incompatible_type_application(len(tp.variables),
Expand All @@ -1411,11 +1407,7 @@ def replace_tvars_any(self, tp: Type) -> Type:
a runtime expression. Basically, this function finishes what could not be done
in similar funtion from typeanal.py.
"""
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)):
return tp
typ_args = (tp.args if isinstance(tp, Instance) else
tp.items if not isinstance(tp, CallableType) else
tp.arg_types + [tp.ret_type])
typ_args = get_typ_args(tp)
new_args = typ_args[:]
for i, arg in enumerate(typ_args):
if isinstance(arg, UnboundType):
Expand All @@ -1428,14 +1420,7 @@ def replace_tvars_any(self, tp: Type) -> Type:
new_args[i] = AnyType()
else:
new_args[i] = self.replace_tvars_any(arg)
if isinstance(tp, Instance):
return Instance(tp.type, new_args, tp.line)
if isinstance(tp, TupleType):
return tp.copy_modified(items=new_args)
if isinstance(tp, UnionType):
return UnionType.make_union(new_args, tp.line)
if isinstance(tp, CallableType):
return tp.copy_modified(arg_types=new_args[:-1], ret_type=new_args[-1])
return set_typ_args(tp, new_args)

def visit_list_expr(self, e: ListExpr) -> Type:
"""Type check a list expression [...]."""
Expand Down
6 changes: 3 additions & 3 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1732,13 +1732,13 @@ class TypeAliasExpr(Expression):
"""Type alias expression (rvalue)."""

type = None # type: mypy.types.Type
line = None # type: int
fback = None # type: mypy.types.Type
runtime = False # type: bool

def __init__(self, type: 'mypy.types.Type', line: int = -1,
def __init__(self, type: 'mypy.types.Type', fback: 'mypy.types.Type' = None,
runtime: bool = False) -> None:
self.type = type
self.line = line
self.fback = fback
self.runtime = runtime

def accept(self, visitor: NodeVisitor[T]) -> T:
Expand Down
20 changes: 18 additions & 2 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
node.kind = TYPE_ALIAS
node.type_override = res
if isinstance(s.rvalue, IndexExpr):
s.rvalue.analyzed = TypeAliasExpr(res)
s.rvalue.analyzed = TypeAliasExpr(res, fback=self.alias_fallback(res))
if s.type:
# Store type into nodes.
for lvalue in s.lvalues:
Expand Down Expand Up @@ -1213,6 +1213,20 @@ def analyze_simple_literal_type(self, rvalue: Expression) -> Optional[Type]:
return self.named_type_or_none('builtins.unicode')
return None

def alias_fallback(self, tp: Type) -> Instance:
"""Make a dummy Instance with no methods. It is used as a fallback type
to detect errors for non-Instance aliases (i.e. Unions, Tuples, Callables).
"""

kind = (' to Callable' if isinstance(tp, CallableType) else
' to Tuple' if isinstance(tp, TupleType) else
' to Union' if isinstance(tp, UnionType) else '')
cdef = ClassDef('Type alias{}'.format(kind), Block([]))
fb_info = TypeInfo(SymbolTable(), cdef, self.cur_mod_id)
fb_info.bases = [self.object_type()]
fb_info.mro = [fb_info, self.object_type().type]
return Instance(fb_info, [])

def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None:
"""Check if assignment creates a type alias and set it up as needed."""
# For now, type aliases only work at the top level of a module.
Expand Down Expand Up @@ -2370,7 +2384,7 @@ def visit_index_expr(self, expr: IndexExpr) -> None:
self.lookup_qualified,
self.lookup_fully_qualified,
self.fail)
expr.analyzed = TypeAliasExpr(res, line=expr.line, runtime=True)
expr.analyzed = TypeAliasExpr(res, fback=self.alias_fallback(res), runtime=True)
elif refers_to_class_or_function(expr.base):
# Special form -- type application.
# Translate index to an unanalyzed type.
Expand Down Expand Up @@ -3061,6 +3075,8 @@ def visit_decorator(self, dec: Decorator) -> None:

def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
self.analyze(s.type)
if isinstance(s.rvalue, IndexExpr) and isinstance(s.rvalue.analyzed, TypeAliasExpr):
self.analyze(s.rvalue.analyzed.type)
super().visit_assignment_stmt(s)

def visit_cast_expr(self, e: CastExpr) -> None:
Expand Down
24 changes: 4 additions & 20 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from mypy.types import (
Type, UnboundType, TypeVarType, TupleType, UnionType, Instance,
AnyType, CallableType, Void, NoneTyp, DeletedType, TypeList, TypeVarDef, TypeVisitor,
StarType, PartialType, EllipsisType, UninhabitedType, TypeType
StarType, PartialType, EllipsisType, UninhabitedType, TypeType, get_typ_args, set_typ_args
)
from mypy.nodes import (
BOUND_TVAR, UNBOUND_TVAR, TYPE_ALIAS, UNBOUND_IMPORTED,
Expand Down Expand Up @@ -209,11 +209,7 @@ def get_type_var_names(self, tp: Type) -> List[str]:
in order of textual appearance (recursively, if needed).
"""
tvars = [] # type: List[str]
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)):
return tvars
typ_args = (tp.args if isinstance(tp, Instance) else
tp.items if not isinstance(tp, CallableType) else
tp.arg_types + [tp.ret_type])
typ_args = get_typ_args(tp)
for arg in typ_args:
tvar = self.get_tvar_name(arg)
if tvar:
Expand Down Expand Up @@ -243,11 +239,7 @@ def replace_alias_tvars(self, tp: Type, vars: List[str], subs: List[Type]) -> Ty
""" 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
tp.items if not isinstance(tp, CallableType) else
tp.arg_types + [tp.ret_type])
typ_args = get_typ_args(tp)
new_args = typ_args[:]
for i, arg in enumerate(typ_args):
tvar = self.get_tvar_name(arg)
Expand All @@ -257,15 +249,7 @@ def replace_alias_tvars(self, tp: Type, vars: List[str], subs: List[Type]) -> Ty
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):
return tp.copy_modified(items=new_args)
if isinstance(tp, UnionType):
return UnionType.make_union(new_args, tp.line)
if isinstance(tp, CallableType):
return tp.copy_modified(arg_types=new_args[:-1], ret_type=new_args[-1])
return set_typ_args(tp, new_args)

def visit_any(self, t: AnyType) -> Type:
return t
Expand Down
19 changes: 19 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1524,3 +1524,22 @@ def function_type(func: mypy.nodes.FuncBase, fallback: Instance) -> FunctionLike
name,
implicit=True,
)

def get_typ_args(tp: Type) -> List[Type]:
if not isinstance(tp, (Instance, UnionType, TupleType, CallableType)):
return []
typ_args = (tp.args if isinstance(tp, Instance) else
tp.items if not isinstance(tp, CallableType) else
tp.arg_types + [tp.ret_type])
return typ_args

def set_typ_args(tp: Type, args: List[Type]) -> Type:
if isinstance(tp, Instance):
return Instance(tp.type, new_args, tp.line)
if isinstance(tp, TupleType):
return tp.copy_modified(items=new_args)
if isinstance(tp, UnionType):
return UnionType.make_union(new_args, tp.line)
if isinstance(tp, CallableType):
return tp.copy_modified(arg_types=new_args[:-1], ret_type=new_args[-1])
return tp

0 comments on commit 64fc96e

Please sign in to comment.