Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EXPERIMENT: derive Debug as a single function call #95637

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion compiler/rustc_builtin_macros/src/deriving/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP};
use rustc_span::Span;

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

if let ast::VariantData::Struct(..) = vdata {
let mut name_exprs = Vec::with_capacity(fields.len() + 1);
let mut value_exprs = Vec::with_capacity(fields.len());

name_exprs.push(name);
for field in fields {
name_exprs.push(cx.expr_lit(
field.span,
ast::LitKind::Str(field.name.unwrap().name, ast::StrStyle::Cooked),
));

// Use double indirection to make sure this works for unsized types
let value_ref = cx.expr_addr_of(field.span, field.self_.clone());
value_exprs.push(cx.expr_addr_of(field.span, value_ref));
}

// `let names: &'static _ = &["TheType", "field1", "field2"];`
let lt_static = Some(cx.lifetime_static(span));
let ty_static_ref = cx.ty_rptr(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
let names_let = cx.stmt_let_ty(
span,
false,
Ident::new(sym::names, span),
Some(ty_static_ref),
cx.expr_array_ref(span, name_exprs),
);

// `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
let ty_dyn_debug = cx.ty(
span,
ast::TyKind::TraitObject(vec![cx.trait_bound(path_debug)], ast::TraitObjectSyntax::Dyn),
);
let ty_slice = cx.ty(
span,
ast::TyKind::Slice(cx.ty_rptr(span, ty_dyn_debug, None, ast::Mutability::Not)),
);
let values_let = cx.stmt_let_ty(
span,
false,
Ident::new(sym::values, span),
Some(cx.ty_rptr(span, ty_slice, None, ast::Mutability::Not)),
cx.expr_array_ref(span, value_exprs),
);

// `fmt::DebugStruct::internal_debug_from_slices(fmt, names, values)`
let fn_path_debug_internal =
cx.std_path(&[sym::fmt, sym::DebugStruct, sym::internal_debug_from_slices]);
let args = vec![
fmt,
cx.expr_ident(span, Ident::new(sym::names, span)),
cx.expr_ident(span, Ident::new(sym::values, span)),
];
let expr = cx.expr_call_global(span, fn_path_debug_internal, args);

let block = cx.block(span, vec![names_let, values_let, cx.stmt_expr(expr)]);
return cx.expr_block(block);
}

let builder = Ident::new(sym::debug_trait_builder, span);
let builder_expr = cx.expr_ident(span, builder);

Expand Down Expand Up @@ -111,6 +170,9 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugTuple, sym::finish]);
}
ast::VariantData::Struct(..) => {
cx.span_bug(span, "structs should have been handled above");

/*
// normal struct/struct variant
let fn_path_debug_struct = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_struct]);
let expr = cx.expr_call_global(span, fn_path_debug_struct, vec![fmt, name]);
Expand All @@ -135,6 +197,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
stmts.push(stmt_let_underscore(cx, span, expr));
}
fn_path_finish = cx.std_path(&[sym::fmt, sym::DebugStruct, sym::finish]);
*/
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/decodable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fn decodable_substructure(
cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms),
);
let lambda = cx.lambda(trait_span, vec![blkarg, variant], result);
let variant_vec = cx.expr_vec(trait_span, variants);
let variant_vec = cx.expr_array(trait_span, variants);
let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
let fn_read_enum_variant_path: Vec<_> =
cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]);
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ impl<'a, 'b> Context<'a, 'b> {

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

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

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

let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]);
let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new());
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/src/proc_macro_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
proc_macro_ty_method_path(cx, custom_derive),
vec![
cx.expr_str(cd.span, cd.trait_name),
cx.expr_vec_slice(
cx.expr_array_ref(
span,
cd.attrs
.iter()
Expand Down Expand Up @@ -365,7 +365,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
ast::Mutability::Not,
),
ast::Mutability::Not,
cx.expr_vec_slice(span, decls),
cx.expr_array_ref(span, decls),
)
.map(|mut i| {
let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/test_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
debug!("building test vector from {} tests", cx.test_cases.len());
let ecx = &cx.ext_cx;

ecx.expr_vec_slice(
ecx.expr_array_ref(
sp,
cx.test_cases
.iter()
Expand Down
29 changes: 25 additions & 4 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ impl<'a> ExtCtxt<'a> {
P(ast::Ty { id: ast::DUMMY_NODE_ID, span, kind, tokens: None })
}

pub fn ty_infer(&self, span: Span) -> P<ast::Ty> {
self.ty(span, ast::TyKind::Infer)
}

pub fn ty_path(&self, path: ast::Path) -> P<ast::Ty> {
self.ty(path.span, ast::TyKind::Path(None, path))
}
Expand Down Expand Up @@ -139,11 +143,26 @@ impl<'a> ExtCtxt<'a> {
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
}

pub fn lifetime_static(&self, span: Span) -> ast::Lifetime {
self.lifetime(span, Ident::new(kw::StaticLifetime, span))
}

pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
}

pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
self.stmt_let_ty(sp, mutbl, ident, None, ex)
}

pub fn stmt_let_ty(
&self,
sp: Span,
mutbl: bool,
ident: Ident,
ty: Option<P<ast::Ty>>,
ex: P<ast::Expr>,
) -> ast::Stmt {
let pat = if mutbl {
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
self.pat_ident_binding_mode(sp, ident, binding_mode)
Expand All @@ -152,7 +171,7 @@ impl<'a> ExtCtxt<'a> {
};
let local = P(ast::Local {
pat,
ty: None,
ty,
id: ast::DUMMY_NODE_ID,
kind: LocalKind::Init(ex),
span: sp,
Expand Down Expand Up @@ -310,11 +329,13 @@ impl<'a> ExtCtxt<'a> {
self.expr_lit(sp, ast::LitKind::Bool(value))
}

pub fn expr_vec(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
/// `[expr1, expr2, ...]`
pub fn expr_array(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
self.expr(sp, ast::ExprKind::Array(exprs))
}
pub fn expr_vec_slice(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
self.expr_addr_of(sp, self.expr_vec(sp, exprs))
/// `&[expr1, expr2, ...]`
pub fn expr_array_ref(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
self.expr_addr_of(sp, self.expr_array(sp, exprs))
}
pub fn expr_str(&self, sp: Span, s: Symbol) -> P<ast::Expr> {
self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked))
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ symbols! {
integer_: "integer",
integral,
intel,
internal_debug_from_slices,
into_future,
into_iter,
intra_doc_pointers,
Expand Down
81 changes: 81 additions & 0 deletions library/core/src/fmt/builders.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(unused_imports)]

use crate::fmt::{self, Debug, Formatter};
use crate::iter;

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

impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
/// Makes the full debug output using the data from the provided slices.
///
/// This is a horrible API that encodes far more in the `names` slice than
/// any good API ever would. It will never be stabilized.
///
/// But this a great way for the derive in `rustc_builtin_macros` to be able
/// to just have a `'static` slice for `names`, and a runtime array for
/// `values`, rather than needing to codegen a call for every field.
///
/// # Examples
///
/// ```
/// #![feature(fmt_helpers_for_derive)]
/// use std::fmt;
///
/// struct Bar {
/// bar: i32,
/// another: String,
/// }
///
/// impl fmt::Debug for Bar {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt::DebugStruct::internal_debug_from_slices(
/// fmt,
/// &["Bartender", "bar", "another"],
/// &[&self.bar, &self.another],
/// )
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }),
/// "Bartender { bar: 10, another: \"Hello World\" }",
/// );
///
/// struct Bat {
/// bat: i32,
/// }
///
/// impl fmt::Debug for Bat {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt::DebugStruct::internal_debug_from_slices(
/// fmt,
/// &["Baton", "battle", ".."],
/// &[&self.bat],
/// )
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Bat { bat: 123 }),
/// "Baton { battle: 123, .. }",
/// );
/// ```
//#[doc(hidden)]
#[unstable(feature = "fmt_helpers_for_derive", issue = "none")]
pub fn internal_debug_from_slices(
fmt: &mut fmt::Formatter<'_>,
names: &[&str],
values: &[&dyn Debug],
) -> fmt::Result {
// The derive will never call this without a name.
debug_assert_ne!(names.len(), 0);

let Some((struct_name, field_names)) = names.split_first() else {
return Err(fmt::Error);
};

let mut builder = fmt.debug_struct(struct_name);
for (n, v) in iter::zip(field_names, values) {
builder.field(n, v);
}

if field_names.len() > values.len() {
builder.finish_non_exhaustive()
} else {
builder.finish()
}
}

/// Adds a new field to the generated struct output.
///
/// # Examples
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ pub(crate) mod macros {
/// Derive macro generating an impl of the trait `Debug`.
#[rustc_builtin_macro]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[allow_internal_unstable(core_intrinsics)]
#[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)]
pub macro Debug($item:item) {
/* compiler built-in */
}
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
#![feature(intra_doc_pointers)]
#![feature(intrinsics)]
#![feature(lang_items)]
#![feature(let_else)]
#![feature(link_llvm_intrinsics)]
#![feature(min_specialization)]
#![feature(mixed_integer_ops)]
Expand Down