mirror of
https://github.com/python/cpython.git
synced 2026-06-21 18:41:52 -04:00
gh-150700: Fix class-scope inline comprehensions when nested scopes reference __class__ and friends (#150735)
* Fix class-scope inline comprehensions when nested scopes reference `__class__` and friends In `inline_comprehension()`, when `__class__` / `__classdict__` / `__conditional_annotations__` appears as `FREE` in a comprehension's symbol table because a nested scope captured it (e.g. nested lambdas), this name is still discarded from `comp_free` unconditionally. This prevents `drop_class_free()` from seeing it, so the appropriate `ste_needs_(...)` flag is never set on the enclosing class. That leads to `codegen_make_closure()` throwing `SystemError` when it couldn't find `__class__` / `__classdict__` / `__conditional_annotations__` in the class's cellvars. From now on we just discard from `comp_free` when no child scope (e.g. a lambda) still needs the name as `FREE`. When a child scope does need it, keep it in `comp_free` so `drop_class_free()` can set the appropriate flag and the class creates the implicit cell. * Fix tests * Fix typo * Fix formatting * Add test checking validity of `__class__` returned * Prefer 'used' to 'deferred'
This commit is contained in:
+9
-4
@@ -834,17 +834,22 @@ inline_comprehension(PySTEntryObject *ste, PySTEntryObject *comp,
|
||||
return 0;
|
||||
}
|
||||
// __class__, __classdict__ and __conditional_annotations__ are
|
||||
// never allowed to be free through a class scope (see
|
||||
// drop_class_free)
|
||||
// not allowed to be free through a class scope (see
|
||||
// drop_class_free) unless children scopes need it
|
||||
if (scope == FREE && ste->ste_type == ClassBlock &&
|
||||
(_PyUnicode_EqualToASCIIString(k, "__class__") ||
|
||||
_PyUnicode_EqualToASCIIString(k, "__classdict__") ||
|
||||
_PyUnicode_EqualToASCIIString(k, "__conditional_annotations__"))) {
|
||||
scope = GLOBAL_IMPLICIT;
|
||||
if (PySet_Discard(comp_free, k) < 0) {
|
||||
int child_needs_free = is_free_in_any_child(comp, k);
|
||||
if (child_needs_free < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!child_needs_free) {
|
||||
if (PySet_Discard(comp_free, k) < 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_PyUnicode_EqualToASCIIString(k, "__class__")) {
|
||||
remove_dunder_class = 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user