Skip to content

Commit 788dded

Browse files
committed
Auto merge of #98190 - nnethercote:optimize-derive-Debug-code, r=scottmcm
Improve `derive(Debug)` r? `@ghost`
2 parents 7125846 + 20f0cda commit 788dded

File tree

10 files changed

+413
-233
lines changed

10 files changed

+413
-233
lines changed

compiler/rustc_builtin_macros/src/deriving/debug.rs

+117-88
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@ use crate::deriving::generic::*;
33
use crate::deriving::path_std;
44

55
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
6+
use rustc_ast::{self as ast, Expr, MetaItem};
77
use rustc_expand::base::{Annotatable, ExtCtxt};
8-
use rustc_span::symbol::{sym, Ident};
9-
use rustc_span::{Span, DUMMY_SP};
10-
11-
fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> {
12-
cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr))
13-
}
8+
use rustc_span::symbol::{sym, Ident, Symbol};
9+
use rustc_span::Span;
1410

1511
pub fn expand_deriving_debug(
1612
cx: &mut ExtCtxt<'_>,
@@ -49,11 +45,7 @@ pub fn expand_deriving_debug(
4945
trait_def.expand(cx, mitem, item, push)
5046
}
5147

52-
/// We use the debug builders to do the heavy lifting here
5348
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> {
54-
// build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
55-
// or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
56-
// based on the "shape".
5749
let (ident, vdata, fields) = match substr.fields {
5850
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
5951
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
@@ -67,93 +59,130 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
6759
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
6860
let fmt = substr.nonself_args[0].clone();
6961

70-
// Special fast path for unit variants. In the common case of an enum that is entirely unit
71-
// variants (i.e. a C-like enum), this fast path allows LLVM to eliminate the entire switch in
72-
// favor of a lookup table.
73-
if let ast::VariantData::Unit(..) = vdata {
74-
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
75-
let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
76-
let stmts = vec![cx.stmt_expr(expr)];
77-
let block = cx.block(span, stmts);
78-
return cx.expr_block(block);
79-
}
80-
81-
let builder = Ident::new(sym::debug_trait_builder, span);
82-
let builder_expr = cx.expr_ident(span, builder);
83-
84-
let mut stmts = Vec::with_capacity(fields.len() + 2);
85-
let fn_path_finish;
86-
match vdata {
62+
// Struct and tuples are similar enough that we use the same code for both,
63+
// with some extra pieces for structs due to the field names.
64+
let (is_struct, args_per_field) = match vdata {
8765
ast::VariantData::Unit(..) => {
88-
cx.span_bug(span, "unit variants should have been handled above");
66+
// Special fast path for unit variants.
67+
//let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
68+
//return cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
69+
assert!(fields.is_empty());
70+
(false, 0)
8971
}
90-
ast::VariantData::Tuple(..) => {
91-
// tuple struct/"normal" variant
92-
let fn_path_debug_tuple = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_tuple]);
93-
let expr = cx.expr_call_global(span, fn_path_debug_tuple, vec![fmt, name]);
94-
let expr = make_mut_borrow(cx, span, expr);
95-
stmts.push(cx.stmt_let(span, false, builder, expr));
96-
97-
for field in fields {
98-
// Use double indirection to make sure this works for unsized types
99-
let field = cx.expr_addr_of(field.span, field.self_.clone());
100-
let field = cx.expr_addr_of(field.span, field);
101-
102-
let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::field]);
103-
let expr =
104-
cx.expr_call_global(span, fn_path_field, vec![builder_expr.clone(), field]);
105-
106-
// Use `let _ = expr;` to avoid triggering the
107-
// unused_results lint.
108-
stmts.push(stmt_let_underscore(cx, span, expr));
109-
}
72+
ast::VariantData::Tuple(..) => (false, 1),
73+
ast::VariantData::Struct(..) => (true, 2),
74+
};
11075

111-
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
112-
}
113-
ast::VariantData::Struct(..) => {
114-
// normal struct/struct variant
115-
let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
116-
let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]);
117-
let expr = make_mut_borrow(cx, span, expr);
118-
stmts.push(cx.stmt_let(DUMMY_SP, false, builder, expr));
119-
120-
for field in fields {
76+
// The number of fields that can be handled without an array.
77+
const CUTOFF: usize = 5;
78+
79+
if fields.is_empty() {
80+
// Special case for no fields.
81+
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
82+
cx.expr_call_global(span, fn_path_write_str, vec![fmt, name])
83+
} else if fields.len() <= CUTOFF {
84+
// Few enough fields that we can use a specific-length method.
85+
let debug = if is_struct {
86+
format!("debug_struct_field{}_finish", fields.len())
87+
} else {
88+
format!("debug_tuple_field{}_finish", fields.len())
89+
};
90+
let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
91+
92+
let mut args = Vec::with_capacity(2 + fields.len() * args_per_field);
93+
args.extend([fmt, name]);
94+
for i in 0..fields.len() {
95+
let field = &fields[i];
96+
if is_struct {
12197
let name = cx.expr_lit(
12298
field.span,
12399
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
124100
);
125-
126-
// Use double indirection to make sure this works for unsized types
127-
let fn_path_field = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::field]);
128-
let field = cx.expr_addr_of(field.span, field.self_.clone());
129-
let field = cx.expr_addr_of(field.span, field);
130-
let expr = cx.expr_call_global(
131-
span,
132-
fn_path_field,
133-
vec![builder_expr.clone(), name, field],
134-
);
135-
stmts.push(stmt_let_underscore(cx, span, expr));
101+
args.push(name);
136102
}
137-
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
103+
// Use double indirection to make sure this works for unsized types
104+
let field = cx.expr_addr_of(field.span, field.self_.clone());
105+
let field = cx.expr_addr_of(field.span, field);
106+
args.push(field);
138107
}
139-
}
108+
cx.expr_call_global(span, fn_path_debug, args)
109+
} else {
110+
// Enough fields that we must use the any-length method.
111+
let mut name_exprs = Vec::with_capacity(fields.len());
112+
let mut value_exprs = Vec::with_capacity(fields.len());
113+
114+
for field in fields {
115+
if is_struct {
116+
name_exprs.push(cx.expr_lit(
117+
field.span,
118+
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
119+
));
120+
}
140121

141-
let expr = cx.expr_call_global(span, fn_path_finish, vec![builder_expr]);
122+
// Use double indirection to make sure this works for unsized types
123+
let value_ref = cx.expr_addr_of(field.span, field.self_.clone());
124+
value_exprs.push(cx.expr_addr_of(field.span, value_ref));
125+
}
142126

143-
stmts.push(cx.stmt_expr(expr));
144-
let block = cx.block(span, stmts);
145-
cx.expr_block(block)
146-
}
127+
// `let names: &'static _ = &["field1", "field2"];`
128+
let names_let = if is_struct {
129+
let lt_static = Some(cx.lifetime_static(span));
130+
let ty_static_ref =
131+
cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
132+
Some(cx.stmt_let_ty(
133+
span,
134+
false,
135+
Ident::new(sym::names, span),
136+
Some(ty_static_ref),
137+
cx.expr_array_ref(span, name_exprs),
138+
))
139+
} else {
140+
None
141+
};
142+
143+
// `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
144+
let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
145+
let ty_dyn_debug = cx.ty(
146+
span,
147+
ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn),
148+
);
149+
let ty_slice = cx.ty(
150+
span,
151+
ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)),
152+
);
153+
let values_let = cx.stmt_let_ty(
154+
span,
155+
false,
156+
Ident::new(sym::values, span),
157+
Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)),
158+
cx.expr_array_ref(span, value_exprs),
159+
);
160+
161+
// `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or
162+
// `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)`
163+
let sym_debug = if is_struct {
164+
sym::debug_struct_fields_finish
165+
} else {
166+
sym::debug_tuple_fields_finish
167+
};
168+
let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
169+
170+
let mut args = Vec::with_capacity(4);
171+
args.push(fmt);
172+
args.push(name);
173+
if is_struct {
174+
args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
175+
}
176+
args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
177+
let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
147178

148-
fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> ast::Stmt {
149-
let local = P(ast::Local {
150-
pat: cx.pat_wild(sp),
151-
ty: None,
152-
id: ast::DUMMY_NODE_ID,
153-
kind: LocalKind::Init(expr),
154-
span: sp,
155-
attrs: ast::AttrVec::new(),
156-
tokens: None,
157-
});
158-
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
179+
let mut stmts = Vec::with_capacity(3);
180+
if is_struct {
181+
stmts.push(names_let.unwrap());
182+
}
183+
stmts.push(values_let);
184+
stmts.push(cx.stmt_expr(expr));
185+
186+
cx.expr_block(cx.block(span, stmts))
187+
}
159188
}

compiler/rustc_builtin_macros/src/deriving/decodable.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,13 @@ fn decodable_substructure(
162162
cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms),
163163
);
164164
let lambda = cx.lambda(trait_span, vec![blkarg, variant], result);
165-
let variant_vec = cx.expr_vec(trait_span, variants);
166-
let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
165+
let variant_array_ref = cx.expr_array_ref(trait_span, variants);
167166
let fn_read_enum_variant_path: Vec<_> =
168167
cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]);
169168
let result = cx.expr_call_global(
170169
trait_span,
171170
fn_read_enum_variant_path,
172-
vec![blkdecoder, variant_vec, lambda],
171+
vec![blkdecoder, variant_array_ref, lambda],
173172
);
174173
let fn_read_enum_path: Vec<_> =
175174
cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]);

compiler/rustc_builtin_macros/src/format.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ impl<'a, 'b> Context<'a, 'b> {
776776

777777
// First, build up the static array which will become our precompiled
778778
// format "string"
779-
let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces);
779+
let pieces = self.ecx.expr_array_ref(self.fmtsp, self.str_pieces);
780780

781781
// We need to construct a &[ArgumentV1] to pass into the fmt::Arguments
782782
// constructor. In general the expressions in this slice might be
@@ -849,7 +849,7 @@ impl<'a, 'b> Context<'a, 'b> {
849849
fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg));
850850
}
851851

852-
let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
852+
let args_array = self.ecx.expr_array(self.macsp, fmt_args);
853853
let args_slice = self.ecx.expr_addr_of(
854854
self.macsp,
855855
if no_need_for_match {
@@ -879,7 +879,7 @@ impl<'a, 'b> Context<'a, 'b> {
879879
} else {
880880
// Build up the static array which will store our precompiled
881881
// nonstandard placeholders, if there are any.
882-
let fmt = self.ecx.expr_vec_slice(self.macsp, self.pieces);
882+
let fmt = self.ecx.expr_array_ref(self.macsp, self.pieces);
883883

884884
let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]);
885885
let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new());

compiler/rustc_builtin_macros/src/proc_macro_harness.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
317317
proc_macro_ty_method_path(cx, custom_derive),
318318
vec![
319319
cx.expr_str(span, cd.trait_name),
320-
cx.expr_vec_slice(
320+
cx.expr_array_ref(
321321
span,
322322
cd.attrs.iter().map(|&s| cx.expr_str(span, s)).collect::<Vec<_>>(),
323323
),
@@ -362,7 +362,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
362362
ast::Mutability::Not,
363363
),
364364
ast::Mutability::Not,
365-
cx.expr_vec_slice(span, decls),
365+
cx.expr_array_ref(span, decls),
366366
)
367367
.map(|mut i| {
368368
let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);

compiler/rustc_builtin_macros/src/test_harness.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
352352
debug!("building test vector from {} tests", cx.test_cases.len());
353353
let ecx = &cx.ext_cx;
354354

355-
ecx.expr_vec_slice(
355+
ecx.expr_array_ref(
356356
sp,
357357
cx.test_cases
358358
.iter()

compiler/rustc_expand/src/build.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> {
5757
P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
5858
}
5959

60+
pub fn ty_infer(&self, span: Span) -> P<ast::Ty> {
61+
self.ty(span, ast::TyKind::Infer)
62+
}
63+
6064
pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
6165
self.ty(path.span, ast::TyKind::Path(None, path))
6266
}
@@ -140,11 +144,26 @@ impl<'a> ExtCtxt<'a> {
140144
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
141145
}
142146

147+
pub fn lifetime_static(&self, span: Span) -> ast::Lifetime {
148+
self.lifetime(span, Ident::new(kw::StaticLifetime, span))
149+
}
150+
143151
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
144152
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
145153
}
146154

147155
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
156+
self.stmt_let_ty(sp, mutbl, ident, None, ex)
157+
}
158+
159+
pub fn stmt_let_ty(
160+
&self,
161+
sp: Span,
162+
mutbl: bool,
163+
ident: Ident,
164+
ty: Option<P<ast::Ty>>,
165+
ex: P<ast::Expr>,
166+
) -> ast::Stmt {
148167
let pat = if mutbl {
149168
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
150169
self.pat_ident_binding_mode(sp, ident, binding_mode)
@@ -153,7 +172,7 @@ impl<'a> ExtCtxt<'a> {
153172
};
154173
let local = P(ast::Local {
155174
pat,
156-
ty: None,
175+
ty,
157176
id: ast::DUMMY_NODE_ID,
158177
kind: LocalKind::Init(ex),
159178
span: sp,
@@ -315,12 +334,16 @@ impl<'a> ExtCtxt<'a> {
315334
self.expr_lit(sp, ast::LitKind::Bool(value))
316335
}
317336

318-
pub fn expr_vec(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
337+
/// `[expr1, expr2, ...]`
338+
pub fn expr_array(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
319339
self.expr(sp, ast::ExprKind::Array(exprs))
320340
}
321-
pub fn expr_vec_slice(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
322-
self.expr_addr_of(sp, self.expr_vec(sp, exprs))
341+
342+
/// `&[expr1, expr2, ...]`
343+
pub fn expr_array_ref(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
344+
self.expr_addr_of(sp, self.expr_array(sp, exprs))
323345
}
346+
324347
pub fn expr_str(&self, sp: Span, s: Symbol) -> P<ast::Expr> {
325348
self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked))
326349
}

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,10 @@ symbols! {
567567
debug_assert_ne_macro,
568568
debug_assertions,
569569
debug_struct,
570+
debug_struct_fields_finish,
570571
debug_trait_builder,
571572
debug_tuple,
573+
debug_tuple_fields_finish,
572574
debugger_visualizer,
573575
decl_macro,
574576
declare_lint_pass,

compiler/rustc_type_ir/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(fmt_helpers_for_derive)]
12
#![feature(min_specialization)]
23
#![feature(rustc_attrs)]
34

0 commit comments

Comments
 (0)