Skip to content

Commit ed038de

Browse files
committed
Auto merge of rust-lang#131663 - veera-sivarajan:fix-128709-final, r=<try>
Evaluate `std::fmt::Arguments::new_const()` during Compile Time Fixes rust-lang#128709 This PR aims to optimize calls to string formating macros without any arguments by evaluating `std::fmt::Arguments::new_const()` in a const context. Currently, `println!("hola")` compiles to `std::io::_print(std::fmt::Arguments::new_const(&["hola\n"]))`. With this PR, `println!("hola")` compiles to `std::io::_print(const { std::fmt::Arguments::new_const(&["hola\n"]) })`. This is accomplished in two steps: 1. Const stabilize `std::fmt::Arguments::new_const()`. 2. Wrap calls to `std::fmt::Arguments::new_const()` in an inline const block when lowering the AST to HIR. This reduces the generated code to a `memcpy` instead of multiple `getelementptr` and `store` instructions even with `-C no-prepopulate-passes -C opt-level=0`. Godbolt for code comparison: https://rust.godbolt.org/z/P7Px7de6c This is a safe and sound transformation because `std::fmt::Arguments::new_const()` is a trivial constructor function taking a slice containing a `'static` string literal as input. CC rust-lang#99012
2 parents 3678036 + 7638733 commit ed038de

File tree

11 files changed

+75
-19
lines changed

11 files changed

+75
-19
lines changed

compiler/rustc_ast_lowering/src/format.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_ast::visit::Visitor;
55
use rustc_ast::*;
66
use rustc_data_structures::fx::FxIndexMap;
77
use rustc_hir as hir;
8+
use rustc_hir::def::DefKind;
89
use rustc_session::config::FmtDebug;
910
use rustc_span::symbol::{Ident, kw};
1011
use rustc_span::{Span, Symbol, sym};
@@ -24,6 +25,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
2425
expand_format_args(self, sp, &fmt, allow_const)
2526
}
2627

28+
/// Wraps given `ExprKind` in an inline const block.
29+
///
30+
/// Caller must ensure it's safe and sound to do so.
31+
fn wrap_in_const_context(
32+
&mut self,
33+
sp: Span,
34+
kind: hir::ExprKind<'hir>,
35+
) -> hir::ExprKind<'hir> {
36+
let expr = hir::Expr { hir_id: self.next_id(), kind, span: self.lower_span(sp) };
37+
let const_node_id = self.next_node_id();
38+
let parent_def_id = self.current_def_id_parent;
39+
let def_id =
40+
self.create_def(parent_def_id, const_node_id, kw::Empty, DefKind::InlineConst, sp);
41+
let hir_id = self.lower_node_id(const_node_id);
42+
let const_block = self.with_new_scopes(sp, |this| hir::ConstBlock {
43+
def_id,
44+
hir_id,
45+
body: this.with_def_id_parent(def_id, |this| this.lower_body(|_| (&[], expr))),
46+
});
47+
hir::ExprKind::ConstBlock(const_block)
48+
}
49+
2750
/// Try to convert a literal into an interned string
2851
fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
2952
match LitKind::from_token_lit(lit) {
@@ -464,14 +487,14 @@ fn expand_format_args<'hir>(
464487

465488
if allow_const && arguments.is_empty() && argmap.is_empty() {
466489
// Generate:
467-
// <core::fmt::Arguments>::new_const(lit_pieces)
490+
// const { <core::fmt::Arguments>::new_const(lit_pieces) }
468491
let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
469492
macsp,
470493
hir::LangItem::FormatArguments,
471494
sym::new_const,
472495
));
473496
let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
474-
return hir::ExprKind::Call(new, new_args);
497+
return ctx.wrap_in_const_context(macsp, hir::ExprKind::Call(new, new_args));
475498
}
476499

477500
// If the args array contains exactly all the original arguments once,

library/core/src/fmt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ pub struct Arguments<'a> {
333333
#[unstable(feature = "fmt_internals", issue = "none")]
334334
impl<'a> Arguments<'a> {
335335
#[inline]
336-
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
336+
#[rustc_const_stable(feature = "const_fmt_arguments_new", since = "CURRENT_RUSTC_VERSION")]
337337
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
338338
const { assert!(N <= 1) };
339339
Arguments { pieces, fmt: None, args: &[] }

library/core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,6 @@
120120
#![feature(const_char_encode_utf16)]
121121
#![feature(const_eval_select)]
122122
#![feature(const_exact_div)]
123-
#![feature(const_fmt_arguments_new)]
124123
#![feature(const_hash)]
125124
#![feature(const_heap)]
126125
#![feature(const_index_range_slice_index)]

library/core/src/macros/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ pub(crate) mod builtin {
10381038
///
10391039
/// This macro will be removed once `format_args` is allowed in const contexts.
10401040
#[unstable(feature = "const_format_args", issue = "none")]
1041-
#[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)]
1041+
#[allow_internal_unstable(fmt_internals)]
10421042
#[rustc_builtin_macro]
10431043
#[macro_export]
10441044
macro_rules! const_format_args {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ compile-flags: -C no-prepopulate-passes -C opt-level=0
2+
3+
#![crate_type = "lib"]
4+
5+
// String formating macros without any arguments should compile
6+
// to a `memcpy` followed by a call to a library function.
7+
8+
#[no_mangle]
9+
pub fn code() {
10+
// CHECK-LABEL: @code
11+
// CHECK-NOT: getelementptr
12+
// CHECK-NOT: store
13+
// CHECK-NOT: ; call core::fmt::Arguments::new_const
14+
// CHECK: call void @llvm.memcpy
15+
// CHECK-NEXT: ; call std::io::stdio::_print
16+
println!("hello world");
17+
}

tests/ui/borrowck/issue-64453.rs

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ struct Value;
33

44
static settings_dir: String = format!("");
55
//~^ ERROR cannot call non-const fn
6-
//~| ERROR is not yet stable as a const
76

87
fn from_string(_: String) -> Value {
98
Value

tests/ui/borrowck/issue-64453.stderr

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
error: `Arguments::<'a>::new_const` is not yet stable as a const fn
2-
--> $DIR/issue-64453.rs:4:31
3-
|
4-
LL | static settings_dir: String = format!("");
5-
| ^^^^^^^^^^^
6-
|
7-
= help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable
8-
= note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
9-
101
error[E0015]: cannot call non-const fn `format` in statics
112
--> $DIR/issue-64453.rs:4:31
123
|
@@ -18,7 +9,7 @@ LL | static settings_dir: String = format!("");
189
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
1910

2011
error[E0507]: cannot move out of static item `settings_dir`
21-
--> $DIR/issue-64453.rs:14:37
12+
--> $DIR/issue-64453.rs:13:37
2213
|
2314
LL | let settings_data = from_string(settings_dir);
2415
| ^^^^^^^^^^^^ move occurs because `settings_dir` has type `String`, which does not implement the `Copy` trait
@@ -28,7 +19,7 @@ help: consider cloning the value if the performance cost is acceptable
2819
LL | let settings_data = from_string(settings_dir.clone());
2920
| ++++++++
3021

31-
error: aborting due to 3 previous errors
22+
error: aborting due to 2 previous errors
3223

3324
Some errors have detailed explanations: E0015, E0507.
3425
For more information about an error, try `rustc --explain E0015`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub fn main() {
2+
const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
3+
//~^ use of unstable library feature
4+
//~| temporary value dropped while borrowed
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0658]: use of unstable library feature 'fmt_internals'
2+
--> $DIR/const-format-arguments.rs:3:36
3+
|
4+
LL | const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: add `#![feature(fmt_internals)]` to the crate attributes to enable
8+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
9+
10+
error[E0716]: temporary value dropped while borrowed
11+
--> $DIR/const-format-arguments.rs:3:68
12+
|
13+
LL | const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
14+
| --------------------------------^^^^^^^^^-
15+
| | | |
16+
| | | temporary value is freed at the end of this statement
17+
| | creates a temporary value which is freed while still in use
18+
| using this value as a constant requires that borrow lasts for `'static`
19+
20+
error: aborting due to 2 previous errors
21+
22+
Some errors have detailed explanations: E0658, E0716.
23+
For more information about an error, try `rustc --explain E0658`.

tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//@ compile-flags: -Znext-solver
22
#![allow(incomplete_features)]
3-
#![feature(const_fmt_arguments_new)]
43
#![feature(const_trait_impl, effects)]
54

65
#[const_trait]

tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0015]: cannot call non-const fn `_print` in constant functions
2-
--> $DIR/issue-79450.rs:11:9
2+
--> $DIR/issue-79450.rs:10:9
33
|
44
LL | println!("lul");
55
| ^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)