Skip to content

interpreter: give the frame getset accessors named descr_typecheck wrappers - #1515

Merged
youknowone merged 1 commit into
mainfrom
getset-descr-typecheck-wrappers
Aug 27, 2026
Merged

interpreter: give the frame getset accessors named descr_typecheck wrappers#1515
youknowone merged 1 commit into
mainfrom
getset-descr-typecheck-wrappers

Conversation

@youknowone

Copy link
Copy Markdown
Owner

init_frame_type registered each GetSetProperty accessor as an anonymous
closure literal. Upstream does not: typedef.py _make_descr_typecheck_wrapper
generates one named function per property and role — descr_typecheck_%s
over the wrapped method — memoised by @specialize.memo() so each is a
prebuilt constant, and GetSetProperty.__init__ binds that as fget / fset
/ fdel.

This moves the 15 accessor bodies to pyframe.rs as descr_typecheck_* items
and has the registration pass the path.

Bodies are carried over unchanged apart from frame_ptr(args[1]), which
becomes args.get(1).copied().unwrap_or(pyre_object::PY_NULL) as *mut PyFrame
now that the helper nested in init_frame_type is out of scope.

clear and __repr__ keep their closures: upstream registers those through
interp2app, which does not build a typecheck wrapper.

This is structural parity — the reachability gain is NOT demonstrated

Naming these was proposed on the theory that the JIT cannot see an anonymous
closure. Measured on the shipped LLBC, that is false. charon emits, per
closure, a closure item plus an as_fn FunDecl with a stable id:

typedef::init_frame_type::closure#0::as_fn  -> {"Fun":7043}   typedef.rs:8157
typedef::init_frame_type::closure#1::as_fn  -> {"Fun":7044}   typedef.rs:8177
… #2..#16 likewise, one per closure, spans matching the source exactly

So the front end already had these bodies. Whatever the getset path costs the
JIT, it is not front-end visibility — the getset fget is reached through an
indirect call off the descriptor, and upstream resolves that through the
graph set the annotator attaches to indirect_call (call.py graphs_from).
Whether pyre resolves it is a separate question this PR does not touch, and no
jitstats move is claimed.

The justification here is that upstream builds a named wrapper per property and
pyre inlined an anonymous one. Nothing more.

Conflict resolved against main, not taken from one side

#1512's neighbourhood aside, main rewrote the f_lasti getter body while this
was in flight — the not-yet-started generator frame resting after
RETURN_GENERATOR rather than on the RESUME. Taking either side whole would
have dropped one of the two changes, so typedef.rs takes the wrapper path and
the wrapper carries main's new body. Main's 44-line explanation moved with
it: that block ends "so both adaptations live here rather than in
PyFrame::fget_f_lasti", which stops being true if the body moves and the prose
does not.

Verification

  • cargo check -p pyre-interpreter --features dynasm on this commit — clean
    (one pre-existing dead_code warning on error.rs write_report_line, not
    touched here).
  • cargo fmt --all -- --check — clean.
  • No LLBC re-extraction was run, so nothing here is graded against a JIT
    measurement; CI is the gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_017wapwfqfqNe7kFcxQRx85P

…appers

`init_frame_type` registered each `GetSetProperty` accessor as an anonymous
closure literal.  `typedef.py _make_descr_typecheck_wrapper` generates one
named function per property and role, so the 15 accessor bodies move to
`pyframe.rs` as `descr_typecheck_*` items and the registration passes the
path.

The bodies are carried over unchanged apart from `frame_ptr(args[1])`, which
becomes `args.get(1).copied().unwrap_or(pyre_object::PY_NULL) as *mut PyFrame`
now that the helper nested in `init_frame_type` is out of scope.

`clear` and `__repr__` keep their closures: upstream registers those through
`interp2app`, which does not build a typecheck wrapper.

Assisted-by: Claude
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 21 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 96588484-3841-4949-ae2b-cdba91ba4c1f

📥 Commits

Reviewing files that changed from the base of the PR and between 51eb5e5 and 3eaa3b3.

📒 Files selected for processing (2)
  • pyre/pyre-interpreter/src/pyframe.rs
  • pyre/pyre-interpreter/src/typedef.rs

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

https://github.com/youknowone/pyre/blob/3eaa3b3623d81f426dfd4761093d7765e5f30a8f/pyre-interpreter/src/typedef.rs#L8168
P1 Badge Give f_locals its named wrapper too

This named-registration sequence skips f_locals immediately below, leaving its getter as the sole anonymous closure among the frame getsets. Upstream PyFrame.typedef registers f_locals through GetSetProperty(PyFrame.fget_getdictscope), so _make_descr_typecheck_wrapper generates a named descr_typecheck_fget_getdictscope just as it does for the accessors converted here; add the corresponding free wrapper and register it to complete the structural conversion.

AGENTS.md reference: AGENTS.md:L223-L226

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 3eaa3b3).
Updated: 2026-08-27T01:08:59.371Z

Files in the reviewed diff
pyre/pyre-interpreter/src/pyframe.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)

None.

4. Structural adaptations

  • pyre/pyre-interpreter/src/pyframe.rs:2743 ↔ pypy/interpreter/pyframe.py:770f_lasti scales Pyre’s instruction index by two and supplies CPython-compatible entry offsets, while PyPy returns its bytecode offset directly. This is the permitted CPython-compiler opcode adaptation, retained exactly from upstream/main.

  • pyre/pyre-interpreter/src/pyframe.rs:2682 ↔ pypy/interpreter/typedef.py:736 — Pyre exposes frame.f_generator; PyPy’s PyFrame.typedef omits that public getset despite retaining the owner lookup at pypy/interpreter/pyframe.py:799. This matches CPython’s observable contract (lib-python/3/test/test_frame.py:254, :258, :266); the relevant PyPy owner path has no governing @jit.*, immutability, attrs, resize, or rgc.* hint.

@youknowone
youknowone merged commit af845a4 into main Aug 27, 2026
18 of 19 checks passed
@youknowone
youknowone deleted the getset-descr-typecheck-wrappers branch August 27, 2026 02:40
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