Skip to content

Commit 7348f26

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 8da2d28 + b16703b commit 7348f26

3 files changed

Lines changed: 207 additions & 11 deletions

File tree

compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 172 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,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+
}

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: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,10 +1236,14 @@ 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+
unsafe {
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+
let __start = *__OFFSET.get_unchecked(__d);
1245+
let __end = *__OFFSET.get_unchecked(__d + 1usize);
1246+
__NAMES.get_unchecked(__start..__end)
12431247
})
12441248
}
12451249
}
@@ -1294,6 +1298,25 @@ impl ::core::cmp::Ord for Fieldless {
12941298
}
12951299
}
12961300

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

0 commit comments

Comments
 (0)