Skip to content

interp: correct four comments about why the frame virtualizable forces exist - #1044

Merged
youknowone merged 1 commit into
mainfrom
single-walker
Aug 5, 2026
Merged

interp: correct four comments about why the frame virtualizable forces exist#1044
youknowone merged 1 commit into
mainfrom
single-walker

Conversation

@youknowone

@youknowone youknowone commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Comments only — no code change, no counter moves.

Follow-up to #1025. While root-causing that defect I had to establish, from the
upstream sources, why pyre carries hand-placed force_frame calls where
upstream's sys._getframe carries none. Four comments in the tree answer that
question wrongly, and they are exactly the statements that read as a licence to
delete those calls — a change that has now been attempted, measured, and
reverted twice.

The two wrong claims

1. "Upstream needs no force because escape analysis materializes the frame."

Upstream injects the force. rvirtualizable.py:49-53 hook_access_field genops
jit_force_virtualizable on every redirected FIELD access;
virtualizable.py:288-292 rewrites those into a
force_virtualizable_if_necessary call across translator.graphs; and
jtransform.py:2164-2172 rewrite_op_jit_force_virtualizable drops it again only
in the graphs the codewriter looks inside. That is why a traced-through
getframe is correct without one.

executioncontext.rs:13-26 already stated this correctly. Ten lines below,
:36-43 stated escape analysis instead, and module/sys/vm.rs's getframe
docstring repeated it while citing :36-43 as its authority.

pyre has no such injection: rclass.rs buildinstancerepr returns an error for
any _virtualizable_ class — "VirtualizableInstanceRepr integration requires rclass.py FieldListAccessor/_parse_field_list parity" — and
rvirtualizable.rs should_force_field has no callers outside #[cfg(test)].
So the hand-placed calls are that mechanism relocated to a hand-picked consumer
set, which is why they are load-bearing.

2. "gettopframe_nohidden runs force_frame on every frame it walks."

It does not. Its first line is force_vref(self.topframeref); the walk after
that follows f_backref and tests hide() with no force at all. So it
materializes the top frame only.

That still explains why locals() works — locals() reports on the top frame,
verified: locals() and vars() inside a 20000-iteration hot loop match
PYRE_NO_JIT=1 on both native backends. But builtins.rs topframe_for_locals
claimed a per-frame force_frame and cited executioncontext.rs:409-421, which
is now clone_for_thread internals; and typedef.rs's f_locals getset claimed
sys._getframe gets its materialization "for free" from the same walk, when it
comes from getframe's own explicit force_frame.

Files

file what changed
executioncontext.rs force_frame_before_locals_read escape analysis → the injected-force chain; gettopframe_nohidden scoped to the top frame
builtins.rs topframe_for_locals per-frame force_frame → the vref force; dead line citation dropped
typedef.rs f_locals "for free from gettopframe_nohidden" → getframe's own force_frame
module/sys/vm.rs getframe escape analysis → the injected-force chain, and why the two calls ARE that mechanism

Verification

cargo fmt --all -- --check and cargo check --all --no-default-features --features dynasm both clean. Nothing else runs differently — these are doc and
line comments.

authored by Claude

Summary by CodeRabbit

  • Documentation
    • Clarified frame materialization and resolution behavior for stack and traceback inspection.
    • Documented how virtual frames are forced when accessing locals and through sys._getframe.
    • Explained why explicit frame-forcing steps remain necessary and how they differ from top-frame handling.

…s exist

The four doc comments around the hand-placed `force_frame` calls attributed
upstream's freedom from them to escape analysis, and attributed pyre's own
materialization to the frame-chain walk. Both are wrong, and they are the
statements that read as a licence to delete the calls.

Upstream injects the force: `rvirtualizable.py:49-53 hook_access_field` genops
`jit_force_virtualizable` on every redirected field access,
`virtualizable.py:288-292` rewrites those into a
`force_virtualizable_if_necessary` call across `translator.graphs`, and
`jtransform.py:2164-2172 rewrite_op_jit_force_virtualizable` drops it again only
in the graphs the codewriter looks inside. `executioncontext.rs:13-26` already
said this; `:36-43` a few lines below said escape analysis instead.

Pyre has no such injection: `rclass.rs buildinstancerepr` returns an error for
any `_virtualizable_` class, naming `FieldListAccessor` / `_parse_field_list`
parity as the blocker, and `rvirtualizable.rs should_force_field` has no
callers. So the hand-placed calls are that mechanism relocated to a hand-picked
consumer set.

`gettopframe_nohidden` does not substitute for them. It forces the vref of the
frame it starts from and then walks `f_backref` unforced, so it materializes the
top frame only. That is why `locals()` works — it reports on the top frame —
and `builtins.rs topframe_for_locals` claimed a per-frame `force_frame` instead,
citing `executioncontext.rs:409-421`, which is now `clone_for_thread` internals.
`typedef.rs` f_locals claimed `sys._getframe` gets the materialization "for
free" from the same walk; it comes from `getframe`'s own `force_frame`.

Comments only.

Assisted-by: Claude
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The changes update documentation for virtualizable frame forcing, JIT behavior, sys._getframe, gettopframe_nohidden, and traceback frame access. No public declarations or runtime logic changed.

Changes

Frame Materialization

