Skip to content

Commit 3515cc0

Browse files
committed
remove DEF_COMP_CELL
1 parent 48f9865 commit 3515cc0

8 files changed

Lines changed: 116 additions & 37 deletions

File tree

Doc/whatsnew/3.16.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,10 @@ symtable
500500

501501
* Inlined list, set and dict comprehensions (:pep:`709`) are now represented
502502
as their own symbol table entries, of type
503-
:attr:`~symtable.SymbolTableType.INLINED_COMPREHENSION`. This entry type
504-
represents a sub-scope, and holds information only on the symbols whose
505-
scopes are different in the comprehension and the enclosing scope.
506-
Sub-scopes are a new mechanism that can be used when a symbol's scope
507-
changes within the same compilation unit.
503+
:attr:`~symtable.SymbolTableType.INLINED_COMPREHENSION`. Each such entry is
504+
a lexical child of the enclosing scope and records the comprehension's own
505+
locals, cells, and free names. It does not correspond to a separate
506+
compilation unit.
508507
(Contributed by Irit Katriel in :gh:`124697`.)
509508

510509

Include/internal/pycore_symtable.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ _Py_IsPrivateName(PyObject *);
177177
#define DEF_ANNOT (2<<7) /* this name is annotated */
178178
#define DEF_COMP_ITER (2<<8) /* this name is a comprehension iteration variable */
179179
#define DEF_TYPE_PARAM (2<<9) /* this name is a type parameter */
180-
#define DEF_COMP_CELL (2<<10) /* this name is a cell in an inlined comprehension */
181180

182181
#define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT)
183182

Lib/symtable.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
DEF_NONLOCAL, DEF_LOCAL,
88
DEF_PARAM, DEF_TYPE_PARAM, DEF_FREE_CLASS,
99
DEF_IMPORT, DEF_BOUND, DEF_ANNOT,
10-
DEF_COMP_ITER, DEF_COMP_CELL,
10+
DEF_COMP_ITER,
1111
SCOPE_OFF, SCOPE_MASK,
1212
FREE, LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL
1313
)
@@ -154,8 +154,10 @@ def lookup(self, name):
154154
flags = self._table.symbols[name]
155155
namespaces = self.__check_children(name)
156156
module_scope = (self._table.name == "top")
157+
inlined = (self._table.type == _symtable.TYPE_INLINED_COMPREHENSION)
157158
sym = self._symbols[name] = Symbol(name, flags, namespaces,
158-
module_scope=module_scope)
159+
module_scope=module_scope,
160+
inlined_comprehension=inlined)
159161
return sym
160162

161163
def get_symbols(self):
@@ -249,12 +251,14 @@ class Class(SymbolTable):
249251

250252
class Symbol:
251253

252-
def __init__(self, name, flags, namespaces=None, *, module_scope=False):
254+
def __init__(self, name, flags, namespaces=None, *, module_scope=False,
255+
inlined_comprehension=False):
253256
self.__name = name
254257
self.__flags = flags
255258
self.__scope = _get_scope(flags)
256259
self.__namespaces = namespaces or ()
257260
self.__module_scope = module_scope
261+
self.__inlined_comprehension = inlined_comprehension
258262

259263
def __repr__(self):
260264
flags_str = '|'.join(self._flags_str())
@@ -348,7 +352,7 @@ def is_comp_iter(self):
348352
def is_comp_cell(self):
349353
"""Return *True* if the symbol is a cell in an inlined comprehension.
350354
"""
351-
return bool(self.__flags & DEF_COMP_CELL)
355+
return self.is_cell() and self.__inlined_comprehension
352356

353357
def is_namespace(self):
354358
"""Returns *True* if name binding introduces new namespace.

Lib/test/test_listcomps.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,20 @@ def f():
277277
outputs = {"y": [1]}
278278
self._check_in_scopes(code, outputs, scopes=["module", "function"])
279279

280+
def test_inlined_comp_cell_with_enclosing_free(self):
281+
# The listcomp cell and the enclosing free must not share an index.
282+
code = """
283+
def outer(y):
284+
def inner():
285+
return [lambda: x for x in (1, 2)], y
286+
return inner()
287+
funcs, val = outer(99)
288+
z = [f() for f in funcs]
289+
w = val
290+
"""
291+
outputs = {"z": [2, 2], "w": 99}
292+
self._check_in_scopes(code, outputs)
293+
280294
def test_free_inner_cell_outer(self):
281295
code = """
282296
g = 2

Lib/test/test_symtable.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ def test_symbol_repr(self):
431431

