| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
📝 Walkthrough
WalkthroughRefactors coroutine send logic by centralizing post-run handling into finalize_send_result and adding send_none; simultaneously threads vm: &VirtualMachine into multiple frame specialization entry points and adds helpers to guide fast-path decisions. Additional changes infer and propagate native call flags and re-enable method flag constants. Changes
Sequence DiagramsequenceDiagram
participant Caller
participant Coroutine as Coroutine (send)
participant Frame as ExecutingFrame (run_with_context)
participant VM as VirtualMachine
participant Finalize as finalize_send_result
Caller->>Coroutine: send(value)
alt coroutine just-started
Coroutine->>Coroutine: compute value = None
else already started
Coroutine->>Coroutine: use provided value
end
Coroutine->>Frame: run_with_context(vm, computed_value)
Frame->>VM: execute frame
VM-->>Frame: ExecutionResult
Frame-->>Coroutine: ExecutionResult
Coroutine->>Finalize: finalize_send_result(jen, result, vm)
Finalize->>Finalize: map StopIteration / StopAsyncIteration
Finalize->>Coroutine: maybe_close frame if final
Finalize-->>Caller: PyIterReturn / Err
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem🚥 Pre-merge checks | ✅ 2 | ❌ 1 ❌ Failed checks (1 warning)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches 🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. ❤️ ShareComment @coderabbitai help to get the list of available commands and usage tips. |
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)crates/vm/src/frame.rs (1)🤖 Prompt for all review comments with AI agents3160-3164: SendGen can use send_none for the common None case.
Instruction::Send already does this, but Instruction::SendGen still always calls send. Aligning both paths keeps the intended fastpath consistent.
Suggested patch🤖 Prompt for AI Agents- match coro.send(receiver, val, vm)? { + let ret = if vm.is_none(&val) { + coro.send_none(receiver, vm)? + } else { + coro.send(receiver, val, vm)? + }; + match ret {Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/frame.rs` around lines 3160 - 3164, The SendGen path currently calls coro.send(...) even when val is None; change it to use the fastpath method coro.send_none(...) (or send_none) when val equals PyNone to match Instruction::Send behavior—locate the block gated by can_fast_send in the SendGen handling (symbols: SendGen, can_fast_send, top_value, builtin_coro, send) and replace the call to coro.send(receiver, val, vm) with a conditional that invokes coro.send_none(receiver, vm) for the None case and falls back to coro.send(...) otherwise, preserving the existing PyIterReturn handling and error propagation.
Verify each finding against the current code and only fix it if needed. Inline comments: In `@crates/vm/src/frame.rs`: - Around line 8145-8148: The specialization_eval_frame_active method currently returns false unconditionally, disabling frame-aware specialization guards; fix it by reading the VM tracing flags instead of returning false: in specialization_eval_frame_active(&self, vm: &VirtualMachine) check vm.trace_func, vm.profile_func, and vm.use_tracing (matching the pattern used in vm/mod.rs around the other checks) and return true when any of those indicate tracing/profile is active so frame-aware specialization is disabled when tracing or profiling is enabled. --- Nitpick comments: In `@crates/vm/src/frame.rs`: - Around line 3160-3164: The SendGen path currently calls coro.send(...) even when val is None; change it to use the fastpath method coro.send_none(...) (or send_none) when val equals PyNone to match Instruction::Send behavior—locate the block gated by can_fast_send in the SendGen handling (symbols: SendGen, can_fast_send, top_value, builtin_coro, send) and replace the call to coro.send(receiver, val, vm) with a conditional that invokes coro.send_none(receiver, vm) for the None case and falls back to coro.send(...) otherwise, preserving the existing PyIterReturn handling and error propagation.
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 2278b9c7-6e88-4782-9d3f-ed74f4874b53
📥 CommitsReviewing files that changed from the base of the PR and between 375b547 and 3e92a7a.
📒 Files selected for processing (2)
Sorry, something went wrong.
- Implement specialization_eval_frame_active to check vm.use_tracing so specializations are skipped when tracing/profiling is active - Add send_none fastpath in SendGen handler for the common None case
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)crates/vm/src/coroutine.rs (1)🤖 Prompt for all review comments with AI agentscrates/vm/src/frame.rs (1)146-182: Deduplicate send_none/send shared execution path.
Both functions duplicate the same closed-check / frame-prepare / resume / finalize flow and only differ in value preparation.
♻️ Proposed refactor+ fn send_impl<F>( + &self, + jen: &PyObject, + vm: &VirtualMachine, + value_for_state: F, + ) -> PyResult<PyIterReturn> + where + F: FnOnce(bool) -> PyResult<Option<PyObjectRef>>, + { + if self.closed.load() { + return Ok(PyIterReturn::StopIteration(None)); + } + let started = self.frame.lasti() > 0; + let value = value_for_state(started)?; + self.frame.locals_to_fast(vm)?; + let result = self.run_with_context(jen, vm, |f| f.resume(value, vm)); + self.finalize_send_result(jen, result, vm) + } + pub(crate) fn send_none(&self, jen: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> { - if self.closed.load() { - return Ok(PyIterReturn::StopIteration(None)); - } - self.frame.locals_to_fast(vm)?; - let value = if self.frame.lasti() > 0 { - Some(vm.ctx.none()) - } else { - None - }; - let result = self.run_with_context(jen, vm, |f| f.resume(value, vm)); - self.finalize_send_result(jen, result, vm) + self.send_impl(jen, vm, |started| { + Ok(if started { Some(vm.ctx.none()) } else { None }) + }) } pub fn send( &self, jen: &PyObject, value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<PyIterReturn> { - if self.closed.load() { - return Ok(PyIterReturn::StopIteration(None)); - } - self.frame.locals_to_fast(vm)?; - let value = if self.frame.lasti() > 0 { - Some(value) - } else if !vm.is_none(&value) { - return Err(vm.new_type_error(format!( - "can't send non-None value to a just-started {}", - gen_name(jen, vm), - ))); - } else { - None - }; - let result = self.run_with_context(jen, vm, |f| f.resume(value, vm)); - self.finalize_send_result(jen, result, vm) + self.send_impl(jen, vm, |started| { + if started { + Ok(Some(value)) + } else if !vm.is_none(&value) { + Err(vm.new_type_error(format!( + "can't send non-None value to a just-started {}", + gen_name(jen, vm), + ))) + } else { + Ok(None) + } + }) }As per coding guidelines "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code".
🤖 Prompt for AI AgentsVerify each finding against the current code and only fix it if needed. In `@crates/vm/src/coroutine.rs` around lines 146 - 182, Both send_none and send duplicate the same closed-check, frame.locals_to_fast call, resume via run_with_context, and finalize_send_result logic; extract that shared flow into a helper (e.g., send_common or resume_with_value) that accepts the prepared Option<PyObjectRef> (or a closure producing it) and performs closed.load check, self.frame.locals_to_fast(vm)?, calls run_with_context(jen, vm, |f| f.resume(value, vm)), and returns finalize_send_result; then simplify send_none to compute its value (using frame.lasti() and vm.ctx.none()) and call the helper, and simplify send to compute its value (respecting frame.lasti() and the non-None error) and call the same helper, keeping all original calls to frame.lasti, run_with_context, finalize_send_result, and closed.load.7837-7861: Reduce duplicated guard/backoff branches in descriptor call-conv selection.
The NOARGS and O branches duplicate the same backoff/return structure with only expected-arity differing. Extract expected arity first, then run one shared guard+backoff path.
As per coding guidelines, "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code".
🤖 Prompt for AI AgentsVerify each finding against the current code and only fix it if needed. In `@crates/vm/src/frame.rs` around lines 7837 - 7861, The NOARGS and O branches in the descriptor call-conv selection duplicate the same guard/backoff/return logic; refactor by computing the expected arity from call_conv (e.g., map PyMethodFlags::NOARGS -> 0, PyMethodFlags::O -> 1) and choose the Instruction variant after the check, then perform a single shared guard that compares nargs to expected_arity and on mismatch runs the adaptive backoff using self.code.instructions.read_adaptive_counter(cache_base) and write_adaptive_counter(...bytecode::adaptive_counter_backoff(...)) and returns; update references to new_op, call_conv, nargs, cache_base, and the Instruction variants (CallMethodDescriptorNoargs, etc.) accordingly.
Verify each finding against the current code and only fix it if needed. Inline comments: In `@crates/derive-impl/src/pyclass.rs`: - Around line 1018-1028: The drop_first_typed logic incorrectly ignores the parsed raw flag, causing methods marked #[pymethod(raw)] or #[pyclassmethod(raw)] without a receiver to drop the first typed argument; update the match that sets drop_first_typed (which currently checks self.inner.attr_name and has_receiver) to also require !raw before returning 1 so that raw methods do not decrement the first typed arg, then pass the unchanged drop_first_typed into infer_native_call_flags(func.sig(), drop_first_typed). --- Nitpick comments: In `@crates/vm/src/coroutine.rs`: - Around line 146-182: Both send_none and send duplicate the same closed-check, frame.locals_to_fast call, resume via run_with_context, and finalize_send_result logic; extract that shared flow into a helper (e.g., send_common or resume_with_value) that accepts the prepared Option<PyObjectRef> (or a closure producing it) and performs closed.load check, self.frame.locals_to_fast(vm)?, calls run_with_context(jen, vm, |f| f.resume(value, vm)), and returns finalize_send_result; then simplify send_none to compute its value (using frame.lasti() and vm.ctx.none()) and call the helper, and simplify send to compute its value (respecting frame.lasti() and the non-None error) and call the same helper, keeping all original calls to frame.lasti, run_with_context, finalize_send_result, and closed.load. In `@crates/vm/src/frame.rs`: - Around line 7837-7861: The NOARGS and O branches in the descriptor call-conv selection duplicate the same guard/backoff/return logic; refactor by computing the expected arity from call_conv (e.g., map PyMethodFlags::NOARGS -> 0, PyMethodFlags::O -> 1) and choose the Instruction variant after the check, then perform a single shared guard that compares nargs to expected_arity and on mismatch runs the adaptive backoff using self.code.instructions.read_adaptive_counter(cache_base) and write_adaptive_counter(...bytecode::adaptive_counter_backoff(...)) and returns; update references to new_op, call_conv, nargs, cache_base, and the Instruction variants (CallMethodDescriptorNoargs, etc.) accordingly.
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: a11e8867-e0c0-4327-8d74-a8a000cacd05
📥 CommitsReviewing files that changed from the base of the PR and between 3e92a7a and c7d095a.
📒 Files selected for processing (6)
Sorry, something went wrong.
| let has_receiver = func | ||
| .sig() | ||
| .inputs | ||
| .iter() | ||
| .any(|arg| matches!(arg, syn::FnArg::Receiver(_))); | ||
| let drop_first_typed = match self.inner.attr_name { | ||
| AttrName::Method | AttrName::ClassMethod if !has_receiver => 1, | ||
| _ => 0, | ||
| }; | ||
| let call_flags = infer_native_call_flags(func.sig(), drop_first_typed); | ||
|
|
There was a problem hiding this comment.
⚠️ Potential issue | 🔴 Critical
🧩 Analysis chain🏁 Script executed:
cat -n crates/derive-impl/src/pyclass.rs | sed -n '1010,1040p'Repository: RustPython/RustPython
Length of output: 1499
🏁 Script executed:
# Let's also search for raw method attributes in the codebase
rg "#\[pymethod.*raw\]|#\[pyclassmethod.*raw\]" -A 3 crates/Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Let's understand the infer_native_call_flags function to see what drop_first_typed affects
rg "fn infer_native_call_flags" -A 20 crates/Repository: RustPython/RustPython
Length of output: 1544
🏁 Script executed:
# Let's see how the `raw` variable is used after it's extracted at line 1016
sed -n '1016,1100p' crates/derive-impl/src/pyclass.rs | grep -n "raw"Repository: RustPython/RustPython
Length of output: 122
🏁 Script executed:
# Search for documentation or usage of the raw attribute
rg "raw.*method|method.*raw" -B 2 -A 2 crates/ --type rs | head -50Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Find examples of raw method signatures in tests or examples
rg "#\[pymethod" -A 5 crates/ --type rs | grep -B 5 "raw" | head -100Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Let's look at lines around where raw is used (1016 to 1050)
sed -n '1016,1050p' crates/derive-impl/src/pyclass.rsRepository: RustPython/RustPython
Length of output: 1305
🏁 Script executed:
# Search for raw method examples more broadly
grep -r "pymethod.*raw" crates/ | head -20Repository: RustPython/RustPython
Length of output: 118
🏁 Script executed:
# Search for raw method definitions in tests
find crates -name "*.rs" -exec grep -l "pymethod.*raw" {} \; | head -10Repository: RustPython/RustPython
Length of output: 97
🏁 Script executed:
# Let's look at the raw method example in object.rs
grep -B 5 -A 10 "#\[pymethod(raw)\]" crates/vm/src/builtins/object.rs | head -50Repository: RustPython/RustPython
Length of output: 500
🏁 Script executed:
# Find all raw method examples
grep -r "#\[pymethod(raw)\]" crates/ --include="*.rs" -A 5 | head -100Repository: RustPython/RustPython
Length of output: 465
🏁 Script executed:
# Let's check what MethodNurseryItem does with the raw flag
grep -A 30 "struct MethodNurseryItem" crates/derive-impl/src/pyclass.rsRepository: RustPython/RustPython
Length of output: 893
🏁 Script executed:
# Let's search for how the raw flag is used in code generation
grep -n "raw" crates/derive-impl/src/pyclass.rs | head -30Repository: RustPython/RustPython
Length of output: 392
🏁 Script executed:
# Let's look at how MethodNurseryItem's to_tokens handles raw
grep -A 50 "impl ToTokens for MethodNursery" crates/derive-impl/src/pyclass.rs | head -80Repository: RustPython/RustPython
Length of output: 2095
🏁 Script executed:
# Search for other raw method examples to understand the pattern
grep -r "#\[pymethod(raw)\]" crates/ --include="*.rs" -A 3 -B 1 | head -80Repository: RustPython/RustPython
Length of output: 445
🏁 Script executed:
# Let's understand what new_raw_const vs new_const do
grep -n "new_raw_const\|new_const" crates/ -r --include="*.rs" | grep -E "fn new_raw_const|fn new_const" | head -10Repository: RustPython/RustPython
Length of output: 202
🏁 Script executed:
# Let's look for more raw method examples to see the pattern
grep -r "#\[pymethod(raw)\]" crates/ --include="*.rs" -B 2 -A 8 | head -150Repository: RustPython/RustPython
Length of output: 774
🏁 Script executed:
# Let's check if raw methods are used differently in call_flags or elsewhere
grep -B 10 -A 10 "drop_first_typed" crates/derive-impl/src/pyclass.rsRepository: RustPython/RustPython
Length of output: 1054
🏁 Script executed:
# Let's look at the new_raw_const and new_const signatures to understand the difference
sed -n '76,110p' crates/vm/src/function/method.rsRepository: RustPython/RustPython
Length of output: 888
🏁 Script executed:
# Let's search for documentation or comments about raw methods
grep -r "raw.*method\|method.*raw" crates/derive-impl/ --include="*.rs" -B 2 -A 2 | head -80Repository: RustPython/RustPython
Length of output: 417
🏁 Script executed:
# Let's check if raw methods should have different call_flags handling
grep -B 5 -A 15 "fn infer_native_call_flags" crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 830
🏁 Script executed:
# Let's understand what drop_first_typed actually affects - search for where call_flags is used
grep -n "call_flags" crates/derive-impl/src/pyclass.rs | head -20Repository: RustPython/RustPython
Length of output: 433
🏁 Script executed:
# Let's check what the calling convention flags mean
grep -r "METH_O\|METH_NOARGS\|METH_VARARGS" crates/ --include="*.rs" -B 2 -A 2 | head -100Repository: RustPython/RustPython
Length of output: 831
🏁 Script executed:
# Let's trace how infer_native_call_flags uses drop_first_typed
sed -n '100,180p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 2496
🏁 Script executed:
# Let's see the complete infer_native_call_flags function
sed -n '100,200p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 3069
🏁 Script executed:
# Let's check if there are any raw methods without receiver that should help us understand the pattern
grep -r "fn.*raw\]\|#\[pymethod(raw)\]" crates/ --include="*.rs" -B 2 -A 8 | grep -A 8 "raw\]" | head -100Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Let's check how raw methods are different - search for any conditional logic based on raw
grep -n "\.raw" crates/derive-impl/src/pyclass.rsRepository: RustPython/RustPython
Length of output: 153
🏁 Script executed:
# Let's get the complete infer_native_call_flags function to see how it uses drop_first_typed
sed -n '75,150p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 2226
🏁 Script executed:
# Let's look at how the example raw method is actually used
sed -n '1730,1760p' crates/vm/src/builtins/object.rsRepository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check if there are any tests or comments about raw method call_flags
grep -r "raw.*call_flags\|call_flags.*raw" crates/ --include="*.rs" -B 3 -A 3Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Let's get the exact line numbers for infer_native_call_flags
grep -n "pub(crate) fn infer_native_call_flags" crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 165
🏁 Script executed:
# Get lines around the function
sed -n '75,125p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 1379
🏁 Script executed:
# Let's look for where drop_first_typed is used in the function
sed -n '75,160p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 2397
🏁 Script executed:
# Let's read the function starting from line 735
sed -n '735,820p' crates/derive-impl/src/util.rsRepository: RustPython/RustPython
Length of output: 2960
🏁 Script executed:
# Let's find the complete infer_native_call_flags function using a different approach
awk '/pub\(crate\) fn infer_native_call_flags/,/^}$/{print NR": "$0}' crates/derive-impl/src/util.rs | head -100Repository: RustPython/RustPython
Length of output: 2764
🏁 Script executed:
# Let's understand what the raw flag actually means - is it supposed to affect drop_first_typed?
grep -B 10 -A 10 "raw" crates/derive-impl/src/pyclass.rs | sed -n '1,80p'Repository: RustPython/RustPython
Length of output: 2442
🏁 Script executed:
# Let's check if there are any tests with raw methods to understand the expected behavior
find crates -name "*.rs" -exec grep -l "pymethod.*raw\|pyclassmethod.*raw" {} \;Repository: RustPython/RustPython
Length of output: 97
🏁 Script executed:
# Let's check if raw methods should have different drop_first_typed handling
grep -r "if.*raw\|raw.*if" crates/derive-impl/ --include="*.rs" -B 3 -A 3 | head -60Repository: RustPython/RustPython
Length of output: 543
🏁 Script executed:
# Let's check if there are any other raw method examples we haven't found
find crates -name "*.rs" -exec grep -B 3 -A 8 "pymethod(raw)\|pyclassmethod(raw)" {} + | head -200Repository: RustPython/RustPython
Length of output: 808
🏁 Script executed:
# Let's check if the raw flag should affect drop_first_typed by looking at how new_raw_const differs
grep -B 5 -A 25 "fn new_raw_const" crates/vm/src/function/method.rsRepository: RustPython/RustPython
Length of output: 908
🏁 Script executed:
# Let's understand the context better - what does raw mean in PyMethodDef?
grep -B 10 -A 10 "static_raw_func\|new_raw_const" crates/vm/src/function/ -r --include="*.rs" | head -100Repository: RustPython/RustPython
Length of output: 4761
🏁 Script executed:
# Let's trace through what happens with the raw method example
# The method is: fn __getstate__(vm: &VirtualMachine, args: FuncArgs)
# Let's verify what drop_first_typed does to FuncArgs
python3 <<'EOF'
# Simulate the infer_native_call_flags logic with the raw method example
sig_inputs = ["&VirtualMachine", "FuncArgs"]
# Filter out typed args (non-receiver)
typed_args = []
for arg in sig_inputs:
# Skip VirtualMachine
if arg.startswith('&') and "VirtualMachine" in arg:
continue
typed_args.append(arg)
print(f"typed_args after filtering: {typed_args}")
# Apply drop_first_typed = 1
drop_first_typed = 1
user_args = typed_args[drop_first_typed:]
print(f"user_args after dropping {drop_first_typed}: {user_args}")
# Now check what flags would be inferred
has_keywords = False
variable_arity = False
fixed_positional = 0
for ty in user_args:
if "FuncArgs" in ty:
has_keywords = True
variable_arity = True
continue
fixed_positional += 1
print(f"has_keywords={has_keywords}, variable_arity={variable_arity}, fixed_positional={fixed_positional}")
if has_keywords:
print("Result: FASTCALL | KEYWORDS")
elif variable_arity:
print("Result: FASTCALL")
else:
if fixed_positional == 0:
print("Result: NOARGS")
elif fixed_positional == 1:
print("Result: O")
else:
print("Result: FASTCALL")
EOFRepository: RustPython/RustPython
Length of output: 212
🏁 Script executed:
# Now let's check what SHOULD happen if drop_first_typed = 0 for raw
python3 <<'EOF'
sig_inputs = ["&VirtualMachine", "FuncArgs"]
typed_args = []
for arg in sig_inputs:
if arg.startswith('&') and "VirtualMachine" in arg:
continue
typed_args.append(arg)
print(f"typed_args after filtering: {typed_args}")
# Apply drop_first_typed = 0 (if raw methods were excluded)
drop_first_typed = 0
user_args = typed_args[drop_first_typed:]
print(f"user_args after dropping {drop_first_typed}: {user_args}")
has_keywords = False
variable_arity = False
fixed_positional = 0
for ty in user_args:
if "FuncArgs" in ty:
has_keywords = True
variable_arity = True
continue
fixed_positional += 1
print(f"has_keywords={has_keywords}, variable_arity={variable_arity}, fixed_positional={fixed_positional}")
if has_keywords:
print("Result: FASTCALL | KEYWORDS")
elif variable_arity:
print("Result: FASTCALL")
else:
if fixed_positional == 0:
print("Result: NOARGS")
elif fixed_positional == 1:
print("Result: O")
else:
print("Result: FASTCALL")
EOFRepository: RustPython/RustPython
Length of output: 233
🏁 Script executed:
# Let's verify the concern is valid by checking more raw method examples
grep -r "pymethod(raw)\|pyclassmethod(raw)" crates/vm/src/builtins/ --include="*.rs" -A 3 | head -60Repository: RustPython/RustPython
Length of output: 392
🏁 Script executed:
# Let's check if there are any other raw method patterns besides FuncArgs
grep -r "pymethod(raw)\|pyclassmethod(raw)" crates/ --include="*.rs" -A 5 | grep -E "fn |FuncArgs|args:" | head -40Repository: RustPython/RustPython
Length of output: 168
🏁 Script executed:
# Let's verify the interpretation: raw methods take FuncArgs directly as the first argument
# So dropping it would be wrong. Let's check if the raw flag should affect drop_first_typed
grep -B 20 "let drop_first_typed" crates/derive-impl/src/pyclass.rs | tail -30Repository: RustPython/RustPython
Length of output: 849
Raw methods without receiver should not drop the first typed argument.
The raw flag is extracted at line 1016 but not used in the drop_first_typed logic (lines 1023-1025). When #[pymethod(raw)] or #[pyclassmethod(raw)] is applied to a method without an explicit Rust receiver, the current code incorrectly sets drop_first_typed = 1, which causes the first typed argument to be skipped during call-flags inference.
Raw methods have different parameter semantics than normal methods: they receive FuncArgs directly as their first argument without an implicit self/class parameter. For example, the existing raw method __getstate__(vm: &VirtualMachine, args: FuncArgs) has no receiver and no implicit self. With the current logic, FuncArgs gets incorrectly dropped, resulting in NOARGS flags instead of the correct FASTCALL | KEYWORDS flags.
The fix should condition drop_first_typed on !raw:
let drop_first_typed = match self.inner.attr_name {
AttrName::Method | AttrName::ClassMethod if !has_receiver && !raw => 1,
_ => 0,
};Verify each finding against the current code and only fix it if needed. In `@crates/derive-impl/src/pyclass.rs` around lines 1018 - 1028, The drop_first_typed logic incorrectly ignores the parsed raw flag, causing methods marked #[pymethod(raw)] or #[pyclassmethod(raw)] without a receiver to drop the first typed argument; update the match that sets drop_first_typed (which currently checks self.inner.attr_name and has_receiver) to also require !raw before returning 1 so that raw methods do not decrement the first typed arg, then pass the unchanged drop_first_typed into infer_native_call_flags(func.sig(), drop_first_typed).
Sorry, something went wrong.
…7359) * vm: align specialization guards with CPython patterns * vm: tighten call specialization runtime guards * vm: add send_none fastpath for generator specialization * vm: restrict method-descriptor specialization to methods * vm: deopt call specializations on guard misses * vm: match CPython send/for-iter closed-frame guards * vm: restrict len/isinstance specialization to builtins * vm: use exact-type guards for call specializations * vm: align class-call specialization flow with CPython * vm: prefer FAST call opcodes for positional builtin calls * vm: add callable identity guard to CALL_LIST_APPEND * vm: make CALL_LIST_APPEND runtime guard pointer-based * vm: align call guard cache and fallback behavior with CPython * vm: use base vectorcall fallback for EXIT-style call misses * vm: simplify CALL_LEN/CALL_ISINSTANCE runtime guards * vm: infer call-convention flags for CPython-style CALL specialization * vm: check use_tracing in eval_frame_active, add SendGen send_none - Implement specialization_eval_frame_active to check vm.use_tracing so specializations are skipped when tracing/profiling is active - Add send_none fastpath in SendGen handler for the common None case
| Back | FazBrowse Home | New Git URL |
Summary
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit