Skip to content

Box object literals with methods/accessors as reference-typed ObjectType#249

Merged
ASDAlexander77 merged 1 commit into
mainfrom
object-literal-boxing-flip
Jul 18, 2026
Merged

Box object literals with methods/accessors as reference-typed ObjectType#249
ASDAlexander77 merged 1 commit into
mainfrom
object-literal-boxing-flip

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

  • Second of three staged PRs generalizing the generator-wrapper's BoxAsObject boxing (PR Box the generator wrapper as a heap-allocated ObjectType instead of a value tuple #245, infra in Add ObjectType-boxing infrastructure: getFields, spread, tuple cast #248) — design in docs/object-literal-boxing-design.md. This is the actual flip: mlirGen(ObjectLiteralExpression) now boxes any literal with a method or accessor as ObjectType, not just synthetic wrappers carrying the flag.
  • Every alias of such a literal (second binding, parameter, closure capture, array/object element) now shares mutable state, matching JS semantics. Pure-data literals (no methods) keep value/struct semantics — unchanged.
  • The 2-line flip surfaced five more missing ObjectType cases across the compiler (each already-existing code that only ever saw tuples for method-bearing literals before). All fixed:
    • Boxing seed value now routed through mlirGenCreateTuple instead of the generic cast pipeline, avoiding a spurious "losing this reference" warning.
    • TupleGetSetAccessor's get/set now handles a pointer (ObjectType/RefType) operand via PropertyRefOp+LoadOp, not just ExtractPropertyOp (which is restricted to value-typed structs).
    • castObjectToInterface gained the same field-type-coercion fallback castTupleToInterface already had (e.g. an inferred si32 field vs. an interface declaring number).
    • Element access (obj[Symbol.x]) dispatches ObjectType like the existing ClassType/InterfaceType cases.
    • Generic intersection-type merge (D & M) and generic parameter inference look through ObjectType's storage type, same as MLIRTypeHelper::getFields.
  • One pre-existing, unrelated bug found and not fixed here (documented, worked around in the new test): an accessor whose backing field infers si32 but whose declared parameter type is number corrupts the write — reproduces identically on unmodified pre-PR-B main.

Test plan

  • New test test/tester/tests/00object_ref_semantics.ts covers alias mutation through: plain re-binding, function parameters, closure capture, array elements, nested-object elements, accessors, a global const literal (generalizes the Fix crash lowering a global const object literal with a mutating method #246 repro), and conditional-expression merges of two same-shape literals.
  • Full suite green: 353/353 JIT + 357/357 AOT (ctest --test-dir <builddir> -C Debug -R "^test-jit-" / "^test-compile-" -j8) — includes the three tests that initially regressed (00interface_object5, 00symbol, 00type_aliases_in_generics), each root-caused via MLIR/LLVM IR diffs against the pre-flip baseline and fixed.

🤖 Generated with Claude Code

Generalizes the generator-wrapper's BoxAsObject boxing (PR #245, PR #248)
to any object literal with a method or accessor: mlirGen(ObjectLiteralExpression)
now boxes whenever oli.methodInfos/methodInfosWithCaptures is non-empty, not
just synthetic wrappers carrying the flag. Every alias of such a literal
(second binding, parameter, closure capture, array/object element) now
shares mutable state, matching JS semantics; pure-data literals keep value
(struct) semantics.

The flip itself is two lines, but exercising ObjectType through every path
that previously only saw tuples for method-bearing literals surfaced five
more missing ObjectType cases, each fixed:
- boxing seed value routed through mlirGenCreateTuple instead of the
  generic cast pipeline, avoiding a spurious "losing this reference" warning
- TupleGetSetAccessor's get/set now handles a pointer (ObjectType/RefType)
  operand via PropertyRefOp+LoadOp, not just ExtractPropertyOp
- castObjectToInterface gained the same field-type-coercion fallback
  castTupleToInterface already had (e.g. si32 vs number)
- element access (obj[Symbol.x]) dispatches ObjectType like Class/Interface
- generic intersection-type merge (D & M) and parameter inference look
  through ObjectType's storage type, same as MLIRTypeHelper::getFields

New test 00object_ref_semantics.ts covers alias mutation through bindings,
parameters, closures, array/object elements, accessors, globals, and
conditional-expression merges. Full suite green: 353/353 JIT + 357/357 AOT.

See docs/object-literal-boxing-design.md (PR B of three staged PRs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 9f0d1a6 into main Jul 18, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the object-literal-boxing-flip branch July 18, 2026 18:19
ASDAlexander77 added a commit that referenced this pull request Jul 18, 2026
The #246 isBoundMethodTupleConstant special case in GlobalOpLowering
decided whether a global's initializer needs the constructor path based on
whether any ConstantOp in it had a bound-method-shaped tuple field. Since
PR #249 boxes every method-bearing object literal, such a literal's
initializer always contains a NewOp (the boxing recipe), which the walk's
existing NewOp case already forces onto the constructor path -- making the
bound-method check redundant. Verified via a diagnostic probe at that call
site across every test file (zero hits) before removing it for real.

Investigated but did NOT remove needsIdentityStorage/boundRefMaterializedCache:
contrary to the original plan (docs/object-literal-boxing-design.md), these
are still load-bearing. Whenever a boxed literal is unboxed back into a
plain tuple to match a declared/inferred type (annotated const, parameter,
interface-cast fallback, generic intersection/inference), the resulting
tuple's method field is still bound-method-shaped even though the tuple
itself is a plain value -- that machinery is what makes calling through it
work correctly instead of crash. Root-caused via site-tagged diagnostic
probes across the full test corpus; documented in the design doc so this
isn't re-attempted without re-deriving the same finding.

Full suite green: 353/353 JIT + 357/357 AOT.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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