]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-104357: fix inlined comprehensions that close over iteration var (#104368)
authorCarl Meyer <carl@oddbird.net>
Thu, 11 May 2023 01:08:40 +0000 (19:08 -0600)
committerGitHub <noreply@github.com>
Thu, 11 May 2023 01:08:40 +0000 (18:08 -0700)
Lib/test/test_listcomps.py
Python/symtable.c

index 92fed98dd0004a49e0cebbb85cb8f0803f6ec483..1cc202bb599ae66d087d8841cd7ef8490cc60dad 100644 (file)
@@ -163,6 +163,16 @@ class ListComprehensionTest(unittest.TestCase):
         outputs = {"y": [4, 4, 4, 4, 4], "i": 20}
         self._check_in_scopes(code, outputs)
 
+    def test_inner_cell_shadows_outer_no_store(self):
+        code = """
+            def f(x):
+                return [lambda: x for x in range(x)], x
+            fns, x = f(2)
+            y = [fn() for fn in fns]
+        """
+        outputs = {"y": [1, 1], "x": 2}
+        self._check_in_scopes(code, outputs)
+
     def test_closure_can_jump_over_comp_scope(self):
         code = """
             items = [(lambda: y) for i in range(5)]
index 6e74d764245a57bd51235f136b0ea73b332f6010..9361674bf1659428ab1f155065c6555502591b03 100644 (file)
@@ -607,12 +607,19 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
             SET_SCOPE(scopes, k, scope);
         }
         else {
-            // free vars in comprehension that are locals in outer scope can
-            // now simply be locals, unless they are free in comp children
-            if ((PyLong_AsLong(existing) & DEF_BOUND) &&
-                    !is_free_in_any_child(comp, k)) {
-                if (PySet_Discard(comp_free, k) < 0) {
-                    return 0;
+            if (PyLong_AsLong(existing) & DEF_BOUND) {
+                // cell vars in comprehension that are locals in outer scope
+                // must be promoted to cell so u_cellvars isn't wrong
+                if (scope == CELL && ste->ste_type == FunctionBlock) {
+                    SET_SCOPE(scopes, k, scope);
+                }
+
+                // free vars in comprehension that are locals in outer scope can
+                // now simply be locals, unless they are free in comp children
+                if (!is_free_in_any_child(comp, k)) {
+                    if (PySet_Discard(comp_free, k) < 0) {
+                        return 0;
+                    }
                 }
             }
         }