Skip to content

Add composable serialization intrinsics - #1224

Merged
Frotty merged 5 commits into
masterfrom
agent/serialization-intrinsics
Aug 11, 2026
Merged

Add composable serialization intrinsics#1224
Frotty merged 5 commits into
masterfrom
agent/serialization-intrinsics

Conversation

@Frotty

@Frotty Frotty commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Adds the minimal compiler surface needed for a composable standard-library serialization framework while keeping serialization policy and runtime metadata in the library.

  • Adds IDE-visible @compilerintrinsic declarations for canonical wurstForFields, wurstMapFields, and wurstNewInstance<T>() APIs.
  • Retains the original unprefixed spellings as compatibility fallbacks.
  • Lowers field iteration to direct class/tuple accesses and generic construction to ordinary constructor calls.
  • Supports explicit class and tuple targets, including inherited and module-injected fields.
  • Keeps tuple mapping capture-safe by mapping through a fresh compiler temporary and writing the final tuple back once.
  • Preserves ordinary overload behavior: when an applicable user function is visible, intrinsic declarations are excluded from overload resolution.
  • Preserves the annotated wurstNewInstance<T>() declaration link for hover and definition navigation without changing intrinsic lowering.
  • Keeps schema IDs, migrations, wire format, parsing, versioning, and integrity hashing library-owned; no serialization registry or reflection metadata is emitted.

Why the wurst prefix

The canonical names use a Wurst-style prefix instead of the earlier double-underscore convention. This makes accidental collisions much less likely while remaining readable and discoverable. The resolver fix also makes collisions deterministic: an applicable ordinary overload wins over an imported intrinsic contract for all three operations.

Serialization composition

The compiler exposes field structure and concrete field types through per-field callback specialization. A library can overload codec calls for primitives, nested opted-in classes, tuples, enums, nullable values, and generic collections such as ArrayList<T>. Stable persisted field IDs remain an explicit library concern rather than compiler-generated save metadata.

Backend constraints

Both JASS and Lua emit direct accesses/construction. The change adds no runtime reflection, class-name lookup, global type-ID dispatch, uninitialized allocation, or broad Lua monomorphization.

Tests

  • FieldIterationTests: JASS/Lua lowering, explicit targets, tuples, nested/composite field kinds, inherited/module fields, generic construction, diagnostics, compatibility spellings, ordinary-overload precedence, and nested-closure tuple capture safety.
  • Focused completion, hover, and definition-navigation tests for documented intrinsic declarations, including wurstNewInstance<T>().
  • Full gradlew test: passed on d43e35954 in 10m24s.

@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: ff48f039f0

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

Comment on lines +616 to +617
&& ((idParameter != null && access.getVarName().equals(idParameter))
|| access.getVarName().equals(nameParameter) || access.getVarName().equals(valueParameter))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve nested locals named like the schema ID

When a schema-aware callback contains a nested closure with an ordinary local named id—for example, () -> begin let id = 100; use(id) end—the outer shadowing check intentionally skips that closure, but this visitor replaces the local read because its block/local tracking only recognizes nameParameter and valueParameter. The read therefore becomes the current field's schema ID, changing runtime behavior; include idParameter in both local-shadow tracking paths. Only __wurst-prefixed nested callback locals are documented as unsupported.

AGENTS.md reference: AGENTS.md:L307-L309

Useful? React with 👍 / 👎.

Frotty commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@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: 9c5de3918b

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

Comment on lines +342 to +343
field.declaration.addError("Schema-aware field iteration requires @saveField(id) on field "
+ field.key() + ".");

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 Keep save schemas out of field iteration

When a caller uses the new three-parameter form, this branch refuses to lower a class unless every included field carries the compiler-recognized @saveField annotation, while schemaId also special-cases @saveFields. This makes compiler semantics depend directly on serialization-format metadata, contrary to the repository contract that field iteration remain general-purpose and contain no save-format knowledge; keep ID policy in the serialization library instead of recognizing these annotations in SyntacticSugar.

AGENTS.md reference: AGENTS.md:L298-L301

Useful? React with 👍 / 👎.

@Frotty

Frotty commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@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: 0ae98c79cd

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

Comment on lines +525 to +526
if (CompilerIntrinsics.isDeclaration(candidate.getDef())) {
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude intrinsic declarations from ordinary overload sets

When an imported @compilerintrinsic declaration and an applicable ordinary closure overload are both visible, this skip makes hasApplicableUserFunction disable intrinsic lowering, but normal overload resolution still retains the annotated declaration. I reproduced this with matching imported forFields(IntCallback) declarations: the call reports both as ambiguous instead of selecting the ordinary function. Filter the intrinsic contract from subsequent resolution whenever an ordinary candidate wins; the same issue applies to mapFields and newInstance.

AGENTS.md reference: AGENTS.md:L310-L311

Useful? React with 👍 / 👎.

Frotty commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@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: 54131e219f

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

Comment on lines +245 to +248
statements.remove(targetVariable);
statements.clearAttributes();
targetVariable = null;
targetName = targetAccess.getVarName();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep tuple target accesses capture-safe

When mapFields targets a tuple local and the callback contains a nested closure whose parameter reuses the tuple variable's name, replacing the fresh temporary with only targetAccess.getVarName() makes substituted field-value accesses bind to that nested parameter. For example, mapping payload with an inner (Pair payload) -> value + payload.left reads both operands from the inner tuple and writes incorrect values to the outer tuple; retain a capture-free target reference and write the result back instead. Ordinary nested callback names are supported—the documented exception is limited to __wurst-prefixed names.

AGENTS.md reference: AGENTS.md:L308-L310

Useful? React with 👍 / 👎.

Comment on lines +46 to +47
public static boolean isDeclaration(de.peeeq.wurstscript.ast.FunctionDefinition definition) {
return definition.attrHasAnnotation(ANNOTATION);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Expose construction intrinsic declarations to language tooling

When an IDE resolves an annotated wurstNewInstance<T>() call, this declaration is excluded from ordinary-function detection, after which AttrFuncDef.calculate(ExprFunctionCall) returns null immediately because CompilerIntrinsics.isNew(node) is true. Consequently get-definition and hover return no result for the documented construction intrinsic, even though the new declaration mechanism works for the field-iteration intrinsics; preserve the declaration link for tooling while keeping compiler lowering intact.

Useful? React with 👍 / 👎.

Frotty commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: d43e359541

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

@Frotty
Frotty merged commit 4948323 into master Aug 11, 2026
4 checks passed
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