Repository: cython/cython

Issue:
"local variable 'result' referenced before assignment" compiler error with auto_pickle in 0.26
gevent [fails to compile](https://travis-ci.org/gevent/gevent/jobs/255646182#L538) with cython 0.26 (observed on OS X, Windows and Linux): 
```
    cython -o gevent.ares.c src/gevent/ares.pyx
    
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
    def __pyx_unpickle_result(__pyx_type, long __pyx_checksum, __pyx_state):
        if __pyx_checksum != 0xc9b5cdc:
            from pickle import PickleError
            raise PickleError("Incompatible checksums (%s vs 0xc9b5cdc = (exception, value))" % __pyx_checksum)
        result = result.__new__(__pyx_type)
                      ^
    ------------------------------------------------------------
    
    (tree fragment):5:19: local variable 'result' referenced before assignment
    
    Error compiling Cython file:
    ------------------------------------------------------------
    ...
        if __pyx_checksum != 0xc9b5cdc:
            from pickle import PickleError
            raise PickleError("Incompatible checksums (%s vs 0xc9b5cdc = (exception, value))" % __pyx_checksum)
        result = result.__new__(__pyx_type)
        if __pyx_state is not None:
            __pyx_unpickle_result__set_state(<result> result, __pyx_state)
```

Where `result` is a cdef class. It seems like the name `result` itself is special. This produces it:
```
$ cat /tmp/ares.pyx
cdef class result:
	pass
$ cython -o /tmp/a.c /tmp/ares.pyx

Error compiling Cython file:
------------------------------------------------------------
...
def __pyx_unpickle_result(__pyx_type, long __pyx_checksum, __pyx_state):
    if __pyx_checksum != 0xd41d8cd:
        from pickle import PickleError
        raise PickleError("Incompatible checksums (%s vs 0xd41d8cd = ())" % __pyx_checksum)
    result = result.__new__(__pyx_type)
                  ^
------------------------------------------------------------

(tree fragment):5:19: local variable 'result' referenced before assignment

Error compiling Cython file:
------------------------------------------------------------
...
    if __pyx_checksum != 0xd41d8cd:
        from pickle import PickleError
        raise PickleError("Incompatible checksums (%s vs 0xd41d8cd = ())" % __pyx_checksum)
    result = result.__new__(__pyx_type)
    if __pyx_state is not None:
        __pyx_unpickle_result__set_state(<result> result, __pyx_state)
                                         ^
------------------------------------------------------------

(tree fragment):7:42: 'result' is not a type identifier
```

If I set `auto_pickle` to False in the directives, the class complies fine. Likewise, if I use a different name for the class ("result2"), it compiles fine:
```
$ cat /tmp/ares2.pyx
cdef class result2:
	pass
$ cython -o /tmp/a.c /tmp/ares2.pyx
$ 
```



Discussion:
This looks like normal Python semantics. If you assign to a variable, it becomes a local variable. The fix is to rename the local variable so that it does not shadow the global type name any more. Don't know what changed in Cython 0.26 that prevented it from showing up before, but the bug is definitely not in Cython.

The code in question is *not* part of the application. It's (incorrectly) auto-generated by Cython. There's a clash in the names generated by Cython. 

Cython 0.26 will fail to compile this trivial file:
```python
cdef class result:
   pass
```

Adding `#cython: auto-pickle=False` to the top of the file resolves the issue (because the incorrect code is no longer generated). Likewise, changing the name of the trivial class to `result2` also works (because the name no longer clashes).

Having a class named `result` shouldn't break Cython, IMHO.

Resolving patch:
diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py
--- a/Cython/Compiler/ParseTreeTransforms.py
+++ b/Cython/Compiler/ParseTreeTransforms.py
@@ -1673,22 +1673,22 @@ def %(unpickle_func_name)s(__pyx_type, long __pyx_checksum, __pyx_state):
                     if __pyx_checksum != %(checksum)s:
                         from pickle import PickleError
                         raise PickleError("Incompatible checksums (%%s vs %(checksum)s = (%(members)s))" %% __pyx_checksum)
-                    result = %(class_name)s.__new__(__pyx_type)
+                    __pyx_result = %(class_name)s.__new__(__pyx_type)
                     if __pyx_state is not None:
-                        %(unpickle_func_name)s__set_state(<%(class_name)s> result, __pyx_state)
-                    return result
+                        %(unpickle_func_name)s__set_state(<%(class_name)s> __pyx_result, __pyx_state)
+                    return __pyx_result
 
-                cdef %(unpickle_func_name)s__set_state(%(class_name)s result, tuple __pyx_state):
+                cdef %(unpickle_func_name)s__set_state(%(class_name)s __pyx_result, tuple __pyx_state):
                     %(assignments)s
-                    if hasattr(result, '__dict__'):
-                        result.__dict__.update(__pyx_state[%(num_members)s])
+                    if hasattr(__pyx_result, '__dict__'):
+                        __pyx_result.__dict__.update(__pyx_state[%(num_members)s])
                 """ % {
                     'unpickle_func_name': unpickle_func_name,
                     'checksum': checksum,
                     'members': ', '.join(all_members_names),
                     'class_name': node.class_name,
                     'assignments': '; '.join(
-                        'result.%s = __pyx_state[%s]' % (v, ix)
+                        '__pyx_result.%s = __pyx_state[%s]' % (v, ix)
                         for ix, v in enumerate(all_members_names)),
                     'num_members': len(all_members_names),
                 }, level='module', pipeline=[NormalizeTree(None)]).substitute({})


Test patch:
diff --git a/tests/run/reduce_pickle.pyx b/tests/run/reduce_pickle.pyx
--- a/tests/run/reduce_pickle.pyx
+++ b/tests/run/reduce_pickle.pyx
@@ -114,6 +114,19 @@ cdef class DefaultReduceSubclass(DefaultReduce):
         return "DefaultReduceSubclass(i=%s, s=%r, x=%s)" % (self.i, self.s, self.x)
 
 
+cdef class result(DefaultReduceSubclass):
+    """
+    >>> a = result(i=11, s='abc', x=1.5); a
+    result(i=11, s='abc', x=1.5)
+    >>> import pickle
+    >>> pickle.loads(pickle.dumps(a))
+    result(i=11, s='abc', x=1.5)
+    """
+
+    def __repr__(self):
+        return "result(i=%s, s=%r, x=%s)" % (self.i, self.s, self.x)
+
+
 class DefaultReducePySubclass(DefaultReduce):
     """
     >>> a = DefaultReducePySubclass(i=11, s='abc', x=1.5); a