Give compiler error on cdef assignments in class bodies, more comments.
authorDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Sun, 20 Jul 2008 10:23:12 +0000 (12:23 +0200)
committerDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Sun, 20 Jul 2008 10:23:12 +0000 (12:23 +0200)
Cython/Compiler/Errors.py
Cython/Compiler/Main.py
Cython/Compiler/Nodes.py
Cython/Compiler/ParseTreeTransforms.py
tests/errors/e_cdefassign.pyx [new file with mode: 0644]
tests/run/cdefassign.pyx

index bc105fafa97d33cf3ae218b457e0771e94e61d4e..76a699aff25c8297526eec95cde20222372f8991 100644 (file)
@@ -33,6 +33,7 @@ class CompileError(PyrexError):
     def __init__(self, position = None, message = ""):
         self.position = position
         self.message_only = message
+        self.reported = False
     # Deprecated and withdrawn in 2.6:
     #   self.message = message
         if position:
@@ -88,17 +89,23 @@ def close_listing_file():
         listing_file.close()
         listing_file = None
 
-def error(position, message):
-    #print "Errors.error:", repr(position), repr(message) ###
+def report_error(err):
     global num_errors
-    err = CompileError(position, message)
-#    if position is not None: raise Exception(err) # debug
+    # See Main.py for why dual reporting occurs. Quick fix for now.
+    if err.reported: return
+    err.reported = True
     line = "%s\n" % err
     if listing_file:
         listing_file.write(line)
     if echo_file:
         echo_file.write(line)
     num_errors = num_errors + 1
+
+def error(position, message):
+    #print "Errors.error:", repr(position), repr(message) ###
+    err = CompileError(position, message)
+#    if position is not None: raise Exception(err) # debug
+    report_error(err)
     return err
 
 LEVEL=1 # warn about all errors level 1 or higher
index 59fdf8403ced169664e16e1b8c11fd7eb52e7c93..dfe10a24612139368c5cb2686e95d703b6162221 100644 (file)
@@ -329,8 +329,9 @@ class Context:
         try:
             for phase in pipeline:
                 data = phase(data)
-        except CompileError:
+        except CompileError, err:
             errors_occurred = True
+            Errors.report_error(err)
         return (errors_occurred, data)
 
 def create_parse(context):
index 8fdaae427cb2c73d0a6d8b172d8162c37ef83e69..c5e0487e949c8723f420295a98071ed293106ece 100644 (file)
@@ -388,7 +388,7 @@ class CNameDeclaratorNode(CDeclaratorNode):
             self.default.release_temp(env)
 
     def generate_execution_code(self, code):
-        assert self.default is None
+        raise RuntimeError("Deprecated")
         # PostParse creates assignment statements for any
         # default values
 
index 57828983284b33e8e56e783c5b4a0f354c26e0fd..81eeecf84d9e546cdea028e245250b204881a950 100644 (file)
@@ -82,6 +82,7 @@ ERR_BUF_DUP = '"%s" buffer option already supplied'
 ERR_BUF_MISSING = '"%s" missing'
 ERR_BUF_INT = '"%s" must be an integer'
 ERR_BUF_NONNEG = '"%s" must be non-negative'
+ERR_CDEF_INCLASS = 'Cannot assign default value to cdef class attributes'
 
 class PostParse(CythonTransform):
     """
@@ -92,7 +93,8 @@ class PostParse(CythonTransform):
 
     Specifically:
     - Default values to cdef assignments are turned into single
-    assignments following the declaration.
+    assignments following the declaration (everywhere but in class
+    bodies, where they raise a compile error)
     - CBufferAccessTypeNode has its options interpreted:
     Any first positional argument goes into the "dtype" attribute,
     any "ndim" keyword argument goes into the "ndim" attribute and
@@ -103,9 +105,28 @@ class PostParse(CythonTransform):
     if a more pure Abstract Syntax Tree is wanted.
     """
 
+    # Track our context.
+    in_class_body = False
+    def visit_ClassDefNode(self, node):
+        prev = self.in_class_body
+        self.in_class_body = True
+        self.visitchildren(node)
+        self.in_class_body = prev
+        return node
+
+    def visit_FuncDefNode(self, node):
+        prev = self.in_class_body
+        self.in_class_body = False
+        self.visitchildren(node)
+        self.in_class_body = prev
+        return node
+
+    # cdef variables
     def visit_CVarDefNode(self, node):
         # This assumes only plain names and pointers are assignable on
-        # declaration.
+        # declaration. Also, it makes use of the fact that a cdef decl
+        # must appear before the first use, so we don't have to deal with
+        # "i = 3; cdef int i = i" and can simply move the nodes around.
         self.visitchildren(node)
         stats = [node]
         for decl in node.declarators:
@@ -113,13 +134,15 @@ class PostParse(CythonTransform):
                 decl = decl.base
             if isinstance(decl, CNameDeclaratorNode):
                 if decl.default is not None:
+                    if self.in_class_body:
+                        raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
                     stats.append(SingleAssignmentNode(node.pos,
                         lhs=NameNode(node.pos, name=decl.name),
                         rhs=decl.default))
                     decl.default = None
         return stats
-            
 
+    # buffer access
     buffer_options = ("dtype", "ndim") # ordered!
     def visit_CBufferAccessTypeNode(self, node):
         options = {}
diff --git a/tests/errors/e_cdefassign.pyx b/tests/errors/e_cdefassign.pyx
new file mode 100644 (file)
index 0000000..fe66df1
--- /dev/null
@@ -0,0 +1,6 @@
+cdef class A:
+    cdef int value = 3
+
+_ERRORS = u"""
+2:13: Cannot assign default value to cdef class attributes
+"""
index 3ac1795d0be1406d6c2be3aaff9b5c0ef02ff820..eba76a9667b95ba75d6951a0cbe1dacb17279a69 100644 (file)
@@ -1,19 +1,17 @@
 
 __doc__ = """
    >>> test(1, 2)
-   4 1 2 2 0
-   >>> A().value
-   4
+   4 1 2 2 0 7 8
 """
 
-cdef class A:
-    cdef int value = 4
+cdef int g = 7
 
 def test(x, int y):
     if True:
         before = 0
     cdef int a = 4, b = x, c = y, *p = &y
-    print a, b, c, p[0], before
+    cdef object o = int(8)
+    print a, b, c, p[0], before, g, o
 
 # Also test that pruning cdefs doesn't hurt 
 def empty():