diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index ecf70da6d96c5..bf6c69deb4097 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -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) -> P { 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<'_> 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); @@ -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]); @@ -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]); + */ } } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 1d892b20729d5..085317fe9f7bb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -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]); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 31213412d45f2..4ba1d0f216516 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -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 @@ -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 { @@ -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()); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 407ca2301e124..b25f87f61406f 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -318,7 +318,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { 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() @@ -365,7 +365,7 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { 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); diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e2553ab40cad4..780bdabbb83f2 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -351,7 +351,7 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P { 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() diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b8ed75cb6bb74..015b5624933dc 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -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 { + self.ty(span, ast::TyKind::Infer) + } + pub fn ty_path(&self, path: ast::Path) -> P { self.ty(path.span, ast::TyKind::Path(None, path)) } @@ -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::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::Stmt { + self.stmt_let_ty(sp, mutbl, ident, None, ex) + } + + pub fn stmt_let_ty( + &self, + sp: Span, + mutbl: bool, + ident: Ident, + ty: Option>, + ex: P, + ) -> ast::Stmt { let pat = if mutbl { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); self.pat_ident_binding_mode(sp, ident, binding_mode) @@ -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, @@ -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 { + /// `[expr1, expr2, ...]` + pub fn expr_array(&self, sp: Span, exprs: Vec>) -> P { self.expr(sp, ast::ExprKind::Array(exprs)) } - pub fn expr_vec_slice(&self, sp: Span, exprs: Vec>) -> P { - self.expr_addr_of(sp, self.expr_vec(sp, exprs)) + /// `&[expr1, expr2, ...]` + pub fn expr_array_ref(&self, sp: Span, exprs: Vec>) -> P { + self.expr_addr_of(sp, self.expr_array(sp, exprs)) } pub fn expr_str(&self, sp: Span, s: Symbol) -> P { self.expr_lit(sp, ast::LitKind::Str(s, ast::StrStyle::Cooked)) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2737c26708bc4..22b99ff718d35 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -782,6 +782,7 @@ symbols! { integer_: "integer", integral, intel, + internal_debug_from_slices, into_future, into_iter, intra_doc_pointers, diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 32d1a4e550834..d9786c76f8432 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -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), @@ -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 diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 6c1d20f36e2f6..68f4281569398 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -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 */ } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 445a7ba6e2d66..39ba41358e81b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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)]