Skip to content

Commit

Permalink
Auto merge of rust-lang#131663 - veera-sivarajan:fix-128709-final, r=…
Browse files Browse the repository at this point in the history
…<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
  • Loading branch information
bors committed Oct 13, 2024
2 parents 3678036 + 7638733 commit ed038de
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 19 deletions.
27 changes: 25 additions & 2 deletions compiler/rustc_ast_lowering/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_ast::visit::Visitor;
use rustc_ast::*;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_session::config::FmtDebug;
use rustc_span::symbol::{Ident, kw};
use rustc_span::{Span, Symbol, sym};
Expand All @@ -24,6 +25,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
expand_format_args(self, sp, &fmt, allow_const)
}

/// Wraps given `ExprKind` in an inline const block.
///
/// Caller must ensure it's safe and sound to do so.
fn wrap_in_const_context(
&mut self,
sp: Span,
kind: hir::ExprKind<'hir>,
) -> hir::ExprKind<'hir> {
let expr = hir::Expr { hir_id: self.next_id(), kind, span: self.lower_span(sp) };
let const_node_id = self.next_node_id();
let parent_def_id = self.current_def_id_parent;
let def_id =
self.create_def(parent_def_id, const_node_id, kw::Empty, DefKind::InlineConst, sp);
let hir_id = self.lower_node_id(const_node_id);
let const_block = self.with_new_scopes(sp, |this| hir::ConstBlock {
def_id,
hir_id,
body: this.with_def_id_parent(def_id, |this| this.lower_body(|_| (&[], expr))),
});
hir::ExprKind::ConstBlock(const_block)
}

/// Try to convert a literal into an interned string
fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
match LitKind::from_token_lit(lit) {
Expand Down Expand Up @@ -464,14 +487,14 @@ fn expand_format_args<'hir>(

if allow_const && arguments.is_empty() && argmap.is_empty() {
// Generate:
// <core::fmt::Arguments>::new_const(lit_pieces)
// const { <core::fmt::Arguments>::new_const(lit_pieces) }
let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatArguments,
sym::new_const,
));
let new_args = ctx.arena.alloc_from_iter([lit_pieces]);
return hir::ExprKind::Call(new, new_args);
return ctx.wrap_in_const_context(macsp, hir::ExprKind::Call(new, new_args));
}

// If the args array contains exactly all the original arguments once,
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ pub struct Arguments<'a> {
#[unstable(feature = "fmt_internals", issue = "none")]
impl<'a> Arguments<'a> {
#[inline]
#[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")]
#[rustc_const_stable(feature = "const_fmt_arguments_new", since = "CURRENT_RUSTC_VERSION")]
pub const fn new_const<const N: usize>(pieces: &'a [&'static str; N]) -> Self {
const { assert!(N <= 1) };
Arguments { pieces, fmt: None, args: &[] }
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@
#![feature(const_char_encode_utf16)]
#![feature(const_eval_select)]
#![feature(const_exact_div)]
#![feature(const_fmt_arguments_new)]
#![feature(const_hash)]
#![feature(const_heap)]
#![feature(const_index_range_slice_index)]
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ pub(crate) mod builtin {
///
/// This macro will be removed once `format_args` is allowed in const contexts.
#[unstable(feature = "const_format_args", issue = "none")]
#[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)]
#[allow_internal_unstable(fmt_internals)]
#[rustc_builtin_macro]
#[macro_export]
macro_rules! const_format_args {
Expand Down
17 changes: 17 additions & 0 deletions tests/codegen/issues/issue-128709-format-without-args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//@ compile-flags: -C no-prepopulate-passes -C opt-level=0

#![crate_type = "lib"]

// String formating macros without any arguments should compile
// to a `memcpy` followed by a call to a library function.

#[no_mangle]
pub fn code() {
// CHECK-LABEL: @code
// CHECK-NOT: getelementptr
// CHECK-NOT: store
// CHECK-NOT: ; call core::fmt::Arguments::new_const
// CHECK: call void @llvm.memcpy
// CHECK-NEXT: ; call std::io::stdio::_print
println!("hello world");
}
1 change: 0 additions & 1 deletion tests/ui/borrowck/issue-64453.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ struct Value;

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

fn from_string(_: String) -> Value {
Value
Expand Down
13 changes: 2 additions & 11 deletions tests/ui/borrowck/issue-64453.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
error: `Arguments::<'a>::new_const` is not yet stable as a const fn
--> $DIR/issue-64453.rs:4:31
|
LL | static settings_dir: String = format!("");
| ^^^^^^^^^^^
|
= help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable
= 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)

error[E0015]: cannot call non-const fn `format` in statics
--> $DIR/issue-64453.rs:4:31
|
Expand All @@ -18,7 +9,7 @@ LL | static settings_dir: String = format!("");
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)

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

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0015, E0507.
For more information about an error, try `rustc --explain E0015`.
5 changes: 5 additions & 0 deletions tests/ui/consts/const-format-arguments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub fn main() {
const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
//~^ use of unstable library feature
//~| temporary value dropped while borrowed
}
23 changes: 23 additions & 0 deletions tests/ui/consts/const-format-arguments.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0658]: use of unstable library feature 'fmt_internals'
--> $DIR/const-format-arguments.rs:3:36
|
LL | const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(fmt_internals)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0716]: temporary value dropped while borrowed
--> $DIR/const-format-arguments.rs:3:68
|
LL | const A: std::fmt::Arguments = std::fmt::Arguments::new_const(&[&"hola"]);
| --------------------------------^^^^^^^^^-
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary value which is freed while still in use
| using this value as a constant requires that borrow lasts for `'static`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0658, E0716.
For more information about an error, try `rustc --explain E0658`.
1 change: 0 additions & 1 deletion tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//@ compile-flags: -Znext-solver
#![allow(incomplete_features)]
#![feature(const_fmt_arguments_new)]
#![feature(const_trait_impl, effects)]

#[const_trait]
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0015]: cannot call non-const fn `_print` in constant functions
--> $DIR/issue-79450.rs:11:9
--> $DIR/issue-79450.rs:10:9
|
LL | println!("lul");
| ^^^^^^^^^^^^^^^
Expand Down

0 comments on commit ed038de

Please sign in to comment.