432432
st2 = symtable.symtable("[(lambda: x) for x in [1]]", "?", "exec")
433433
self.assertEqual(repr(st2.get_children()[0].lookup("x")),
434-
"<symbol 'x': CELL, DEF_LOCAL|DEF_COMP_ITER|DEF_COMP_CELL>")
434+
"<symbol 'x': CELL, DEF_LOCAL|DEF_COMP_ITER>")
435435

436436
st3 = symtable.symtable("def f():\n"
437437
" x = 1\n"
@@ -556,6 +556,20 @@ def test_inlined_comprehension_use_of_enclosing_free_in_function(self):
556556
self.assertTrue(comp.lookup("x").is_free())
557557
self.assertTrue(comp.lookup("x").is_referenced())
558558

559+
def test_inlined_comprehension_comp_cell_not_on_enclosing(self):
560+
st = symtable.symtable(
561+
"def f():\n"
562+
" x = 1\n"
563+
" return [(lambda: x) for x in [1]]",
564+
"?", "exec")
565+
f = find_block(st, "f")
566+
self.assertTrue(f.lookup("x").is_cell())
567+
self.assertFalse(f.lookup("x").is_comp_cell())
568+
comp, = (c for c in f.get_children()
569+
if c.get_type() is symtable.SymbolTableType.INLINED_COMPREHENSION)
570+
self.assertTrue(comp.lookup("x").is_cell())
571+
self.assertTrue(comp.lookup("x").is_comp_cell())
572+
559573
def test_inlined_comprehension_use_of_enclosing_free_in_class(self):
560574
st = symtable.symtable(
561575
"def f():\n"
@@ -600,8 +614,10 @@ def test_inlined_nested_comprehension_class_iter_var(self):
600614
children[0], ["x"], ["_", "x"], nested=False)
601615
self.assertFalse(C.lookup("x").is_free())
602616
self.assertTrue(C.lookup("x").is_local())
617+
self.assertFalse(C.lookup("x").is_comp_cell())
603618
self.assertFalse(children[0].lookup("x").is_free())
604619
self.assertTrue(children[0].lookup("x").is_cell())
620+
self.assertTrue(children[0].lookup("x").is_comp_cell())
605621
self.assertFalse(inner.lookup("_").is_free())
606622
self.assertTrue(inner.lookup("x").is_free())
607623
self.assertTrue(inner.lookup("x").is_referenced())

Modules/symtablemodule.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ symtable_init_constants(PyObject *m)
128128
if (PyModule_AddIntMacro(m, DEF_BOUND) < 0) return -1;
129129
if (PyModule_AddIntMacro(m, DEF_ANNOT) < 0) return -1;
130130
if (PyModule_AddIntMacro(m, DEF_COMP_ITER) < 0) return -1;
131-
if (PyModule_AddIntMacro(m, DEF_COMP_CELL) < 0) return -1;
132131

133132
if (PyModule_AddIntConstant(m, "TYPE_FUNCTION", FunctionBlock) < 0)
134133
return -1;

Python/compile.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,75 @@ dictbytype(PyObject *src, int scope_type, int flag, Py_ssize_t offset)
595595
return dest;
596596
}
597597

