From 3faa52ecc4aeb30f8913b4dd105184f6f7bc733d Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 1 Feb 2001 22:48:12 +0000 Subject: Allow 'continue' inside 'try' clause SF patch 102989 by Thomas Wouters --- Doc/ref/ref7.tex | 20 +++++++++++--------- Include/opcode.h | 1 + Lib/dis.py | 1 + Lib/test/output/test_exceptions | 6 +----- Lib/test/output/test_grammar | 2 ++ Lib/test/test_exceptions.py | 21 ++------------------- Lib/test/test_grammar.py | 19 +++++++++++++++++++ Python/ceval.c | 28 ++++++++++++++++++++++++---- Python/compile.c | 21 +++++++++++++-------- 9 files changed, 74 insertions(+), 45 deletions(-) diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex index d5db0a8..b8fac0b 100644 --- a/Doc/ref/ref7.tex +++ b/Doc/ref/ref7.tex @@ -260,17 +260,19 @@ The \keyword{try}...\keyword{finally} form specifies a `cleanup' handler. The \keyword{try} clause, the exception is temporarily saved, the \keyword{finally} clause is executed, and then the saved exception is re-raised. If the \keyword{finally} clause raises another exception or -executes a \keyword{return}, \keyword{break} or \keyword{continue} statement, -the saved exception is lost. The exception information is not -available to the program during execution of the \keyword{finally} -clause. +executes a \keyword{return} or \keyword{break} statement, the saved +exception is lost. A \keyword{continue} statement is illegal in the +\keyword{finally} clause. (The reason is a problem with the current +implementation -- thsi restriction may be lifted in the future). The +exception information is not available to the program during execution of +the \keyword{finally} clause. \kwindex{finally} -When a \keyword{return} or \keyword{break} statement is executed in the -\keyword{try} suite of a \keyword{try}...\keyword{finally} statement, the -\keyword{finally} clause is also executed `on the way out.' A -\keyword{continue} statement is illegal in the \keyword{try} clause. (The -reason is a problem with the current implementation --- this +When a \keyword{return}, \keyword{break} or \keyword{continue} statement is +executed in the \keyword{try} suite of a \keyword{try}...\keyword{finally} +statement, the \keyword{finally} clause is also executed `on the way out.' A +\keyword{continue} statement is illegal in the \keyword{finally} clause. +(The reason is a problem with the current implementation --- this restriction may be lifted in the future). \stindex{return} \stindex{break} diff --git a/Include/opcode.h b/Include/opcode.h index 89813ef..546ad08 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -104,6 +104,7 @@ extern "C" { #define LOAD_GLOBAL 116 /* Index in name list */ +#define CONTINUE_LOOP 119 /* Start of loop (absolute) */ #define SETUP_LOOP 120 /* Target address (absolute) */ #define SETUP_EXCEPT 121 /* "" */ #define SETUP_FINALLY 122 /* "" */ diff --git a/Lib/dis.py b/Lib/dis.py index 269304e..2dcecdb 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -259,6 +259,7 @@ jrel_op('FOR_LOOP', 114) # Number of bytes to skip name_op('LOAD_GLOBAL', 116) # Index in name list +jabs_op('CONTINUE_LOOP', 119) # Target address jrel_op('SETUP_LOOP', 120) # Distance to target address jrel_op('SETUP_EXCEPT', 121) # "" jrel_op('SETUP_FINALLY', 122) # "" diff --git a/Lib/test/output/test_exceptions b/Lib/test/output/test_exceptions index 8ce0154..e1e146a 100644 --- a/Lib/test/output/test_exceptions +++ b/Lib/test/output/test_exceptions @@ -27,11 +27,7 @@ RuntimeError (not used any more?) spam SyntaxError -'continue' not supported inside 'try' clause -ok -'continue' not supported inside 'try' clause -ok -'continue' not supported inside 'try' clause +'continue' not supported inside 'finally' clause ok 'continue' not properly in loop ok diff --git a/Lib/test/output/test_grammar b/Lib/test/output/test_grammar index 172a597..319177c 100644 --- a/Lib/test/output/test_grammar +++ b/Lib/test/output/test_grammar @@ -33,6 +33,8 @@ pass_stmt flow_stmt break_stmt continue_stmt +continue + try/except ok +continue + try/finally ok return_stmt raise_stmt import_stmt diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 73c2489..9f42659 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -104,28 +104,11 @@ def ckmsg(src, msg): s = '''\ while 1: try: - continue - except: - pass -''' -ckmsg(s, "'continue' not supported inside 'try' clause") -s = '''\ -while 1: - try: - continue - finally: pass -''' -ckmsg(s, "'continue' not supported inside 'try' clause") -s = '''\ -while 1: - try: - if 1: - continue finally: - pass + continue ''' -ckmsg(s, "'continue' not supported inside 'try' clause") +ckmsg(s, "'continue' not supported inside 'finally' clause") s = '''\ try: continue diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 6e0fe91..b7af64a 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -349,6 +349,25 @@ print 'continue_stmt' # 'continue' i = 1 while i: i = 0; continue +msg = "" +while not msg: + msg = "continue + try/except ok" + try: + continue + msg = "continue failed to continue inside try" + except: + msg = "continue inside try called except block" +print msg + +msg = "" +while not msg: + msg = "finally block not called" + try: + continue + finally: + msg = "continue + try/finally ok" +print msg + print 'return_stmt' # 'return' [testlist] def g1(): return def g2(): return 1 diff --git a/Python/ceval.c b/Python/ceval.c index 8eaa8bd..264ba30 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -322,7 +322,8 @@ enum why_code { WHY_EXCEPTION, /* Exception occurred */ WHY_RERAISE, /* Exception re-raised by 'finally' */ WHY_RETURN, /* 'return' statement */ - WHY_BREAK /* 'break' statement */ + WHY_BREAK, /* 'break' statement */ + WHY_CONTINUE, /* 'continue' statement */ }; static enum why_code do_raise(PyObject *, PyObject *, PyObject *); @@ -1357,6 +1358,11 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, case BREAK_LOOP: why = WHY_BREAK; break; + + case CONTINUE_LOOP: + retval = PyInt_FromLong(oparg); + why = WHY_CONTINUE; + break; case RAISE_VARARGS: u = v = w = NULL; @@ -1419,7 +1425,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, v = POP(); if (PyInt_Check(v)) { why = (enum why_code) PyInt_AsLong(v); - if (why == WHY_RETURN) + if (why == WHY_RETURN || + why == CONTINUE_LOOP) retval = POP(); } else if (PyString_Check(v) || PyClass_Check(v)) { @@ -1834,7 +1841,7 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, case SETUP_EXCEPT: case SETUP_FINALLY: PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, - STACK_LEVEL()); + STACK_LEVEL()); continue; case SET_LINENO: @@ -2110,6 +2117,18 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, while (why != WHY_NOT && f->f_iblock > 0) { PyTryBlock *b = PyFrame_BlockPop(f); + + if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { + /* For a continue inside a try block, + don't pop the block for the loop. */ + PyFrame_BlockSetup(f, b->b_type, b->b_level, + b->b_handler); + why = WHY_NOT; + JUMPTO(PyInt_AS_LONG(retval)); + Py_DECREF(retval); + break; + } + while (STACK_LEVEL() > b->b_level) { v = POP(); Py_XDECREF(v); @@ -2145,7 +2164,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, PUSH(exc); } else { - if (why == WHY_RETURN) + if (why == WHY_RETURN || + why == CONTINUE_LOOP) PUSH(retval); v = PyInt_FromLong((long)why); PUSH(v); diff --git a/Python/compile.c b/Python/compile.c index 68f9e7f..3dae4c8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -5,7 +5,7 @@ XXX add __doc__ attribute == co_doc to code object attributes? XXX (it's currently the first item of the co_const tuple) XXX Generate simple jump for break/return outside 'try...finally' - XXX Allow 'continue' inside try-finally + XXX Allow 'continue' inside finally clause of try-finally XXX New opcode for loading the initial index for a for loop XXX other JAR tricks? */ @@ -3247,19 +3247,24 @@ com_continue_stmt(struct compiling *c, node *n) } else { int j; - for (j = 0; j <= i; ++j) { + for (j = i-1; j >= 0; --j) { if (c->c_block[j] == SETUP_LOOP) break; } - if (j < i+1) { + if (j >= 0) { /* there is a loop, but something interferes */ - for (++j; j <= i; ++j) { - if (c->c_block[i] == SETUP_EXCEPT - || c->c_block[i] == SETUP_FINALLY) { - com_error(c, PyExc_SyntaxError, - "'continue' not supported inside 'try' clause"); + for (; i > j; --i) { + if (c->c_block[i] == SETUP_EXCEPT || + c->c_block[i] == SETUP_FINALLY) { + com_addoparg(c, CONTINUE_LOOP, + c->c_begin); return; } + if (c->c_block[i] == END_FINALLY) { + com_error(c, PyExc_SyntaxError, + "'continue' not supported inside 'finally' clause"); + return; + } } } com_error(c, PyExc_SyntaxError, -- cgit v0.12