#4617: Previously it was illegal to delete a name from the local
namespace if it occurs as a free variable in a nested block. This limitation
of the compiler has been lifted, and a new opcode introduced (DELETE_DEREF).
This sample was valid in 2.6, but fails to compile in 3.x without this change::
>>> def f():
... def print_error():
... print(e)
... try:
... something
... except Exception as e:
... print_error()
... # implicit "del e" here
This sample has always been invalid in Python, and now works::
>>> def outer(x):
... def inner():
... return x
... inner()
... del x
There is no need to bump the PYC magic number: the new opcode is used
for code that did not compile before.
diff --git a/Python/ceval.c b/Python/ceval.c
index 1f78f95..f0d278c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -135,6 +135,7 @@
static PyObject * import_from(PyObject *, PyObject *);
static int import_all_from(PyObject *, PyObject *);
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
+static void format_exc_unbound(PyCodeObject *co, int oparg);
static PyObject * unicode_concatenate(PyObject *, PyObject *,
PyFrameObject *, unsigned char *);
static PyObject * special_lookup(PyObject *, char *, PyObject **);
@@ -2143,6 +2144,16 @@
);
break;
+ TARGET(DELETE_DEREF)
+ x = freevars[oparg];
+ if (PyCell_GET(x) != NULL) {
+ PyCell_Set(x, NULL);
+ continue;
+ }
+ err = -1;
+ format_exc_unbound(co, oparg);
+ break;
+
TARGET(LOAD_CLOSURE)
x = freevars[oparg];
Py_INCREF(x);
@@ -2158,22 +2169,7 @@
DISPATCH();
}
err = -1;
- /* Don't stomp existing exception */
- if (PyErr_Occurred())
- break;
- if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) {
- v = PyTuple_GET_ITEM(co->co_cellvars,
- oparg);
- format_exc_check_arg(
- PyExc_UnboundLocalError,
- UNBOUNDLOCAL_ERROR_MSG,
- v);
- } else {
- v = PyTuple_GET_ITEM(co->co_freevars, oparg -
- PyTuple_GET_SIZE(co->co_cellvars));
- format_exc_check_arg(PyExc_NameError,
- UNBOUNDFREE_ERROR_MSG, v);
- }
+ format_exc_unbound(co, oparg);
break;
TARGET(STORE_DEREF)
@@ -4352,6 +4348,28 @@
PyErr_Format(exc, format_str, obj_str);
}
+static void
+format_exc_unbound(PyCodeObject *co, int oparg)
+{
+ PyObject *name;
+ /* Don't stomp existing exception */
+ if (PyErr_Occurred())
+ return;
+ if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) {
+ name = PyTuple_GET_ITEM(co->co_cellvars,
+ oparg);
+ format_exc_check_arg(
+ PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ name);
+ } else {
+ name = PyTuple_GET_ITEM(co->co_freevars, oparg -
+ PyTuple_GET_SIZE(co->co_cellvars));
+ format_exc_check_arg(PyExc_NameError,
+ UNBOUNDFREE_ERROR_MSG, name);
+ }
+}
+
static PyObject *
unicode_concatenate(PyObject *v, PyObject *w,
PyFrameObject *f, unsigned char *next_instr)
diff --git a/Python/compile.c b/Python/compile.c
index 6963a15..5341e6b 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -857,6 +857,8 @@
return 1;
case STORE_DEREF:
return -1;
+ case DELETE_DEREF:
+ return 0;
default:
fprintf(stderr, "opcode = %d\n", opcode);
Py_FatalError("opcode_stack_effect()");
@@ -2506,13 +2508,7 @@
case AugLoad:
case AugStore:
break;
- case Del:
- PyErr_Format(PyExc_SyntaxError,
- "can not delete variable '%S' referenced "
- "in nested scope",
- name);
- Py_DECREF(mangled);
- return 0;
+ case Del: op = DELETE_DEREF; break;
case Param:
default:
PyErr_SetString(PyExc_SystemError,
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 8b59c2d..a91da79 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -137,7 +137,7 @@
&&TARGET_LOAD_CLOSURE,
&&TARGET_LOAD_DEREF,
&&TARGET_STORE_DEREF,
- &&_unknown_opcode,
+ &&TARGET_DELETE_DEREF,
&&_unknown_opcode,
&&TARGET_CALL_FUNCTION_VAR,
&&TARGET_CALL_FUNCTION_KW,