◐ Shell
clean mode source ↗

Assign opcode ids by ShaharNaveh · Pull Request #6637 · RustPython/RustPython

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the compile.rs file at the mentioned lines
head -c 1000000 crates/codegen/src/compile.rs | wc -l

Repository: RustPython/RustPython

Length of output: 69


🏁 Script executed:

# Look at the code around the specified lines
sed -n '1875,1890p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 762


🏁 Script executed:

# Find all occurrences of CallKw and Call instructions in the codebase
rg -n "CallKw|Instruction::Call\b" crates/codegen/src/compile.rs | head -50

Repository: RustPython/RustPython

Length of output: 1029


🏁 Script executed:

# Find the compile_call_inner function
rg -n "fn compile_call_inner" crates

Repository: RustPython/RustPython

Length of output: 126


🏁 Script executed:

# Search for CALL and CALL_KW instruction implementations in the VM
rg -n "CALL_KW|CALL\b" crates --type rust | grep -i "opcode\|instruction\|match" | head -40

Repository: RustPython/RustPython

Length of output: 158


🏁 Script executed:

# Read the compile_call_inner function (around line 6127)
sed -n '6120,6200p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 3137


🏁 Script executed:

# Look for CallKw enum variant definition
rg -n "CallKw" crates/codegen/src/compile.rs -B2 -A2

Repository: RustPython/RustPython

Length of output: 583


🏁 Script executed:

# Find instruction definitions - look for CallKw struct/variant
fd "bytecode.rs\|instruction" crates --type f | head -10

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Search for where Call/CallKw instructions are defined
rg -n "pub enum Instruction|struct Call|struct CallKw" crates --type rust | head -20

Repository: RustPython/RustPython

Length of output: 249


🏁 Script executed:

# Read the Instruction enum definition in bytecode.rs around line 677
sed -n '677,750p' crates/compiler-core/src/bytecode.rs

Repository: RustPython/RustPython

Length of output: 1937


🏁 Script executed:

# Search for Call and CallKw variants in the enum
rg -n "Call\s*\{|CallKw\s*\{" crates/compiler-core/src/bytecode.rs -A2

Repository: RustPython/RustPython

Length of output: 1373


🏁 Script executed:

# Find VM implementation of CALL and CALL_KW instructions
rg -n "Instruction::Call\s*\{|Instruction::CallKw\s*\{" crates/vm/src --type rust -B2 -A5 | head -80

Repository: RustPython/RustPython

Length of output: 927


🏁 Script executed:

# Look for the execute function that handles instructions in the VM
fd "interpreter.rs\|execute.rs" crates/vm/src --type f

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Find collect_keyword_args and collect_positional_args implementations
rg -n "fn collect_keyword_args|fn collect_positional_args" crates/vm/src -A10

Repository: RustPython/RustPython

Length of output: 1214


🏁 Script executed:

# Look at the context around CallKw in frame.rs to understand the full call flow
sed -n '700,730p' crates/vm/src/frame.rs

Repository: RustPython/RustPython

Length of output: 1505


🏁 Script executed:

# Search for stack effect calculations to understand the contract better
rg -n "CallKw.*stack|collect_keyword" crates --type rust | head -20

Repository: RustPython/RustPython

Length of output: 351


🏁 Script executed:

# Look at the context around line 3733-3735 where CallKw is emitted differently
sed -n '3720,3760p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1713


🏁 Script executed:

# Find what function this is in
sed -n '3700,3750p' crates/codegen/src/compile.rs | head -60

Repository: RustPython/RustPython

Length of output: 2052


🏁 Script executed:

# Check if there are multiple compile functions handling different call contexts
rg -n "fn compile.*call" crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 304


🏁 Script executed:

# Look for the function containing the class definition code at line 3733
sed -n '3600,3650p' crates/codegen/src/compile.rs | head -60

Repository: RustPython/RustPython

Length of output: 1745


🏁 Script executed:

# Search for tests of generic class definitions to verify this code path executes
rg -n "generic.*class|TypeParam" crates --type rust | grep test | head -20

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Get the full function signature where line 3733 is located
sed -n '3600,3650p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1745


🏁 Script executed:

# Look at the full context of the compile_class_def function, especially the CallKw usage
sed -n '3630,3780p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 5811


🏁 Script executed:

# Check if this is within an if statement checking for generics/type params
sed -n '3650,3770p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 4735


🏁 Script executed:

# Let's verify how the generic class CallKw differs from the normal call path
# by examining what should be on the stack
rg -n "emit_load_const.*Tuple|BuildTuple" crates/codegen/src/compile.rs | grep -A2 -B2 "keyword\|kw"

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Check the implementation of compile_keywords to see the correct pattern
rg -n "fn compile_keywords" crates/codegen/src/compile.rs -A20

Repository: RustPython/RustPython

Length of output: 1112


🏁 Script executed:

# Look at how non-generic class handles keywords via compile_call_inner
sed -n '6160,6175p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 663


🏁 Script executed:

# Verify the emit_load_const pattern used in the generic class case (line 3728)
sed -n '3720,3745p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1017


🏁 Script executed:

# Let's trace through what stack state emerges from the generic class keyword code
# Look at the exact sequence of stack operations
sed -n '3721,3743p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 931


🏁 Script executed:

# Search for tests or examples of generic classes with keyword arguments
rg -n "class.*\[.*\].*:" tests --type py | head -10

Repository: RustPython/RustPython

Length of output: 110


🏁 Script executed:

# Check if there are any Python test files with generic classes
fd "test.*\.py" tests --type f | xargs grep -l "class.*\[.*\]" | head -5

Repository: RustPython/RustPython

Length of output: 157


🏁 Script executed:

# Find test directories
find . -type d -name "*test*" | head -10

Repository: RustPython/RustPython

Length of output: 136


🏁 Script executed:

# Look for Python files with generics
rg -n "TypeVar|class.*\[" --type py | head -20

Repository: RustPython/RustPython

Length of output: 1579


🏁 Script executed:

# Let's check if there are any tests that would exercise generic class definitions
rg -n "class.*Generic\|TypeParams" --type py | head -10

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Let's verify the stack state at the generic class CallKw point
# by checking if the pattern matches what compile_keywords produces
sed -n '6059,6085p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1154


🏁 Script executed:

# Let's verify the exact stack layout difference
# For the generic class case, trace what's on stack before CallKw
# Line 3721: first load function
# Line 3724: load class name string
# Line 3727-3730: load bases
# Line 3732: load generic_base
# Then the keyword loop at 3734-3742

# Now let's check what compile_keywords produces vs what generic class produces
sed -n '6059,6085p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1154


🏁 Script executed:

# Check the exact difference: does compile_keywords build a map or tuple?
sed -n '6079,6088p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 304


🏁 Script executed:

# Look at CallMethodKeyword to see if there's another CallKw pattern
rg -n "CallMethodKeyword" crates/codegen/src/compile.rs -B3 -A3

Repository: RustPython/RustPython

Length of output: 446


🏁 Script executed:

# Check the stack effect difference between the two patterns
# Look at line 6165 in compile_call_inner
sed -n '6163,6167p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 258


🏁 Script executed:

# Let me verify the critical difference in stack patterns
# Generic class (line 3734-3742): for each keyword: emit STRING, then compile_expression for value
# compile_call_inner (line 6161-6167): builds a TUPLE of all strings, then compiles expressions

# Let's confirm this difference by looking at what's built
echo "=== Generic class pattern ===" 
sed -n '3734,3742p' crates/codegen/src/compile.rs

echo ""
echo "=== compile_call_inner pattern ==="
sed -n '6161,6167p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 744


🏁 Script executed:

# Verify if compile_keywords (used by compile_call_inner) creates a tuple
# and compile_keywords is called from where
rg -n "compile_keywords" crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 215


🏁 Script executed:

# The key question: does the generic class CallKw ever get executed?
# Look for any tests or examples that would use generic classes with kwargs
rg -n "class.*\[.*\].*:" extra_tests --type py | head -20

Repository: RustPython/RustPython

Length of output: 176


🏁 Script executed:

# Let me trace the exact stack layout for the generic class CallKw case
# At line 3734-3742, what's the exact sequence?

echo "=== Full generic class keyword handling (lines 3721-3743) ==="
sed -n '3721,3743p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 993


🏁 Script executed:

# Now compare with the correct compile_call_inner pattern (lines 6161-6168)
echo "=== Correct compile_call_inner CallKw pattern (lines 6161-6168) ==="
sed -n '6161,6168p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 452


🏁 Script executed:

# Let's verify the critical difference:
# In generic class: individual STRING constants are emitted
# In compile_call_inner: a TUPLE of strings is built and emitted as a single constant

# Check the exact bytecode produced
echo "=== What does emit_load_const produce for each? ==="
rg -n "fn emit_load_const" crates/codegen/src/compile.rs -A5

Repository: RustPython/RustPython

Length of output: 387


🏁 Script executed:

# Let me understand the exact stack layout difference
# For generic class, we need to see the full picture

echo "=== Stack analysis ==="
echo "Generic class with kwargs: emits individual STRINGS in loop, then CallKw"
echo "compile_call_inner with kwargs: builds TUPLE of strings, emits LoadConst with tuple, then CallKw"
echo ""

# Let's look at what the VM actually does with CallKw
sed -n '2072,2090p' crates/vm/src/frame.rs

Repository: RustPython/RustPython

Length of output: 986


🏁 Script executed:

# The key insight: generic class emits strings individually in a loop
# But collect_keyword_args pops a TUPLE first!
# This is a stack mismatch!

# Let's verify by checking if generic class CallKw is even reachable
# Search for any test that would hit the is_generic && !arguments.keywords.is_empty() path

rg -n "TypeParam\|generic" extra_tests --type py | head -20

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Search for Python 3.12+ generic class tests
fd "*.py" extra_tests | xargs grep -l "class.*\[.*\]:" 2>/dev/null | head -10

Repository: RustPython/RustPython

Length of output: 348


🏁 Script executed:

# Final confirmation: check if there's any comment or note about this being a known issue
rg -n "TODO\|FIXME\|BUG\|HACK" crates/codegen/src/compile.rs | grep -A2 -B2 "373[0-5]\|CallKw"

Repository: RustPython/RustPython

Length of output: 47


🏁 Script executed:

# Verify the nargs calculation in the generic class case one more time
echo "=== Generic class nargs calculation ==="
sed -n '3739,3746p' crates/codegen/src/compile.rs

echo ""
echo "=== compile_call_inner nargs calculation ==="
sed -n '6170,6172p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 452


🏁 Script executed:

# Let me get the exact nargs value at line 3733-3735
sed -n '3720,3747p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 1130


🏁 Script executed:

# Understand what nargs contains before the keyword handling
sed -n '3718,3735p' crates/codegen/src/compile.rs

Repository: RustPython/RustPython

Length of output: 774


Critical: CallKw nargs ABI violation and stack layout mismatch in generic class handler

The generic class keyword argument handling at lines 3721–3747 contains two critical bugs that contradict the compile_call_inner pattern (lines 6161–6168):

The non-generic class path correctly uses compile_call_inner (line 3777), which follows the proper ABI. Fix the generic class code to build a tuple of keyword name strings (via ConstantData::Tuple) and pass only the positional argument count as nargs, matching the compile_call_inner implementation.