| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -120,14 +120,15 @@ extern PyObject* _Py_Mangle(PyObject *p, PyObject *name); | |||
| 120 | 120 | #define DEF_ANNOT 2<<7 /* this name is annotated */ | |
| 121 | 121 | #define DEF_COMP_ITER 2<<8 /* this name is a comprehension iteration variable */ | |
| 122 | 122 | #define DEF_TYPE_PARAM 2<<9 /* this name is a type parameter */ | |
| 123 | + #define DEF_COMP_CELL 2<<10 /* this name is a cell in an inlined comprehension */ | ||
| 123 | 124 | ||
| 124 | 125 | #define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT) | |
| 125 | 126 | ||
| 126 | 127 | /* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol | |
| 127 | 128 | table. GLOBAL is returned from PyST_GetScope() for either of them. | |
| 128 | - It is stored in ste_symbols at bits 12-15. | ||
| 129 | + It is stored in ste_symbols at bits 13-16. | ||
| 129 | 130 | */ | |
| 130 | - #define SCOPE_OFFSET 11 | ||
| 131 | + #define SCOPE_OFFSET 12 | ||
| 131 | 132 | #define SCOPE_MASK (DEF_GLOBAL | DEF_LOCAL | DEF_PARAM | DEF_NONLOCAL) | |
| 132 | 133 | ||
| 133 | 134 | #define LOCAL 1 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -381,6 +381,32 @@ def f(): | |||
| 381 | 381 | with self.assertRaises(UnboundLocalError): | |
| 382 | 382 | f() | |
| 383 | 383 | ||
| 384 | + def test_global_outside_cellvar_inside_plus_freevar(self): | ||
| 385 | + code = """ | ||
| 386 | + a = 1 | ||
| 387 | + def f(): | ||
| 388 | + func, = [(lambda: b) for b in [a]] | ||
| 389 | + return b, func() | ||
| 390 | + x = f() | ||
| 391 | + """ | ||
| 392 | + self._check_in_scopes( | ||
| 393 | + code, {"x": (2, 1)}, ns={"b": 2}, scopes=["function", "module"]) | ||
| 394 | + # inside a class, the `a = 1` assignment is not visible | ||
| 395 | + self._check_in_scopes(code, raises=NameError, scopes=["class"]) | ||
| 396 | + | ||
| 397 | + def test_cell_in_nested_comprehension(self): | ||
| 398 | + code = """ | ||
| 399 | + a = 1 | ||
| 400 | + def f(): | ||
| 401 | + (func, inner_b), = [[lambda: b for b in c] + [b] for c in [[a]]] | ||
| 402 | + return b, inner_b, func() | ||
| 403 | + x = f() | ||
| 404 | + """ | ||
| 405 | + self._check_in_scopes( | ||
| 406 | + code, {"x": (2, 2, 1)}, ns={"b": 2}, scopes=["function", "module"]) | ||
| 407 | + # inside a class, the `a = 1` assignment is not visible | ||
| 408 | + self._check_in_scopes(code, raises=NameError, scopes=["class"]) | ||
| 409 | + | ||
| 384 | 410 | def test_name_error_in_class_scope(self): | |
| 385 | 411 | code = """ | |
| 386 | 412 | y = 1 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1252,7 +1252,7 @@ compiler_enter_scope(struct compiler *c, identifier name, | |||
| 1252 | 1252 | } | |
| 1253 | 1253 | u->u_metadata.u_name = Py_NewRef(name); | |
| 1254 | 1254 | u->u_metadata.u_varnames = list2dict(u->u_ste->ste_varnames); | |
| 1255 | - u->u_metadata.u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, 0, 0); | ||
| 1255 | + u->u_metadata.u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, DEF_COMP_CELL, 0); | ||
| 1256 | 1256 | if (!u->u_metadata.u_varnames || !u->u_metadata.u_cellvars) { | |
| 1257 | 1257 | compiler_unit_free(u); | |
| 1258 | 1258 | return ERROR; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -632,7 +632,7 @@ is_free_in_any_child(PySTEntryObject *entry, PyObject *key) | |||
| 632 | 632 | static int | |
| 633 | 633 | inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, | |
| 634 | 634 | PyObject *scopes, PyObject *comp_free, | |
| 635 | - PyObject *promote_to_cell) | ||
| 635 | + PyObject *inlined_cells) | ||
| 636 | 636 | { | |
| 637 | 637 | PyObject *k, *v; | |
| 638 | 638 | Py_ssize_t pos = 0; | |
@@ -645,6 +645,11 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, | |||
| 645 | 645 | } | |
| 646 | 646 | int scope = (comp_flags >> SCOPE_OFFSET) & SCOPE_MASK; | |
| 647 | 647 | int only_flags = comp_flags & ((1 << SCOPE_OFFSET) - 1); | |
| 648 | + if (scope == CELL || only_flags & DEF_COMP_CELL) { | ||
| 649 | + if (PySet_Add(inlined_cells, k) < 0) { | ||
| 650 | + return 0; | ||
| 651 | + } | ||
| 652 | + } | ||
| 648 | 653 | PyObject *existing = PyDict_GetItemWithError(ste->ste_symbols, k); | |
| 649 | 654 | if (existing == NULL && PyErr_Occurred()) { | |
| 650 | 655 | return 0; | |
@@ -665,14 +670,6 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, | |||
| 665 | 670 | } | |
| 666 | 671 | else { | |
| 667 | 672 | if (PyLong_AsLong(existing) & DEF_BOUND) { | |
| 668 | - // cell vars in comprehension that are locals in outer scope | ||
| 669 | - // must be promoted to cell so u_cellvars isn't wrong | ||
| 670 | - if (scope == CELL && _PyST_IsFunctionLike(ste)) { | ||
| 671 | - if (PySet_Add(promote_to_cell, k) < 0) { | ||
| 672 | - return 0; | ||
| 673 | - } | ||
| 674 | - } | ||
| 675 | - | ||
| 676 | 673 | // free vars in comprehension that are locals in outer scope can | |
| 677 | 674 | // now simply be locals, unless they are free in comp children, | |
| 678 | 675 | // or if the outer scope is a class block | |
@@ -698,7 +695,7 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp, | |||
| 698 | 695 | */ | |
| 699 | 696 | ||
| 700 | 697 | static int | |
| 701 | - analyze_cells(PyObject *scopes, PyObject *free, PyObject *promote_to_cell) | ||
| 698 | + analyze_cells(PyObject *scopes, PyObject *free, PyObject *inlined_cells) | ||
| 702 | 699 | { | |
| 703 | 700 | PyObject *name, *v, *v_cell; | |
| 704 | 701 | int success = 0; | |
@@ -713,7 +710,7 @@ analyze_cells(PyObject *scopes, PyObject *free, PyObject *promote_to_cell) | |||
| 713 | 710 | scope = PyLong_AS_LONG(v); | |
| 714 | 711 | if (scope != LOCAL) | |
| 715 | 712 | continue; | |
| 716 | - if (!PySet_Contains(free, name) && !PySet_Contains(promote_to_cell, name)) | ||
| 713 | + if (!PySet_Contains(free, name) && !PySet_Contains(inlined_cells, name)) | ||
| 717 | 714 | continue; | |
| 718 | 715 | /* Replace LOCAL with CELL for this name, and remove | |
| 719 | 716 | from free. It is safe to replace the value of name | |
@@ -753,7 +750,8 @@ drop_class_free(PySTEntryObject *ste, PyObject *free) | |||
| 753 | 750 | */ | |
| 754 | 751 | static int | |
| 755 | 752 | update_symbols(PyObject *symbols, PyObject *scopes, | |
| 756 | - PyObject *bound, PyObject *free, int classflag) | ||
| 753 | + PyObject *bound, PyObject *free, | ||
| 754 | + PyObject *inlined_cells, int classflag) | ||
| 757 | 755 | { | |
| 758 | 756 | PyObject *name = NULL, *itr = NULL; | |
| 759 | 757 | PyObject *v = NULL, *v_scope = NULL, *v_new = NULL, *v_free = NULL; | |
@@ -764,6 +762,9 @@ update_symbols(PyObject *symbols, PyObject *scopes, | |||
| 764 | 762 | long scope, flags; | |
| 765 | 763 | assert(PyLong_Check(v)); | |
| 766 | 764 | flags = PyLong_AS_LONG(v); | |
| 765 | + if (PySet_Contains(inlined_cells, name)) { | ||
| 766 | + flags |= DEF_COMP_CELL; | ||
| 767 | + } | ||
| 767 | 768 | v_scope = PyDict_GetItemWithError(scopes, name); | |
| 768 | 769 | assert(v_scope && PyLong_Check(v_scope)); | |
| 769 | 770 | scope = PyLong_AS_LONG(v_scope); | |
@@ -870,7 +871,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, | |||
| 870 | 871 | PySTEntryObject *class_entry) | |
| 871 | 872 | { | |
| 872 | 873 | PyObject *name, *v, *local = NULL, *scopes = NULL, *newbound = NULL; | |
| 873 | - PyObject *newglobal = NULL, *newfree = NULL, *promote_to_cell = NULL; | ||
| 874 | + PyObject *newglobal = NULL, *newfree = NULL, *inlined_cells = NULL; | ||
| 874 | 875 | PyObject *temp; | |
| 875 | 876 | int success = 0; | |
| 876 | 877 | Py_ssize_t i, pos = 0; | |
@@ -902,8 +903,8 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, | |||
| 902 | 903 | newbound = PySet_New(NULL); | |
| 903 | 904 | if (!newbound) | |
| 904 | 905 | goto error; | |
| 905 | - promote_to_cell = PySet_New(NULL); | ||
| 906 | - if (!promote_to_cell) | ||
| 906 | + inlined_cells = PySet_New(NULL); | ||
| 907 | + if (!inlined_cells) | ||
| 907 | 908 | goto error; | |
| 908 | 909 | ||
| 909 | 910 | /* Class namespace has no effect on names visible in | |
@@ -997,7 +998,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, | |||
| 997 | 998 | goto error; | |
| 998 | 999 | } | |
| 999 | 1000 | if (inline_comp) { | |
| 1000 | - if (!inline_comprehension(ste, entry, scopes, child_free, promote_to_cell)) { | ||
| 1001 | + if (!inline_comprehension(ste, entry, scopes, child_free, inlined_cells)) { | ||
| 1001 | 1002 | Py_DECREF(child_free); | |
| 1002 | 1003 | goto error; | |
| 1003 | 1004 | } | |
@@ -1028,12 +1029,12 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, | |||
| 1028 | 1029 | } | |
| 1029 | 1030 | ||
| 1030 | 1031 | /* Check if any local variables must be converted to cell variables */ | |
| 1031 | - if (_PyST_IsFunctionLike(ste) && !analyze_cells(scopes, newfree, promote_to_cell)) | ||
| 1032 | + if (_PyST_IsFunctionLike(ste) && !analyze_cells(scopes, newfree, inlined_cells)) | ||
| 1032 | 1033 | goto error; | |
| 1033 | 1034 | else if (ste->ste_type == ClassBlock && !drop_class_free(ste, newfree)) | |
| 1034 | 1035 | goto error; | |
| 1035 | 1036 | /* Records the results of the analysis in the symbol table entry */ | |
| 1036 | - if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, | ||
| 1037 | + if (!update_symbols(ste->ste_symbols, scopes, bound, newfree, inlined_cells, | ||
| 1037 | 1038 | ste->ste_type == ClassBlock)) | |
| 1038 | 1039 | goto error; | |
| 1039 | 1040 | ||
@@ -1048,7 +1049,7 @@ analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, | |||
| 1048 | 1049 | Py_XDECREF(newbound); | |
| 1049 | 1050 | Py_XDECREF(newglobal); | |
| 1050 | 1051 | Py_XDECREF(newfree); | |
| 1051 | - Py_XDECREF(promote_to_cell); | ||
| 1052 | + Py_XDECREF(inlined_cells); | ||
| 1052 | 1053 | if (!success) | |
| 1053 | 1054 | assert(PyErr_Occurred()); | |
| 1054 | 1055 | return success; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments