@@ -3,14 +3,10 @@ use crate::deriving::generic::*;
3
3
use crate::deriving::path_std;
4
4
5
5
use rustc_ast::ptr::P;
6
- use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
6
+ use rustc_ast::{self as ast, Expr, MetaItem};
7
7
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;
14
10
15
11
pub fn expand_deriving_debug(
16
12
cx: &mut ExtCtxt<'_>,
@@ -49,11 +45,7 @@ pub fn expand_deriving_debug(
49
45
trait_def.expand(cx, mitem, item, push)
50
46
}
51
47
52
- /// We use the debug builders to do the heavy lifting here
53
48
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".
57
49
let (ident, vdata, fields) = match substr.fields {
58
50
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
59
51
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
@@ -67,93 +59,130 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
67
59
let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked));
68
60
let fmt = substr.nonself_args[0].clone();
69
61
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 {
87
65
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)
89
71
}
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
+ };
110
75
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 {
121
97
let name = cx.expr_lit(
122
98
field.span,
123
99
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
124
100
);
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);
136
102
}
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);
138
107
}
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
+ }
140
121
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
+ }
142
126
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);
147
178
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
+ }
159
188
}
0 commit comments