@@ -3,14 +3,10 @@ use crate::deriving::generic::*;
33use crate :: deriving:: path_std;
44
55use rustc_ast:: ptr:: P ;
6- use rustc_ast:: { self as ast, Expr , LocalKind , MetaItem } ;
6+ use rustc_ast:: { self as ast, Expr , MetaItem } ;
77use 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
1511pub 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
5348fn 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}
0 commit comments