Skip to content

Commit 8aa2c66

Browse files
committed
EXPERIMENT: derive Debug as a single function call
Rather than needing a bunch of method calls, build arrays of the names and values, leaving the loop up to the implementation in core.
1 parent eb82fac commit 8aa2c66

File tree

10 files changed

+180
-13
lines changed

10 files changed

+180
-13
lines changed

compiler/rustc_builtin_macros/src/deriving/debug.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_ast::ptr::P;
66
use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
77
use rustc_expand::base::{Annotatable, ExtCtxt};
88
use rustc_span::symbol::{sym, Ident};
9-
use rustc_span::{Span, DUMMY_SP};
9+
use rustc_span::Span;
1010

1111
fn make_mut_borrow(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<Expr>) -> P<Expr> {
1212
cx.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, expr))
@@ -78,6 +78,65 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
7878
return cx.expr_block(block);
7979
}
8080

81+
if let ast::VariantData::Struct(..) = vdata {
82+
let mut name_exprs = Vec::with_capacity(fields.len() + 1);
83+
let mut value_exprs = Vec::with_capacity(fields.len());
84+
85+
name_exprs.push(name);
86+
for field in fields {
87+
name_exprs.push(cx.expr_lit(
88+
field.span,
89+
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
90+
));
91+
92+
// Use double indirection to make sure this works for unsized types
93+
let value_ref = cx.expr_addr_of(field.span, field.self_.clone());
94+
value_exprs.push(cx.expr_addr_of(field.span, value_ref));
95+
}
96+
97+
// `let names: &'static _ = &["TheType", "field1", "field2"];`
98+
let lt_static = Some(cx.lifetime_static(span));
99+
let ty_static_ref = cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
100+
let names_let = cx.stmt_let_ty(
101+
span,
102+
false,
103+
Ident::new(sym::names, span),
104+
Some(ty_static_ref),
105+
cx.expr_array_ref(span, name_exprs),
106+
);
107+
108+
// `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
109+
let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
110+
let ty_dyn_debug = cx.ty(
111+
span,
112+
ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn),
113+
);
114+
let ty_slice = cx.ty(
115+
span,
116+
ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)),
117+
);
118+
let values_let = cx.stmt_let_ty(
119+
span,
120+
false,
121+
Ident::new(sym::values, span),
122+
Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)),
123+
cx.expr_array_ref(span, value_exprs),
124+
);
125+
126+
// `fmt::DebugStruct::internal_debug_from_slices(fmt, names, values)`
127+
let fn_path_debug_internal =
128+
cx.std_path(&[sym::fmt, sym::DebugStruct, sym::internal_debug_from_slices]);
129+
let args = vec![
130+
fmt,
131+
cx.expr_ident(span, Ident::new(sym::names, span)),
132+
cx.expr_ident(span, Ident::new(sym::values, span)),
133+
];
134+
let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
135+
136+
let block = cx.block(span, vec![names_let, values_let, cx.stmt_expr(expr)]);
137+
return cx.expr_block(block);
138+
}
139+
81140
let builder = Ident::new(sym::debug_trait_builder, span);
82141
let builder_expr = cx.expr_ident(span, builder);
83142

@@ -111,6 +170,9 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
111170
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
112171
}
113172
ast::VariantData::Struct(..) => {
173+
cx.span_bug(span, "structs should have been handled above");
174+
175+
/*
114176
// normal struct/struct variant
115177
let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
116178
let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]);
@@ -135,6 +197,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
135197
stmts.push(stmt_let_underscore(cx, span, expr));
136198
}
137199
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
200+
*/
138201
}
139202
}
140203

compiler/rustc_builtin_macros/src/deriving/decodable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ fn decodable_substructure(
162162
cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms),
163163
);
164164
let lambda = cx.lambda(trait_span, vec![blkarg, variant], result);
165-
let variant_vec = cx.expr_vec(trait_span, variants);
165+
let variant_vec = cx.expr_array(trait_span, variants);
166166
let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
167167
let fn_read_enum_variant_path: Vec<_> =
168168
cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]);

compiler/rustc_builtin_macros/src/format.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ impl<'a, 'b> Context<'a, 'b> {
765765

766766
// First, build up the static array which will become our precompiled
767767
// format "string"
768-
let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces);
768+
let pieces = self.ecx.expr_array_ref(self.fmtsp, self.str_pieces);
769769

