8000 gh-118513: Fix sibling comprehensions with a name bound in one and gl… · carljm/cpython@1258fad · GitHub
[go: up one dir, main page]

Skip to content

Commit 1258fad

Browse files
committed
pythongh-118513: Fix sibling comprehensions with a name bound in one and global in the other
1 parent 16acecd commit 1258fad

File tree

2 files changed

+46
-39
lines changed

2 files changed

+46
-39
lines changed

Lib/test/test_listcomps.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,13 @@ def test_code_replace_extended_arg(self):
666666
self._check_in_scopes(code, expected)
667667
self._check_in_scopes(code, expected, exec_func=self._replacing_exec)
668668

669+
def test_multiple_comprehension_name_reuse(self):
670+
code = """
671+
[x for x in [1]]
672+
y = [x for _ in [1]]
673+
"""
674+
self._check_in_scopes(code, {"y": [3]}, ns={"x": 3})
675+
669676

670677
__test__ = {'doctests' : doctests}
671678

Python/compile.c

Lines changed: 39 additions & 39 deletions
< 3C76 td data-grid-cell-id="diff-ebc983d9f91e5bcf73500e377ac65e85863c4f77fd5b6b6caf4fcdf7c0f0b057-5568-5568-0" data-selected="false" role="gridcell" style="background-color:var(--bgColor-default);text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative diff-line-number-neutral left-side">5568
Original file line numberDiff line numberDiff line change
@@ -5500,10 +5500,45 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
55005500
while (PyDict_Next(entry->ste_symbols, &pos, &k, &v)) {
55015501
assert(PyLong_Check(v));
55025502
long symbol = PyLong_AS_LONG(v);
5503-
// only values bound in the comprehension (DEF_LOCAL) need to be handled
5504-
// at all; DEF_LOCAL | DEF_NONLOCAL can occur in the case of an
5505-
// assignment expression to a nonlocal in the comprehension, these don't
5506-
// need handling here since they shouldn't be isolated
5503+
long scope = (symbol >> SCOPE_OFFSET) & SCOPE_MASK;
5504+
PyObject *outv = PyDict_GetItemWithError(c->u->u_ste->ste_symbols, k);
5505+
if (outv == NULL) {
5506+
outv = _PyLong_GetZero();
5507+
}
5508+
assert(PyLong_Check(outv));
5509+
long outsc = (PyLong_AS_LONG(outv) >> SCOPE_OFFSET) & SCOPE_MASK;
5510+
// If a name has different scope inside than outside the comprehension,
5511+
// we need to temporarily handle it with the right scope while
5512+
// compiling the comprehension. If it's free in the comprehension
5513+
// scope, no special handling; it should be handled the same as the
5514+
// enclosing scope. (If it's free in outer scope and cell in inner
5515+
// scope, we can't treat it as both cell and free in the same function,
5516+
// but treating it as free throughout is fine; it's *_DEREF
5517+
// either way.)
5518+
if ((scope != outsc && scope != FREE && !(scope == CELL && outsc == FREE))
5519+
|| in_class_block) {
5520+
if (state->temp_symbols == NULL) {
5521+
state->temp_symbols = PyDict_New();
5522+
if (state->temp_symbols == NULL) {
5523+
return ERROR;
5524+
}
5525+
}
5526+
// update the symbol to the in-comprehension version and save
5527+
// the outer version; we'll restore it after running the
5528+
// comprehension
5529+
Py_INCREF(outv);
5530+
if (PyDict_SetItem(c->u->u_ste->ste_symbols, k, v) < 0) {
5531+
Py_DECREF(outv);
5532+
return ERROR;
5533+
}
5534+
if (PyDict_SetItem(state->temp_symbols, k, outv) < 0) {
5535+
Py_DECREF(outv);
5536+
return ERROR;
5537+
}
5538+
Py_DECREF(outv);
5539+
}
5540+
// locals handling for names bound in comprehension (DEF_LOCAL |
5541+
// DEF_NONLOCAL occurs in assignment expression to nonlocal)
55075542
if ((symbol & DEF_LOCAL && !(symbol & DEF_NONLOCAL)) || in_class_block) {
55085543
if (!_PyST_IsFunctionLike(c->u->u_ste)) {
55095544
// non-function scope: override this name to use fast locals
@@ -5528,41 +5563,6 @@ push_inlined_comprehension_state(struct compiler *c, location loc,
55285563
}
55295564
}
55305565
}
5531-
long scope = (symbol >> SCOPE_OFFSET) & SCOPE_MASK;
5532-
PyObject *outv = PyDict_GetItemWithError(c->u->u_ste->ste_symbols, k);
5533-
if (outv == NULL) {
5534-
outv = _PyLong_GetZero();
5535-
}
5536-
assert(PyLong_Check(outv));
5537-
long outsc = (PyLong_AS_LONG(outv) >> SCOPE_OFFSET) & SCOPE_MASK;
5538-
if (scope != outsc && !(scope == CELL && outsc == FREE)) {
5539-
// If a name has different scope inside than outside the
5540-
// comprehension, we need to temporarily handle it with the
5541-
// right scope while compiling the comprehension. (If it's free
5542-
// in outer scope and cell in inner scope, we can't treat it as
5543-
// both cell and free in the same function, but treating it as
5544-
// free throughout is fine; it's *_DEREF either way.)
5545-
5546-
if (state->temp_symbols == NULL) {
5547-
state->temp_symbols = PyDict_New();
5548-
if (state->temp_symbols == NULL) {
5549-
return ERROR;
5550-
}
5551-
}
5552-
// update the symbol to the in-comprehension version and save
5553-
// the outer version; we'll restore it after running the
5554-
// comprehension
5555-
Py_INCREF(outv);
5556-
if (PyDict_SetItem(c->u->u_ste->ste_symbols, k, v) < 0) {
5557-
Py_DECREF(outv);
5558-
return ERROR;
5559-
}
5560-
if (PyDict_SetItem(state->temp_symbols, k, outv) < 0) {
5561-
Py_DECREF(outv);
5562-
return ERROR;
5563-
}
5564-
Py_DECREF(outv);
5565-
}
55665566
// local names bound in comprehension must be isolated from
55675567
// outer scope; push existing value (which may be NULL if
5568
// not defined) on stack

0 commit comments

Comments
 (0)
0