Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: parenthetical syntax for message sends (cycles, timeout etc.) #4608

Open
wants to merge 271 commits into
base: master
Choose a base branch
from

Conversation

ggreif
Copy link
Contributor

@ggreif ggreif commented Jul 10, 2024

This PR introduces new syntax (parenthetical notes) for async-valued calls and async self-sends. Two attributes are recognised therein: cycles : Nat and timeout : Nat32. The type-checker enforces these types (when attributes present) and warns on unknown ones. Having a timeout attribute present will make the message send a bounded-wait one (also called best-effort) according to the new replica semantics. Specifying cycles works like the (soon to be deprecated) Cycles.add<system>, but one has to decide which to use (either Cycles.add or a parenthetical) since there is no mix-and-match.


ICCallPrim (and friends) should carry the fragment to set the SystemCyclesAddPrim while the call is being set up towards the replica. This now happens in the desugarer by assigning to variables (@cycles and @timeout).

Parentheticals

  • now: (with cycles = 42_000_000) Actor.call(param)
  • also now: (with timeout = 10)
    ic0.call_with_best_effort_response : (timeout_seconds : i32) -> ()
  • maybe: (with receiveMax = 50_000) (to limit the response size)
  • maybe: (with resend = { tries = 5; delay = 3 }) when SYS_UNKNOWN reject response (like Unix EINTR, but saves cycles by not re-encoding the arguments)
  • support feat(system-api): add call_cycles_add128_up_to ic#1158
  • usecase (with memoryLimit = 1G) ActorClass(<args>)
  • default/suppress call-meta-attributes: func() : async () = (with) async { ... } — document this!

TODOs:

  • Changelog.md
  • documentation
  • test one-ways
  • typecheck
    • warn when an attribute is moot
    • cycles : Nat for canister sends (self and raw sends too)
    • timeout : Nat32 for best-effort
    • parenthetical should have no send cap (test still missing)
  • async blocks
  • ICCallPrim, see top
  • ICCallRawPrim, not directly annotatable (desisting for now, but see feat: passing cycles to call_raw #4868)
  • FIXMEs, TODOs
    • ICCallPrim should have a non-option setup
    • break out unrelated PRs (optimisations?) and beautifications — see chore: backend tweaks #4890
  • warn on queries?
  • best-effort: new error code when deadline passed?
  • decide whether (with cycles) async or async (with cycles) — decided: keep as prefix!
  • write a temporary test that Cycles.add still works
  • test that parenthetical have no send capability
  • test (with cycles) (func () -> async () {})() direct application
  • test (with cycles) (system Lib.Class)(...)()
  • test error on async* (and call)

@ggreif ggreif changed the title WIP: surface syntax for parentheticals feat: parenthetical syntax for cycles etc. Jul 10, 2024
@ggreif ggreif self-assigned this Jul 10, 2024
@ggreif ggreif added the language design Requires design work label Jul 10, 2024
@crusso
Copy link
Contributor

crusso commented Jul 12, 2024

Notes:

// foo.mo
actor class Foo() {}

translates too something like:

module {
  type Foo = actor { .. }
  func Foo() : async Foo = async {
    let p = await* Internals.create_helper() {
      let available = Cycles.availabe();
      Cycles.add(availabe);
      let p = await IC.create_cansiter();
      let _ = await IC.install_code(p,...);
      p
   };
   actor (..p..) : Foo
};

// test
import Lib "foo"
public func test() : async () {
       addCycles(10);
       let a : Lib.Foo = Lib.Foo();
       ();
  }
}

@ggreif ggreif force-pushed the gabor/parentheticals branch from 69b026a to 463f12a Compare July 29, 2024 18:23
when there is a closure. The first component is the parenthetical record
and the second is the closure environment for the call.
object base {
public func cycles() {}
};
await (base with) async ();
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, this is kinda ugly.
I wonder if
(with object-combination) would work in the parser...
(with base)
(with cycles = 100l, ...)
(with base with cycles = 100).
Probably not because of punning...

ggreif and others added 3 commits February 21, 2025 11:43
let cycles, clean_cycles =
if T.(sub par.note.note_typ (Obj (Object, [T.cycles_fld])))
then [fun parV -> dotE parV T.cycles_lab T.nat |> assignVarE "@cycles" |> expD], []
else [], [natE Mo_values.Numerics.Nat.zero |> assignVarE "@cycles" |> expD] in
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
else [], [natE Mo_values.Numerics.Nat.zero |> assignVarE "@cycles" |> expD] in
else [], [] in

If you did this, wouldn't the old mechanism still work if you only apply at timeout but don't mention cycles?
Then the Changelog doesn't have to say the approaches can't be mixed.

Copy link
Contributor Author

@ggreif ggreif Feb 21, 2025

Choose a reason for hiding this comment

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

As you say, a prior Cycles.add would continue to work. Ah, you are worried about codebase migration, where folks want to add (with timeout) but are hesitant to check for the presence of Cycles.add (and remove it in favour of cycles =) in front of the call?

@ggreif ggreif force-pushed the gabor/parentheticals branch from 8a957ac to a40febf Compare February 21, 2025 11:22
* Added support for sending `cycles` and setting a `timeout` in parentheticals.
This is an **experimental feature**, with new syntax, and now also allowing best-effort
message sends. The legacy call `Cycles.add<system>` is still supported, but the
two approaches cannot be mixed (#4608).
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment in desugar.ml and how to make them coexist.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why would coexistence be important? If the user attaches a parenthetical, why not go all the way?

@ggreif ggreif force-pushed the gabor/parentheticals branch from b3e320f to fe40ca4 Compare February 21, 2025 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
canisters Language or compiler support for canister functionality feature New feature or request language design Requires design work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants