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,165 @@ 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+ /// Special 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, unsafe {
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+ /// let __start = *__OFFSET.get_unchecked(__d);
271+ /// let __end = *__OFFSET.get_unchecked(__d + 1);
272+ /// __NAMES.get_unchecked(__start..__end)
273+ /// })
274+ /// }
275+ /// }
276+ /// ```
277+ fn show_fieldless_enum_concat_str (
278+ cx : & ExtCtxt < ' _ > ,
279+ span : Span ,
280+ def : & EnumDef ,
281+ ) -> Option < Box < ast:: Expr > > {
282+ let variant_count = def. variants . len ( ) ;
283+
284+ let variant_names = def
285+ . variants
286+ . iter ( )
287+ . map ( |v| v. disr_expr . is_none ( ) . then_some ( v. ident . name . as_str ( ) ) )
288+ . collect :: < Option < ThinVec < _ > > > ( ) ?;
289+
290+ let total_bytes: usize = variant_names. iter ( ) . map ( |n| n. len ( ) ) . sum ( ) ;
291+ let mut concatenated_names = String :: with_capacity ( total_bytes) ;
292+ let mut offset_indices = Vec :: with_capacity ( variant_names. len ( ) + 1 ) ;
293+ offset_indices. push ( 0 ) ;
294+
295+ for name in variant_names. iter ( ) {
296+ concatenated_names. push_str ( name) ;
297+ offset_indices. push ( concatenated_names. len ( ) ) ;
298+ }
299+
300+ // Create the constant concatenated string
301+ let names_ident = Ident :: from_str_and_span ( "__NAMES" , span) ;
302+ let str_ty = cx. ty (
303+ span,
304+ TyKind :: Ref (
305+ None ,
306+ ast:: MutTy {
307+ ty : cx. ty (
308+ span,
309+ TyKind :: Path ( None , ast:: Path :: from_ident ( Ident :: new ( sym:: str, span) ) ) ,
310+ ) ,
311+ mutbl : ast:: Mutability :: Not ,
312+ } ,
313+ ) ,
314+ ) ;
315+ let names_str_expr =
316+ ast:: ConstItemRhsKind :: new_body ( cx. expr_str ( span, Symbol :: intern ( & concatenated_names) ) ) ;
317+ let names_const_item = cx. item_const ( span, names_ident, str_ty, names_str_expr) ;
318+
319+ // Create the constant offset array
320+ let offset_ident = Ident :: from_str_and_span ( "__OFFSET" , span) ;
321+ let offset_index_exprs =
322+ offset_indices. iter ( ) . map ( |s| cx. expr_usize ( span, * s) ) . collect :: < ThinVec < _ > > ( ) ;
323+ let starts_array_expr =
324+ ast:: ConstItemRhsKind :: new_body ( cx. expr_array ( span, offset_index_exprs) ) ;
325+ let usize_ty =
326+ cx. ty ( span, TyKind :: Path ( None , ast:: Path :: from_ident ( Ident :: new ( sym:: usize, span) ) ) ) ;
327+ let offset_array_len_expr = cx. anon_const (
328+ span,
329+ ExprKind :: Lit ( token:: Lit :: new (
330+ token:: LitKind :: Integer ,
331+ Symbol :: intern ( & ( variant_count + 1 ) . to_string ( ) ) ,
332+ None ,
333+ ) ) ,
334+ ) ;
335+ let offset_const_item = cx. item_const (
336+ span,
337+ offset_ident,
338+ cx. ty ( span, TyKind :: Array ( usize_ty, offset_array_len_expr) ) ,
339+ starts_array_expr,
340+ ) ;
341+
342+ // let __d = ::core::intrinsics::discriminant_value(self) as usize;
343+ let discriminant_ident = Ident :: from_str_and_span ( "__d" , span) ;
344+ let discriminant_intrinsic_path = cx. std_path ( & [ sym:: intrinsics, sym:: discriminant_value] ) ;
345+ let discriminant_cast_expr = cx. expr (
346+ span,
347+ ast:: ExprKind :: Cast (
348+ cx. expr_call_global ( span, discriminant_intrinsic_path, thin_vec ! [ cx. expr_self( span) ] ) ,
349+ cx. ty_path ( ast:: Path :: from_ident ( Ident :: new ( sym:: usize, span) ) ) ,
350+ ) ,
351+ ) ;
352+ let discriminant_let_stmt =
353+ cx. stmt_let ( span, false , discriminant_ident, discriminant_cast_expr) ;
354+
355+ // __d & __d + 1 expressions
356+ let discriminant_expr = cx. expr_ident ( span, discriminant_ident) ;
357+ let discriminant_plus_one_expr = cx. expr_binary (
358+ span,
359+ ast:: BinOpKind :: Add ,
360+ discriminant_expr. clone ( ) ,
361+ cx. expr_usize ( span, 1 ) ,
362+ ) ;
363+
364+ // let __start = *__OFFSET.get_unchecked(__d);
365+ let start_ident = Ident :: from_str_and_span ( "__start" , span) ;
366+ let start_index_expr = cx. expr_method_call (
367+ span,
368+ cx. expr_ident ( span, offset_ident) ,
369+ Ident :: from_str_and_span ( "get_unchecked" , span) ,
370+ thin_vec ! [ discriminant_expr] ,
371+ ) ;
372+ let deref_start_index_expr = cx. expr_deref ( span, start_index_expr) ;
373+ let start_let_stmt = cx. stmt_let ( span, false , start_ident, deref_start_index_expr) ;
374+
375+ // let __end = *__OFFSET.get_unchecked(__d + 1);
376+ let end_ident = Ident :: from_str_and_span ( "__end" , span) ;
377+ let end_index_expr = cx. expr_method_call (
378+ span,
379+ cx. expr_ident ( span, offset_ident) ,
380+ Ident :: from_str_and_span ( "get_unchecked" , span) ,
381+ thin_vec ! [ discriminant_plus_one_expr] ,
382+ ) ;
383+ let deref_end_index_expr = cx. expr_deref ( span, end_index_expr) ;
384+ let end_let_stmt = cx. stmt_let ( span, false , end_ident, deref_end_index_expr) ;
385+
386+ // __start..__end
387+ let start_index_expr = cx. expr_ident ( span, start_ident) ;
388+ let end_index_expr = cx. expr_ident ( span, end_ident) ;
389+ let slice_range_expr = cx. expr (
390+ span,
391+ ExprKind :: Range (
392+ Some ( start_index_expr) ,
393+ Some ( end_index_expr) ,
394+ rustc_ast:: RangeLimits :: HalfOpen ,
395+ ) ,
396+ ) ;
397+
398+ // __NAMES.get_unchecked(__start..__end]
399+ let name_get_unchecked_call_expr = cx. expr_method_call (
400+ span,
401+ cx. expr_ident ( span, names_ident) ,
402+ Ident :: from_str_and_span ( "get_unchecked" , span) ,
403+ thin_vec ! [ slice_range_expr] ,
404+ ) ;
405+
406+ Some ( cx. expr_block ( Box :: new ( ast:: Block {
407+ stmts : thin_vec ! [
408+ cx. stmt_item( span, names_const_item) ,
409+ cx. stmt_item( span, offset_const_item) ,
410+ discriminant_let_stmt,
411+ start_let_stmt,
412+ end_let_stmt,
413+ cx. stmt_expr( name_get_unchecked_call_expr) ,
414+ ] ,
415+ id : ast:: DUMMY_NODE_ID ,
416+ rules : ast:: BlockCheckMode :: Unsafe ( ast:: CompilerGenerated ) ,
417+ span,
418+ tokens : None ,
419+ } ) ) )
420+ }
0 commit comments