diff --git a/Lib/test/test_listcomps.py b/Lib/test/test_listcomps.py index dfcfdc73f9dc84..6094a74574a3aa 100644 --- a/Lib/test/test_listcomps.py +++ b/Lib/test/test_listcomps.py @@ -114,7 +114,6 @@ def get_output(moddict, name): def _f(): {code} return locals() - import dis; dis.dis(_f) # TODO _out = _f() """).format(code=textwrap.indent(code, " ")) def get_output(moddict, name): diff --git a/Python/symtable.c b/Python/symtable.c index 98ea7c59b4c66f..d5306abeeca01c 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -793,7 +793,7 @@ is_free_in_any_child(PySTEntryObject *entry, PyObject *key) static int inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, PyObject *scopes, PyObject *comp_free, - PyObject *inlined_cells) + PyObject *inlined_cells, PyObject *inlined_locals) { PyObject *k, *v; Py_ssize_t pos = 0; @@ -843,6 +843,11 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, return 0; } SET_SCOPE(scopes, k, scope); + if (scope == LOCAL) { + if (PySet_Add(inlined_locals, k) < 0) { + return 0; + } + } } else { long flags = PyLong_AsLong(existing); @@ -882,15 +887,20 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, */ static int -analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells) +analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells, PyObject *inlined_locals) { - PyObject *name, *v, *v_cell; + PyObject *name, *v, *v_cell, *v_free; int success = 0; Py_ssize_t pos = 0; v_cell = PyLong_FromLong(CELL); if (!v_cell) return 0; + v_free = PyLong_FromLong(FREE); + if (!v_free) { + Py_DECREF(v_cell); + return 0; + } while (PyDict_Next(scopes, &pos, &name, &v)) { long scope = PyLong_AsLong(v); if (scope == -1 && PyErr_Occurred()) { @@ -911,6 +921,19 @@ analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells) continue; } } + /* If the name is defined by an inlined comprehension, it must be + * preserved as free in the outer scope. */ + contains = PySet_Contains(inlined_locals, name); + if (contains < 0) { + goto error; + } + if (contains) { + if (PyDict_SetItem(scopes, name, v_free) < 0) { + goto error; + } + continue; + } + /* Replace LOCAL with CELL for this name, and remove from free. It is safe to replace the value of name in the dict, because it will not cause a resize. @@ -923,6 +946,7 @@ analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells) success = 1; error: Py_DECREF(v_cell); + Py_DECREF(v_free); return success; } @@ -1099,7 +1123,8 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, PySTEntryObject *class_entry) { PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL; - PyObject *newglobal = NULL, *newfree = NULL, *inlined_cells = NULL; + PyObject *newglobal = NULL, *newfree = NULL; + PyObject *inlined_cells = NULL, *inlined_locals = NULL; PyObject *temp; int success = 0; Py_ssize_t i, pos = 0; @@ -1134,6 +1159,9 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, inlined_cells = PySet_New(NULL); if (!inlined_cells) goto error; + inlined_locals = PySet_New(NULL); + if (!inlined_locals) + goto error; /* Class namespace has no effect on names visible in nested functions, so populate the global and bound @@ -1231,7 +1259,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, goto error; } if (inline_comp) { - if (!inline_comprehension(ste, entry, scopes, child_free, inlined_cells)) { + if (!inline_comprehension(ste, entry, scopes, child_free, inlined_cells, inlined_locals)) { Py_DECREF(child_free); goto error; } @@ -1259,7 +1287,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, } /* Check if any local variables must be converted to cell variables */ - if (_PyST_IsFunctionLike(ste) && !analyze_cells(scopes, newfree, inlined_cells)) + if (_PyST_IsFunctionLike(ste) && !analyze_cells(scopes, newfree, inlined_cells, inlined_locals)) goto error; else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree)) goto error;