Repository: python/mypy

Issue:
New crashes related to partial types
We recently upgraded to a newer mypy version (c00006211f15adb315d5d3ef5bd0474eed0dfdad) and are seeing new crashes. One has a traceback ending in
```
[...]
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/checkexpr.py", line 753, in infer_function_type_arguments_using_context
    args = infer_type_arguments(callable.type_var_ids(), ret_type, erased_ctx)
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/infer.py", line 43, in infer_type_arguments
    return solve_constraints(type_var_ids, constraints)
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/solve.py", line 52, in solve_constraints
    top = meet_types(top, c.target)
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/meet.py", line 25, in meet_types
    return t.accept(TypeMeetVisitor(s))
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/types.py", line 1287, in accept
    return visitor.visit_partial_type(self)
File "/Users/damien/src/client/.mypy/venv/lib/python3.6/site-packages/mypy/meet.py", line 288, in visit_partial_type
    assert False, 'Internal error'
AssertionError: Internal error
```
And the other:
```
  File "/Users/guido/src/server/.mypy/venv/lib/python3.6/site-packages/mypy/nodes.py", line 817, in accept
    return visitor.visit_assignment_stmt(self)
  File "/Users/guido/src/server/.mypy/venv/lib/python3.6/site-packages/mypy/checker.py", line 1307, in visit_assignment_stmt
    self.check_assignment(s.lvalues[-1], s.rvalue, s.type is None, s.new_syntax)
  File "/Users/guido/src/server/.mypy/venv/lib/python3.6/site-packages/mypy/checker.py", line 1367, in check_assignment
    self.infer_partial_type(lvalue_type.var, lvalue, rvalue_type)
  File "/Users/guido/src/server/.mypy/venv/lib/python3.6/site-packages/mypy/checker.py", line 1833, in infer_partial_type
    self.set_inferred_type(name, lvalue, partial_type)
  File "/Users/guido/src/server/.mypy/venv/lib/python3.6/site-packages/mypy/checker.py", line 1848, in set_inferred_type
    assert lvalue.def_var is not None
AssertionError: 
```
I haven't investigated these nor tried to reduce them. I do have a repeatable demo of the latter.


Discussion:
Does the first crash occurs only in quick mode? It would be quite strange.
Hm, you're right, the first crash also happened in plain --incremental mode.
It looks like I have also a repro for the second crash that doesn't even require ``--incremental``:
```python
class C:
    x = None
    def __init__(self) -> None:
        self.x = []
```
@gvanrossum 
I still can't reproduce the first crash. Do you have some more hints?

Resolving patch:
diff --git a/mypy/applytype.py b/mypy/applytype.py
--- a/mypy/applytype.py
+++ b/mypy/applytype.py
@@ -23,6 +23,7 @@ def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optiona
     # values and bounds.  Also, promote subtype values to allowed values.
     types = list(orig_types)
     for i, type in enumerate(types):
+        assert not isinstance(type, PartialType), "Internal error: must never apply partial type"
         values = callable.variables[i].values
         if values and type:
             if isinstance(type, AnyType):
@@ -34,14 +35,13 @@ def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optiona
                        for v1 in type.values):
                     continue
             for value in values:
-                if isinstance(type, PartialType) or mypy.subtypes.is_subtype(type, value):
+                if mypy.subtypes.is_subtype(type, value):
                     types[i] = value
                     break
             else:
                 msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context)
         upper_bound = callable.variables[i].upper_bound
-        if (type and not isinstance(type, PartialType) and
-                not mypy.subtypes.is_subtype(type, upper_bound)):
+        if type and not mypy.subtypes.is_subtype(type, upper_bound):
             msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context)
 
     # Create a map from type variable id to target type.
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -110,6 +110,9 @@ class TypeChecker(NodeVisitor[None], CheckerPluginInterface):
     dynamic_funcs = None  # type: List[bool]
     # Stack of collections of variables with partial types
     partial_types = None  # type: List[Dict[Var, Context]]
