1
Fork 0

Rollup merge of #123940 - kornelski:remove-derived-debug, r=Urgau

debug-fmt-detail option

I'd like to propose a new option that makes `#[derive(Debug)]` generate no-op implementations that don't print anything, and makes `{:?}` in format strings a no-op.

There are a couple of motivations for this:

1. A more thorough stripping of debug symbols. Binaries stripped of debug symbols still retain some of them through `Debug` implementations. It's hard to avoid that without compiler's help, because debug formatting can be used in many places, including dependencies, and their loggers, asserts, panics, etc.
   * In my testing it gives about 2% binary size reduction on top of all other binary-minimizing best practices (including `panic_immediate_abort`). There are targets like Web WASM or embedded where users pay attention to binary sizes.
   * Users distributing closed-source binaries may not want to "leak" any symbol names as a matter of principle.
2. Adds ability to test whether code depends on specifics of the `Debug` format implementation in unwise ways (e.g. trying to get data unavailable via public interface, or using it as a serialization format). Because current Rust's debug implementation doesn't change, there's a risk of it becoming a fragile de-facto API that [won't be possible to change in the future](https://www.hyrumslaw.com/). An option that "breaks" it can act as a [grease](https://www.rfc-editor.org/rfc/rfc8701.html).

This implementation is a `-Z fmt-debug=opt` flag that takes:

* `full` — the default, current state.
* `none` — makes derived `Debug` and `{:?}` no-ops. Explicit `impl Debug for T` implementations are left unharmed, but `{:?}` format won't use them, so they may get dead-code eliminated if they aren't invoked directly.
* `shallow` — makes derived `Debug` print only the type's name, without recursing into fields. Fieldless enums print their variant names. `{:?}` works.

The `shallow` option is a compromise between minimizing the `Debug` code, and compatibility. There are popular proc-macro crates that use `Debug::fmt` as a way to convert enum values into their Rust source code.

There's a corresponding `cfg` flag: `#[cfg(fmt_debug = "none")]` that can be used in user code to react to this setting to minimize custom `Debug` implementations or remove unnecessary formatting helper functions.
This commit is contained in:
Guillaume Gomez 2024-08-29 16:21:46 +02:00 committed by GitHub
commit 015e9371e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 285 additions and 56 deletions

View file

@ -1,5 +1,6 @@
use rustc_ast::{self as ast, EnumDef, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_session::config::FmtDebug;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
use thin_vec::{thin_vec, ThinVec};
@ -49,6 +50,11 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
// We want to make sure we have the ctxt set so that we can use unstable methods
let span = cx.with_def_site_ctxt(span);
let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
if fmt_detail == FmtDebug::None {
return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
}
let (ident, vdata, fields) = match substr.fields {
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
EnumMatching(_, v, fields) => (v.ident, &v.data, fields),
@ -61,6 +67,13 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
let name = cx.expr_str(span, ident.name);
let fmt = substr.nonselflike_args[0].clone();
// Fieldless enums have been special-cased earlier
if fmt_detail == FmtDebug::Shallow {
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
return BlockOrExpr::new_expr(expr);
}
// Struct and tuples are similar enough that we use the same code for both,
// with some extra pieces for structs due to the field names.
let (is_struct, args_per_field) = match vdata {