Skip to content

Commit 5a131e3

Browse files
committed
Auto merge of #155452 - makai410:enum-debug-array, r=<try>
Implement `Debug` for C-like enums with a concatenated string
2 parents e9e32ac + bf398d3 commit 5a131e3

3 files changed

Lines changed: 180 additions & 11 deletions

File tree

compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_ast::{self as ast, EnumDef, MetaItem, Safety};
1+
use rustc_ast::{self as ast, EnumDef, ExprKind, MetaItem, Safety, TyKind, token};
22
use rustc_expand::base::{Annotatable, ExtCtxt};
33
use rustc_session::config::FmtDebug;
44
use 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+
}

tests/ui/deriving/deriving-all-codegen.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ enum Fieldless {
154154
C,
155155
}
156156

157+
// A C-like, fieldless enum with variants of varying name lengths.
158+
#[derive(Debug)]
159+
enum Fieldless0 {
160+
A,
161+
BBB,
162+
CC,
163+
}
164+
157165
// An enum with multiple fieldless and fielded variants.
158166
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
159167
enum Mixed {

tests/ui/deriving/deriving-all-codegen.stdout

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,10 +1236,12 @@ impl ::core::fmt::Debug for Fieldless {
12361236
#[inline]
12371237
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
12381238
::core::fmt::Formatter::write_str(f,
1239-
match self {
1240-
Fieldless::A => "A",
1241-
Fieldless::B => "B",
1242-
Fieldless::C => "C",
1239+
{
1240+
const __NAMES: &str = "ABC";
1241+
const __OFFSET: [usize; 4] = [0usize, 1usize, 2usize, 3usize];
1242+
let __d =
1243+
::core::intrinsics::discriminant_value(self) as usize;
1244+
&__NAMES[__OFFSET[__d]..__OFFSET[__d + 1usize]]
12431245
})
12441246
}
12451247
}
@@ -1294,6 +1296,23 @@ impl ::core::cmp::Ord for Fieldless {
12941296
}
12951297
}
12961298

1299+
// A C-like, fieldless enum with variants of varying name lengths.
1300+
enum Fieldless0 { A, BBB, CC, }
1301+
#[automatically_derived]
1302+
impl ::core::fmt::Debug for Fieldless0 {
1303+
#[inline]
1304+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
1305+
::core::fmt::Formatter::write_str(f,
1306+
{
1307+
const __NAMES: &str = "ABBBCC";
1308+
const __OFFSET: [usize; 4] = [0usize, 1usize, 4usize, 6usize];
1309+
let __d =
1310+
::core::intrinsics::discriminant_value(self) as usize;
1311+
&__NAMES[__OFFSET[__d]..__OFFSET[__d + 1usize]]
1312+
})
1313+
}
1314+
}
1315+
12971316
// An enum with multiple fieldless and fielded variants.
12981317
enum Mixed {
12991318

0 commit comments

Comments
 (0)