added
S-waiting-on-review
labels
Jul 14, 2026
connortsui20
changed the title
make debug builders with closures impl with dyn
Implement Debug helpers that take closures with &mut dyn FnMut
Open
rust-bors Bot pushed a commit that referenced this pull request
Jul 14, 2026
rustbot
added
S-waiting-on-author
and removed S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.labels
Jul 15, 2026Closed
rust-bors
Bot
added
S-waiting-on-bors
and removed S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.labels
Jul 16, 2026jhpratt added a commit to jhpratt/rust that referenced this pull request
Jul 17, 2026…hanna-kruppe Implement `Debug` helpers via `Cell` Related to rust-lang#149745, but does not fix it (yet). Following @jmillikin's [suggestion](rust-lang#159302 (comment)), the builder logic now lives in the pre-existing non-generic methods taking `&dyn fmt::Debug` (where it used to exist), and each `*_with` method wraps its closure in a private `DebugOnce` struct that implements `Debug` by calling the closure, so the body is compiled once. Just for context: A `dyn FnOnce` [can't be called behind a reference](rust-lang#149745 (comment)), hence the `Cell<Option<..>>` stuff in `DebugOnce`. ###### repro.rs ```rust #![feature(debug_closure_helpers)] #![crate_type = "lib"] use core::fmt; pub struct Point { pub x: u32, pub y: u32, } impl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Point") .field_with("x", |f| self.x.fmt(f)) .field_with("y", |f| self.y.fmt(f)) .finish() } } ``` ```bash rustc +nightly --edition=2024 --emit=llvm-ir -Copt-level=0 -o before.ll repro.rs rustc +stage1 --edition=2024 --emit=llvm-ir -Copt-level=0 -o after.ll repro.rs ``` On the repro above, I saw the LLVM IR drop from 739 to 268 lines (an earlier version of this PR using `&mut dyn FnMut` measured 372). Since the stable `&dyn fmt::Debug` methods no longer go through any closure indirection, this should also avoid the potential runtime regression in the `derive(Debug)` microbenchmark. This was the only remaining [blocker](rust-lang#117729 (comment)) for stabilizing `debug_closure_helpers`, so it should unblock rust-lang#146099.
Closed
rust-bors Bot pushed a commit that referenced this pull request
Jul 17, 2026Rollup of 10 pull requests Successful merges: - #156977 (interpret: properly check for inhabitedness of nested references) - #159365 (fix: point at method call chain when a return-position `impl Trait` assoc type diverges) - #159402 (Clarify safety requirements for SIMD shl/shr and masked load/store) - #159410 (rustdoc: remove old `--emit` types) - #159302 (Implement `Debug` helpers via `Cell`) - #159386 (add a fallback for `fmuladdf*`) - #159391 (Update tests for LLVM 23) - #159400 (Update books) - #159401 (Gate `tests/debuginfo/function-call.rs` on min GDB 15.1) - #159404 ([aarch64][win] Pass oversized c-variadic args indirectly on Arm64EC)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request
Jul 17, 2026…hanna-kruppe Implement `Debug` helpers via `Cell` Related to rust-lang#149745, but does not fix it (yet). Following @jmillikin's [suggestion](rust-lang#159302 (comment)), the builder logic now lives in the pre-existing non-generic methods taking `&dyn fmt::Debug` (where it used to exist), and each `*_with` method wraps its closure in a private `DebugOnce` struct that implements `Debug` by calling the closure, so the body is compiled once. Just for context: A `dyn FnOnce` [can't be called behind a reference](rust-lang#149745 (comment)), hence the `Cell<Option<..>>` stuff in `DebugOnce`. ###### repro.rs ```rust #![feature(debug_closure_helpers)] #![crate_type = "lib"] use core::fmt; pub struct Point { pub x: u32, pub y: u32, } impl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Point") .field_with("x", |f| self.x.fmt(f)) .field_with("y", |f| self.y.fmt(f)) .finish() } } ``` ```bash rustc +nightly --edition=2024 --emit=llvm-ir -Copt-level=0 -o before.ll repro.rs rustc +stage1 --edition=2024 --emit=llvm-ir -Copt-level=0 -o after.ll repro.rs ``` On the repro above, I saw the LLVM IR drop from 739 to 268 lines (an earlier version of this PR using `&mut dyn FnMut` measured 372). Since the stable `&dyn fmt::Debug` methods no longer go through any closure indirection, this should also avoid the potential runtime regression in the `derive(Debug)` microbenchmark. This was the only remaining [blocker](rust-lang#117729 (comment)) for stabilizing `debug_closure_helpers`, so it should unblock rust-lang#146099.
Closed
rust-bors Bot pushed a commit that referenced this pull request
Jul 17, 2026…uwer Rollup of 10 pull requests Successful merges: - #159365 (fix: point at method call chain when a return-position `impl Trait` assoc type diverges) - #159402 (Clarify safety requirements for SIMD shl/shr and masked load/store) - #159410 (rustdoc: remove old `--emit` types) - #159302 (Implement `Debug` helpers via `Cell`) - #159386 (add a fallback for `fmuladdf*`) - #159391 (Update tests for LLVM 23) - #159400 (Update books) - #159401 (Gate `tests/debuginfo/function-call.rs` on min GDB 15.1) - #159404 ([aarch64][win] Pass oversized c-variadic args indirectly on Arm64EC) - #159405 (Manually implement Clone for GrowableBitSet)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request
Jul 17, 2026…hanna-kruppe Implement `Debug` helpers via `Cell` Related to rust-lang#149745, but does not fix it (yet). Following @jmillikin's [suggestion](rust-lang#159302 (comment)), the builder logic now lives in the pre-existing non-generic methods taking `&dyn fmt::Debug` (where it used to exist), and each `*_with` method wraps its closure in a private `DebugOnce` struct that implements `Debug` by calling the closure, so the body is compiled once. Just for context: A `dyn FnOnce` [can't be called behind a reference](rust-lang#149745 (comment)), hence the `Cell<Option<..>>` stuff in `DebugOnce`. ###### repro.rs ```rust #![feature(debug_closure_helpers)] #![crate_type = "lib"] use core::fmt; pub struct Point { pub x: u32, pub y: u32, } impl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Point") .field_with("x", |f| self.x.fmt(f)) .field_with("y", |f| self.y.fmt(f)) .finish() } } ``` ```bash rustc +nightly --edition=2024 --emit=llvm-ir -Copt-level=0 -o before.ll repro.rs rustc +stage1 --edition=2024 --emit=llvm-ir -Copt-level=0 -o after.ll repro.rs ``` On the repro above, I saw the LLVM IR drop from 739 to 268 lines (an earlier version of this PR using `&mut dyn FnMut` measured 372). Since the stable `&dyn fmt::Debug` methods no longer go through any closure indirection, this should also avoid the potential runtime regression in the `derive(Debug)` microbenchmark. This was the only remaining [blocker](rust-lang#117729 (comment)) for stabilizing `debug_closure_helpers`, so it should unblock rust-lang#146099.
Closed
rust-bors Bot pushed a commit that referenced this pull request
Jul 17, 2026…uwer Rollup of 16 pull requests Successful merges: - #150732 (Convert `-Ctarget-cpu` into a target-modifier for AVR, AMDGCN and NVPTX ) - #159301 (Update Enzyme to handle LLVM23) - #159365 (fix: point at method call chain when a return-position `impl Trait` assoc type diverges) - #159402 (Clarify safety requirements for SIMD shl/shr and masked load/store) - #159410 (rustdoc: remove old `--emit` types) - #158398 (Comment about empty run_passes, fixup of #158040) - #158843 (Fix ICE in `write_interface` when the interface file can't be written) - #159302 (Implement `Debug` helpers via `Cell`) - #159332 (Honor field-level lint attributes in non_snake_case) - #159386 (add a fallback for `fmuladdf*`) - #159391 (Update tests for LLVM 23) - #159400 (Update books) - #159401 (Gate `tests/debuginfo/function-call.rs` on min GDB 15.1) - #159404 ([aarch64][win] Pass oversized c-variadic args indirectly on Arm64EC) - #159405 (Manually implement Clone for GrowableBitSet) - #159415 (rustdoc: rename the doc parts metadata params)
Merged
rust-bors Bot pushed a commit that referenced this pull request
Jul 17, 2026…uwer Rollup of 17 pull requests Successful merges: - #159301 (Update Enzyme to handle LLVM23) - #159365 (fix: point at method call chain when a return-position `impl Trait` assoc type diverges) - #159402 (Clarify safety requirements for SIMD shl/shr and masked load/store) - #159408 (rustc_data_structures: Expand documentation for rustc jobserver APIs) - #159410 (rustdoc: remove old `--emit` types) - #158398 (Comment about empty run_passes, fixup of #158040) - #158843 (Fix ICE in `write_interface` when the interface file can't be written) - #159302 (Implement `Debug` helpers via `Cell`) - #159332 (Honor field-level lint attributes in non_snake_case) - #159340 (Rename `errors.rs` file to `diagnostics.rs` (14/N)) - #159386 (add a fallback for `fmuladdf*`) - #159391 (Update tests for LLVM 23) - #159400 (Update books) - #159401 (Gate `tests/debuginfo/function-call.rs` on min GDB 15.1) - #159404 ([aarch64][win] Pass oversized c-variadic args indirectly on Arm64EC) - #159405 (Manually implement Clone for GrowableBitSet) - #159415 (rustdoc: rename the doc parts metadata params)
rust-timer added a commit that referenced this pull request
Jul 17, 2026Rollup merge of #159302 - connortsui20:dyn-debug-helpers, r=hanna-kruppe Implement `Debug` helpers via `Cell` Related to #149745, but does not fix it (yet). Following @jmillikin's [suggestion](#159302 (comment)), the builder logic now lives in the pre-existing non-generic methods taking `&dyn fmt::Debug` (where it used to exist), and each `*_with` method wraps its closure in a private `DebugOnce` struct that implements `Debug` by calling the closure, so the body is compiled once. Just for context: A `dyn FnOnce` [can't be called behind a reference](#149745 (comment)), hence the `Cell<Option<..>>` stuff in `DebugOnce`. ###### repro.rs ```rust #![feature(debug_closure_helpers)] #![crate_type = "lib"] use core::fmt; pub struct Point { pub x: u32, pub y: u32, } impl fmt::Debug for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Point") .field_with("x", |f| self.x.fmt(f)) .field_with("y", |f| self.y.fmt(f)) .finish() } } ``` ```bash rustc +nightly --edition=2024 --emit=llvm-ir -Copt-level=0 -o before.ll repro.rs rustc +stage1 --edition=2024 --emit=llvm-ir -Copt-level=0 -o after.ll repro.rs ``` On the repro above, I saw the LLVM IR drop from 739 to 268 lines (an earlier version of this PR using `&mut dyn FnMut` measured 372). Since the stable `&dyn fmt::Debug` methods no longer go through any closure indirection, this should also avoid the potential runtime regression in the `derive(Debug)` microbenchmark. This was the only remaining [blocker](#117729 (comment)) for stabilizing `debug_closure_helpers`, so it should unblock #146099.
Kobzol pushed a commit to Kobzol/rustc_codegen_cranelift that referenced this pull request
Jul 23, 2026Open