diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 7e88b58c0e29d..d85d892d5282e 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -3,8 +3,8 @@ use Position::*;
use rustc_ast as ast;
use rustc_ast::ptr::P;
-use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
+use rustc_ast::{token, BinOpKind, Mutability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
use rustc_expand::base::{self, *};
@@ -57,12 +57,10 @@ struct Context<'a, 'b> {
/// The latest consecutive literal strings, or empty if there weren't any.
literal: String,
- /// Collection of the compiled `rt::Argument` structures
- pieces: Vec
>,
+ /// Collection of the `cmds` for in the resulting `Arguments`
+ cmds: Vec
>,
/// Collection of string literals
- str_pieces: Vec
>,
- /// Stays `true` if all formatting parameters are default (as in "{}{}").
- all_pieces_simple: bool,
+ //str_pieces: Vec
>, XXX
/// Mapping between positional argument references and indices into the
/// final generated static argument array. We record the starting indices
@@ -95,10 +93,6 @@ struct Context<'a, 'b> {
/// Number of count slots assigned.
count_positions_count: usize,
- /// Current position of the implicit positional arg pointer, as if it
- /// still existed in this phase of processing.
- /// Used only for `all_pieces_simple` tracking in `build_piece`.
- curarg: usize,
/// Current piece being evaluated, used for error reporting.
curpiece: usize,
/// Keep track of invalid references to positional arguments.
@@ -368,7 +362,7 @@ impl<'a, 'b> Context<'a, 'b> {
let mut zero_based_note = false;
- let count = self.pieces.len()
+ let count = 70 // XXX self.pieces.len()
+ self.arg_with_formatting.iter().filter(|fmt| fmt.precision_span.is_some()).count();
if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
e = self.ecx.struct_span_err(
@@ -431,7 +425,7 @@ impl<'a, 'b> Context<'a, 'b> {
zero_based_note = true;
}
parse::CountIsParam(pos) => {
- let count = self.pieces.len()
+ let count = 70 // XXX self.pieces.len()
+ self
.arg_with_formatting
.iter()
@@ -606,58 +600,42 @@ impl<'a, 'b> Context<'a, 'b> {
self.count_args_index_offset = sofar;
}
- fn rtpath(ecx: &ExtCtxt<'_>, s: Symbol) -> Vec {
- ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s])
- }
-
- fn build_count(&self, c: parse::Count) -> P {
- let sp = self.macsp;
- let count = |c, arg| {
- let mut path = Context::rtpath(self.ecx, sym::Count);
- path.push(Ident::new(c, sp));
- match arg {
- Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
- None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
- }
- };
- match c {
- parse::CountIs(i) => count(sym::Is, Some(self.ecx.expr_usize(sp, i))),
- parse::CountIsParam(i) => {
- // This needs mapping too, as `i` is referring to a macro
- // argument. If `i` is not found in `count_positions` then
- // the error had already been emitted elsewhere.
- let i = self.count_positions.get(&i).cloned().unwrap_or(0)
- + self.count_args_index_offset;
- count(sym::Param, Some(self.ecx.expr_usize(sp, i)))
- }
- parse::CountImplied => count(sym::Implied, None),
- // should never be the case, names are already resolved
- parse::CountIsName(_) => panic!("should never happen"),
- }
+ fn rtpath(ecx: &ExtCtxt<'_>, sp: Span, s: Symbol) -> P {
+ ecx.expr_path(ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, s])))
}
/// Build a literal expression from the accumulated string literals
- fn build_literal_string(&mut self) -> P {
+ fn build_literal_string(&mut self) {
let sp = self.fmtsp;
- let s = Symbol::intern(&self.literal);
+ let string_length = self.ecx.expr_usize(sp, self.literal.len());
+ let string = self.ecx.expr_str(sp, Symbol::intern(&self.literal));
+ let string_as_ptr = self.ecx.expr_cast(
+ self.macsp,
+ string,
+ self.ecx.ty_ptr(
+ self.macsp,
+ self.ecx.ty_path(self.ecx.path_ident(self.macsp, Ident::new(sym::str, self.macsp))),
+ Mutability::Not,
+ ),
+ );
+ self.cmds.push(string_length);
+ self.cmds.push(string_as_ptr);
self.literal.clear();
- self.ecx.expr_str(sp, s)
}
/// Builds a static `rt::Argument` from a `parse::Piece` or append
/// to the `literal` string.
- fn build_piece(
- &mut self,
- piece: &parse::Piece<'a>,
- arg_index_consumed: &mut Vec,
- ) -> Option> {
+ fn build_piece(&mut self, piece: &parse::Piece<'a>, arg_index_consumed: &mut Vec) {
let sp = self.macsp;
match *piece {
parse::String(s) => {
self.literal.push_str(s);
- None
}
parse::NextArgument(ref arg) => {
+ if !self.literal.is_empty() {
+ self.build_literal_string();
+ }
+
// Build the position
let pos = {
match arg.position {
@@ -674,7 +652,12 @@ impl<'a, 'b> Context<'a, 'b> {
arg_idx
}
};
- self.ecx.expr_usize(sp, arg_idx)
+ self.ecx.expr_binary(
+ sp,
+ BinOpKind::Add,
+ Context::rtpath(self.ecx, sp, sym::CMD_ARG),
+ self.ecx.expr_usize(sp, arg_idx),
+ )
}
// should never be the case, because names are already
@@ -683,181 +666,170 @@ impl<'a, 'b> Context<'a, 'b> {
}
};
- let simple_arg = parse::Argument {
- position: {
- // We don't have ArgumentNext any more, so we have to
- // track the current argument ourselves.
- let i = self.curarg;
- self.curarg += 1;
- parse::ArgumentIs(i)
- },
- format: parse::FormatSpec {
- fill: arg.format.fill,
- align: parse::AlignUnknown,
- flags: 0,
- precision: parse::CountImplied,
- precision_span: None,
- width: parse::CountImplied,
- width_span: None,
- ty: arg.format.ty,
- ty_span: arg.format.ty_span,
- },
- };
-
- let fill = arg.format.fill.unwrap_or(' ');
-
- let pos_simple = arg.position.index() == simple_arg.position.index();
-
if arg.format.precision_span.is_some() || arg.format.width_span.is_some() {
self.arg_with_formatting.push(arg.format);
}
- if !pos_simple || arg.format != simple_arg.format || fill != ' ' {
- self.all_pieces_simple = false;
+
+ let fill = arg.format.fill.unwrap_or(' ');
+
+ if fill != ' ' {
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_FILL));
+ self.cmds.push(self.ecx.expr_usize(sp, fill as usize));
}
- // Build the format
- let fill = self.ecx.expr_lit(sp, ast::LitKind::Char(fill));
- let align = |name| {
- let mut p = Context::rtpath(self.ecx, sym::Alignment);
- p.push(Ident::new(name, sp));
- self.ecx.path_global(sp, p)
- };
let align = match arg.format.align {
- parse::AlignLeft => align(sym::Left),
- parse::AlignRight => align(sym::Right),
- parse::AlignCenter => align(sym::Center),
- parse::AlignUnknown => align(sym::Unknown),
+ parse::AlignLeft => 0,
+ parse::AlignRight => 1,
+ parse::AlignCenter => 2,
+ parse::AlignUnknown => 3,
};
- let align = self.ecx.expr_path(align);
- let flags = self.ecx.expr_u32(sp, arg.format.flags);
- let prec = self.build_count(arg.format.precision);
- let width = self.build_count(arg.format.width);
- let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, sym::FormatSpec));
- let fmt = self.ecx.expr_struct(
- sp,
- path,
- vec![
- self.ecx.field_imm(sp, Ident::new(sym::fill, sp), fill),
- self.ecx.field_imm(sp, Ident::new(sym::align, sp), align),
- self.ecx.field_imm(sp, Ident::new(sym::flags, sp), flags),
- self.ecx.field_imm(sp, Ident::new(sym::precision, sp), prec),
- self.ecx.field_imm(sp, Ident::new(sym::width, sp), width),
- ],
- );
- let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, sym::Argument));
- Some(self.ecx.expr_struct(
- sp,
- path,
- vec![
- self.ecx.field_imm(sp, Ident::new(sym::position, sp), pos),
- self.ecx.field_imm(sp, Ident::new(sym::format, sp), fmt),
- ],
- ))
+ if align != 3 || arg.format.flags != 0 {
+ let flags = align | (arg.format.flags as usize) << 2;
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_FLAGS));
+ self.cmds.push(self.ecx.expr_usize(sp, flags));
+ }
+
+ match arg.format.width {
+ parse::CountIs(n) => {
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_WIDTH));
+ self.cmds.push(self.ecx.expr_usize(sp, n));
+ }
+ parse::CountIsParam(i) => {
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_WIDTH_ARG));
+ let i = self.count_positions.get(&i).cloned().unwrap_or(0)
+ + self.count_args_index_offset;
+ self.cmds.push(self.ecx.expr_usize(sp, i));
+ }
+ parse::CountImplied => {}
+ parse::CountIsName(_) => unreachable!(),
+ }
+
+ match arg.format.precision {
+ parse::CountIs(n) => {
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_PRECISION));
+ self.cmds.push(self.ecx.expr_usize(sp, n));
+ }
+ parse::CountIsParam(i) => {
+ self.cmds.push(Context::rtpath(self.ecx, sp, sym::CMD_PRECISION_ARG));
+ let i = self.count_positions.get(&i).cloned().unwrap_or(0)
+ + self.count_args_index_offset;
+ self.cmds.push(self.ecx.expr_usize(sp, i));
+ }
+ parse::CountImplied => {}
+ parse::CountIsName(_) => unreachable!(),
+ }
+
+ self.cmds.push(pos);
}
}
}
/// Actually builds the expression which the format_args! block will be
/// expanded to.
- fn into_expr(self) -> P {
- let mut locals =
- Vec::with_capacity((0..self.args.len()).map(|i| self.arg_unique_types[i].len()).sum());
- let mut counts = Vec::with_capacity(self.count_args.len());
- let mut pats = Vec::with_capacity(self.args.len());
- let mut heads = Vec::with_capacity(self.args.len());
-
- let names_pos: Vec<_> = (0..self.args.len())
- .map(|i| Ident::from_str_and_span(&format!("arg{}", i), self.macsp))
- .collect();
-
- // First, build up the static array which will become our precompiled
- // format "string"
- let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces);
-
- // Before consuming the expressions, we have to remember spans for
- // count arguments as they are now generated separate from other
- // arguments, hence have no access to the `P`'s.
- let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect();
-
- // Right now there is a bug such that for the expression:
- // foo(bar(&1))
- // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
- // valid for the call to `foo`. To work around this all arguments to the
- // format! string are shoved into locals. Furthermore, we shove the address
- // of each variable because we don't want to move out of the arguments
- // passed to this function.
- for (i, e) in self.args.into_iter().enumerate() {
- let name = names_pos[i];
- let span = self.ecx.with_def_site_ctxt(e.span);
- pats.push(self.ecx.pat_ident(span, name));
- for arg_ty in self.arg_unique_types[i].iter() {
- locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, name));
+ fn into_expr(mut self) -> P {
+ let cmds_slice = {
+ let mut cmds = std::mem::take(&mut self.cmds);
+ cmds.push(self.ecx.expr_usize(self.macsp, 0));
+ let cmds = cmds.into_iter().map(|cmd| {
+ self.ecx.expr_cast(
+ self.macsp,
+ cmd,
+ self.ecx.ty_ptr(
+ self.macsp,
+ self.ecx.ty_path(self.ecx.path_ident(self.macsp, Ident::new(sym::u8, self.macsp))),
+ Mutability::Not,
+ ),
+ )
+ }).collect();
+ self.ecx.expr_vec_slice(self.macsp, cmds)
+ };
+
+ let args_slice = {
+ let mut locals = Vec::with_capacity(
+ (0..self.args.len()).map(|i| self.arg_unique_types[i].len()).sum(),
+ );
+ let mut counts = Vec::with_capacity(self.count_args.len());
+ let mut pats = Vec::with_capacity(self.args.len());
+ let mut heads = Vec::with_capacity(self.args.len());
+
+ let names_pos: Vec<_> = (0..self.args.len())
+ .map(|i| Ident::from_str_and_span(&format!("arg{}", i), self.macsp))
+ .collect();
+
+ // Before consuming the expressions, we have to remember spans for
+ // count arguments as they are now generated separate from other
+ // arguments, hence have no access to the `P`'s.
+ let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect();
+
+ // Right now there is a bug such that for the expression:
+ // foo(bar(&1))
+ // the lifetime of `1` doesn't outlast the call to `bar`, so it's not
+ // valid for the call to `foo`. To work around this all arguments to the
+ // format! string are shoved into locals. Furthermore, we shove the address
+ // of each variable because we don't want to move out of the arguments
+ // passed to this function.
+ for (i, e) in self.args.into_iter().enumerate() {
+ let name = names_pos[i];
+ let span = self.ecx.with_def_site_ctxt(e.span);
+ pats.push(self.ecx.pat_ident(span, name));
+ for arg_ty in self.arg_unique_types[i].iter() {
+ locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, name));
+ }
+ heads.push(self.ecx.expr_addr_of(e.span, e));
+ }
+ for pos in self.count_args {
+ let index = match pos {
+ Exact(i) => i,
+ _ => panic!("should never happen"),
+ };
+ let name = names_pos[index];
+ let span = spans_pos[index];
+ counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count, name));
}
- heads.push(self.ecx.expr_addr_of(e.span, e));
- }
- for pos in self.count_args {
- let index = match pos {
- Exact(i) => i,
- _ => panic!("should never happen"),
- };
- let name = names_pos[index];
- let span = spans_pos[index];
- counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count, name));
- }
- // Now create a vector containing all the arguments
- let args = locals.into_iter().chain(counts.into_iter());
-
- let args_array = self.ecx.expr_vec(self.macsp, args.collect());
-
- // Constructs an AST equivalent to:
- //
- // match (&arg0, &arg1) {
- // (tmp0, tmp1) => args_array
- // }
- //
- // It was:
- //
- // let tmp0 = &arg0;
- // let tmp1 = &arg1;
- // args_array
- //
- // Because of #11585 the new temporary lifetime rule, the enclosing
- // statements for these temporaries become the let's themselves.
- // If one or more of them are RefCell's, RefCell borrow() will also
- // end there; they don't last long enough for args_array to use them.
- // The match expression solves the scope problem.
- //
- // Note, it may also very well be transformed to:
- //
- // match arg0 {
- // ref tmp0 => {
- // match arg1 => {
- // ref tmp1 => args_array } } }
- //
- // But the nested match expression is proved to perform not as well
- // as series of let's; the first approach does.
- let pat = self.ecx.pat_tuple(self.macsp, pats);
- let arm = self.ecx.arm(self.macsp, pat, args_array);
- let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads));
- let result = self.ecx.expr_match(self.macsp, head, vec![arm]);
-
- let args_slice = self.ecx.expr_addr_of(self.macsp, result);
-
- // Now create the fmt::Arguments struct with all our locals we created.
- let (fn_name, fn_args) = if self.all_pieces_simple {
- ("new_v1", vec![pieces, args_slice])
- } else {
- // Build up the static array which will store our precompiled
- // nonstandard placeholders, if there are any.
- let fmt = self.ecx.expr_vec_slice(self.macsp, self.pieces);
+ // Now create a vector containing all the arguments
+ let args = locals.into_iter().chain(counts.into_iter());
- ("new_v1_formatted", vec![pieces, args_slice, fmt])
+ let args_array = self.ecx.expr_vec(self.macsp, args.collect());
+
+ // Constructs an AST equivalent to:
+ //
+ // match (&arg0, &arg1) {
+ // (tmp0, tmp1) => args_array
+ // }
+ //
+ // It was:
+ //
+ // let tmp0 = &arg0;
+ // let tmp1 = &arg1;
+ // args_array
+ //
+ // Because of #11585 the new temporary lifetime rule, the enclosing
+ // statements for these temporaries become the let's themselves.
+ // If one or more of them are RefCell's, RefCell borrow() will also
+ // end there; they don't last long enough for args_array to use them.
+ // The match expression solves the scope problem.
+ //
+ // Note, it may also very well be transformed to:
+ //
+ // match arg0 {
+ // ref tmp0 => {
+ // match arg1 => {
+ // ref tmp1 => args_array } } }
+ //
+ // But the nested match expression is proved to perform not as well
+ // as series of let's; the first approach does.
+ let pat = self.ecx.pat_tuple(self.macsp, pats);
+ let arm = self.ecx.arm(self.macsp, pat, args_array);
+ let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads));
+ let result = self.ecx.expr_match(self.macsp, head, vec![arm]);
+
+ self.ecx.expr_addr_of(self.macsp, result)
};
- let path = self.ecx.std_path(&[sym::fmt, sym::Arguments, Symbol::intern(fn_name)]);
- self.ecx.expr_call_global(self.macsp, path, fn_args)
+ let path = self.ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_static]);
+ self.ecx.expr_call_global(self.macsp, path, vec![cmds_slice, args_slice])
}
fn format_arg(
@@ -1013,7 +985,7 @@ pub fn expand_preparsed_format_args(
arg_types,
arg_unique_types,
names,
- curarg: 0,
+ //curarg: 0,
curpiece: 0,
arg_index_map: Vec::new(),
count_args: Vec::new(),
@@ -1021,9 +993,8 @@ pub fn expand_preparsed_format_args(
count_positions_count: 0,
count_args_index_offset: 0,
literal: String::new(),
- pieces: Vec::with_capacity(unverified_pieces.len()),
- str_pieces: Vec::with_capacity(unverified_pieces.len()),
- all_pieces_simple: true,
+ cmds: Vec::new(),
+ //pieces: Vec::with_capacity(unverified_pieces.len()),
macsp,
fmtsp: fmt_span,
invalid_refs: Vec::new(),
@@ -1052,16 +1023,11 @@ pub fn expand_preparsed_format_args(
let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()];
for piece in pieces {
- if let Some(piece) = cx.build_piece(&piece, &mut arg_index_consumed) {
- let s = cx.build_literal_string();
- cx.str_pieces.push(s);
- cx.pieces.push(piece);
- }
+ cx.build_piece(&piece, &mut arg_index_consumed);
}
if !cx.literal.is_empty() {
- let s = cx.build_literal_string();
- cx.str_pieces.push(s);
+ cx.build_literal_string();
}
if !cx.invalid_refs.is_empty() {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b2dac10c83fac..4bcd2538b8b48 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -131,6 +131,13 @@ symbols! {
BinaryHeap,
Borrow,
C,
+ CMD_ARG,
+ CMD_FILL,
+ CMD_FLAGS,
+ CMD_PRECISION,
+ CMD_PRECISION_ARG,
+ CMD_WIDTH,
+ CMD_WIDTH_ARG,
CString,
Center,
Clone,
@@ -776,6 +783,7 @@ symbols! {
never_type,
never_type_fallback,
new,
+ new_static,
new_unchecked,
next,
nll,
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index b760a54f08c76..948e69833f4eb 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -541,6 +541,11 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}
fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
+ if let Some(id) = self.span.ctxt().outer_expn_data().macro_def_id {
+ if fcx.tcx.is_diagnostic_item(sym::format_args, id) {
+ return;
+ }
+ }
let t_cast = self.cast_ty;
let t_expr = self.expr_ty;
let type_asc_or =
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 87042d95fbef0..760ea4bb30e64 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -4,18 +4,25 @@
use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
use crate::char::EscapeDebugExtArgs;
-use crate::iter;
use crate::marker::PhantomData;
use crate::mem;
use crate::num::flt2dec;
use crate::ops::Deref;
use crate::result;
+#[cfg(not(bootstrap))]
+use crate::slice;
use crate::str;
mod builders;
mod float;
mod num;
+#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+const HIGH_1: usize = usize::reverse_bits(0b1);
+
+#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+const HIGH_11: usize = usize::reverse_bits(0b11);
+
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
/// Possible alignments returned by `Formatter::align`
#[derive(Debug)]
@@ -36,9 +43,7 @@ pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
#[doc(hidden)]
-pub mod rt {
- pub mod v1;
-}
+pub mod rt;
/// The type returned by formatter methods.
///
@@ -307,11 +312,36 @@ enum FlagV1 {
}
impl<'a> Arguments<'a> {
+ #[doc(hidden)]
+ #[inline]
+ #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(not(bootstrap))]
+ pub fn new(cmds: &'a [*const u8], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
+ Arguments {
+ cmds: unsafe { mem::transmute(cmds.as_ptr()) },
+ args: unsafe { mem::transmute(args.as_ptr()) },
+ }
+ }
+
+ /// When using the format_args!() macro, this function is used to generate the
+ /// Arguments structure.
+ #[doc(hidden)]
+ #[inline]
+ #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(not(bootstrap))]
+ pub fn new_static(cmds: &'static [*const u8], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
+ Arguments {
+ cmds: unsafe { mem::transmute(cmds.as_ptr()) },
+ args: unsafe { mem::transmute(args.as_ptr()) },
+ }
+ }
+
/// When using the format_args!() macro, this function is used to generate the
/// Arguments structure.
#[doc(hidden)]
#[inline]
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(bootstrap)]
pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
Arguments { pieces, fmt: None, args }
}
@@ -325,6 +355,7 @@ impl<'a> Arguments<'a> {
#[doc(hidden)]
#[inline]
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(bootstrap)]
pub fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [ArgumentV1<'a>],
@@ -333,13 +364,10 @@ impl<'a> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
}
- /// Estimates the length of the formatted text.
- ///
- /// This is intended to be used for setting initial `String` capacity
- /// when using `format!`. Note: this is neither the lower nor upper bound.
#[doc(hidden)]
#[inline]
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(bootstrap)]
pub fn estimated_capacity(&self) -> usize {
let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum();
@@ -357,6 +385,52 @@ impl<'a> Arguments<'a> {
pieces_length.checked_mul(2).unwrap_or(0)
}
}
+
+ /// Estimates the length of the formatted text.
+ ///
+ /// This is intended to be used for setting initial `String` capacity
+ /// when using `format!`. Note: this is neither the lower nor upper bound.
+ #[doc(hidden)]
+ #[inline]
+ #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
+ #[cfg(not(bootstrap))]
+ pub fn estimated_capacity(&self) -> usize {
+ let mut i = 0;
+ let mut starts_with_str = false;
+ let mut str_len = 0;
+ let mut has_args = false;
+ loop {
+ let cmd = unsafe { *self.cmds.get_unchecked(i) } as usize;
+ if cmd == 0 {
+ break;
+ } else if cmd < HIGH_1 {
+ str_len += cmd;
+ if i == 0 {
+ starts_with_str = true;
+ }
+ i += 2;
+ } else if cmd < HIGH_11 {
+ i += 2;
+ } else {
+ has_args = true;
+ i += 1;
+ }
+ }
+
+ if !has_args {
+ str_len
+ } else if !starts_with_str && str_len < 16 {
+ // If the format string starts with an argument,
+ // don't preallocate anything, unless length
+ // of pieces is significant.
+ 0
+ } else {
+ // There are some arguments, so any additional push
+ // will reallocate the string. To avoid that,
+ // we're "pre-doubling" the capacity here.
+ str_len.checked_mul(2).unwrap_or(0)
+ }
+ }
}
/// This structure represents a safely precompiled version of a format string
@@ -385,16 +459,40 @@ impl<'a> Arguments<'a> {
#[derive(Copy, Clone)]
pub struct Arguments<'a> {
// Format string pieces to print.
+ #[cfg(bootstrap)]
pieces: &'a [&'static str],
// Placeholder specs, or `None` if all specs are default (as in "{}{}").
+ #[cfg(bootstrap)]
fmt: Option<&'a [rt::v1::Argument]>,
// Dynamic arguments for interpolation, to be interleaved with string
// pieces. (Every argument is preceded by a string piece.)
+ #[cfg(bootstrap)]
args: &'a [ArgumentV1<'a>],
+
+ // Only a pointer to the start of the array, without the length.
+ #[cfg(not(bootstrap))]
+ cmds: &'a [*const u8; 0],
+
+ // Only a pointer to the start of the array, without the length.
+ // The `cmds` array contains only valid indexes into this array.
+ #[cfg(not(bootstrap))]
+ args: &'a [ArgumentV1<'a>; 0],
}
+// cmds:
+// (in binary)
+// 00000000⋯00000000: end of instructions
+// 0nnnnnnn⋯nnnnnnnn: print string literal of length n. pointer to start of str in next usize
+// 10000000⋯00000000: set flags (incl. alignment) from next usize
+// 10000000⋯00000001: set filller from next usize
+// 10000000⋯00000010: set width from next usize
+// 10000000⋯00000011: set width from arg[next usize]
+// 10000000⋯00000100: set precision from next usize
+// 10000000⋯00000101: set precision from arg[next usize]
+// 110nnnnn⋯nnnnnnnn: print args[n] (and reset flags afterwards)
+
impl<'a> Arguments<'a> {
/// Get the formatted string, if it has no arguments to be formatted.
///
@@ -424,10 +522,33 @@ impl<'a> Arguments<'a> {
#[stable(feature = "fmt_as_str", since = "1.52.0")]
#[inline]
pub fn as_str(&self) -> Option<&'static str> {
- match (self.pieces, self.args) {
- ([], []) => Some(""),
- ([s], []) => Some(s),
- _ => None,
+ #[cfg(bootstrap)]
+ {
+ match (self.pieces, self.args) {
+ ([], []) => Some(""),
+ ([s], []) => Some(s),
+ _ => None,
+ }
+ }
+ #[cfg(not(bootstrap))]
+ {
+ let cmd = unsafe { *self.cmds.get_unchecked(0) } as usize;
+ if cmd == 0 {
+ // 'end' command: no formatting instructions, Arguments empty.
+ return Some("");
+ } else if cmd < usize::MAX / 2 {
+ // 'string literal' command. string pointer in next usize.
+ let len = cmd;
+ let ptr = unsafe { *self.cmds.get_unchecked(1) };
+ let cmd2 = unsafe { *self.cmds.get_unchecked(2) } as usize;
+ if cmd2 == 0 {
+ // next command is 'end' command. this Arguments is only a string literal.
+ return Some(unsafe {
+ str::from_utf8_unchecked(slice::from_raw_parts(ptr, len))
+ });
+ }
+ }
+ None
}
}
}
@@ -1075,6 +1196,89 @@ pub trait UpperExp {
/// [`write!`]: crate::write!
#[stable(feature = "rust1", since = "1.0.0")]
pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
+ write_(output, args)
+}
+
+#[cfg(not(bootstrap))]
+fn write_(output: &mut dyn Write, args: Arguments<'_>) -> Result {
+ let mut formatter = Formatter {
+ flags: 0,
+ width: None,
+ precision: None,
+ buf: output,
+ align: rt::v1::Alignment::Unknown,
+ fill: ' ',
+ };
+
+ let mut i = 0;
+
+ loop {
+ let cmd = unsafe { *args.cmds.get_unchecked(i) } as usize;
+
+ if cmd == 0 {
+ return Ok(());
+ } else if cmd < HIGH_1 {
+ let len = cmd;
+ let ptr = unsafe { *args.cmds.get_unchecked(i + 1) };
+ i += 2;
+ let s = unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)) };
+ formatter.buf.write_str(s)?;
+ } else if cmd < HIGH_11 {
+ let value = unsafe { *args.cmds.get_unchecked(i + 1) } as usize;
+ i += 2;
+ match cmd - HIGH_1 {
+ 0 => {
+ // set flags
+ formatter.flags = (value >> 2) as u32;
+ formatter.align = match value & 3 {
+ 0 => rt::v1::Alignment::Left,
+ 1 => rt::v1::Alignment::Right,
+ 2 => rt::v1::Alignment::Center,
+ _ => rt::v1::Alignment::Unknown,
+ };
+ }
+ 1 => {
+ // set filler
+ formatter.fill = unsafe { char::from_u32_unchecked(value as u32) };
+ }
+ 2 => {
+ // set width
+ formatter.width = Some(value);
+ }
+ 3 => {
+ // set width from arg
+ let value = unsafe { args.args.get_unchecked(value) };
+ formatter.width = value.as_usize();
+ }
+ 4 => {
+ // set precision
+ formatter.precision = Some(value);
+ }
+ 5 => {
+ // set precision from arg
+ let value = unsafe { args.args.get_unchecked(value) };
+ formatter.precision = value.as_usize();
+ }
+ _ => {}
+ }
+ } else {
+ let n = cmd - HIGH_11;
+ let arg = unsafe { args.args.get_unchecked(n) };
+ i += 1;
+ (arg.formatter)(arg.value, &mut formatter)?;
+ formatter.flags = 0;
+ formatter.width = None;
+ formatter.precision = None;
+ formatter.align = rt::v1::Alignment::Unknown;
+ formatter.fill = ' ';
+ }
+ }
+}
+
+#[cfg(bootstrap)]
+fn write_(output: &mut dyn Write, args: Arguments<'_>) -> Result {
+ use crate::iter;
+
let mut formatter = Formatter {
flags: 0,
width: None,
@@ -1116,6 +1320,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
Ok(())
}
+#[cfg(bootstrap)]
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result {
fmt.fill = arg.format.fill;
fmt.align = arg.format.align;
@@ -1137,6 +1342,7 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV
(value.formatter)(value.value, fmt)
}
+#[cfg(bootstrap)]
unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option {
match *cnt {
rt::v1::Count::Is(n) => Some(n),
diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs
new file mode 100644
index 0000000000000..a17e835eb03cb
--- /dev/null
+++ b/library/core/src/fmt/rt.rs
@@ -0,0 +1,9 @@
+pub mod v1;
+
+pub const CMD_ARG: usize = usize::reverse_bits(0b11);
+pub const CMD_FLAGS: usize = usize::reverse_bits(0b1);
+pub const CMD_FILL: usize = usize::reverse_bits(0b1) + 1;
+pub const CMD_WIDTH: usize = usize::reverse_bits(0b1) + 2;
+pub const CMD_WIDTH_ARG: usize = usize::reverse_bits(0b1) + 3;
+pub const CMD_PRECISION: usize = usize::reverse_bits(0b1) + 4;
+pub const CMD_PRECISION_ARG: usize = usize::reverse_bits(0b1) + 5;
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 5d9b0f80d3a6f..30f4539ef8e81 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -826,6 +826,7 @@ pub(crate) mod builtin {
#[allow_internal_unstable(fmt_internals)]
#[rustc_builtin_macro]
#[macro_export]
+ #[rustc_diagnostic_item = "format_args"]
macro_rules! format_args {
($fmt:expr) => {{ /* compiler built-in */ }};
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index 3e3e96fcd7f78..48562f0ab0d76 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -41,13 +41,18 @@ pub fn panic(expr: &'static str) -> ! {
super::intrinsics::abort()
}
- // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially
+ // Use Arguments::new instead of format_args!("{}", expr) to potentially
// reduce size overhead. The format_args! macro uses str's Display trait to
// write expr, which calls Formatter::pad, which must accommodate string
// truncation and padding (even though none is used here). Using
// Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
// output binary, saving up to a few kilobytes.
+
+ #[cfg(bootstrap)]
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
+
+ #[cfg(not(bootstrap))]
+ panic_fmt(fmt::Arguments::new(&[expr.len() as *const u8, expr.as_ptr(), 0 as *const u8], &[]));
}
#[inline]