Skip to content

feat: parenthetical encoder annotation for public actor methods#5996

Draft
ggreif wants to merge 6 commits intomasterfrom
gabor/encoder
Draft

feat: parenthetical encoder annotation for public actor methods#5996
ggreif wants to merge 6 commits intomasterfrom
gabor/encoder

Conversation

@ggreif
Copy link
Copy Markdown
Contributor

@ggreif ggreif commented Apr 9, 2026

Summary

  • Adds (with encoder = <func>) public func name() : async T = ... syntax, where encoder : T -> Blob replaces Candid serialization for the method's reply
  • Encoder expression is attached to the FuncE IR node at desugaring (build_actor), propagated through all IR passes, and threaded into ICReplyPrim (ts, Some enc) by the async CPS transform in async.ml
  • Both classical and enhanced backends call the encoder closure and send raw bytes via IC.reply_with_data, bypassing Candid
  • check_ir validates that the encoder has the right type (T -> Blob) and is effect-free

Test plan

  • make -C test/run-drun parenthetical-public.only passes

🤖 Generated with Claude Code

@ggreif ggreif changed the title feat: parenthetical encoder annotation for public actor methods feat: parenthetical encoder annotation for public actor methods Apr 9, 2026
@ggreif ggreif self-assigned this Apr 9, 2026
@ggreif ggreif added the feature New feature or request label Apr 9, 2026
ggreif and others added 6 commits April 17, 2026 12:52
Adds syntax `(with encoder = <func>) public func name() : async T = ...`
to annotate the serialization encoder for an actor method's return value.

- `vis'` type gains `exp option` (the annotation) alongside the deprecation string;
  moved into the `exp` mutual recursion group to resolve the forward-reference
- Parser: `vis → parenthetical PUBLIC` with `%prec VIS_NO_PAREN` to resolve
  the 3 shift/reduce conflicts from the nullable vis prefix; `--strict` preserved
- Type checker: `check_vis_parenthetical` validates the encoder in checking mode
  against the expected type `ret_typ → Blob` (async peeled via `Promises`/`ts2`);
  non-method placements warn with M0212; effects forbidden (M0215)
- Test: `test/run-drun/parenthetical-public.mo`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…freedom

IR representation:
- `FuncE` gains an 8th `exp option` field for the encoder (None everywhere
  except public actor methods with a `(with encoder = …)` annotation)
- All IR passes, rename, subst_var, arrange, freevars, interpreter, and both
  codegens updated: pass-through or wildcard `_enc` as appropriate

Desugaring:
- `build_encoders` mirrors `build_stabs` to correctly pair each IR dec with
  its optional encoder across IncludeD/TypD expansion
- In `build_actor` the encoder expression is extracted from the vis
  parenthetical's `encoder` field and injected into the IR `FuncE` slot

Checking:
- IR type-checker (`check_ir.ml`): verifies encoder type is `ret_typ → Blob`
  and effect is `T.Triv`
- Source type-checker (`typing.ml`): `check_vis_parenthetical` additionally
  guards `note_eff = T.Triv`, emitting M0215 if the annotation has effects

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactors `ICReplyPrim` to carry an `exp option` encoder slot instead
of using a mutable `reply_encoder` field on the compilation environment.
The encoder expression (user-supplied `T -> Blob` function) is injected
by `desugar.ml`'s `build_actor` into the `FuncE` IR node, propagated
through all IR passes (`erase_typ_field`, `show`, `eq`, `await`, `async`),
and consumed by `async.ml`'s CPS transform to build the reply continuation
`k` as `ICReplyPrim (ts, Some enc')` instead of the default Candid path.

Both classical and enhanced backends call the encoder closure and send
the raw blob bytes via `IC.reply_with_data`, bypassing Candid serialization.
`arrange_ir` and `check_ir` are updated to print/validate the encoder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant