Skip to content

Commit fcd5fb4

Browse files
authored
gh-104357: fix inlined comprehensions that close over iteration var (#104368)
1 parent 94f30c7 commit fcd5fb4

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

Lib/test/test_listcomps.py

+10
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ def test_inner_cell_shadows_outer(self):
163163
outputs = {"y": [4, 4, 4, 4, 4], "i": 20}
164164
self._check_in_scopes(code, outputs)
165165

166+
def test_inner_cell_shadows_outer_no_store(self):
167+
code = """
168+
def f(x):
169+
return [lambda: x for x in range(x)], x
170+
fns, x = f(2)
171+
y = [fn() for fn in fns]
172+
"""
173+
outputs = {"y": [1, 1], "x": 2}
174+
self._check_in_scopes(code, outputs)
175+
166176
def test_closure_can_jump_over_comp_scope(self):
167177
code = """
168178
items = [(lambda: y) for i in range(5)]

Python/symtable.c

+13-6
Original file line numberDiff line numberDiff line change
@@ -607,12 +607,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
607607
SET_SCOPE(scopes, k, scope);
608608
}
609609
else {
610-
// free vars in comprehension that are locals in outer scope can
611-
// now simply be locals, unless they are free in comp children
612-
if ((PyLong_AsLong(existing) & DEF_BOUND) &&
613-
!is_free_in_any_child(comp, k)) {
614-
if (PySet_Discard(comp_free, k) < 0) {
615-
return 0;
610+
if (PyLong_AsLong(existing) & DEF_BOUND) {
611+
// cell vars in comprehension that are locals in outer scope
612+
// must be promoted to cell so u_cellvars isn't wrong
613+
if (scope == CELL && ste->ste_type == FunctionBlock) {
614+
SET_SCOPE(scopes, k, scope);
615+
}
616+
617+
// free vars in comprehension that are locals in outer scope can
618+
// now simply be locals, unless they are free in comp children
619+
if (!is_free_in_any_child(comp, k)) {
620+
if (PySet_Discard(comp_free, k) < 0) {
621+
return 0;
622+
}
616623
}
617624
}
618625
}

0 commit comments

Comments
 (0)