+    # Vars for which partial type errors are already reported
+    # (to avoid logically duplicate errors with different error context).
+    partial_reported = None  # type: Set[Var]
     globals = None  # type: SymbolTable
     modules = None  # type: Dict[str, MypyFile]
     # Nodes that couldn't be checked because some types weren't available. We'll run
@@ -157,6 +160,7 @@ def __init__(self, errors: Errors, modules: Dict[str, MypyFile], options: Option
         self.return_types = []
         self.dynamic_funcs = []
         self.partial_types = []
+        self.partial_reported = set()
         self.deferred_nodes = []
         self.type_map = {}
         self.module_refs = set()
@@ -1681,7 +1685,7 @@ def lvalue_type_for_inference(self, lvalues: List[Lvalue], rvalue_type: TupleTyp
         def append_types_for_inference(lvs: List[Expression], rv_types: List[Type]) -> None:
             for lv, rv_type in zip(lvs, rv_types):
                 sub_lvalue_type, index_expr, inferred = self.check_lvalue(lv)
-                if sub_lvalue_type:
+                if sub_lvalue_type and not isinstance(sub_lvalue_type, PartialType):
                     type_parameters.append(sub_lvalue_type)
                 else:  # index lvalue
                     # TODO Figure out more precise type context, probably
@@ -1692,7 +1696,7 @@ def append_types_for_inference(lvs: List[Expression], rv_types: List[Type]) -> N
 
         if star_lv:
             sub_lvalue_type, index_expr, inferred = self.check_lvalue(star_lv.expr)
-            if sub_lvalue_type:
+            if sub_lvalue_type and not isinstance(sub_lvalue_type, PartialType):
                 type_parameters.extend([sub_lvalue_type] * len(star_rv_types))
             else:  # index lvalue
                 # TODO Figure out more precise type context, probably
@@ -1845,8 +1849,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
             var.is_inferred = True
             if isinstance(lvalue, MemberExpr) and self.inferred_attribute_types is not None:
                 # Store inferred attribute type so that we can check consistency afterwards.
-                assert lvalue.def_var is not None
-                self.inferred_attribute_types[lvalue.def_var] = type
+                if lvalue.def_var is not None:
+                    self.inferred_attribute_types[lvalue.def_var] = type
             self.store_type(lvalue, type)
 
     def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type,
@@ -2655,9 +2659,10 @@ def enter_partial_types(self) -> Iterator[None]:
                 if isinstance(var.type, PartialType) and var.type.type is None:
                     # None partial type: assume variable is intended to have type None
                     var.type = NoneTyp()
-                else:
+                elif var not in self.partial_reported:
                     self.msg.fail(messages.NEED_ANNOTATION_FOR_VAR, context)
                     var.type = AnyType(TypeOfAny.from_error)
+                    self.partial_reported.add(var)
 
     def find_partial_types(self, var: Var) -> Optional[Dict[Var, Context]]:
         for partial_types in reversed(self.partial_types):


Test patch:
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -1887,3 +1887,43 @@ C[0] = 0
 [out]
 main:4: error: Type expected within [...]
 main:4: error: Unsupported target for indexed assignment
+
+[case testNoCrashOnPartialMember]
+class C:
+    x = None
+    def __init__(self) -> None:
+        self.x = []  # E: Need type annotation for variable
+[builtins fixtures/list.pyi]
+[out]
+
+[case testNoCrashOnPartialVariable]
+from typing import Tuple, TypeVar
+T = TypeVar('T', bound=str)
+
+def f(x: T) -> Tuple[T]:
+    ...
+x = None
+(x,) = f('')
+reveal_type(x) # E: Revealed type is 'builtins.str'
+[out]
+
+[case testNoCrashOnPartialVariable2]
+from typing import Tuple, TypeVar
+T = TypeVar('T', bound=str)
+
+def f() -> Tuple[T]:
+    ...
+x = None
+(x,) = f()
+[out]
+
+[case testNoCrashOnPartialVariable3]
+from typing import Tuple, TypeVar
+T = TypeVar('T')
+
+def f(x: T) -> Tuple[T, T]:
+    ...
+x = None
+(x, x) = f('')
+reveal_type(x) # E: Revealed type is 'builtins.str'
+[out]