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]