Allow pyfunction redefinition
authorVitja Makarov <vitja.makarov@gmail.com>
Mon, 6 Dec 2010 18:04:00 +0000 (21:04 +0300)
committerVitja Makarov <vitja.makarov@gmail.com>
Mon, 6 Dec 2010 18:04:00 +0000 (21:04 +0300)
Cython/Compiler/Nodes.py
Cython/Compiler/Symtab.py
tests/errors/e_redeclmeth.pyx [deleted file]
tests/run/pyfuncion_redefine_T489.pyx [new file with mode: 0644]

index 63528fbcfe4331deb14013bb23d92be41e9ab17e..53062f75d3c4881b375285b179cd5dd2c2715652 100644 (file)
@@ -2148,9 +2148,10 @@ class DefNode(FuncDefNode):
         entry = env.lookup_here(name)
         if entry and entry.type.is_cfunction and not self.is_wrapper:
             warning(self.pos, "Overriding cdef method with def method.", 5)
-        entry = env.declare_pyfunction(name, self.pos)
+        entry = env.declare_pyfunction(name, self.pos, allow_redefine=not self.is_wrapper)
         self.entry = entry
-        prefix = env.scope_prefix
+        prefix = env.next_id(env.scope_prefix)
+
         entry.func_cname = \
             Naming.pyfunc_prefix + prefix + name
         entry.pymethdef_cname = \
@@ -2225,7 +2226,7 @@ class DefNode(FuncDefNode):
 
     def needs_assignment_synthesis(self, env, code=None):
         # Should enable for module level as well, that will require more testing...
-        if self.entry.is_lambda:
+        if self.entry.is_anonymous:
             return True
         if env.is_module_scope:
             if code is None:
index 4253bbefba3a14587320e128662eb8ba88578598..a619c671acfc3fed52ef529c084ca0359a89a694 100644 (file)
@@ -75,7 +75,7 @@ class Entry(object):
     # is_cfunction     boolean    Is a C function
     # is_cmethod       boolean    Is a C method of an extension type
     # is_unbound_cmethod boolean  Is an unbound C method of an extension type
-    # is_lambda        boolean    Is a lambda function
+    # is_anonymous     boolean    Is a anonymous pyfunction entry
     # is_type          boolean    Is a type definition
     # is_cclass        boolean    Is an extension class
     # is_cpp_class     boolean    Is a C++ class
@@ -138,7 +138,7 @@ class Entry(object):
     is_cfunction = 0
     is_cmethod = 0
     is_unbound_cmethod = 0
-    is_lambda = 0
+    is_anonymous = 0
     is_type = 0
     is_cclass = 0
     is_cpp_class = 0
@@ -513,18 +513,36 @@ class Scope(object):
         
     def declare_builtin(self, name, pos):
         return self.outer_scope.declare_builtin(name, pos)
-    
-    def declare_pyfunction(self, name, pos):
-        # Add an entry for a Python function.
-        entry = self.lookup_here(name)
+
+    def _declare_pyfunction(self, name, pos, visibility='extern', entry=None):
         if entry and not entry.type.is_cfunction:
-            # This is legal Python, but for now will produce invalid C.
             error(pos, "'%s' already declared" % name)
-        entry = self.declare_var(name, py_object_type, pos, visibility='extern')
+            error(entry.pos, "Previous declaration is here")
+        entry = self.declare_var(name, py_object_type, pos, visibility=visibility)
         entry.signature = pyfunction_signature
         self.pyfunc_entries.append(entry)
         return entry
 
+    def declare_pyfunction(self, name, pos, allow_redefine=False, visibility='extern'):
+        # Add an entry for a Python function.
+        entry = self.lookup_here(name)
+        if not allow_redefine:
+            return self._declare_pyfunction(name, pos, visibility=visibility, entry=entry)
+        if entry:
+            if entry.type.is_unspecified:
+                entry.type = py_object_type
+            elif not entry.type.is_pyobject:
+                return self._declare_pyfunction(name, pos, visibility=visibility, entry=entry)
+        else: # declare entry stub
+            self.declare_var(name, py_object_type, pos, visibility=visibility)
+        entry = self.declare_var(None, py_object_type, pos,
+                                 cname=name, visibility='private')
+        entry.name = EncodedString(name)
+        entry.qualified_name = self.qualify_name(name)
+        entry.signature = pyfunction_signature
+        entry.is_anonymous = True
+        return entry
+
     def declare_lambda_function(self, func_cname, pos):
         # Add an entry for an anonymous Python function.
         entry = self.declare_var(None, py_object_type, pos,
@@ -532,7 +550,7 @@ class Scope(object):
         entry.name = EncodedString(func_cname)
         entry.func_cname = func_cname
         entry.signature = pyfunction_signature
-        entry.is_lambda = True
+        entry.is_anonymous = True
         return entry
 
     def add_lambda_def(self, def_node):
@@ -1337,17 +1355,8 @@ class ClosureScope(LocalScope):
 #        return "%s->%s" % (self.cur_scope_cname, name)
 #        return "%s->%s" % (self.closure_cname, name)
 
-    def declare_pyfunction(self, name, pos):
-        # Add an entry for a Python function.
-        entry = self.lookup_here(name)
-        if entry and not entry.type.is_cfunction:
-            # This is legal Python, but for now may produce invalid C.
-            error(pos, "'%s' already declared" % name)
-        entry = self.declare_var(name, py_object_type, pos)
-        entry.signature = pyfunction_signature
-        self.pyfunc_entries.append(entry)
-        return entry
-
+    def declare_pyfunction(self, name, pos, allow_redefine=False):
+        return LocalScope.declare_pyfunction(self, name, pos, allow_redefine, visibility='private')
 
 class StructOrUnionScope(Scope):
     #  Namespace of a C struct or union.
@@ -1521,7 +1530,7 @@ class CClassScope(ClassScope):
             return entry
 
 
-    def declare_pyfunction(self, name, pos):
+    def declare_pyfunction(self, name, pos, allow_redefine=False):
         # Add an entry for a method.
         if name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__'):
             error(pos, "Special method %s must be implemented via __richcmp__" % name)
@@ -1765,7 +1774,7 @@ class PropertyScope(Scope):
 
     is_property_scope = 1
     
-    def declare_pyfunction(self, name, pos):
+    def declare_pyfunction(self, name, pos, allow_redefine=False):
         # Add an entry for a method.
         signature = get_property_accessor_signature(name)
         if signature:
diff --git a/tests/errors/e_redeclmeth.pyx b/tests/errors/e_redeclmeth.pyx
deleted file mode 100644 (file)
index 0952ee0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-class C:
-       def f(self):
-               pass
-       def f(self):
-               pass
-_ERRORS = u"""
-4:1: 'f' already declared
-"""
diff --git a/tests/run/pyfuncion_redefine_T489.pyx b/tests/run/pyfuncion_redefine_T489.pyx
new file mode 100644 (file)
index 0000000..7add388
--- /dev/null
@@ -0,0 +1,62 @@
+"""
+>>> xxx
+[0, 1, 2, 3]
+"""
+
+xxx = []
+foo = 0
+xxx.append(foo)
+def foo():
+    return 1
+xxx.append(foo())
+def foo():
+    return 2
+xxx.append(foo())
+foo = 3
+xxx.append(foo)
+
+def closure_scope(a):
+    """
+    >>> closure_scope(0)
+    [0, 1, 'X', -4, 3]
+    """
+    ret = []
+    foo = a + 0
+    ret.append(foo)
+    def foo():
+        return a + 1
+    ret.append(foo())
+    def foo():
+        return 'X'
+    ret.append(foo())
+    def foo(b):
+        return a - b
+    ret.append(foo(4))
+    foo = a + 3
+    ret.append(foo)
+    return ret
+
+class ClassScope(object):
+    """
+    >>> obj = ClassScope()
+    [0, 1, 2, 3]
+    """
+    x = []
+    def __init__(self):
+        r = []
+        for x in self.x:
+            if isinstance(x, int):
+                r.append(x)
+            else:
+                r.append(x(self))
+        print r
+    foo = 0
+    x.append(foo)
+    def foo(self):
+        return 1
+    x.append(foo)
+    def foo(self):
+        return 2
+    x.append(foo)
+    foo = 3
+    x.append(foo)