From 7586e79af8771639e57c7d207b51fb9096e5359a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 22 Jun 2022 08:38:24 +1000 Subject: [PATCH 1/3] Rename some `ExtCtxt` methods. The new names are more accurate. Co-authored-by: Scott McMurray --- .../rustc_builtin_macros/src/deriving/decodable.rs | 5 ++--- compiler/rustc_builtin_macros/src/format.rs | 6 +++--- .../rustc_builtin_macros/src/proc_macro_harness.rs | 4 ++-- compiler/rustc_builtin_macros/src/test_harness.rs | 2 +- compiler/rustc_expand/src/build.rs | 10 +++++++--- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 1d892b20729d5..b39f35a9d4056 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -162,14 +162,13 @@ fn decodable_substructure( cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms), ); let lambda = cx.lambda(trait_span, vec![blkarg, variant], result); - let variant_vec = cx.expr_vec(trait_span, variants); - let variant_vec = cx.expr_addr_of(trait_span, variant_vec); + let variant_array_ref = cx.expr_array_ref(trait_span, variants); let fn_read_enum_variant_path: Vec<_> = cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]); let result = cx.expr_call_global( trait_span, fn_read_enum_variant_path, - vec![blkdecoder, variant_vec, lambda], + vec![blkdecoder, variant_array_ref, lambda], ); let fn_read_enum_path: Vec<_> = cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 10348c4967c7c..6c2ac34354441 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -776,7 +776,7 @@ impl<'a, 'b> Context<'a, 'b> { // 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); + let pieces = self.ecx.expr_array_ref(self.fmtsp, self.str_pieces); // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments // constructor. In general the expressions in this slice might be @@ -849,7 +849,7 @@ impl<'a, 'b> Context<'a, 'b> { fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg)); } - let args_array = self.ecx.expr_vec(self.macsp, fmt_args); + let args_array = self.ecx.expr_array(self.macsp, fmt_args); let args_slice = self.ecx.expr_addr_of( self.macsp, if no_need_for_match { @@ -879,7 +879,7 @@ impl<'a, 'b> Context<'a, 'b> { } 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); + let fmt = self.ecx.expr_array_ref(self.macsp, self.pieces); let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]); let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new()); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 03159d4395066..5cfda33491d5b 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -317,7 +317,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { proc_macro_ty_method_path(cx, custom_derive), vec![ cx.expr_str(span, cd.trait_name), - cx.expr_vec_slice( + cx.expr_array_ref( span, cd.attrs.iter().map(|&s| cx.expr_str(span, s)).collect::>(), ), @@ -362,7 +362,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { ast::Mutability::Not, ), ast::Mutability::Not, - cx.expr_vec_slice(span, decls), + cx.expr_array_ref(span, decls), ) .map(|mut i| { let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index db8dce804a31b..27f6ef5877a16 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -351,7 +351,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P { debug!("building test vector from {} tests", cx.test_cases.len()); let ecx = &cx.ext_cx; - ecx.expr_vec_slice( + ecx.expr_array_ref( sp, cx.test_cases .iter() diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e73c31c98fe32..17dae915397d6 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -315,12 +315,16 @@ impl<'a> ExtCtxt<'a> { self.expr_lit(sp, ast::LitKind::Bool(value)) } - pub fn expr_vec(&self, sp: Span, exprs: Vec>) -> P { + /// `[expr1, expr2, ...]` + pub fn expr_array(&self, sp: Span, exprs: Vec>) -> P { self.expr(sp, ast::ExprKind::Array(exprs)) } - pub fn expr_vec_slice(&self, sp: Span, exprs: Vec>) -> P { - self.expr_addr_of(sp, self.expr_vec(sp, exprs)) + + /// `&[expr1, expr2, ...]` + pub fn expr_array_ref(&self, sp: Span, exprs: Vec>) -> P { + self.expr_addr_of(sp, self.expr_array(sp, exprs)) } + pub fn expr_str(&self, sp: Span, s: Symbol) -> P { self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked)) } From 5b54363961c6adac7b2acc1aade81c0173797e52 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 17 Jun 2022 17:08:36 +1000 Subject: [PATCH 2/3] Optimize the code produced by `derive(Debug)`. This commit adds new methods that combine sequences of existing formatting methods. - `Formatter::debug_{tuple,struct}_field[12345]_finish`, equivalent to a `Formatter::debug_{tuple,struct}` + N x `Debug{Tuple,Struct}::field` + `Debug{Tuple,Struct}::finish` call sequence. - `Formatter::debug_{tuple,struct}_fields_finish` is similar, but can handle any number of fields by using arrays. These new methods are all marked as `doc(hidden)` and unstable. They are intended for the compiler's own use. Special-casing up to 5 fields gives significantly better performance results than always using arrays (as was tried in #95637). The commit also changes the `Debug` deriving code to use these new methods. For example, where the old `Debug` code for a struct with two fields would be like this: ``` fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self { Self { f1: ref __self_0_0, f2: ref __self_0_1, } => { let debug_trait_builder = &mut ::core::fmt::Formatter::debug_struct(f, "S2"); let _ = ::core::fmt::DebugStruct::field(debug_trait_builder, "f1", &&(*__self_0_0)); let _ = ::core::fmt::DebugStruct::field(debug_trait_builder, "f2", &&(*__self_0_1)); ::core::fmt::DebugStruct::finish(debug_trait_builder) } } } ``` the new code is like this: ``` fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self { Self { f1: ref __self_0_0, f2: ref __self_0_1, } => ::core::fmt::Formatter::debug_struct_field2_finish( f, "S2", "f1", &&(*__self_0_0), "f2", &&(*__self_0_1), ), } } ``` This shrinks the code produced for `Debug` instances considerably, reducing compile times and binary sizes. Co-authored-by: Scott McMurray --- .../src/deriving/debug.rs | 205 +++++++++------- compiler/rustc_expand/src/build.rs | 21 +- compiler/rustc_span/src/symbol.rs | 2 + library/core/src/fmt/mod.rs | 228 +++++++++++++++++- 4 files changed, 366 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index ecf70da6d96c5..1fffd6f9727d7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -3,14 +3,10 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, LocalKind, MetaItem}; +use rustc_ast::{self as ast, Expr, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; - -fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> P { - cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr)) -} +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; pub fn expand_deriving_debug( cx: &mut ExtCtxt<'_>, @@ -49,11 +45,7 @@ pub fn expand_deriving_debug( trait_def.expand(cx, mitem, item, push) } -/// We use the debug builders to do the heavy lifting here fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P { - // build fmt.debug_struct().field(, &)....build() - // or fmt.debug_tuple().field(&)....build() - // based on the "shape". let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), @@ -67,93 +59,130 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); let fmt = substr.nonself_args[0].clone(); - // Special fast path for unit variants. In the common case of an enum that is entirely unit - // variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in - // favor of a lookup table. - if let ast::VariantData::Unit(..) = vdata { - let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); - let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); - let stmts = vec![cx.stmt_expr(expr)]; - let block = cx.block(span, stmts); - return cx.expr_block(block); - } - - let builder = Ident::new(sym::debug_trait_builder, span); - let builder_expr = cx.expr_ident(span, builder); - - let mut stmts = Vec::with_capacity(fields.len() + 2); - let fn_path_finish; - match vdata { + // Struct and tuples are similar enough that we use the same code for both, + // with some extra pieces for structs due to the field names. + let (is_struct, args_per_field) = match vdata { ast::VariantData::Unit(..) => { - cx.span_bug(span, "unit variants should have been handled above"); + // Special fast path for unit variants. + //let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + //return cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); + assert!(fields.is_empty()); + (false, 0) } - ast::VariantData::Tuple(..) => { - // tuple struct/"normal" variant - let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]); - let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]); - let expr = make_mut_borrow(cx, span, expr); - stmts.push(cx.stmt_let(span, false, builder, expr)); - - for field in fields { - // Use double indirection to make sure this works for unsized types - let field = cx.expr_addr_of(field.span, field.self_.clone()); - let field = cx.expr_addr_of(field.span, field); - - let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]); - let expr = - cx.expr_call_global(span, fn_path_field, vec![builder_expr.clone(), field]); - - // Use `let _ = expr;` to avoid triggering the - // unused_results lint. - stmts.push(stmt_let_underscore(cx, span, expr)); - } + ast::VariantData::Tuple(..) => (false, 1), + ast::VariantData::Struct(..) => (true, 2), + }; - fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]); - } - ast::VariantData::Struct(..) => { - // normal struct/struct variant - let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]); - let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]); - let expr = make_mut_borrow(cx, span, expr); - stmts.push(cx.stmt_let(DUMMY_SP, false, builder, expr)); - - for field in fields { + // The number of fields that can be handled without an array. + const CUTOFF: usize = 5; + + if fields.is_empty() { + // Special case for no fields. + let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]) + } else if fields.len() <= CUTOFF { + // Few enough fields that we can use a specific-length method. + let debug = if is_struct { + format!("debug_struct_field{}_finish", fields.len()) + } else { + format!("debug_tuple_field{}_finish", fields.len()) + }; + let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]); + + let mut args = Vec::with_capacity(2 + fields.len() * args_per_field); + args.extend([fmt, name]); + for i in 0..fields.len() { + let field = &fields[i]; + if is_struct { let name = cx.expr_lit( field.span, ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), ); - - // Use double indirection to make sure this works for unsized types - let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]); - let field = cx.expr_addr_of(field.span, field.self_.clone()); - let field = cx.expr_addr_of(field.span, field); - let expr = cx.expr_call_global( - span, - fn_path_field, - vec![builder_expr.clone(), name, field], - ); - stmts.push(stmt_let_underscore(cx, span, expr)); + args.push(name); } - fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]); + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + args.push(field); } - } + cx.expr_call_global(span, fn_path_debug, args) + } else { + // Enough fields that we must use the any-length method. + let mut name_exprs = Vec::with_capacity(fields.len()); + let mut value_exprs = Vec::with_capacity(fields.len()); + + for field in fields { + if is_struct { + name_exprs.push(cx.expr_lit( + field.span, + ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked), + )); + } - let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_expr]); + // Use double indirection to make sure this works for unsized types + let value_ref = cx.expr_addr_of(field.span, field.self_.clone()); + value_exprs.push(cx.expr_addr_of(field.span, value_ref)); + } - stmts.push(cx.stmt_expr(expr)); - let block = cx.block(span, stmts); - cx.expr_block(block) -} + // `let names: &'static _ = &["field1", "field2"];` + let names_let = if is_struct { + let lt_static = Some(cx.lifetime_static(span)); + let ty_static_ref = + cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not); + Some(cx.stmt_let_ty( + span, + false, + Ident::new(sym::names, span), + Some(ty_static_ref), + cx.expr_array_ref(span, name_exprs), + )) + } else { + None + }; + + // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];` + let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug])); + let ty_dyn_debug = cx.ty( + span, + ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn), + ); + let ty_slice = cx.ty( + span, + ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)), + ); + let values_let = cx.stmt_let_ty( + span, + false, + Ident::new(sym::values, span), + Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)), + cx.expr_array_ref(span, value_exprs), + ); + + // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or + // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)` + let sym_debug = if is_struct { + sym::debug_struct_fields_finish + } else { + sym::debug_tuple_fields_finish + }; + let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]); + + let mut args = Vec::with_capacity(4); + args.push(fmt); + args.push(name); + if is_struct { + args.push(cx.expr_ident(span, Ident::new(sym::names, span))); + } + args.push(cx.expr_ident(span, Ident::new(sym::values, span))); + let expr = cx.expr_call_global(span, fn_path_debug_internal, args); -fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> ast::Stmt { - let local = P(ast::Local { - pat: cx.pat_wild(sp), - ty: None, - id: ast::DUMMY_NODE_ID, - kind: LocalKind::Init(expr), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, - }); - ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp } + let mut stmts = Vec::with_capacity(3); + if is_struct { + stmts.push(names_let.unwrap()); + } + stmts.push(values_let); + stmts.push(cx.stmt_expr(expr)); + + cx.expr_block(cx.block(span, stmts)) + } } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 17dae915397d6..1694a8865dde2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> { P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None }) } + pub fn ty_infer(&self, span: Span) -> P { + self.ty(span, ast::TyKind::Infer) + } + pub fn ty_path(&self, path: ast::Path) -> P { self.ty(path.span, ast::TyKind::Path(None, path)) } @@ -140,11 +144,26 @@ impl<'a> ExtCtxt<'a> { ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) } } + pub fn lifetime_static(&self, span: Span) -> ast::Lifetime { + self.lifetime(span, Ident::new(kw::StaticLifetime, span)) + } + pub fn stmt_expr(&self, expr: P) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } } pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P) -> ast::Stmt { + self.stmt_let_ty(sp, mutbl, ident, None, ex) + } + + pub fn stmt_let_ty( + &self, + sp: Span, + mutbl: bool, + ident: Ident, + ty: Option>, + ex: P, + ) -> ast::Stmt { let pat = if mutbl { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); self.pat_ident_binding_mode(sp, ident, binding_mode) @@ -153,7 +172,7 @@ impl<'a> ExtCtxt<'a> { }; let local = P(ast::Local { pat, - ty: None, + ty, id: ast::DUMMY_NODE_ID, kind: LocalKind::Init(ex), span: sp, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8a6941a451621..48766c6791096 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -567,8 +567,10 @@ symbols! { debug_assert_ne_macro, debug_assertions, debug_struct, + debug_struct_fields_finish, debug_trait_builder, debug_tuple, + debug_tuple_fields_finish, debugger_visualizer, decl_macro, declare_lint_pass, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 9e4a574818a14..1d4be42b4a213 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -4,6 +4,7 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell}; use crate::char::EscapeDebugExtArgs; +use crate::iter; use crate::marker::PhantomData; use crate::mem; use crate::num::fmt as numfmt; @@ -693,7 +694,7 @@ pub(crate) mod macros { /// Derive macro generating an impl of the trait `Debug`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] - #[allow_internal_unstable(core_intrinsics)] + #[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)] pub macro Debug($item:item) { /* compiler built-in */ } @@ -1964,6 +1965,129 @@ impl<'a> Formatter<'a> { builders::debug_struct_new(self, name) } + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_struct_fields_finish` is more general, but this is faster for 1 field. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_field1_finish<'b>( + &'b mut self, + name: &str, + name1: &str, + value1: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_struct_new(self, name); + builder.field(name1, value1); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_struct_fields_finish` is more general, but this is faster for 2 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_field2_finish<'b>( + &'b mut self, + name: &str, + name1: &str, + value1: &dyn Debug, + name2: &str, + value2: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_struct_new(self, name); + builder.field(name1, value1); + builder.field(name2, value2); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_struct_fields_finish` is more general, but this is faster for 3 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_field3_finish<'b>( + &'b mut self, + name: &str, + name1: &str, + value1: &dyn Debug, + name2: &str, + value2: &dyn Debug, + name3: &str, + value3: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_struct_new(self, name); + builder.field(name1, value1); + builder.field(name2, value2); + builder.field(name3, value3); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_struct_fields_finish` is more general, but this is faster for 4 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_field4_finish<'b>( + &'b mut self, + name: &str, + name1: &str, + value1: &dyn Debug, + name2: &str, + value2: &dyn Debug, + name3: &str, + value3: &dyn Debug, + name4: &str, + value4: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_struct_new(self, name); + builder.field(name1, value1); + builder.field(name2, value2); + builder.field(name3, value3); + builder.field(name4, value4); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_struct_fields_finish` is more general, but this is faster for 5 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_field5_finish<'b>( + &'b mut self, + name: &str, + name1: &str, + value1: &dyn Debug, + name2: &str, + value2: &dyn Debug, + name3: &str, + value3: &dyn Debug, + name4: &str, + value4: &dyn Debug, + name5: &str, + value5: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_struct_new(self, name); + builder.field(name1, value1); + builder.field(name2, value2); + builder.field(name3, value3); + builder.field(name4, value4); + builder.field(name5, value5); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// For the cases not covered by `debug_struct_field[12345]_finish`. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_struct_fields_finish<'b>( + &'b mut self, + name: &str, + names: &[&str], + values: &[&dyn Debug], + ) -> Result { + assert_eq!(names.len(), values.len()); + let mut builder = builders::debug_struct_new(self, name); + for (name, value) in iter::zip(names, values) { + builder.field(name, value); + } + builder.finish() + } + /// Creates a `DebugTuple` builder designed to assist with creation of /// `fmt::Debug` implementations for tuple structs. /// @@ -1995,6 +2119,108 @@ impl<'a> Formatter<'a> { builders::debug_tuple_new(self, name) } + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_tuple_fields_finish` is more general, but this is faster for 1 field. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_field1_finish<'b>(&'b mut self, name: &str, value1: &dyn Debug) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + builder.field(value1); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_tuple_fields_finish` is more general, but this is faster for 2 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_field2_finish<'b>( + &'b mut self, + name: &str, + value1: &dyn Debug, + value2: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + builder.field(value1); + builder.field(value2); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_tuple_fields_finish` is more general, but this is faster for 3 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_field3_finish<'b>( + &'b mut self, + name: &str, + value1: &dyn Debug, + value2: &dyn Debug, + value3: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + builder.field(value1); + builder.field(value2); + builder.field(value3); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_tuple_fields_finish` is more general, but this is faster for 4 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_field4_finish<'b>( + &'b mut self, + name: &str, + value1: &dyn Debug, + value2: &dyn Debug, + value3: &dyn Debug, + value4: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + builder.field(value1); + builder.field(value2); + builder.field(value3); + builder.field(value4); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// `debug_tuple_fields_finish` is more general, but this is faster for 5 fields. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_field5_finish<'b>( + &'b mut self, + name: &str, + value1: &dyn Debug, + value2: &dyn Debug, + value3: &dyn Debug, + value4: &dyn Debug, + value5: &dyn Debug, + ) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + builder.field(value1); + builder.field(value2); + builder.field(value3); + builder.field(value4); + builder.field(value5); + builder.finish() + } + + /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// For the cases not covered by `debug_tuple_field[12345]_finish`. + #[doc(hidden)] + #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] + pub fn debug_tuple_fields_finish<'b>( + &'b mut self, + name: &str, + values: &[&dyn Debug], + ) -> Result { + let mut builder = builders::debug_tuple_new(self, name); + for value in values { + builder.field(value); + } + builder.finish() + } + /// Creates a `DebugList` builder designed to assist with creation of /// `fmt::Debug` implementations for list-like structures. /// From 20f0cdaa6356c6a6562ad735adccb51d9e25aae8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 21 Jun 2022 14:53:28 +1000 Subject: [PATCH 3/3] Rewrite `TyKind::fmt`. The handwritten versions more compact and easier to read than the derived version. --- compiler/rustc_type_ir/src/lib.rs | 1 + compiler/rustc_type_ir/src/sty.rs | 162 ++++++------------------------ 2 files changed, 32 insertions(+), 131 deletions(-) diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index cb1602816ae5e..f9708d6d9195f 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(fmt_helpers_for_derive)] #![feature(min_specialization)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 5cd2901324a2e..74737e30bb4d3 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -554,137 +554,37 @@ impl hash::Hash for TyKind { // This is manually implemented because a derive would require `I: Debug` impl fmt::Debug for TyKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match (&*self,) { - (&Bool,) => fmt::Formatter::write_str(f, "Bool"), - (&Char,) => fmt::Formatter::write_str(f, "Char"), - (&Int(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Int"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Uint(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Uint"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Float(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Float"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Adt(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Adt"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Foreign(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Foreign"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Str,) => fmt::Formatter::write_str(f, "Str"), - (&Array(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Array"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Slice(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Slice"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&RawPtr(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "RawPtr"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Ref(ref __self_0, ref __self_1, ref __self_2),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Ref"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_2); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&FnDef(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "FnDef"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&FnPtr(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "FnPtr"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Dynamic(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Dynamic"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Closure(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Closure"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Generator(ref __self_0, ref __self_1, ref __self_2),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Generator"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_2); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&GeneratorWitness(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "GeneratorWitness"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Never,) => fmt::Formatter::write_str(f, "Never"), - (&Tuple(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Tuple"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Projection(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Projection"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Opaque(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Opaque"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Param(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Param"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Bound(ref __self_0, ref __self_1),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Bound"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_1); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Placeholder(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Placeholder"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Infer(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Infer"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } - (&Error(ref __self_0),) => { - let debug_trait_builder = &mut fmt::Formatter::debug_tuple(f, "Error"); - let _ = fmt::DebugTuple::field(debug_trait_builder, &__self_0); - fmt::DebugTuple::finish(debug_trait_builder) - } + use std::fmt::*; + match self { + Bool => Formatter::write_str(f, "Bool"), + Char => Formatter::write_str(f, "Char"), + Int(f0) => Formatter::debug_tuple_field1_finish(f, "Int", f0), + Uint(f0) => Formatter::debug_tuple_field1_finish(f, "Uint", f0), + Float(f0) => Formatter::debug_tuple_field1_finish(f, "Float", f0), + Adt(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Adt", f0, f1), + Foreign(f0) => Formatter::debug_tuple_field1_finish(f, "Foreign", f0), + Str => Formatter::write_str(f, "Str"), + Array(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Array", f0, f1), + Slice(f0) => Formatter::debug_tuple_field1_finish(f, "Slice", f0), + RawPtr(f0) => Formatter::debug_tuple_field1_finish(f, "RawPtr", f0), + Ref(f0, f1, f2) => Formatter::debug_tuple_field3_finish(f, "Ref", f0, f1, f2), + FnDef(f0, f1) => Formatter::debug_tuple_field2_finish(f, "FnDef", f0, f1), + FnPtr(f0) => Formatter::debug_tuple_field1_finish(f, "FnPtr", f0), + Dynamic(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Dynamic", f0, f1), + Closure(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Closure", f0, f1), + Generator(f0, f1, f2) => { + Formatter::debug_tuple_field3_finish(f, "Generator", f0, f1, f2) + } + GeneratorWitness(f0) => Formatter::debug_tuple_field1_finish(f, "GeneratorWitness", f0), + Never => Formatter::write_str(f, "Never"), + Tuple(f0) => Formatter::debug_tuple_field1_finish(f, "Tuple", f0), + Projection(f0) => Formatter::debug_tuple_field1_finish(f, "Projection", f0), + Opaque(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Opaque", f0, f1), + Param(f0) => Formatter::debug_tuple_field1_finish(f, "Param", f0), + Bound(f0, f1) => Formatter::debug_tuple_field2_finish(f, "Bound", f0, f1), + Placeholder(f0) => Formatter::debug_tuple_field1_finish(f, "Placeholder", f0), + Infer(f0) => Formatter::debug_tuple_field1_finish(f, "Infer", f0), + TyKind::Error(f0) => Formatter::debug_tuple_field1_finish(f, "Error", f0), } } }