Commit 3faa52ec authored by Jeremy Hylton's avatar Jeremy Hylton

Allow 'continue' inside 'try' clause

SF patch 102989 by Thomas Wouters
parent 1bbc0483
...@@ -260,17 +260,19 @@ The \keyword{try}...\keyword{finally} form specifies a `cleanup' handler. The ...@@ -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{try} clause, the exception is temporarily saved, the
\keyword{finally} clause is executed, and then the saved exception is \keyword{finally} clause is executed, and then the saved exception is
re-raised. If the \keyword{finally} clause raises another exception or re-raised. If the \keyword{finally} clause raises another exception or
executes a \keyword{return}, \keyword{break} or \keyword{continue} statement, executes a \keyword{return} or \keyword{break} statement, the saved
the saved exception is lost. The exception information is not exception is lost. A \keyword{continue} statement is illegal in the
available to the program during execution of the \keyword{finally} \keyword{finally} clause. (The reason is a problem with the current
clause. 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} \kwindex{finally}
When a \keyword{return} or \keyword{break} statement is executed in the When a \keyword{return}, \keyword{break} or \keyword{continue} statement is
\keyword{try} suite of a \keyword{try}...\keyword{finally} statement, the executed in the \keyword{try} suite of a \keyword{try}...\keyword{finally}
\keyword{finally} clause is also executed `on the way out.' A statement, the \keyword{finally} clause is also executed `on the way out.' A
\keyword{continue} statement is illegal in the \keyword{try} clause. (The \keyword{continue} statement is illegal in the \keyword{finally} clause.
reason is a problem with the current implementation --- this (The reason is a problem with the current implementation --- this
restriction may be lifted in the future). restriction may be lifted in the future).
\stindex{return} \stindex{return}
\stindex{break} \stindex{break}
......
...@@ -104,6 +104,7 @@ extern "C" { ...@@ -104,6 +104,7 @@ extern "C" {
#define LOAD_GLOBAL 116 /* Index in name list */ #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_LOOP 120 /* Target address (absolute) */
#define SETUP_EXCEPT 121 /* "" */ #define SETUP_EXCEPT 121 /* "" */
#define SETUP_FINALLY 122 /* "" */ #define SETUP_FINALLY 122 /* "" */
......
...@@ -259,6 +259,7 @@ jrel_op('FOR_LOOP', 114) # Number of bytes to skip ...@@ -259,6 +259,7 @@ jrel_op('FOR_LOOP', 114) # Number of bytes to skip
name_op('LOAD_GLOBAL', 116) # Index in name list 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_LOOP', 120) # Distance to target address
jrel_op('SETUP_EXCEPT', 121) # "" jrel_op('SETUP_EXCEPT', 121) # ""
jrel_op('SETUP_FINALLY', 122) # "" jrel_op('SETUP_FINALLY', 122) # ""
......
...@@ -27,11 +27,7 @@ RuntimeError ...@@ -27,11 +27,7 @@ RuntimeError
(not used any more?) (not used any more?)
spam spam
SyntaxError SyntaxError
'continue' not supported inside 'try' clause 'continue' not supported inside 'finally' clause
ok
'continue' not supported inside 'try' clause
ok
'continue' not supported inside 'try' clause
ok ok
'continue' not properly in loop 'continue' not properly in loop
ok ok
......
...@@ -33,6 +33,8 @@ pass_stmt ...@@ -33,6 +33,8 @@ pass_stmt
flow_stmt flow_stmt
break_stmt break_stmt
continue_stmt continue_stmt
continue + try/except ok
continue + try/finally ok
return_stmt return_stmt
raise_stmt raise_stmt
import_stmt import_stmt
......
...@@ -104,28 +104,11 @@ def ckmsg(src, msg): ...@@ -104,28 +104,11 @@ def ckmsg(src, msg):
s = '''\ s = '''\
while 1: while 1:
try: try:
continue
except:
pass pass
'''
ckmsg(s, "'continue' not supported inside 'try' clause")
s = '''\
while 1:
try:
continue
finally: finally:
pass
'''
ckmsg(s, "'continue' not supported inside 'try' clause")
s = '''\
while 1:
try:
if 1:
continue continue
finally:
pass
''' '''
ckmsg(s, "'continue' not supported inside 'try' clause") ckmsg(s, "'continue' not supported inside 'finally' clause")
s = '''\ s = '''\
try: try:
continue continue
......
...@@ -349,6 +349,25 @@ print 'continue_stmt' # 'continue' ...@@ -349,6 +349,25 @@ print 'continue_stmt' # 'continue'
i = 1 i = 1
while i: i = 0; continue 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] print 'return_stmt' # 'return' [testlist]
def g1(): return def g1(): return
def g2(): return 1 def g2(): return 1
......
...@@ -322,7 +322,8 @@ enum why_code { ...@@ -322,7 +322,8 @@ enum why_code {
WHY_EXCEPTION, /* Exception occurred */ WHY_EXCEPTION, /* Exception occurred */
WHY_RERAISE, /* Exception re-raised by 'finally' */ WHY_RERAISE, /* Exception re-raised by 'finally' */
WHY_RETURN, /* 'return' statement */ 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 *); static enum why_code do_raise(PyObject *, PyObject *, PyObject *);
...@@ -1358,6 +1359,11 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, ...@@ -1358,6 +1359,11 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
why = WHY_BREAK; why = WHY_BREAK;
break; break;
case CONTINUE_LOOP:
retval = PyInt_FromLong(oparg);
why = WHY_CONTINUE;
break;
case RAISE_VARARGS: case RAISE_VARARGS:
u = v = w = NULL; u = v = w = NULL;
switch (oparg) { switch (oparg) {
...@@ -1419,7 +1425,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, ...@@ -1419,7 +1425,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
v = POP(); v = POP();
if (PyInt_Check(v)) { if (PyInt_Check(v)) {
why = (enum why_code) PyInt_AsLong(v); why = (enum why_code) PyInt_AsLong(v);
if (why == WHY_RETURN) if (why == WHY_RETURN ||
why == CONTINUE_LOOP)
retval = POP(); retval = POP();
} }
else if (PyString_Check(v) || PyClass_Check(v)) { else if (PyString_Check(v) || PyClass_Check(v)) {
...@@ -2110,6 +2117,18 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, ...@@ -2110,6 +2117,18 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
while (why != WHY_NOT && f->f_iblock > 0) { while (why != WHY_NOT && f->f_iblock > 0) {
PyTryBlock *b = PyFrame_BlockPop(f); 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) { while (STACK_LEVEL() > b->b_level) {
v = POP(); v = POP();
Py_XDECREF(v); Py_XDECREF(v);
...@@ -2145,7 +2164,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals, ...@@ -2145,7 +2164,8 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
PUSH(exc); PUSH(exc);
} }
else { else {
if (why == WHY_RETURN) if (why == WHY_RETURN ||
why == CONTINUE_LOOP)
PUSH(retval); PUSH(retval);
v = PyInt_FromLong((long)why); v = PyInt_FromLong((long)why);
PUSH(v); PUSH(v);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
XXX add __doc__ attribute == co_doc to code object attributes? XXX add __doc__ attribute == co_doc to code object attributes?
XXX (it's currently the first item of the co_const tuple) XXX (it's currently the first item of the co_const tuple)
XXX Generate simple jump for break/return outside 'try...finally' 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 New opcode for loading the initial index for a for loop
XXX other JAR tricks? XXX other JAR tricks?
*/ */
...@@ -3247,17 +3247,22 @@ com_continue_stmt(struct compiling *c, node *n) ...@@ -3247,17 +3247,22 @@ com_continue_stmt(struct compiling *c, node *n)
} }
else { else {
int j; int j;
for (j = 0; j <= i; ++j) { for (j = i-1; j >= 0; --j) {
if (c->c_block[j] == SETUP_LOOP) if (c->c_block[j] == SETUP_LOOP)
break; break;
} }
if (j < i+1) { if (j >= 0) {
/* there is a loop, but something interferes */ /* there is a loop, but something interferes */
for (++j; j <= i; ++j) { for (; i > j; --i) {
if (c->c_block[i] == SETUP_EXCEPT if (c->c_block[i] == SETUP_EXCEPT ||
|| c->c_block[i] == SETUP_FINALLY) { 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, com_error(c, PyExc_SyntaxError,
"'continue' not supported inside 'try' clause"); "'continue' not supported inside 'finally' clause");
return; return;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment