Skip to content

#346: resolve dot-joined constructor base, residualize w_long_new, name colliding classdefs - #411

Merged
youknowone merged 4 commits into
mainfrom
rtyper-legacy
Jul 8, 2026
Merged

#346: resolve dot-joined constructor base, residualize w_long_new, name colliding classdefs#411
youknowone merged 4 commits into
mainfrom
rtyper-legacy

Conversation

@youknowone

@youknowone youknowone commented Jul 8, 2026

Copy link
Copy Markdown
Owner

(untracked)

Follow-on slices to #405, reducing two-phase rtyper prepass lift failures by wiring classdef common bases and residualizing the numeric boxing tail. Toward gh#346 (retire the rtyper legacy walker): as classdef unification succeeds and boxing tails residualize, more graphs lift through the two-phase CodeWriter path and cutover::is_known_unported shrinks.

Commits

  • name colliding classdefs in the no-common-base UnionError — enrich both no-common-base UnionError arms (SomeInstance, SomeWeakRef) with the two colliding classdef names so the failure identifies the exact pair; the skip substring is preserved. SomeWeakRef merge flattened to a single Option<classdef> match.
  • resolve header base for dot-joined constructor qualnamesintern_class_by_qualname's header-chain walk resolves the enum-base check and field lookup through a ::-normalized spelling while keeping the classdef cache key raw, so a boxed subtype and its base unify via their shared base instead of raising no-common-base.
  • residualize w_long_new via dont_look_inside — annotate w_long_new #[dont_look_inside] (with the fnaddr bind) so the annotator models it by signature (PyObjectRef return) instead of tracing into the BigInt boxing tail; alloc_bigint_stable / w_long_from_raw return *mut BigInt. Closes the PyObject ∪ BigInt collision.

Effect

Measured over these three slices: no-common-base cannot unify occurrences 106→60, two-phase prepass phaseA failures 323→307. check.py 3/3 bit-exact (dynasm / cranelift / wasm, 183/183 each).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved JIT tracing support for long-integer construction, including an additional function mapping and clearer handling of the allocation path.
  • Bug Fixes

    • Fixed type-name matching for constructor-qualified names with dot notation, reducing lookup mismatches.
    • Error messages for failed type unification now include the conflicting class names, making issues easier to diagnose.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@youknowone, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a843826b-bbfd-47ea-9987-5fbec5bce6d4

📥 Commits

Reviewing files that changed from the base of the PR and between 3f098d8 and bb3191a.

📒 Files selected for processing (4)
  • majit/majit-translate/src/annotator/bookkeeper.rs
  • majit/majit-translate/src/annotator/model.rs
  • pyre/pyre-interpreter/src/jit_fnaddr.rs
  • pyre/pyre-object/src/longobject.rs

Walkthrough

This PR normalizes dot-joined constructor qualnames to :: format for registry lookups in the bookkeeper's enum/class interning logic, enriches union error messages with conflicting class names, and registers a JIT fnaddr alias for w_long_new while marking it non-traceable via dont_look_inside.

Changes

Type Annotator Fixes

Layer / File(s) Summary
Normalize constructor qualnames in bookkeeper lookup
majit/majit-translate/src/annotator/bookkeeper.rs
intern_class_by_qualname converts dot-joined qualnames to ::-style for struct-field registry lookups and enum-base parent checks, aligning classdef cache keys with registry keys.
Include class names in union error messages
majit/majit-translate/src/annotator/model.rs
union() failures for SomeValue::Instance and SomeValue::WeakRef on commonbase(...) == None now return UnionError messages that include both conflicting class names; the WeakRef path is refactored into an explicit match.

JIT w_long_new Tracing and Alias Registration

Layer / File(s) Summary
Register w_long_new alias and block JIT trace-through
pyre/pyre-interpreter/src/jit_fnaddr.rs, pyre/pyre-object/src/longobject.rs
jit_trace_fnaddrs() registers module-qualified and root-alias fnaddr mappings for w_long_new, and longobject.rs adds a #[majit_macros::dont_look_inside] attribute with an explanatory comment to block JIT tracing into its allocation delegation.

Estimated code review effort: 2 (Simple) | ~12 minutes

Possibly related PRs

  • youknowone/pyre#184: Both PRs modify union() for SomeValue::Instance commonbase-None handling in the same file/function.
  • youknowone/pyre#251: Both PRs modify w_long_new JIT tracing behavior in longobject.rs.
  • youknowone/pyre#294: Both PRs touch w_long_new's allocation/tracing behavior at the function level.

Poem

A dot becomes a double-colon,
enum parents no longer forlorn,
two class names now join the fray
when union paths won't find their way,
and w_long_new hides safe from trace—
this bunny hops with tidy grace. 🐇✨

🚥 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 accurately summarizes the three main changes: dot-joined constructor base resolution, residualizing w_long_new, and clearer colliding classdef errors.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rtyper-legacy

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 Jul 8, 2026

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit bcf6ed6).

1. Regressions to PyPy parity introduced by this patch

None.

2. Other mismatches introduced by this patch

  • majit/majit-translate/src/annotator/model.rs:2993 ↔ rpython/annotator/binaryop.py:673: Rust now reports "RPython cannot unify instances with no common base class: {} ∪ {}"; RPython raises "RPython cannot unify instances with no common base class" without class names. This is diagnostic-only; the union behavior still matches.

  • majit/majit-translate/src/annotator/model.rs:3176 ↔ rpython/annotator/binaryop.py:858: Rust now reports "RPython cannot unify weakrefs with no common base class: {} ∪ {}"; RPython raises UnionError(s_wrf1, s_wrf2) with no explicit message. This is diagnostic-only; the no-common-base failure behavior remains equivalent.

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

  • majit/majit-translate/src/annotator/model.rs:3168 ↔ rpython/annotator/binaryop.py:851: Rust treats either dead weakref side as None via (None, _) | (_, None) => None; RPython preserves the live side: if s_wrf1.classdef is None: basedef = s_wrf2.classdef, and symmetrically at lines 853-854. This mismatch existed on upstream/main.

4. Structural adaptations

  • majit/majit-translate/src/annotator/bookkeeper.rs:2054 ↔ rpython/annotator/bookkeeper.py:168: Rust normalizes constructor lookup spelling with cur.replace('.', "::"); RPython receives an actual Python class object in getuniqueclassdef(cls). This is a Rust/Charon naming adaptation, not a PyPy semantic deviation.

  • pyre/pyre-object/src/longobject.rs:273 ↔ pypy/objspace/std/longobject.py:495: Rust marks w_long_new as #[dont_look_inside]; PyPy’s newlong(space, bigint) is not decorated there. This is a structural Rust/JIT boundary adaptation for the raw BigInt allocation path.

  • pyre/pyre-interpreter/src/jit_fnaddr.rs:495 ↔ rpython/rlib/jit.py:139: Rust registers a residual-call address for pyre_object::longobject::w_long_new; RPython’s equivalent effect is the generic func._jit_look_inside_ = False marker. The explicit function-address table is pyre’s implementation-language adaptation for residual calls.

@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/3f098d88df4871ccdca811357984e6f20d5e4fe5/pyre-object/src/longobject.rs#L273-L274
P1 Badge Use a pointer-ABI wrapper for residual long boxing

When a traced long path reaches this dont_look_inside call, the codewriter will residualize the registered w_long_new fnaddr and pass the opaque BigInt value through the normal i/r/f residual-call registers. The argument modeled for these bigint paths is a ref/pointer (ValueType::Ref(None) / *mut BigInt from the jit_bigint_* helpers), but this callee's Rust ABI still expects an owned BigInt aggregate by value, so the residual call will interpret a pointer-sized register as a BigInt and can corrupt or crash on bigint allocation. Please residualize a pointer-shaped wrapper such as w_long_from_raw(*mut BigInt) instead of this by-value function.

ℹ️ 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".

@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 `@majit/majit-translate/src/annotator/model.rs`:
- Around line 3167-3188: The Instance and WeakRef union branches duplicate the
same commonbase failure pattern, so extract that logic into a shared helper used
by the union handling around the Instance and WeakRef cases in
annotator/model.rs. Add a helper that takes the two ClassDef refs, the kind
string (“instances”/“weakrefs”), and the lhs/rhs values, then calls
ClassDef::commonbase and returns either the base or a UnionError with the
appropriate message. Update both union arms to use this helper so the error
formatting stays consistent and the duplicated match/format! logic is removed.
🪄 Autofix (Beta)

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

Run ID: 4bcdd0f9-610c-497a-8c1d-ec1e58c89ab5

📥 Commits

Reviewing files that changed from the base of the PR and between e266d6e and 3f098d8.

📒 Files selected for processing (4)
  • majit/majit-translate/src/annotator/bookkeeper.rs
  • majit/majit-translate/src/annotator/model.rs
  • pyre/pyre-interpreter/src/jit_fnaddr.rs
  • pyre/pyre-object/src/longobject.rs

Comment on lines 3167 to 3188
(SomeValue::WeakRef(a), SomeValue::WeakRef(b)) => {
let merged_classdef = match (&a.classdef, &b.classdef) {
(None, _) | (_, None) => Some(None),
(Some(ca), Some(cb)) => ClassDef::commonbase(ca, cb).map(Some),
};
let Some(merged_classdef) = merged_classdef else {
return Err(UnionError {
lhs: s1.clone(),
rhs: s2.clone(),
msg: "RPython cannot unify weakrefs with no common base class".into(),
});
};
let merged_classdef: Option<Rc<RefCell<ClassDef>>> =
match (&a.classdef, &b.classdef) {
(None, _) | (_, None) => None,
(Some(ca), Some(cb)) => match ClassDef::commonbase(ca, cb) {
Some(base) => Some(base),
None => {
return Err(UnionError {
lhs: s1.clone(),
rhs: s2.clone(),
msg: format!(
"RPython cannot unify weakrefs with no \
common base class: {} ∪ {}",
ca.borrow().name,
cb.borrow().name
),
});
}
},
};
Ok(SomeValue::WeakRef(SomeWeakRef::new(merged_classdef)))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting a shared helper for the Instance/WeakRef commonbase-error pattern.

Both this block and the Instance-union block (lines 2980-3005) repeat the same match (Some(ca), Some(cb)) => match commonbase(...) { Some => ..., None => Err(UnionError{ ... format!(...) }) } shape, differing only in the "instances"/"weakrefs" wording. A small helper (e.g. fn commonbase_or_union_err(ca, cb, kind: &str, s1, s2) -> Result<Rc<RefCell<ClassDef>>, UnionError>) would remove the duplication and keep both error messages in sync going forward.

♻️ Sketch of a shared helper
+fn commonbase_or_err(
+    ca: &Rc<RefCell<ClassDef>>,
+    cb: &Rc<RefCell<ClassDef>>,
+    kind: &str,
+    s1: &SomeValue,
+    s2: &SomeValue,
+) -> Result<Rc<RefCell<ClassDef>>, UnionError> {
+    ClassDef::commonbase(ca, cb).ok_or_else(|| UnionError {
+        lhs: s1.clone(),
+        rhs: s2.clone(),
+        msg: format!(
+            "RPython cannot unify {kind} with no common base class: {} ∪ {}",
+            ca.borrow().name,
+            cb.borrow().name
+        ),
+    })
+}
🤖 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 `@majit/majit-translate/src/annotator/model.rs` around lines 3167 - 3188, The
Instance and WeakRef union branches duplicate the same commonbase failure
pattern, so extract that logic into a shared helper used by the union handling
around the Instance and WeakRef cases in annotator/model.rs. Add a helper that
takes the two ClassDef refs, the kind string (“instances”/“weakrefs”), and the
lhs/rhs values, then calls ClassDef::commonbase and returns either the base or a
UnionError with the appropriate message. Update both union arms to use this
helper so the error formatting stays consistent and the duplicated match/format!
logic is removed.

@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

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ef7be9db7

ℹ️ 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".

&mut entries,
"pyre_object::longobject::w_long_new",
"pyre_object::w_long_new",
pyre_object::longobject::w_long_new as *const (),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Register an ABI-safe wrapper for w_long_new

When a traced long-overflow/math path residualizes w_long_new, the native residual dispatcher does not call Rust signatures directly: majit-backend/src/call_stub.rs collects only i64/f64 slots and transmutes the fnaddr to an extern "C" fn(i64, ...). This entry publishes fn(BigInt) -> PyObjectRef, whose by-value BigInt parameter is not a single C-ABI slot, so the generated call will enter w_long_new with the wrong ABI and can corrupt or crash. Please register an ABI shim that takes an encoded pointer/slot, or residualize a raw-pointer helper instead.

Useful? React with 👍 / 👎.

The SomeInstance and SomeWeakRef union arms raise UnionError when both
sides carry a classdef and ClassDef::commonbase returns None. Append the
two classdef names to the message so the skip-classified panic path is
diagnosable. The "cannot unify instances with no common base class" /
"cannot unify weakrefs with no common base class" phrases are preserved
verbatim so the dual-gate skip classifier still matches.

Flatten the SomeWeakRef merge to a single Option<classdef> match so its
None-classdef success case and commonbase-None failure case read the
same as the SomeInstance arm.

Assisted-by: Claude
intern_class_by_qualname walks the embedded-header chain (`ob_header:
PyObject`) to set a struct class's base, but its `struct_fields` lookup
used the raw name. A constructor mints its class under the dot-joined,
crate-included qualname (`pyre_object.intobject.W_IntObject`), while
`struct_fields` is keyed by the `::` name_path and the bare leaf, so the
lookup missed and the box class minted base-less. commonbase then
returned None for W_IntObject ∪ W_LongObject / W_FloatObject /
W_ObjectObject, raising UnionError at the numeric return-phi.

Reduce `.`→`::` for the header-chain registry lookups only; the classdef
cache key keeps the raw spelling, so the base is seeded without
collapsing the constructor class onto the `::`-spelled field-read class.

Assisted-by: Claude
w_long_new delegates through alloc_bigint_stable -> *mut BigInt, so the
annotator traced into it leaked the raw BigInt pointee into the return
model. At the int boxing tail the slow/overflow arm then unified against
the w_int_new fast path as PyObject ∪ BigInt, raising UnionError in
mergeinputargs for bigint_result / int_lshift / int_floordiv.

Mark w_long_new #[dont_look_inside] so it is modeled by its PyObjectRef
signature (a plain GCREF, no discriminant to erase) rather than traced,
and add its fnaddr alias-pair bind next to w_str_new. The BigInt by-value
argument carries no i64 trampoline, so the bind is path-resolution
completeness; the overflow tail was already interp-only slow path.

Assisted-by: Claude
Assisted-by: Claude
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