diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 809f9838d20be..882bb5d6737bc 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -2,8 +2,8 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use ast::EnumDef; -use rustc_ast::{self as ast, MetaItem}; +use rustc_ast::{self as ast, token, EnumDef, ExprKind, MetaItem, TyKind}; + use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -152,15 +152,13 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug])); let ty_dyn_debug = cx.ty( span, - ast::TyKind::TraitObject( + TyKind::TraitObject( vec![cx.trait_bound(path_debug, false)], ast::TraitObjectSyntax::Dyn, ), ); - let ty_slice = cx.ty( - span, - ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)), - ); + let ty_slice = + cx.ty(span, TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not))); let values_let = cx.stmt_let_ty( span, false, @@ -216,6 +214,14 @@ fn show_fieldless_enum( substr: &Substructure<'_>, ) -> BlockOrExpr { let fmt = substr.nonselflike_args[0].clone(); + let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); + if let Some(name) = show_fieldless_enum_array(cx, span, def) { + return BlockOrExpr::new_expr(cx.expr_call_global( + span, + fn_path_write_str, + thin_vec![fmt, name], + )); + } let arms = def .variants .iter() @@ -236,6 +242,74 @@ fn show_fieldless_enum( }) .collect::>(); let name = cx.expr_match(span, cx.expr_self(span), arms); - let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name])) } + +/// Specialer case for fieldless enums with no discriminants. Builds +/// ```text +/// impl ::core::fmt::Debug for A { +/// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { +/// ::core::fmt::Formatter::write_str(f, { +/// const __NAMES: [&str; 3] = ["A", "B", "C"]; +/// __NAMES[::core::intrinsics::discriminant_value(self) as usize] +/// }) +/// } +/// } +/// ``` +fn show_fieldless_enum_array( + cx: &mut ExtCtxt<'_>, + span: Span, + def: &EnumDef, +) -> Option> { + if def.variants.len() >= cx.sess.target.pointer_width as usize { + return None; + } + let names = def + .variants + .iter() + .map(|v| if v.disr_expr.is_none() { Some(cx.expr_str(span, v.ident.name)) } else { None }) + .collect::>>()?; + let str_ty = cx.ty( + span, + TyKind::Ref( + None, + ast::MutTy { + ty: cx.ty( + span, + TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::str, span))), + ), + mutbl: ast::Mutability::Not, + }, + ), + ); + + let size = cx.anon_const( + span, + ExprKind::Lit(token::Lit::new( + token::LitKind::Integer, + Symbol::intern(&def.variants.len().to_string()), + None, + )), + ); + // Create a constant to prevent the array being stack-allocated + let arr_name = Ident::from_str_and_span("__NAMES", span); + let names_const = cx.item_const( + span, + arr_name, + cx.ty(span, TyKind::Array(str_ty, size)), + cx.expr_array(span, names), + ); + let discrim_value = cx.std_path(&[sym::intrinsics, sym::discriminant_value]); + + let index = cx.expr_cast( + span, + cx.expr_call_global(span, discrim_value, thin_vec![cx.expr_self(span)]), + cx.ty_path(ast::Path::from_ident(Ident::new(sym::usize, span))), + ); + let name = cx.expr(span, ExprKind::Index(cx.expr_ident(span, arr_name), index)); + Some( + cx.expr_block( + cx.block(span, thin_vec![cx.stmt_item(span, names_const), cx.stmt_expr(name)]), + ), + ) +} diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index 5bca83e87f878..3a452f41d00aa 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -990,10 +990,9 @@ impl ::core::marker::Copy for Fieldless { } impl ::core::fmt::Debug for Fieldless { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, - match self { - Fieldless::A => "A", - Fieldless::B => "B", - Fieldless::C => "C", + { + const __NAMES: [&str; 3] = ["A", "B", "C"]; + __NAMES[::core::intrinsics::discriminant_value(self) as usize] }) } }