summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/ref/ref7.tex20
-rw-r--r--Include/opcode.h1
-rw-r--r--Lib/dis.py1
-rw-r--r--Lib/test/output/test_exceptions6
-rw-r--r--Lib/test/output/test_grammar2
-rw-r--r--Lib/test/test_exceptions.py21
-rw-r--r--Lib/test/test_grammar.py19
-rw-r--r--Python/ceval.c28
-rw-r--r--Python/compile.c21
9 files changed, 74 insertions, 45 deletions
diff --git a/Doc/ref/ref7.tex b/Doc/ref/ref7.tex
index d5db0a85f1..b8fac0b81c 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 89813ef0fa..546ad08856 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 269304eefd..2dcecdb3bd 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 8ce0154b9d..e1e146ab36 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 172a597467..319177c22c 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 73c2489022..9f42659adc 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 6e0fe91b66..b7af64a256 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 8eaa8bdbf5..264ba30a75 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 68f9e7f98c..3dae4c85d0 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,