From 53c0810f8ab74bc89ef0f1a8f60b472d17d989f8 Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Sun, 20 Jul 2008 12:23:12 +0200 Subject: [PATCH] Give compiler error on cdef assignments in class bodies, more comments. --- Cython/Compiler/Errors.py | 15 +++++++++---- Cython/Compiler/Main.py | 3 ++- Cython/Compiler/Nodes.py | 2 +- Cython/Compiler/ParseTreeTransforms.py | 29 +++++++++++++++++++++++--- tests/errors/e_cdefassign.pyx | 6 ++++++ tests/run/cdefassign.pyx | 10 ++++----- 6 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 tests/errors/e_cdefassign.pyx diff --git a/Cython/Compiler/Errors.py b/Cython/Compiler/Errors.py index bc105faf..76a699af 100644 --- a/Cython/Compiler/Errors.py +++ b/Cython/Compiler/Errors.py @@ -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 diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py index 59fdf840..dfe10a24 100644 --- a/Cython/Compiler/Main.py +++ b/Cython/Compiler/Main.py @@ -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): diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 8fdaae42..c5e0487e 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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 diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 57828983..81eeecf8 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -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 index 00000000..fe66df1a --- /dev/null +++ b/tests/errors/e_cdefassign.pyx @@ -0,0 +1,6 @@ +cdef class A: + cdef int value = 3 + +_ERRORS = u""" +2:13: Cannot assign default value to cdef class attributes +""" diff --git a/tests/run/cdefassign.pyx b/tests/run/cdefassign.pyx index 3ac1795d..eba76a96 100644 --- a/tests/run/cdefassign.pyx +++ b/tests/run/cdefassign.pyx @@ -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(): -- 2.26.2