From 4c42949b47eff1bce921fae71ed93ec359dd32a4 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 1 Apr 2023 20:52:13 +0200 Subject: Fix a reference leak when a for-loop's "else:" branch contains a "break" for an outer loop. Closes https://github.com/cython/cython/issues/5347 --- Cython/Compiler/Nodes.py | 23 ++++++++++++++--------- tests/run/for_in_break_continue_T533.pyx | 29 ++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index c5ddad0a6..457ae94ad 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -6723,23 +6723,28 @@ class _ForInStatNode(LoopNode, StatNode): code.set_loop_labels(old_loop_labels) if self.else_clause: - # in nested loops, the 'else' block can contain a - # 'continue' statement for the outer loop, but we may need - # to generate cleanup code before taking that path, so we - # intercept it here - orig_continue_label = code.continue_label + # In nested loops, the 'else' block can contain 'continue' or 'break' + # statements for the outer loop, but we may need to generate cleanup code + # before taking those paths, so we intercept them here. + orig_exit_labels = (code.continue_label, code.break_label) code.continue_label = code.new_label('outer_continue') + code.break_label = code.new_label('outer_break') code.putln("/*else*/ {") self.else_clause.generate_execution_code(code) code.putln("}") - if code.label_used(code.continue_label): - code.put_goto(break_label) + needs_goto_end = not self.else_clause.is_terminator + for exit_label, orig_exit_label in zip([code.continue_label, code.break_label], orig_exit_labels): + if not code.label_used(exit_label): + continue + if needs_goto_end: + code.put_goto(break_label) + needs_goto_end = False code.mark_pos(self.pos) - code.put_label(code.continue_label) + code.put_label(exit_label) self.iterator.generate_disposal_code(code) - code.put_goto(orig_continue_label) + code.put_goto(orig_exit_label) code.set_loop_labels(old_loop_labels) code.mark_pos(self.pos) diff --git a/tests/run/for_in_break_continue_T533.pyx b/tests/run/for_in_break_continue_T533.pyx index 0baa9fa49..20dfa4caa 100644 --- a/tests/run/for_in_break_continue_T533.pyx +++ b/tests/run/for_in_break_continue_T533.pyx @@ -1,4 +1,6 @@ -# ticket: 533 +# mode: run +# ticket: t533 +# ticket: gh1093 def for_in(): """ @@ -20,6 +22,7 @@ def for_in(): break return i + def for_from(): """ >>> for_from() @@ -39,3 +42,27 @@ def for_from(): print "BREAK", i break return i + + +def for_in_break2(data, avoid): + """ + >>> for_in_break2([1,2,3,None], avoid=[1,2,3]) + 3 + >>> for_in_break2([1,2,3,None], avoid=[1]) + 1 + >>> for_in_break2([1,2,3,None], avoid=[1]) + 1 + """ + data_iter = iter(data) + value = None + while 1: + match = next(data_iter) + if match is None: + break + for value in avoid: + if match == value: + break + else: + break + + return value -- cgit v1.2.1