@@ -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