1- use rustc_ast:: { self as ast, EnumDef , MetaItem , Safety } ;
1+ use rustc_ast:: { self as ast, EnumDef , ExprKind , MetaItem , Safety , TyKind , token } ;
22use rustc_expand:: base:: { Annotatable , ExtCtxt } ;
33use rustc_session:: config:: FmtDebug ;
44use rustc_span:: { Ident , Span , Symbol , sym} ;
@@ -166,15 +166,13 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
166166 let path_debug = cx. path_global ( span, cx. std_path ( & [ sym:: fmt, sym:: Debug ] ) ) ;
167167 let ty_dyn_debug = cx. ty (
168168 span,
169- ast :: TyKind :: TraitObject (
169+ TyKind :: TraitObject (
170170 vec ! [ cx. trait_bound( path_debug, false ) ] ,
171171 ast:: TraitObjectSyntax :: Dyn ,
172172 ) ,
173173 ) ;
174- let ty_slice = cx. ty (
175- span,
176- ast:: TyKind :: Slice ( cx. ty_ref ( span, ty_dyn_debug, None , ast:: Mutability :: Not ) ) ,
177- ) ;
174+ let ty_slice =
175+ cx. ty ( span, TyKind :: Slice ( cx. ty_ref ( span, ty_dyn_debug, None , ast:: Mutability :: Not ) ) ) ;
178176 let values_let = cx. stmt_let_ty (
179177 span,
180178 false ,
@@ -230,6 +228,14 @@ fn show_fieldless_enum(
230228 substr : & Substructure < ' _ > ,
231229) -> BlockOrExpr {
232230 let fmt = substr. nonselflike_args [ 0 ] . clone ( ) ;
231+ let fn_path_write_str = cx. std_path ( & [ sym:: fmt, sym:: Formatter , sym:: write_str] ) ;
232+ if let Some ( name) = show_fieldless_enum_concat_str ( cx, span, def) {
233+ return BlockOrExpr :: new_expr ( cx. expr_call_global (
234+ span,
235+ fn_path_write_str,
236+ thin_vec ! [ fmt, name] ,
237+ ) ) ;
238+ }
233239 let arms = def
234240 . variants
235241 . iter ( )
@@ -250,6 +256,142 @@ fn show_fieldless_enum(
250256 } )
251257 . collect :: < ThinVec < _ > > ( ) ;
252258 let name = cx. expr_match ( span, cx. expr_self ( span) , arms) ;
253- let fn_path_write_str = cx. std_path ( & [ sym:: fmt, sym:: Formatter , sym:: write_str] ) ;
254259 BlockOrExpr :: new_expr ( cx. expr_call_global ( span, fn_path_write_str, thin_vec ! [ fmt, name] ) )
255260}
261+
262+ /// Specialer case for fieldless enums with no discriminants. Builds
263+ /// ```text
264+ /// impl ::core::fmt::Debug for A {
265+ /// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
266+ /// ::core::fmt::Formatter::write_str(f, {
267+ /// const __NAMES: &str = "ABBBCC";
268+ /// const __OFFSET: [usize; 4] =[0, 1, 4, 6];
269+ /// let __d = ::core::intrinsics::discriminant_value(self) as usize;
270+ /// __NAMES[__OFFSET[d]..__OFFSET[d + 1]]
271+ /// })
272+ /// }
273+ /// }
274+ /// ```
275+ fn show_fieldless_enum_concat_str (
276+ cx : & ExtCtxt < ' _ > ,
277+ span : Span ,
278+ def : & EnumDef ,
279+ ) -> Option < Box < ast:: Expr > > {
280+ let variant_count = def. variants . len ( ) ;
281+ if variant_count >= cx. sess . target . pointer_width as usize {
282+ return None ;
283+ }
284+
285+ let variant_names = def
286+ . variants
287+ . iter ( )
288+ . map ( |v| v. disr_expr . is_none ( ) . then_some ( v. ident . name . as_str ( ) ) )
289+ . collect :: < Option < ThinVec < _ > > > ( ) ?;
290+
291+ let total_bytes: usize = variant_names. iter ( ) . map ( |n| n. len ( ) ) . sum ( ) ;
292+ let mut concatenated_names = String :: with_capacity ( total_bytes) ;
293+ let mut offset_indices = Vec :: with_capacity ( variant_names. len ( ) + 1 ) ;
294+ offset_indices. push ( 0 ) ;
295+
296+ for name in variant_names. iter ( ) {
297+ concatenated_names. push_str ( name) ;
298+ offset_indices. push ( concatenated_names. len ( ) ) ;
299+ }
300+
301+ // Create the constant concatenated string
302+ let names_ident = Ident :: from_str_and_span ( "__NAMES" , span) ;
303+ let str_ty = cx. ty (
304+ span,
305+ TyKind :: Ref (
306+ None ,
307+ ast:: MutTy {
308+ ty : cx. ty (
309+ span,
310+ TyKind :: Path ( None , ast:: Path :: from_ident ( Ident :: new ( sym:: str, span) ) ) ,
311+ ) ,
312+ mutbl : ast:: Mutability :: Not ,
313+ } ,
314+ ) ,
315+ ) ;
316+ let names_str_expr =
317+ ast:: ConstItemRhsKind :: new_body ( cx. expr_str ( span, Symbol :: intern ( & concatenated_names) ) ) ;
318+ let names_const_item = cx. item_const ( span, names_ident, str_ty, names_str_expr) ;
319+
320+ // Create the constant offset array
321+ let offset_ident = Ident :: from_str_and_span ( "__OFFSET" , span) ;
322+ let offset_index_exprs =
323+ offset_indices. iter ( ) . map ( |s| cx. expr_usize ( span, * s) ) . collect :: < ThinVec < _ > > ( ) ;
324+ let starts_array_expr =
325+ ast:: ConstItemRhsKind :: new_body ( cx. expr_array ( span, offset_index_exprs) ) ;
326+ let usize_ty =
327+ cx. ty ( span, TyKind :: Path ( None , ast:: Path :: from_ident ( Ident :: new ( sym:: usize, span) ) ) ) ;
328+ let offset_array_len_expr = cx. anon_const (
329+ span,
330+ ExprKind :: Lit ( token:: Lit :: new (
331+ token:: LitKind :: Integer ,
332+ Symbol :: intern ( & ( variant_count + 1 ) . to_string ( ) ) ,
333+ None ,
334+ ) ) ,
335+ ) ;
336+ let offset_const_item = cx. item_const (
337+ span,
338+ offset_ident,
339+ cx. ty ( span, TyKind :: Array ( usize_ty, offset_array_len_expr) ) ,
340+ starts_array_expr,
341+ ) ;
342+
343+ // let __d = ::core::intrinsics::discriminant_value(self) as usize;
344+ let discriminant_ident = Ident :: from_str_and_span ( "__d" , span) ;
345+ let discriminant_intrinsic_path = cx. std_path ( & [ sym:: intrinsics, sym:: discriminant_value] ) ;
346+ let discriminant_cast_expr = cx. expr (
347+ span,
348+ ast:: ExprKind :: Cast (
349+ cx. expr_call_global ( span, discriminant_intrinsic_path, thin_vec ! [ cx. expr_self( span) ] ) ,
350+ cx. ty_path ( ast:: Path :: from_ident ( Ident :: new ( sym:: usize, span) ) ) ,
351+ ) ,
352+ ) ;
353+ let discriminant_let_stmt =
354+ cx. stmt_let ( span, false , discriminant_ident, discriminant_cast_expr) ;
355+
356+ // __OFFSET[__d]
357+ let discriminant_expr = cx. expr_ident ( span, discriminant_ident) ;
358+ let start_index_expr = cx. expr (
359+ span,
360+ ExprKind :: Index ( cx. expr_ident ( span, offset_ident) , discriminant_expr. clone ( ) , span) ,
361+ ) ;
362+
363+ // __OFFSET[__d + 1]
364+ let one_expr = cx. expr_usize ( span, 1 ) ;
365+ let discriminant_plus_one_expr =
366+ cx. expr_binary ( span, ast:: BinOpKind :: Add , discriminant_expr, one_expr) ;
367+ let end_index_expr = cx. expr (
368+ span,
369+ ExprKind :: Index ( cx. expr_ident ( span, offset_ident) , discriminant_plus_one_expr, span) ,
370+ ) ;
371+
372+ // __OFFSET[__d]..__OFFSET[__d + 1]
373+ let slice_range_expr = cx. expr (
374+ span,
375+ ExprKind :: Range (
376+ Some ( start_index_expr) ,
377+ Some ( end_index_expr) ,
378+ rustc_ast:: RangeLimits :: HalfOpen ,
379+ ) ,
380+ ) ;
381+
382+ // &__NAMES[__STARTS[__d]..__STARTS[__d + 1]]
383+ let name_slice_expr = cx. expr_addr_of (
384+ span,
385+ cx. expr ( span, ExprKind :: Index ( cx. expr_ident ( span, names_ident) , slice_range_expr, span) ) ,
386+ ) ;
387+
388+ Some ( cx. expr_block ( cx. block (
389+ span,
390+ thin_vec ! [
391+ cx. stmt_item( span, names_const_item) ,
392+ cx. stmt_item( span, offset_const_item) ,
393+ discriminant_let_stmt,
394+ cx. stmt_expr( name_slice_expr)
395+ ] ,
396+ ) ) )
397+ }
0 commit comments