598+
static int
599+
add_cell_names_from_symbols(PyObject *symbols, PyObject *names)
600+
{
601+
Py_ssize_t pos = 0;
602+
PyObject *k, *v;
603+
while (PyDict_Next(symbols, &pos, &k, &v)) {
604+
long flags = PyLong_AsLong(v);
605+
if (flags == -1 && PyErr_Occurred()) {
606+
return ERROR;
607+
}
608+
if (SYMBOL_TO_SCOPE(flags) == CELL) {
609+
if (PySet_Add(names, k) < 0) {
610+
return ERROR;
611+
}
612+
}
613+
}
614+
return SUCCESS;
615+
}
616+
617+
static int
618+
add_inlined_comprehension_cell_names(PySTEntryObject *ste, PyObject *names)
619+
{
620+
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(ste->ste_children); i++) {
621+
PySTEntryObject *child =
622+
(PySTEntryObject *)PyList_GET_ITEM(ste->ste_children, i);
623+
if (child->ste_type != InlinedComprehensionBlock) {
624+
continue;
625+
}
626+
if (add_cell_names_from_symbols(child->ste_symbols, names) < 0) {
627+
return ERROR;
628+
}
629+
if (add_inlined_comprehension_cell_names(child, names) < 0) {
630+
return ERROR;
631+
}
632+
}
633+
return SUCCESS;
634+
}
635+
636+
/* Cells of the shared unit: this table's CELL names, plus cells that live
637+
* only on inlined comprehension children. */
638+
static PyObject *
639+
compiler_cellvars(PySTEntryObject *ste)
640+
{
641+
PyObject *names = PySet_New(NULL);
642+
if (names == NULL) {
643+
return NULL;
644+
}
645+
if (add_cell_names_from_symbols(ste->ste_symbols, names) < 0) {
646+
Py_DECREF(names);
647+
return NULL;
648+
}
649+
if (add_inlined_comprehension_cell_names(ste, names) < 0) {
650+
Py_DECREF(names);
651+
return NULL;
652+
}
653+
PyObject *sorted = PySequence_List(names);
654+
Py_DECREF(names);
655+
if (sorted == NULL) {
656+
return NULL;
657+
}
658+
if (PyList_Sort(sorted) < 0) {
659+
Py_DECREF(sorted);
660+
return NULL;
661+
}
662+
PyObject *cellvars = list2dict(sorted);
663+
Py_DECREF(sorted);
664+
return cellvars;
665+
}
666+
598667
int
599668
_PyCompile_EnterScope(compiler *c, identifier name, int scope_type,
600669
void *key, int lineno, PyObject *private,
@@ -626,7 +695,7 @@ _PyCompile_EnterScope(compiler *c, identifier name, int scope_type,
626695
compiler_unit_free(u);
627696
return ERROR;
628697
}
629-
u->u_metadata.u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, DEF_COMP_CELL, 0);
698+
u->u_metadata.u_cellvars = compiler_cellvars(u->u_ste);
630699
if (!u->u_metadata.u_cellvars) {
631700
compiler_unit_free(u);
632701
return ERROR;

Python/symtable.c

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,6 @@ static void _dump_symtable(PySTEntryObject* ste, PyObject* prefix)
361361
if (flags & DEF_ANNOT) printf(" DEF_ANNOT");
362362
if (flags & DEF_COMP_ITER) printf(" DEF_COMP_ITER");
363363
if (flags & DEF_TYPE_PARAM) printf(" DEF_TYPE_PARAM");
364-
if (flags & DEF_COMP_CELL) printf(" DEF_COMP_CELL");
365364
switch (scope) {
366365
case LOCAL: printf(" LOCAL"); break;
367366
case GLOBAL_EXPLICIT: printf(" GLOBAL_EXPLICIT"); break;
@@ -860,23 +859,10 @@ finalize_inlined_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
860859
goto error;
861860
}
862861
int scope = SYMBOL_TO_SCOPE(comp_flags);
863-
int only_flags = comp_flags & ((1 << SCOPE_OFFSET) - 1);
864-
if (scope == CELL || only_flags & DEF_COMP_CELL) {
862+
if (scope == CELL) {
865863
if (PySet_Add(inlined_cells, k) < 0) {
866864
goto error;
867865
}
868-
if (!(only_flags & DEF_COMP_CELL)) {
869-
comp_flags |= DEF_COMP_CELL;
870-
PyObject *newv = PyLong_FromLong(comp_flags);
871-
if (newv == NULL) {
872-
goto error;
873-
}
874-
if (PyDict_SetItem(comp->ste_symbols, k, newv) < 0) {
875-
Py_DECREF(newv);
876-
goto error;
877-
}
878-
Py_DECREF(newv);
879-
}
880866
}
881867
PyObject *existing = PyDict_GetItemWithError(ste->ste_symbols, k);
882868
if (existing == NULL && PyErr_Occurred()) {
@@ -1037,7 +1023,7 @@ drop_class_free(PySTEntryObject *ste, PyObject *free)
10371023
static int
10381024
update_symbols(PyObject *symbols, PyObject *scopes,
10391025
PyObject *bound, PyObject *free,
1040-
PyObject *inlined_cells, int classflag)
1026+
int classflag)
10411027
{
10421028
PyObject *name = NULL, *itr = NULL;
10431029
PyObject *v = NULL, *v_scope = NULL, *v_new = NULL, *v_free = NULL;
@@ -1049,13 +1035,6 @@ update_symbols(PyObject *symbols, PyObject *scopes,
10491035
if (flags == -1 && PyErr_Occurred()) {
10501036
return 0;
10511037
}
1052-
int contains = PySet_Contains(inlined_cells, name);
1053-
if (contains < 0) {
1054-
return 0;
1055-
}
1056-
if (contains) {
1057-
flags |= DEF_COMP_CELL;
1058-
}
10591038
if (PyDict_GetItemRef(scopes, name, &v_scope) < 0) {
10601039
return 0;
10611040
}
@@ -1336,7 +1315,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free,
13361315
goto error;
13371316
}
13381317
/* Records the results of the analysis in the symbol table entry */
1339-
if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, inlined_cells,
1318+
if (!update_symbols(ste->ste_symbols, scopes, bound, newfree,
13401319
(ste->ste_type == ClassBlock) || ste->ste_can_see_class_scope))
13411320
goto error;
13421321
temp = PyNumber_InPlaceOr(free, newfree);

0 commit comments

Comments
 (0)