8000 Start using TypeAliasType in the semantic analyzer by ilevkivskyi · Pull Request #7923 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Start using TypeAliasType in the semantic analyzer #7923

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

Merged
merged 16 commits into from
Nov 14, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Some more ideas
  • Loading branch information
ilevkivskyi committed Nov 9, 2019
8000
commit 51a63a41a62c49c3912f8f4e7e9e63c7686ba9ff
6 changes: 3 additions & 3 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ def infer_constraints(template: Type, actual: Type,
"""
if any(get_proper_type(template) == get_proper_type(t) for t in TypeState._inferring):
return []
if (isinstance(template, TypeAliasType) and isinstance(actual, TypeAliasType) and
template.is_recursive and actual.is_recursive):
if isinstance(template, TypeAliasType) and template.is_recursive:
# This case requires special care because it may cause infinite recursion.
TypeState._inferring.append(template)
res = _infer_constraints(template, actual, direction)
Expand All @@ -105,6 +104,7 @@ def infer_constraints(template: Type, actual: Type,
def _infer_constraints(template: Type, actual: Type,
direction: int) -> List[Constraint]:

orig_template = template
template = get_proper_type(template)
actual = get_proper_type(actual)

Expand All @@ -129,7 +129,7 @@ def _infer_constraints(template: Type, actual: Type,
if direction == SUPERTYPE_OF and isinstance(actual, UnionType):
res = []
for a_item in actual.items:
res.extend(infer_constraints(template, a_item, direction))
res.extend(infer_constraints(orig_template, a_item, direction))
return res

# Now the potential subtype is known not to be a Union or a type
Expand Down
4 changes: 3 additions & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
Type, CallableType, Instance, TypeVarType, TupleType, TypedDictType, LiteralType,
UnionType, NoneType, AnyType, Overloaded, FunctionLike, DeletedType, TypeType,
UninhabitedType, TypeOfAny, UnboundType, PartialType, get_proper_type, ProperType,
get_proper_types
get_proper_types, TypeAliasType
)
from mypy.typetraverser import TypeTraverserVisitor
from mypy.nodes import (
Expand Down Expand Up @@ -1413,6 +1413,8 @@ def format(typ: Type) -> str:
return format_type_inner(typ, verbosity, fullnames)

# TODO: show type alias names in errors.
if isinstance(typ, TypeAliasType) and typ.is_recursive:
return str(typ)
typ = get_proper_type(typ)

if isinstance(typ, Instance):
Expand Down
3 changes: 0 additions & 3 deletions mypy/sametypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

def is_same_type(left: Type, right: Type) -> bool:
"""Is 'left' the same type as 'right'?"""
left = get_proper_type(left)
right = get_proper_type(right)

if isinstance(right, UnboundType):
# Make unbound types same as anything else to reduce the number of
Expand All @@ -32,7 +30,6 @@ def is_same_type(left: Type, right: Type) -> bool:


def simplify_union(t: Type) -> ProperType:
t = get_proper_type(t)
if isinstance(t, UnionType):
return make_simplified_union(t.items)
return t
Expand Down
21 changes: 13 additions & 8 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1911,14 +1911,18 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
return

tag = self.track_incomplete_refs()
self.no_applications = True
s.rvalue.accept(self)
self.no_applications = False
if self.found_incomplete_ref(tag) or self.should_wait_rhs(s.rvalue):
# Initializer couldn't be fully analyzed. Defer the current node and give up.
# Make sure that if we skip the definition of some local names, they can't be
# added later in this scope, since an earlier definition should take precedence.
s.rvalue.accept(self)
for expr in names_modified_by_assignment(s):
self.mark_incomplete(expr.name, expr)
return
s.rvalue.accept(self)

# The r.h.s. is now ready to be classified, first check if it is a special form:
special_form = False
Expand Down Expand Up @@ -2510,9 +2514,6 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:

if existing:
# An alias gets updated.
if self.final_iteration:
self.cannot_resolve_name(lvalue.name, 'iiiname', s)
return True
updated = False
if isinstance(existing.node, TypeAlias):
if existing.node.target != res:
Expand All @@ -2527,9 +2528,13 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
existing.node = alias_node
updated = True
if updated:
self.progress = True
# We need to defer so that this change can get propagated to base classes.
self.defer(s)
if self.final_iteration:
self.cannot_resolve_name(lvalue.name, 'name', s)
return True
else:
self.progress = True
# We need to defer so that this change can get propagated to base classes.
self.defer(s)
else:
self.add_symbol(lvalue.name, alias_node, s)
if isinstance(rvalue, RefExpr) and isinstance(rvalue.node, TypeAlias):
Expand Down Expand Up @@ -3645,8 +3650,8 @@ def visit_index_expr(self, expr: IndexExpr) -> None:
and isinstance(base.node, TypeInfo)
and not base.node.is_generic()):
expr.index.accept(self)
elif ((isinstance(base, RefExpr) and isinstance(base.node, TypeAlias))
or refers_to_class_or_function(base)):
elif (((isinstance(base, RefExpr) and isinstance(base.node, TypeAlias))
or refers_to_class_or_function(base)) and not getattr(self, 'no_applications', False)):
# We need to do full processing on every iteration, since some type
# arguments may contain placeholder types.
self.analyze_type_application(expr)
Expand Down
27 changes: 17 additions & 10 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def _is_subtype(left: Type, right: Type,
ignore_pos_arg_names: bool = False,
ignore_declared_variance: bool = False,
ignore_promotions: bool = False) -> bool:
orig_right = right
orig_left = left
left = get_proper_type(left)
right = get_proper_type(right)

Expand All @@ -113,7 +115,7 @@ def _is_subtype(left: Type, right: Type,
# Normally, when 'left' is not itself a union, the only way
# 'left' can be a subtype of the union 'right' is if it is a
# subtype of one of the items making up the union.
is_subtype_of_item = any(is_subtype(left, item,
is_subtype_of_item = any(is_subtype(orig_left, item,
ignore_type_params=ignore_type_params,
ignore_pos_arg_names=ignore_pos_arg_names,
ignore_declared_variance=ignore_declared_variance,
Expand All @@ -130,7 +132,7 @@ def _is_subtype(left: Type, right: Type,
elif is_subtype_of_item:
return True
# otherwise, fall through
return left.accept(SubtypeVisitor(right,
return left.accept(SubtypeVisitor(orig_right,
ignore_type_params=ignore_type_params,
ignore_pos_arg_names=ignore_pos_arg_names,
ignore_declared_variance=ignore_declared_variance,
Expand All @@ -155,13 +157,14 @@ def is_equivalent(a: Type, b: Type,

class SubtypeVisitor(TypeVisitor[bool]):

def __init__(self, right: ProperType,
def __init__(self, right: Type,
*,
ignore_type_params: bool,
ignore_pos_arg_names: bool = False,
ignore_declared_variance: bool = False,
ignore_promotions: bool = False) -> None:
self.right = right
self.right = get_proper_type(right)
self.orig_right = right
self.ignore_type_params = ignore_type_params
self.ignore_pos_arg_names = ignore_pos_arg_names
self.ignore_declared_variance = ignore_declared_variance
Expand Down Expand Up @@ -449,7 +452,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
return False

def visit_union_type(self, left: UnionType) -> bool:
return all(self._is_subtype(item, self.right) for item in left.items)
return all(self._is_subtype(item, self.orig_right) for item in left.items)

def visit_partial_type(self, left: PartialType) -> bool:
# This is indeterminate as we don't really know the complete type yet.
Expand Down Expand Up @@ -1139,22 +1142,26 @@ def is_proper_subtype(left: Type, right: Type, *, ignore_promotions: bool = Fals

def _is_proper_subtype(left: Type, right: Type, *, ignore_promotions: bool = False,
erase_instances: bool = False) -> bool:
orig_left = left
orig_right = right
left = get_proper_type(left)
right = get_proper_type(right)

if isinstance(right, UnionType) and not isinstance(left, UnionType):
return any([is_proper_subtype(left, item, ignore_promotions=ignore_promotions,
return any([is_proper_subtype(orig_left, item, ignore_promotions=ignore_promotions,
erase_instances=erase_instances)
for item in right.items])
return left.accept(ProperSubtypeVisitor(right, ignore_promotions=ignore_promotions,
return left.accept(ProperSubtypeVisitor(orig_right,
ignore_promotions=ignore_promotions,
erase_instances=erase_instances))


class ProperSubtypeVisitor(TypeVisitor[bool]):
def __init__(self, right: ProperType, *,
def __init__(self, right: Type, *,
ignore_promotions: bool = False,
erase_instances: bool = False) -> None:
self.right = right
self.right = get_proper_type(right)
self.orig_right = right
self.ignore_promotions = ignore_promotions
self.erase_instances = erase_instances
self._subtype_kind = ProperSubtypeVisitor.build_subtype_kind(
Expand Down Expand Up @@ -1313,7 +1320,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
return False

def visit_union_type(self, left: UnionType) -> bool:
return all([self._is_proper_subtype(item, self.right) for item in left.items])
return all([self._is_proper_subtype(item, self.orig_right) for item in left.items])

def visit_partial_type(self, left: PartialType) -> bool:
# TODO: What's the right thing to do here?
Expand Down
1 change: 1 addition & 0 deletions mypy/type_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ def query_types(self, types: Iterable[Type]) -> T:
for t in types:
if isinstance(t, TypeAliasType):
# Avoid infinite recursion for recursive type aliases.
# TODO: Ideally we should fire subvisitors if we care about duplicates.
if t in self.seen_aliases:
continue
self.seen_aliases.add(t)
Expand Down
5 changes: 2 additions & 3 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,11 @@ def make_simplified_union(items: Sequence[Type],
Note: This must NOT be used during semantic analysis, since TypeInfos may not
be fully initialized.
"""
items = get_proper_types(items)
while any(isinstance(typ, UnionType) for typ in items):
all_items = [] # type: List[ProperType]
all_items = [] # type: List[Type]
for typ in items:
if isinstance(typ, UnionType):
all_items.extend(get_proper_types(typ.items))
all_items.extend(typ.items)
else:
all_items.append(typ)
items = all_items
Expand Down
15 changes: 15 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2188,6 +2188,21 @@ def visit_type_var(self, typ: TypeVarType) -> Type:
def visit_placeholder_type(self, typ: PlaceholderType) -> Type:
return typ

def visit_callable_argument(self, t: CallableArgument) -> Type:
pass

def visit_ellipsis_type(self, t: EllipsisType) -> Type:
pass

def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
pass

def visit_star_type(self, t: StarType) -> Type:
pass

def visit_type_list(self, t: TypeList) -> Type:
pass


def replace_alias_tvars(tp: Type, vars: List[str], subs: List[Type],
newline: int, newcolumn: int) -> Type:
Expand Down
27 changes: 17 additions & 10 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -638,19 +638,26 @@ except E as e:

[case testTryFancyAliases]
from typing import Sequence, TypeVar, List, Union, Tuple
dummy: C

T = TypeVar('T')

Nested = Union[int, List[Nested]]
NestedA = List[Union[T, NestedA[T]]]
NestedB = Union[T, List[NestedB[T]]]

x: Nested = [1, [2, [3]]]
reveal_type(x)
xa: NestedA[int] = [1, [2, [3]]]
xb: NestedB[int] = [1, [2, [3]]]
reveal_type(xa)
reveal_type(xb)

B = Tuple[int, B]
y: B
reveal_type(y)
class C:
x: D
def fun_a(x: NestedA[T]) -> T: ...
def fun_b(x: NestedB[T]) -> T: ...

reveal_type(fun_a([1, 2, 3]))
reveal_type(fun_b([1, 2, 3]))

reveal_type(fun_a(xa))
reveal_type(fun_b(xb))

class D: ...
xbf: NestedB[float]
reveal_type([xb, xbf])
[builtins fixtures/list.pyi]
0