Layer / File(s) Summary
Frame forcing and materialization semantics
pyre/pyre-interpreter/src/executioncontext.rs, pyre/pyre-interpreter/src/module/sys/vm.rs, pyre/pyre-interpreter/src/builtins.rs, pyre/pyre-interpreter/src/typedef.rs
Documentation now describes explicit virtualizable-frame forcing, top-frame VREF behavior, JIT materialization, and the distinct roles of frame access APIs.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Possibly related PRs

Suggested reviewers: lifthrasiir, fregataa

Poem

A rabbit reads the frames at night,
VREFs make the locals right.
JIT paths now clearly say,
Which frames force and which just stay.
The docs hop cleanly into place. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the correction of four comments about frame virtualizable forces.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch single-walker

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit d5b8108).
Updated: 2026-08-05T04:26:36.865Z

Files in the reviewed diff
pyre/pyre-interpreter/src/builtins.rs
pyre/pyre-interpreter/src/executioncontext.rs
pyre/pyre-interpreter/src/module/sys/vm.rs
pyre/pyre-interpreter/src/typedef.rs

1. Regressions to PyPy parity introduced by this patch

None.

2. Other mismatches introduced by this patch

None.

3. Pre-existing mismatches (already present before this patch)

  • pyre/pyre-interpreter/src/module/sys/vm.rs:621 ↔ pypy/module/sys/vm.py:51 — PyPy emits audit(space, "sys._getframe", [f]) before returning; Pyre marks the frame escaped and returns without emitting the _getframe audit event.
  • pyre/pyre-interpreter/src/module/sys/vm.rs:2290 ↔ pypy/module/sys/vm.py:480 — Pyre installs sys.audit as an unconditional no-op, while PyPy obtains AuditHolder and dispatches registered audit hooks.

4. Structural adaptations

  • pyre/pyre-interpreter/src/executioncontext.rs:63 ↔ rpython/rtyper/rvirtualizable.py:49 — Pyre uses an explicit force_frame_before_locals_read hook; RPython injects jit_force_virtualizable during redirected virtualizable-field access. This is a translator/JIT implementation adaptation, not introduced by this documentation-only patch.
  • pyre/pyre-interpreter/src/module/sys/vm.rs:606 ↔ pypy/interpreter/executioncontext.py:72 — Pyre’s gettopframe() explicitly forces the virtualizable before the no-hidden walk; PyPy’s gettopframe_nohidden() starts from the virtual-reference result and performs only the frame-chain walk. This compensates for Pyre’s missing VirtualizableInstanceRepr integration.
  • pyre/pyre-interpreter/src/typedef.rs:7221 ↔ pypy/interpreter/typedef.py:749 — Pyre returns a Python 3.14-style write-through FrameLocalsProxy for optimized frames; this PyPy source exposes f_locals directly through fget_getdictscope. This is a Python 3.11-versus-3.14 API adaptation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyre/pyre-interpreter/src/executioncontext.rs`:
- Around line 54-59: The comments describing traceback-frame forcing are
inconsistent. In pyre/pyre-interpreter/src/executioncontext.rs lines 54-59,
clarify that tb_frame bypasses the frame-chain force but f_locals explicitly
calls force_frame_before_locals_read; make the equivalent wording change in
pyre/pyre-interpreter/src/typedef.rs lines 7214-7218 by replacing “gets neither”
with this scoped description.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3e28717e-e2f3-4e0f-84df-1ce51a39d8ba

📥 Commits

Reviewing files that changed from the base of the PR and between 2f61280 and d5b8108.

📒 Files selected for processing (4)
  • pyre/pyre-interpreter/src/builtins.rs
  • pyre/pyre-interpreter/src/executioncontext.rs
  • pyre/pyre-interpreter/src/module/sys/vm.rs
  • pyre/pyre-interpreter/src/typedef.rs

Comment on lines +54 to +59
/// [`PyExecutionContext::gettopframe_nohidden`] does not substitute. It forces
/// the VREF of the frame it starts from and then walks `f_backref` unforced, so
/// it materializes the top frame only — enough for `locals()`, which reports on
/// that frame, and nothing for a frame handed out some other way (a traceback's
/// `tb_frame`), which reaches `fast2locals` unforced; its null slots render as
/// an EMPTY mapping rather than a stale one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use one consistent description of traceback-frame forcing.

Both comments imply that a traceback-derived frame reaches f_locals without any force. The implementation explicitly calls force_frame_before_locals_read(f) before that access.

  • pyre/pyre-interpreter/src/executioncontext.rs#L54-L59: Clarify that tb_frame does not receive the frame-chain force, but f_locals access performs an explicit force.
  • pyre/pyre-interpreter/src/typedef.rs#L7214-L7218: Replace “gets neither” with the same scoped description.
📍 Affects 2 files
  • pyre/pyre-interpreter/src/executioncontext.rs#L54-L59 (this comment)
  • pyre/pyre-interpreter/src/typedef.rs#L7214-L7218
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyre/pyre-interpreter/src/executioncontext.rs` around lines 54 - 59, The
comments describing traceback-frame forcing are inconsistent. In
pyre/pyre-interpreter/src/executioncontext.rs lines 54-59, clarify that tb_frame
bypasses the frame-chain force but f_locals explicitly calls
force_frame_before_locals_read; make the equivalent wording change in
pyre/pyre-interpreter/src/typedef.rs lines 7214-7218 by replacing “gets neither”
with this scoped description.

@youknowone
youknowone merged commit 295b3fc into main Aug 5, 2026
15 of 17 checks passed
@youknowone
youknowone deleted the single-walker branch August 5, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant