Skip to content

fix: Reuse callbacks with matching function shells - #3200

Open
oxura wants to merge 5 commits into
software-mansion:mainfrom
oxura:sol50/typegpu-2453
Open

oxura wants to merge 5 commits into
software-mansion:mainfrom
oxura:sol50/typegpu-2453

Conversation

@oxura

@oxura oxura commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2453.

Reuse an already-shelled callback instead of treating its wrapper as a new JavaScript implementation. Signature checks now use schemasEqualNominally from #3209: matching arrays/pointers work, while distinct struct identities and pointer spaces stay distinct.

Merged main (7c853fa3) and dropped the old helper move. Shellless cache lookup uses the shared utility too.

Checked the TypeGPU source suite: 198 files / 2,960 tests passed. Package typecheck, focused type-aware Oxlint and Oxfmt pass; the core import graph has no circular dependencies. The current public consumer returns 42 on the CPU and rejects seven incompatible signatures. Its fresh WGSL also ran on RX 6600 / RADV / Vulkan (Mesa 26.2.4) and read back 42. No current hosted CI or cross-platform result claimed.

Code, tests and prose are AI-assisted (GPT-6.1 Sol, openai-codex/gpt-6.1-sol).

Copilot AI balanced review requested due to automatic review settings October 15, 2026 08:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Reference equality rejects independently constructed pointer and array schemas with matching signatures.

1 open finding
What changed in this PR

Addresses #2453 by reusing already-shelled callbacks rather than interpreting their wrappers as new implementations.

Changes:

  • Adds callback reuse and signature validation.
  • Tests CPU execution, identity, WGSL preservation, externals, and incompatible signatures.
File Description
packages/​typegpu/​tests/​function.test.ts Adds callback reuse and signature rejection tests.
packages/​typegpu/​src/​core/​function/​tgpuFn.ts Implements and documents reuse for matching shells.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +182 to +184
innerShell.returnType !== shell.returnType ||
innerShell.argTypes.length !== shell.argTypes.length ||
!innerShell.argTypes.every((type, index) => type === shell.argTypes[index])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 6284ddb using the existing nominal-aware shallowEqualSchemas, now shared. Equivalent array arguments/returns and independently constructed pointers reuse correctly; different lengths, element types, nominal structs and pointer spaces reject. Full Node 24 suite: 2,840 tests passed. Generated array WGSL dispatched and read back 42 on RX 6600/Vulkan. AI-assisted: GPT-6.1 Sol, openai-codex/gpt-6.1-sol.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The new signature check compares schemas by reference, so a wrapper whose signature is structurally identical but built from distinct schema objects still throws instead of reusing. Details inline.

Reviewed changes

  • Reuse matching shelled callbacks — createFn early-returns the already-shelled implementation when its shell's returnType/argTypes match the wrapping shell, preserving the original core, raw-WGSL body, externals and slot bindings; mismatches throw at construction time, before WGSL generation.
  • Tests — packages/typegpu/tests/function.test.ts gains three cases: JS-fn reuse (identity + CPU result + WGSL), raw-WGSL reuse preserving $uses externals, and rejection of mismatched arg/return arity and types.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Comment thread packages/typegpu/src/core/function/tgpuFn.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

Changes since the prior pullfrog review (0fe5360 → 6284ddb) replace reference-equality signature matching with structural comparison and add coverage for it.

  • Structural schema matching — createFn now rejects a mismatched shell via shallowEqualSchemas instead of ===, so independently constructed but equivalent array and pointer schemas are reused rather than throwing.
  • Shared helper — shallowEqualSchemas moved from tgsl/shellless.ts into data/deepEqual.ts and is reused by both the callback-reuse path and the shellless cache, removing the duplicate.
  • Tests — added cases for structural array/pointer matching, mismatched array/pointer rejection, and nominal struct identity preservation; the prior review's open thread is addressed and resolved.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@oxura

oxura commented Oct 15, 2026

Copy link
Copy Markdown
Contributor Author

Normally merged current main (no rebase/force push), now 9e2f0f2d. Reverified 194 test files / 2856 tests on Node24 and the package typecheck. The actual refreshed-source WGSL ran on RX6600 / RADV / Vulkan and read back 42; separately constructed equivalent arrays remain accepted and nominal struct mismatches remain rejected. No new semantic change to the reviewed fix.

@iwoplaza iwoplaza changed the title fix: reuse callbacks with matching function shells fix: Reuse callbacks with matching function shells Oct 15, 2026
@iwoplaza

Copy link
Copy Markdown
Collaborator

This PR should wait until #3209 is merged, and use those utilities

@oxura

oxura commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Agreed — holding this for #3209 so I can reuse its equality utilities.

@iwoplaza

Copy link
Copy Markdown
Collaborator

@oxura I merged #3209, you can go ahead and update your implementation to use schemasEqualNominally

@oxura

oxura commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Updated to schemasEqualNominally and removed the old helper move. Main is merged; typecheck, lint/format and 2,960 TypeGPU tests pass. The current consumer also ran on RX 6600 / Vulkan and read back 42.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor coverage suggestion inline.

Reviewed changes

Changes since the prior pullfrog review (9e2f0f2 → c6bd12c), which is a merge of main (7c853fa) into the branch:

  • Merged main and reconciled the PR's matching helper with main's newer utility.
  • Switched createFn and ShelllessRepository.get from the PR-local shallowEqualSchemas to main's shared schemasEqualNominally, and removed the data/deepEqual.ts helper the PR had added (that file was removed on main).
  • Dropped three expect(wrapped).toBe(...) identity assertions from the reuse tests.

I verified on this head that npx vitest run tests/function.test.ts tests/data/schemasEqualNominally.test.ts tests/tgsl passes (577 tests) and that reuse identity still holds for all three removed assertions.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

});
const wrapped = tgpu.fn([d.u32], d.u32)(callback);

expect(wrapped(41)).toBe(42);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The merge commit c6bd12c dropped the expect(wrapped).toBe(callback) assertions here and at the array case (line 42) and pointer case (wrapped/increment, line 64), but reuse still preserves identity for all three — I re-added them locally and they pass. Since direct identity reuse is the PR's core behavior, keeping these guards against a future regression that re-wraps while still emitting equivalent WGSL; the resolve/CPU assertions do not distinguish the two.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Keeping the checks on callback behaviour here: CPU/WGSL results, raw WGSL externals, and incompatible signatures. I removed the identity assertions intentionally so the tests do not constrain which JavaScript object implements that behaviour.

This branch has not been deployed

No deployments
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.

bug: wrapping shelled function in a shell throws an error

3 participants