Resolve test_inspect bytecode parity gaps by youknowone · Pull Request #7926 · RustPython/RustPython
🌐 Web query:
In CPython, does the compiler always store the synthetic classcellinto the class locals namespace, even when the class body declaresglobal classcellornonlocal classcell?
💡 Result:
No—CPython does not always end up storing the synthetic classcell into the class-body locals namespace when the class body declares global classcell or nonlocal classcell. In CPython, the zero-argument super() mechanism is implemented by injecting a hidden class / classcell cell during compilation, but CPython also standardizes behavior so that class and (in 3.6+) classcell do not show up in the class locals while the class body executes [1]. Additionally, the generator/symbol-table machinery for class/classcell interacts “strangely” with explicit nonlocal class in nested namespaces: the injection behaves like a closure/free variable and can be accessed via nonlocal from a nested class, without being treated as a normal local in the class-body locals mapping [1]. This directly implies that declaring nonlocal classcell (analogous to the class case discussed) prevents classcell from being stored like a regular local entry in the class locals namespace. So, when you explicitly declare global classcell or nonlocal classcell, CPython’s compiler/symbol-table handling will not necessarily (and in these cases generally does not) place the synthetic classcell into the class locals mapping the way it would for a normal class body without such declarations; instead the injected cell remains hidden/handled specially for LOAD_BUILD_CLASS and zero-argument super() semantics [1].
Citations:
🏁 Script executed:
# First, check the file size and read the relevant sections
wc -l crates/codegen/src/compile.rsRepository: RustPython/RustPython
Length of output: 101
🏁 Script executed:
# Read around line 6717 sed -n '6710,6725p' crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 690
🏁 Script executed:
# Read around line 6731 sed -n '6724,6738p' crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 557
🏁 Script executed:
# Find the store_name method definition to understand its behavior rg -n "fn store_name" crates/codegen/src/compile.rs | head -5
Repository: RustPython/RustPython
Length of output: 134
🏁 Script executed:
# Search for any special handling of __classcell__ or __classdictcell__ rg -n "__classcell__|__classdictcell__" crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 661
🏁 Script executed:
# Read the store_name implementation sed -n '3146,3160p' crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 684
🏁 Script executed:
# Search for any method that might do unconditional class-local stores rg -n "store_.*local|store_cell|STORE_DEREF|STORE_GLOBAL" crates/codegen/src/compile.rs | head -20
Repository: RustPython/RustPython
Length of output: 380
🏁 Script executed:
# Get broader context around line 6717 - understand the class body compilation sed -n '6690,6745p' crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 1960
🏁 Script executed:
# Find compile_name implementation rg -n "fn compile_name" crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 154
🏁 Script executed:
# Search for enum NameUsage to understand Store variant rg -n "enum NameUsage" crates/codegen/src/compile.rs -A 10
Repository: RustPython/RustPython
Length of output: 386
🏁 Script executed:
# Look for direct instruction emitters that might bypass name resolution rg -n "Instruction::Store|PseudoInstruction::Store" crates/codegen/src/compile.rs | head -20
Repository: RustPython/RustPython
Length of output: 1639
🏁 Script executed:
# Check how __static_attributes__ is handled (line 6707 shows it uses store_name too) # Let's see if there's any special handling for class protocol names rg -n "__staticattr|__firstlineno__|__module__|__qualname__" crates/codegen/src/compile.rs | head -20
Repository: RustPython/RustPython
Length of output: 1309
🏁 Script executed:
# Confirm that __classdict__ uses direct STORE_DEREF sed -n '6648,6656p' crates/codegen/src/compile.rs
Repository: RustPython/RustPython
Length of output: 502
🏁 Script executed:
# Check what the exact instruction types are for these stores rg -n "Instruction::(StoreDeref|StoreName|StoreGlobal|StoreFast)" crates/codegen/src/compile.rs | grep -E "6(6|7)[0-9]{2}:" | head -20
Repository: RustPython/RustPython
Length of output: 234
Use unconditional class-local stores for __classcell__ and __classdictcell__ instead of routing through store_name().
These are class-creation protocol slots that must always be stored to the class namespace, regardless of global or nonlocal declarations in the class body. Routing them through store_name() causes them to honor such declarations, converting them to STORE_DEREF or STORE_GLOBAL, which prevents the class namespace from receiving the cells that __build_class__ expects. Note that __classdict__ is already correctly handled with a direct Instruction::StoreDeref (line 6652); apply the same pattern to both locations.