From 3bad2267f1eec01746c36d3b9a47158e2f0251b5 Mon Sep 17 00:00:00 2001 From: Magnus Lie Hetland Date: Fri, 30 Jan 2009 15:53:16 +0100 Subject: [PATCH] Fix for #196 --- Cython/Compiler/Nodes.py | 14 ++++++++++-- Cython/Compiler/Optimize.py | 3 ++- tests/run/for_decrement.pyx | 43 +++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 tests/run/for_decrement.pyx diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 88c4f3f9..7664e0de 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -3904,9 +3904,15 @@ class ForFromStatNode(LoopNode, StatNode): self.bound1.generate_evaluation_code(code) self.bound2.generate_evaluation_code(code) offset, incop = self.relation_table[self.relation1] + if incop == "++": + decop = "--" + else: + decop = "++" if self.step is not None: self.step.generate_evaluation_code(code) - incop = "%s=%s" % (incop[0], self.step.result()) + step = self.step.result() + incop = "%s=%s" % (incop[0], step) + decop = "%s=%s" % (decop[0], step) loopvar_name = self.loopvar_node.result() code.putln( "for (%s = %s%s; %s %s %s; %s%s) {" % ( @@ -3919,7 +3925,11 @@ class ForFromStatNode(LoopNode, StatNode): self.target.generate_assignment_code(self.py_loopvar_node, code) self.body.generate_execution_code(code) code.put_label(code.continue_label) - code.putln("}") + if getattr(self, "from_range", False): + # Undo last increment to maintain Python semantics: + code.putln("} %s%s;" % (loopvar_name, decop)) + else: + code.putln("}") break_label = code.break_label code.set_loop_labels(old_loop_labels) if self.else_clause: diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 2c16e711..7aec738a 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -144,7 +144,8 @@ class IterationTransform(Visitor.VisitorTransform): relation2=relation2, bound2=bound2, step=step, body=node.body, else_clause=node.else_clause, - loopvar_node=node.target) + loopvar_node=node.target, + from_range=True) return for_node def _transform_dict_iteration(self, node, dict_obj, keys, values): diff --git a/tests/run/for_decrement.pyx b/tests/run/for_decrement.pyx new file mode 100644 index 00000000..f9826fdd --- /dev/null +++ b/tests/run/for_decrement.pyx @@ -0,0 +1,43 @@ +""" +>>> range_loop_indices() +** Calculating step ** +(9, 9, 8, 1, 2) +>>> from_loop_indices() +** Calculating step ** +** Calculating step ** +** Calculating step ** +** Calculating step ** +** Calculating step ** +(10, 10, 0) +""" + +cdef int get_step(): + """ + This should only be called once, when used in range(). + """ + print "** Calculating step **" + return 2 + +def range_loop_indices(): + """ + Optimized integer for loops using range() should follow Python behavior, + and leave the index variable with the last value of the range. + """ + cdef int i, j, k, l, m + for i in range(10): pass + for j in range(2,10): pass + for k in range(0,10,get_step()): pass + for l in range(10,0,-1): pass + for m in range(10,0,-2): pass + return i, j, k, l, m + +def from_loop_indices(): + """ + for-from-loops should follow C behavior, and leave the index variable + incremented one step after the last iteration. + """ + cdef int i, j, k + for i from 0 <= i < 10 by get_step(): pass + for j from 0 <= j < 10: pass + for k from 10 > k > 0: pass + return i, j, k \ No newline at end of file -- 2.26.2