Repository: python/mypy

Issue:
attrs plugin error: "The erased type of self '...*' is not a supertype of its class" on class definition with a generic using a constrained type variable, but then seems to work fine after definition
Consider the following example:

```python3
from typing import TypeVar, Generic
import attr

class V:
    pass

class W:
    pass

T = TypeVar("T", V, W)

@attr.s(auto_attribs=True)
class A(Generic[T]):
    v: T

reveal_type(A(W()))
```

My expectation here would be a revealed type of `A[W*]`, which I *do* get.

But I also get an error on the `@attr.s` definition:
```
The erased type of self 'V*' is not a supertype of its class 'A[T`1]'
The erased type of self 'W*' is not a supertype of its class 'A[T`1]'
```

if I remove the constraints on `T`, (i.e. `T = TypeVar("T")`) then the errors go away.


Discussion:
Maybe the attrs plugin author (@euresti) can shed some insight?
This may be related to https://github.com/python/mypy/issues/5263 (if not a duplicate).
Just to clarify, both issues may be caused by bad interaction of patched `self`/`cls` with `checkmember.bind_self()`.
Actually. I can repro with dataclasses too.  

```
from typing import TypeVar, Generic
import attr
from dataclasses import dataclass

class V:
    pass

class W:
    pass

T = TypeVar("T", V, W)

@dataclass(order=True)
class A(Generic[T]):
    v: T

reveal_type(A(W()))
```

So it's related to the added `__cmp-ish__` methods

OK, the problem is [here](https://github.com/python/mypy/blob/master/mypy/plugins/dataclasses.py#L116) and [here](https://github.com/python/mypy/blob/master/mypy/plugins/dataclasses.py#L138) where we unconditionally grab `id=1` for the self-type type variable thus causing a type variable clash (the same probably happens for `attrs`). I think the solution would be to check somehow the next available id instead.

Also btw by agreement ids for generic functions/methods should have _negative_ numbers.
Would the solution just be to make the id `-1`?  It seems to me like -1 should never be used at this point because negative numbers are used for methods/functions and I'm at this point creating it right?  Or are the id's global in scope?
After some thinking it seems to me just using `-1` should be fine since the methods are auto-generated anyway. Btw, I have found that `senamal_namedtuple` also uses `1` instead of `-1`. Could you please try replacing it by `-1` in both plugins and in named tuple code and play with it trying to break (try some nested classes within generic functions, maybe some scenarios with type inference)?

Resolving patch:
diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py
--- a/mypy/plugins/attrs.py
+++ b/mypy/plugins/attrs.py
@@ -458,7 +458,7 @@ def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None:
     #    AT = TypeVar('AT')
     #    def __lt__(self: AT, other: AT) -> bool
     # This way comparisons with subclasses will work correctly.
-    tvd = TypeVarDef('AT', 'AT', 1, [], object_type)
+    tvd = TypeVarDef('AT', 'AT', -1, [], object_type)
     tvd_type = TypeVarType(tvd)
     args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)]
     for method in ['__lt__', '__le__', '__gt__', '__ge__']:
diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
--- a/mypy/plugins/dataclasses.py
+++ b/mypy/plugins/dataclasses.py
@@ -113,7 +113,7 @@ def transform(self) -> None:
                 # the same type as self (covariant).  Note the
                 # "self_type" parameter to _add_method.
                 obj_type = ctx.api.named_type('__builtins__.object')
-                cmp_tvar_def = TypeVarDef('T', 'T', 1, [], obj_type)
+                cmp_tvar_def = TypeVarDef('T', 'T', -1, [], obj_type)
                 cmp_other_type = TypeVarType(cmp_tvar_def)
                 cmp_return_type = ctx.api.named_type('__builtins__.bool')
 
@@ -135,7 +135,7 @@ def transform(self) -> None:
                 # Like for __eq__ and __ne__, we want "other" to match
                 # the self type.
                 obj_type = ctx.api.named_type('__builtins__.object')
-                order_tvar_def = TypeVarDef('T', 'T', 1, [], obj_type)
+                order_tvar_def = TypeVarDef('T', 'T', -1, [], obj_type)
                 order_other_type = TypeVarType(order_tvar_def)
                 order_return_type = ctx.api.named_type('__builtins__.bool')
                 order_args = [
diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py
--- a/mypy/semanal_namedtuple.py
+++ b/mypy/semanal_namedtuple.py
@@ -333,7 +333,7 @@ def add_field(var: Var, is_initialized_in_class: bool = False,
         add_field(Var('__annotations__', ordereddictype), is_initialized_in_class=True)
         add_field(Var('__doc__', strtype), is_initialized_in_class=True)
 
-        tvd = TypeVarDef('NT', 'NT', 1, [], info.tuple_type)
+        tvd = TypeVarDef('NT', 'NT', -1, [], info.tuple_type)
         selftype = TypeVarType(tvd)
 
         def add_method(funcname: str,


Test patch:
diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test
--- a/test-data/unit/check-attr.test
+++ b/test-data/unit/check-attr.test
@@ -187,10 +187,10 @@ class A:
 reveal_type(A)  # E: Revealed type is 'def (a: builtins.int) -> __main__.A'
 reveal_type(A.__eq__)  # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
 reveal_type(A.__ne__)  # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
-reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__le__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__gt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__ge__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
+reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__le__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__gt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__ge__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
 
 A(1) < A(2)
 A(1) <= A(2)
@@ -654,10 +654,10 @@ class C(A, B): pass
 @attr.s
 class D(A): pass
 
-reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(B.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(C.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(D.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
+reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(B.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(C.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(D.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
 
 A() < A()
 B() < B()
@@ -909,3 +909,15 @@ class A:
 A(None, None)
 
 [builtins fixtures/attr.pyi]
+
+[case testAttrsTypeVarNoCollision]
+from typing import TypeVar, Generic
+import attr
+
+T = TypeVar("T", bytes, str)
+
+# Make sure the generated __le__ (and friends) don't use T for their arguments.
+@attr.s(auto_attribs=True)
+class A(Generic[T]):
+    v: T
+[builtins fixtures/attr.pyi]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -3642,10 +3642,10 @@ from a import A
 reveal_type(A)  # E: Revealed type is 'def (a: builtins.int) -> a.A'
 reveal_type(A.__eq__)  # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
 reveal_type(A.__ne__)  # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
-reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__le__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__gt__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-reveal_type(A.__ge__)  # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
+reveal_type(A.__lt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__le__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__gt__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+reveal_type(A.__ge__)  # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
 
 A(1) < A(2)
 A(1) <= A(2)
@@ -3681,10 +3681,10 @@ class A:
 main:2: error: Revealed type is 'def (a: builtins.int) -> a.A'
 main:3: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
 main:4: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
-main:5: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-main:6: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-main:7: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
-main:8: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
+main:5: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+main:6: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+main:7: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
+main:8: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
 main:17: error: Unsupported operand types for < ("A" and "int")
 main:18: error: Unsupported operand types for <= ("A" and "int")
 main:19: error: Unsupported operand types for > ("A" and "int")
@@ -4526,10 +4526,10 @@ class A:
 tmp/b.py:3: error: Revealed type is 'def (a: builtins.int) -> a.A'
 tmp/b.py:4: error: Revealed type is 'def (builtins.object, builtins.object) -> builtins.bool'
 tmp/b.py:5: error: Revealed type is 'def (builtins.object, builtins.object) -> builtins.bool'
-tmp/b.py:6: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
-tmp/b.py:7: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
-tmp/b.py:8: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
-tmp/b.py:9: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
+tmp/b.py:6: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
+tmp/b.py:7: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
+tmp/b.py:8: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
+tmp/b.py:9: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
 tmp/b.py:18: error: Unsupported operand types for < ("A" and "int")
 tmp/b.py:19: error: Unsupported operand types for <= ("A" and "int")
 tmp/b.py:20: error: Unsupported operand types for > ("A" and "int")