Skip to content

Commit 563c7dc

Browse files
authored
gh-104404: fix crasher with nested comprehensions plus lambdas (#104442)
1 parent 1eb950c commit 563c7dc

File tree

2 files changed

+22
-7
lines changed

2 files changed

+22
-7
lines changed

Lib/test/test_listcomps.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,14 @@ def test_nested_3(self):
338338
outputs = {"y": [1, 3, 5]}
339339
self._check_in_scopes(code, outputs)
340340

341+
def test_nested_4(self):
342+
code = """
343+
items = [([lambda: x for x in range(2)], lambda: x) for x in range(3)]
344+
out = [([fn() for fn in fns], fn()) for fns, fn in items]
345+
"""
346+
outputs = {"out": [([1, 1], 2), ([1, 1], 2), ([1, 1], 2)]}
347+
self._check_in_scopes(code, outputs)
348+
341349
def test_nameerror(self):
342350
code = """
343351
[x for x in [1]]

Python/symtable.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ is_free_in_any_child(PySTEntryObject *entry, PyObject *key)
575575

576576
static int
577577
inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
578-
PyObject *scopes, PyObject *comp_free)
578+
PyObject *scopes, PyObject *comp_free,
579+
PyObject *promote_to_cell)
579580
{
580581
PyObject *k, *v;
581582
Py_ssize_t pos = 0;
@@ -611,7 +612,9 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
611612
// cell vars in comprehension that are locals in outer scope
612613
// must be promoted to cell so u_cellvars isn't wrong
613614
if (scope == CELL && ste->ste_type == FunctionBlock) {
614-
SET_SCOPE(scopes, k, scope);
615+
if (PySet_Add(promote_to_cell, k) < 0) {
616+
return 0;
617+
}
615618
}
616619

617620
// free vars in comprehension that are locals in outer scope can
@@ -638,7 +641,7 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
638641
*/
639642

640643
static int
641-
analyze_cells(PyObject *scopes, PyObject *free)
644+
analyze_cells(PyObject *scopes, PyObject *free, PyObject *promote_to_cell)
642645
{
643646
PyObject *name, *v, *v_cell;
644647
int success = 0;
@@ -653,7 +656,7 @@ analyze_cells(PyObject *scopes, PyObject *free)
653656
scope = PyLong_AS_LONG(v);
654657
if (scope != LOCAL)
655658
continue;
656-
if (!PySet_Contains(free, name))
659+
if (!PySet_Contains(free, name) && !PySet_Contains(promote_to_cell, name))
657660
continue;
658661
/* Replace LOCAL with CELL for this name, and remove
659662
from free. It is safe to replace the value of name
@@ -803,7 +806,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
803806
PyObject *global)
804807
{
805808
PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL;
806-
PyObject *newglobal = NULL, *newfree = NULL;
809+
PyObject *newglobal = NULL, *newfree = NULL, *promote_to_cell = NULL;
807810
PyObject *temp;
808811
int success = 0;
809812
Py_ssize_t i, pos = 0;
@@ -835,6 +838,9 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
835838
newbound = PySet_New(NULL);
836839
if (!newbound)
837840
goto error;
841+
promote_to_cell = PySet_New(NULL);
842+
if (!promote_to_cell)
843+
goto error;
838844

839845
/* Class namespace has no effect on names visible in
840846
nested functions, so populate the global and bound
@@ -915,7 +921,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
915921
goto error;
916922
}
917923
if (inline_comp) {
918-
if (!inline_comprehension(ste, entry, scopes, child_free)) {
924+
if (!inline_comprehension(ste, entry, scopes, child_free, promote_to_cell)) {
919925
Py_DECREF(child_free);
920926
goto error;
921927
}
@@ -946,7 +952,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
946952
}
947953

948954
/* Check if any local variables must be converted to cell variables */
949-
if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree))
955+
if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree, promote_to_cell))
950956
goto error;
951957
else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree))
952958
goto error;
@@ -966,6 +972,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
966972
Py_XDECREF(newbound);
967973
Py_XDECREF(newglobal);
968974
Py_XDECREF(newfree);
975+
Py_XDECREF(promote_to_cell);
969976
if (!success)
970977
assert(PyErr_Occurred());
971978
return success;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy