-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
std.fmt.format — with generators #4059
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
Conversation
1560e8a
to
7e50a2e
Compare
I stumbled upon an elegant solution to the recursion problem. By applying My only concern is that this can easily bloat the generated code, since every recursion step would potentially be its own function (now that max_depth is comptime). |
13c73e9
to
bc47a89
Compare
bc47a89
to
42a47eb
Compare
b07d6bb
to
24e43df
Compare
The new atoi equivalent is ~16x more assembly instructions than existing code. Perf tuning experiments over in https://github.com/fengb/generator-bench Edit: https://godbolt.org/z/_Zve-- seems to indicate ~35 instructions to setup the generator and ~8 instructions per suspend/resume. All of the extra instructions generated by a simple suspend are optimized out with Edit x5: Rust unstable generators have a very similar optimization problem: https://godbolt.org/z/pzsaCZ |
Ran into a footgun. The generator currently checks after a Unfortunately, there's a separate case that I hadn't considered: an actual async function. This is re-exposing the ugly color problem, and to solve it there needs to be some advanced heuristics to differentiate between the generator suspend and the async execution suspend. fn iter(generator: *Generator([]const u8), intermission: anyframe->void) void {
suspend generator.yield(@frame(), "Hello");
// this is a non-yielding suspend
_ = await intermission;
suspend generator.yield(@frame(), "World");
} If we continue with the current behavior, any await/suspend would also terminate the generator. This is kludgy and gross and exhibits spooky action at a distance (e.g. some function somewhere calls os.read() and the generator stops running), but it seems to be at least consistently reproducible. If we want to allow async generators, I have a few ideas:
|
Regression in this implementation — const std = @import("std");
comptime {
var buf: [100]u8 = undefined;
@compileError(std.fmtgen.bufPrint(&buf, "hello {}", .{12}) catch unreachable);
} We'll need either comptime async or real generators, which sounds like a lot more work. |
48cb2bb
to
1c6d6d2
Compare
Encountered too many design flaws. And this usecase is now obsoleted by #4404 |
🌹 R.I.P. 🌹 |
Experiment to convert
std.fmt.format
into an iterator with userland generators. This pattern is a little rougher than I'd like, but it does work (as long as you don't hit async bugs). The biggest benefit here is the ability to seamlessly context dumping providing a nice iterator interface over the old errorset callback style.Ideally, I would like to be able to return generators, but embedding both the async frame and the yield location seems to be impossible at the moment (requires
@resultLocation()
), and even then requiring generators be comptime generic removes a lot of composability.TODO list:
std.fmt.format
(blocked by Async recursion with comptime arguments causes compiler assertion failure #4096)std.fmt: support vector formatting #3904Add 'u' specifier to std.format #3970Discovered a way to remove the manual recursion stack, thus these following problems no longer exist!
FixedBufferAllocator.free
— need to enable freeing the last allocation for this recursion to work properly"??OOM??"
looks silly