about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs205
-rw-r--r--compiler/rustc_expand/src/build.rs21
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/core/src/fmt/mod.rs228
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 ecf70da6d96..1fffd6f9727 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<Expr>) -> P<Expr> {
-    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<Expr> {
-    // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
-    // or fmt.debug_tuple(<name>).field(&<fieldval>)....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::Expr>) -> 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 17dae915397..1694a8865dd 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<ast::Ty> {
+        self.ty(span, ast::TyKind::Infer)
+    }
+
     pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
         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::Expr>) -> 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::Expr>) -> 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<P<ast::Ty>>,
+        ex: P<ast::Expr>,
+    ) -> 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 8a6941a4516..48766c67910 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 9e4a574818a..1d4be42b4a2 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.
     ///