-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
format_args!() could 'inline' literal arguments #78356
Comments
Do we expect this kind of thing to actually happen right now? It seems like we should if anything just lint against it, and the workaround for the single-arg-panic string could be to do |
would that actually work in |
If you're asking if anyone has time to implement this: Yes, I believe I can find the time for this some time soon.
|
Sorry, I'm asking if we expect that something like
Suggesting |
Ah. I think it mostly happens as the result of macro expansion. E.g.
That one requires a Anyway, I opened this issue just as a reminder to investigate the possibility, not because I think we should definitely do this. There are good ways to avoid the need for this like you mention ( |
My observations have consistently suggested that people use |
Hey @rust-lang/wg-const-eval, I'd like to hear your thoughts about the feasibility of this feature. Some examples of things it could do:
For const_panic specifically, 2 would be very useful to have Many of these I could implement by modifying how the So my question is: Can that be implemented without making things extremely complicated? Recognizing and reversing+rebuilding the |
The problem isn't format_args at all. We can make new_v1 const fn today, even new_v1_formatted. The problem is panic_fmt which wants to format the Arguments thing properly. Since at this point the integers or strings to be formatted are only available via rust/library/core/src/fmt/mod.rs Lines 261 to 264 in 6f69192
rust/library/core/src/fmt/mod.rs Lines 408 to 418 in 6f69192
This scheme does not scale to anything but panicking, because if the formatting arg is not a string (or another potentially hardcoded type like integers), then we have to abort with an error. Since we are already panicking, we can just continue and print something like "{unprintable}". |
I'd rather not add support for integers -- if we go down that route we will end up with a (hacky) const-side reimplementation of a subset of the formatting machinery. As much as possible, we should use Rust code for this, not special hacks in the interpreter engine. Given that the CTFE core engine already supports the entire display machinery (demonstrated by the fact that it works in Miri), we might alternatively poke a small hole into the const checks that allows executing formatting machinery from |
macro_rules! log_newline {
($($t:tt)*) => {
$crate::log_fmt(format_args!("log: {}\n", format_args!($($t)*)))
}
} Even if constant evluation could properly handle this nested format_args, it would not result in #[inline]
pub fn log_fmt(x: fmt::Arguments) {
if let Some(s) = x.as_str() {
log_str(s); // No allocation needed. The whole str is already available. \o/
} else {
log_str(&x.to_string());
}
} Ideally, Also, |
Expanding a bit on this idea I mentioned above:
If I understand correctly, getting the values of constants needs to happen at the mir level. So, this idea would mean, I think:
Then const_panic should just have the (Also, --pretty=expanded would get less noisy when using format args, because the macro would no longer expand directly to the full I guess this is in some ways similar to what happens for |
I am not sure I like the idea of making formatting a language primitive based on MIR primitives.
Indeed, and I view that as a rather strong negative.^^ |
I think the better comparison here are generators. We could expand generators on the HIR, it would just be a mess, so we do it on MIR where this is much more manageable. Formatting machinery stuff could be similar, but in any case, we could start with a minimal version that works as a HIR lowering similar to how for loops don't exist in HIR anymore. |
Would implementing this fix #87466? It would be great if this were done before |
@AaronKutch in the issue description
This doesn't really have anything to do with the edition, though. |
Slowly making my way through some of the tickets for const_panic while investigating stuff. I'm a bit confused why this is so controversial. Surely, at least inlining string literals wouldn't be adding so much code we could consider it identical to reimplementing all of fmt as MIR optimisations. I could definitely see making |
I can come up with a few ways that can make it possible to make The easiest ways will first make a) in const_check, somehow retrieve the trait from the second argument ( b) instead of using function pointers, use something else. |
|
You can't call |
That's on purpose, to avoid making that part of the stability guarantees for now. We have an unstable/internal |
2½ years later, I finally got around to implementing it: #106824 |
Opened #106823 to change the documentation of |
Would inlining literal arguments allow |
The inlining logic could play some role if we decide to allow format_args in const, but whether to allow format_args in const is an entirely separate discussion and decision. |
Flatten/inline format_args!() and (string and int) literal arguments into format_args!() Implements rust-lang#78356 Gated behind `-Zflatten-format-args=yes`. Part of rust-lang#99012 This change inlines string literals, integer literals and nested format_args!() into format_args!() during ast lowering, making all of the following pairs result in equivalent hir: ```rust println!("Hello, {}!", "World"); println!("Hello, World!"); ``` ```rust println!("[info] {}", format_args!("error")); println!("[info] error"); ``` ```rust println!("[{}] {}", status, format_args!("error: {}", msg)); println!("[{}] error: {}", status, msg); ``` ```rust println!("{} + {} = {}", 1, 2, 1 + 2); println!("1 + 2 = {}", 1 + 2); ``` And so on. This is useful for macros. E.g. a `log::info!()` macro could just pass the tokens from the user directly into a `format_args!()` that gets efficiently flattened/inlined into a `format_args!("info: {}")`. It also means that `dbg!(x)` will have its file, line, and expression name inlined: ```rust eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!(x), x); // before eprintln!("[example.rs:1] x = {:#?}", x); // after ``` Which can be nice in some cases, but also means a lot more unique static strings than before if dbg!() is used a lot.
This is now available on nightly with |
This is now implemented and enabled by default in Rust 1.71.0: #109999 |
Flatten/inline format_args!() and (string and int) literal arguments into format_args!() Implements rust-lang/rust#78356 Gated behind `-Zflatten-format-args=yes`. Part of #99012 This change inlines string literals, integer literals and nested format_args!() into format_args!() during ast lowering, making all of the following pairs result in equivalent hir: ```rust println!("Hello, {}!", "World"); println!("Hello, World!"); ``` ```rust println!("[info] {}", format_args!("error")); println!("[info] error"); ``` ```rust println!("[{}] {}", status, format_args!("error: {}", msg)); println!("[{}] error: {}", status, msg); ``` ```rust println!("{} + {} = {}", 1, 2, 1 + 2); println!("1 + 2 = {}", 1 + 2); ``` And so on. This is useful for macros. E.g. a `log::info!()` macro could just pass the tokens from the user directly into a `format_args!()` that gets efficiently flattened/inlined into a `format_args!("info: {}")`. It also means that `dbg!(x)` will have its file, line, and expression name inlined: ```rust eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!(x), x); // before eprintln!("[example.rs:1] x = {:#?}", x); // after ``` Which can be nice in some cases, but also means a lot more unique static strings than before if dbg!() is used a lot.
… r=lcnr Outline default query and hook provider function implementations The default query and hook provider functions call `bug!` with a decently long message. Due to argument inlining in `format_args!` ([`flatten_format_args`](rust-lang#78356)), this ends up duplicating the message for each query, adding ~90KB to `librustc_driver.so` of unreachable panic messages. To avoid this, we can outline the common `bug!` logic.
Rollup merge of rust-lang#124016 - DaniPopes:dedup-default-providers, r=lcnr Outline default query and hook provider function implementations The default query and hook provider functions call `bug!` with a decently long message. Due to argument inlining in `format_args!` ([`flatten_format_args`](rust-lang#78356)), this ends up duplicating the message for each query, adding ~90KB to `librustc_driver.so` of unreachable panic messages. To avoid this, we can outline the common `bug!` logic.
Arguments::as_str
allows code to handle argument-lessfmt::Arguments
likeformat_args!("literal")
specially by not pulling in any formatting code.We should investigate if we can improve
format_args!("hello: {}", "literal")
to result in the same trivialfmt::Arguments
asformat_args!("hello: literal")
, such that any.as_str()
-based optimizations are also available for it.That could also make things like
panic!("{}", "message");
work withconst_panic
, removing the need forpanic!(CONST_STR)
.This makes
panic!("hello")
exactly as cheap (and const) aspanic!("{}", "hello")
, which is also important for #78088 to make sure there are no downsides to its suggestions.It'd also reduce the need for using
concat!(..)
, since it'd no longer be less efficient to add a{}
placeholder to concat literals in a formatting string instead.As a bonus:
format_args!("a: {}", format_args!("b: {}", ..))
could maybe also improved to produce the same asformat_args!("a: b: {}", ..)
.Assigning to myself to investigate some time Soon™.
The text was updated successfully, but these errors were encountered: