A function which contains a suspend point becomes implicitly async. An await is implicitly a suspend until the Frame completes, so it also makes the caller's function async. Calling a async function without the `async` keyword is implicitly `await (async f(args))` so same deal.
You ended up not needing to know if `f()` is async or not when you call it ("effectively colorless"). If it is, it just makes you async and bubbles up until the sync boundary (an `async f()` call, `main()`, or an `export fn` C API).
Zig's stdlib could handle the main() case and spin up an event loop if you requested via `io_mode = .evented`. Some stdlib stuff like net/fs would then use the event loop while others like os/Thread would stay the same if you still wanted to do sync stuff.
This was the common case, but async is a lang construct not an stdlib construct so it works for all targets. For example, you could use it in wasm, the kernel, etc. You would just need to write your own starting / scheduling of the frames, known generically as an "event loop".