Repository: python/mypy

Issue:
Dict constructor with unknown base class
Consider the following:

```
from typing import Any, Dict
from base import Base

class Derived(Base):
    def to_dict(self) -> Dict[str, Any]:
        return dict(self)
```

When we run mypy with `--slient-imports` on only this file, we get the following error:

```
note: In member "to_dict" of class "Derived":
error: No overload variant of "dict" matches argument types [test_mypy_dict.Derived]
```



Discussion:
Looks like this was caused by 1423a54. I think the fix will be easy.

OK, it's not as easy as I thought. :-( It is not finding any overloads because the arg (self) has a type that is _derived_ from Any, but is not exactly Any. So the allowance for Any made in overload_arg_similarity() doesn't trigger. If I make the allowance more flexible by using `is_subtype(actual, AnyType())` then it arbitrarily picks the first overload with the right number of parameters, and then we get

```
Argument 1 to "dict" has incompatible type "Derived"; expected Mapping[str, Any]
```

Maybe the right solution is just to make self's type equal to Any? @JukkaL any thoughts?

Making self's type `Any` wouldn't help in general, because the same issue could happen for non-self values as well.

We could make anything derived from `Any` be a subtype of every type, since there could be any number of base classes that we don't see. The above problem would likely go away if we did this. Of course, this would hide some real errors, but the behavior would be more correct.

For example, this would be okay:

```
from typing import Any

a = int  # type: Any
class B(a): pass

def f(x: int) -> None: pass
def g(x: str) -> None: pass

f(B())  # currently error, but should perhaps be fine
g(B())  # same as above, perhaps should not be an error
```


Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1625,7 +1625,8 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int:
     """
     if (isinstance(actual, NoneTyp) or isinstance(actual, AnyType) or
             isinstance(formal, AnyType) or isinstance(formal, TypeVarType) or
-            isinstance(formal, CallableType)):
+            isinstance(formal, CallableType) or
+            (isinstance(actual, Instance) and actual.type.fallback_to_any)):
         # These could match anything at runtime.
         return 2
     if isinstance(actual, UnionType):
diff --git a/mypy/subtypes.py b/mypy/subtypes.py
--- a/mypy/subtypes.py
+++ b/mypy/subtypes.py
@@ -95,6 +95,8 @@ def visit_deleted_type(self, left: DeletedType) -> bool:
         return True
 
     def visit_instance(self, left: Instance) -> bool:
+        if left.type.fallback_to_any:
+            return True
         right = self.right
         if isinstance(right, Instance):
             if left.type._promote and is_subtype(left.type._promote,


Test patch:
diff --git a/mypy/test/data/check-overloading.test b/mypy/test/data/check-overloading.test
--- a/mypy/test/data/check-overloading.test
+++ b/mypy/test/data/check-overloading.test
@@ -567,3 +567,19 @@ f(y=[''], x=0)() # E: "int" not callable
 a = f(y=[['']], x=0) # E: List item 0 has incompatible type List[str]
 a() # E: "int" not callable
 [builtins fixtures/list.py]
+
+[case testOverloadWithDerivedFromAny]
+from typing import Any, overload
+Base = None  # type: Any
+
+class C:
+    @overload
+    def __init__(self, a: str) -> None: pass
+    @overload
+    def __init__(self, a: int) -> None: pass
+
+class Derived(Base):
+    def to_dict(self) -> C:
+        return C(self)  # fails without the fix for #1363
+C(Derived())  # fails without the hack
+C(Base())  # Always ok