4 ms·
It's not accurate that there's only one representation of every function at runtime. Completely unrelated to async, Zig uses compile-time parameters to do stuff
by anonymoushn 4y ago
It's not accurate that there's only one representation of every function at runtime. Completely unrelated to async, Zig uses compile-time parameters to do stuff similar to templates which will get you lots and lots of monomorphized functions, and you obviously can't at runtime have a pointer to a function that takes a compile-time argument (just like you can't have a pointer to vector<T>::push, to which you would pass the type T at runtime, then a this pointer which is a vector<T>, then a T t to append, haha). Then, for async specifically, usually the way that the same function can be compiled to be async or non-async is for it to be generic over some other stuff at compile time and for that other stuff to either be async or not be async. For example, you could have a function that writes a websocket frame to a writer, and that function is async if the writer is async, but it's not async otherwise. But it's also specialized to each writer in other ways, so if you use it on a lot of types of writers, you'll get more than just 2 copies (one async and one non-async) of it in your compiled program.
In most programs you end up with all async functions or all non-async functions in a specific context, so if someone handed you a pointer you'd have no problem knowing which calling convention to use. But if you actually end up not knowing, because you really do have a mix of different types of pointers, you could call all of the pointers via the @asyncCall builtin. I have done this successfully, but I'm not actually sure whether it worked by having a pointer to a non-async function at runtime or by specializing the function that I was using that did not need to be async (because its body and callees did not use await or suspend) to use the async calling convention. Or you could use a tagged union of multiple function types and make your own `call` method for that union. This is pretty weird though, because you'll end up requiring the end user to provide storage for the async frame even when the function isn't async and doesn't actually need that storage.