770770
// We need to construct a &[ArgumentV1] to pass into the fmt::Arguments
771771
// constructor. In general the expressions in this slice might be
@@ -838,7 +838,7 @@ impl<'a, 'b> Context<'a, 'b> {
838838
fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg));
839839
}
840840

841-
let args_array = self.ecx.expr_vec(self.macsp, fmt_args);
841+
let args_array = self.ecx.expr_array(self.macsp, fmt_args);
842842
let args_slice = self.ecx.expr_addr_of(
843843
self.macsp,
844844
if no_need_for_match {
@@ -868,7 +868,7 @@ impl<'a, 'b> Context<'a, 'b> {
868868
} else {
869869
// Build up the static array which will store our precompiled
870870
// nonstandard placeholders, if there are any.
871-
let fmt = self.ecx.expr_vec_slice(self.macsp, self.pieces);
871+
let fmt = self.ecx.expr_array_ref(self.macsp, self.pieces);
872872

873873
let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]);
874874
let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new());

compiler/rustc_builtin_macros/src/proc_macro_harness.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
318318
proc_macro_ty_method_path(cx, custom_derive),
319319
vec![
320320
cx.expr_str(cd.span, cd.trait_name),
321-
cx.expr_vec_slice(
321+
cx.expr_array_ref(
322322
span,
323323
cd.attrs
324324
.iter()
@@ -365,7 +365,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
365365
ast::Mutability::Not,
366366
),
367367
ast::Mutability::Not,
368-
cx.expr_vec_slice(span, decls),
368+
cx.expr_array_ref(span, decls),
369369
)
370370
.map(|mut i| {
371371
let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);

compiler/rustc_builtin_macros/src/test_harness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
351351
debug!("building test vector from {} tests", cx.test_cases.len());
352352
let ecx = &cx.ext_cx;
353353

354-
ecx.expr_vec_slice(
354+
ecx.expr_array_ref(
355355
sp,
356356
cx.test_cases
357357
.iter()

compiler/rustc_expand/src/build.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> {
5757
P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
5858
}
5959

60+
pub fn ty_infer(&self, span: Span) -> P<ast::Ty> {
61+
self.ty(span, ast::TyKind::Infer)
62+
}
63+
6064
pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
6165
self.ty(path.span, ast::TyKind::Path(None, path))
6266
}
@@ -139,11 +143,26 @@ impl<'a> ExtCtxt<'a> {
139143
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
140144
}
141145

146+
pub fn lifetime_static(&self, span: Span) -> ast::Lifetime {
147+
self.lifetime(span, Ident::new(kw::StaticLifetime, span))
148+
}
149+
142150
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
143151
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
144152
}
145153

146154
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
155+
self.stmt_let_ty(sp, mutbl, ident, None, ex)
156+
}
157+
158+
pub fn stmt_let_ty(
159+
&self,
160+
sp: Span,
161+
mutbl: bool,
162+
ident: Ident,
163+
ty: Option<P<ast::Ty>>,
164+
ex: P<ast::Expr>,
165+
) -> ast::Stmt {
147166
let pat = if mutbl {
148167
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
149168
self.pat_ident_binding_mode(sp, ident, binding_mode)
@@ -152,7 +171,7 @@ impl<'a> ExtCtxt<'a> {
152171
};
153172
let local = P(ast::Local {
154173
pat,
155-
ty: None,
174+
ty,
156175
id: ast::DUMMY_NODE_ID,
157176
kind: LocalKind::Init(ex),
158177
span: sp,
@@ -310,11 +329,13 @@ impl<'a> ExtCtxt<'a> {
310329
self.expr_lit(sp, ast::LitKind::Bool(value))
311330
}
312331

313-
pub fn expr_vec(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
332+
/// `[expr1, expr2, ...]`
333+
pub fn expr_array(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
314334
self.expr(sp, ast::ExprKind::Array(exprs))
315335
}
316-
pub fn expr_vec_slice(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
317-
self.expr_addr_of(sp, self.expr_vec(sp, exprs))
336+
/// `&[expr1, expr2, ...]`
337+
pub fn expr_array_ref(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
338+
self.expr_addr_of(sp, self.expr_array(sp, exprs))
318339
}
319340
pub fn expr_str(&self, sp: Span, s: Symbol) -> P<ast::Expr> {
320341
self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked))

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@ symbols! {
782782
integer_: "integer",
783783
integral,
784784
intel,
785+
internal_debug_from_slices,
785786
into_future,
786787
into_iter,
787788
intra_doc_pointers,

library/core/src/fmt/builders.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(unused_imports)]
22

33
use crate::fmt::{self, Debug, Formatter};
4+
use crate::iter;
45

56
struct PadAdapter<'buf, 'state> {
67
buf: &'buf mut (dyn fmt::Write + 'buf),
@@ -101,6 +102,86 @@ pub(super) fn debug_struct_new<'a, 'b>(
101102
}
102103

103104
impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
105+
/// Makes the full debug output using the data from the provided slices.
106+
///
107+
/// This is a horrible API that encodes far more in the `names` slice than
108+
/// any good API ever would. It will never be stabilized.
109+
///
110+
/// But this a great way for the derive in `rustc_builtin_macros` to be able
111+
/// to just have a `'static` slice for `names`, and a runtime array for
112+
/// `values`, rather than needing to codegen a call for every field.
113+
///
114+
/// # Examples
115+
///
116+
/// ```
117+
/// #![feature(fmt_helpers_for_derive)]
118+
/// use std::fmt;
119+
///
120+
/// struct Bar {
121+
/// bar: i32,
122+
/// another: String,
123+
/// }
124+
///
125+
/// impl fmt::Debug for Bar {
126+
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
127+
/// fmt::DebugStruct::internal_debug_from_slices(
128+
/// fmt,
129+
/// &["Bartender", "bar", "another"],
130+
/// &[&self.bar, &self.another],
131+
/// )
132+
/// }
133+
/// }
134+
///
135+
/// assert_eq!(
136+
/// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }),
137+
/// "Bartender { bar: 10, another: \"Hello World\" }",
138+
/// );
139+
///
140+
/// struct Bat {
141+
/// bat: i32,
142+
/// }
143+
///
144+
/// impl fmt::Debug for Bat {
145+
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
146+
/// fmt::DebugStruct::internal_debug_from_slices(
147+
/// fmt,
148+
/// &["Baton", "battle", ".."],
149+
/// &[&self.bat],
150+
/// )
151+
/// }
152+
/// }
153+
///
154+
/// assert_eq!(
155+
/// format!("{:?}", Bat { bat: 123 }),
156+
/// "Baton { battle: 123, .. }",
157+
/// );
158+
/// ```
159+
//#[doc(hidden)]
160+
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
161+
pub fn internal_debug_from_slices(
162+
fmt: &mut fmt::Formatter<'_>,
163+
names: &[&str],
164+
values: &[&dyn Debug],
165+
) -> fmt::Result {
166+
// The derive will never call this without a name.
167+
debug_assert_ne!(names.len(), 0);
168+
169+
let Some((struct_name, field_names)) = names.split_first() else {
170+
return Err(fmt::Error);
171+
};
172+
173+
let mut builder = fmt.debug_struct(struct_name);
174+
for (n, v) in iter::zip(field_names, values) {
175+
builder.field(n, v);
176+
}
177+
178+
if field_names.len() > values.len() {
179+
builder.finish_non_exhaustive()
180+
} else {
181+
builder.finish()
182+
}
183+
}
184+
104185
/// Adds a new field to the generated struct output.
105186
///
106187
/// # Examples

library/core/src/fmt/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ pub(crate) mod macros {
690690
/// Derive macro generating an impl of the trait `Debug`.
691691
#[rustc_builtin_macro]
692692
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
693-
#[allow_internal_unstable(core_intrinsics)]
693+
#[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)]
694694
pub macro Debug($item:item) {
695695
/* compiler built-in */
696696
}

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@
183183
#![feature(intra_doc_pointers)]
184184
#![feature(intrinsics)]
185185
#![feature(lang_items)]
186+
#![feature(let_else)]
186187
#![feature(link_llvm_intrinsics)]
187188
#![feature(min_specialization)]
188189
#![feature(mixed_integer_ops)]

0 commit comments

Comments
 (0)