don't drop arguments' temporaries in dbg! rust-lang/rust#154074
library/std/src/macros/tests.rs
I'm not totally sure where this should go. Conceptually, it makes the most sense for it to be a std test, but I can't find where dbg! is actually tested. It also only needs to pass borrowck, not be built and executed, so we could maybe save CI time by making it a //@ check-pass ui test or such.
I think this is OK. It probably doesn't need to be an integration test though? i.e., you could add it to macros/test.rs or so?
Oh, I wasn't aware there was a difference! Done.
Unit tests get compiled as part of the general crate whereas integ tests (in tests/ at the top level) each get compiled separately, so they're generally slower to compile (on a per-test amortized basis).
library/std/src/macros/tests.rs
| 9 | *dbg!(&temp()); |
|
| 10 | *dbg!(&temp(), 1).0; |
|
| 11 | *dbg!(0, &temp()).1; |
|
| 12 | *dbg!(0, &temp(), 2).1; |
Only the last couple of these actually regressed, but I figure we may as well be resilient to changes in dbg!.
Also, the dereferences of the temporary aren't necessary to reproduce this and weren't present in the reproducer found by crater, but I feel like being explicit makes the intent of the test clearer.
library/std/src/macros.rs
| 389 | $crate::stringify!($processed), |
|
| 390 | // The `&T: Debug` check happens here (not in the format literal desugaring) |
|
| 391 | // to avoid format literal related messages and suggestions. |
|
| 392 | &&$bound as &dyn $crate::fmt::Debug |
Why do we need a double reference here? Is it to support some crazy code doing impl Debug for &Thing ?
I'm not sure! The double reference just coerces away as part of the cast, right? But it's been that way since #142594, so I figured I'd leave it be.
.. though on second thought, just removing the & would be a very small PR on its own, so maybe it could be bundled in here, especially if the decision is to backport a revert of #149869 instead of this.
Huh. This code has compiled ever since the stabilization of dbg!() in 1.32.0.
use std::fmt::{self, Debug, Formatter};
struct Thing;
impl Debug for &Thing {
fn fmt(&self, _: &mut Formatter) -> fmt::Result {
Ok(())
}
}
fn main() {
dbg!(Thing);
}Removing the double reference would break this code.
That feels probably accidental ^^ but it definitely shouldn't be changed as part of this, then.
The dbg! macro has always done the equivalent of println!("{:?}", &x). The double reference is necessary to keep those semantics.
You can do something like
#![feature(impl_trait_in_bindings)]
fn main() {
let x = 4_u8;
let y: impl std::fmt::Debug = &x;
}if you really want to get rid of the double reference/cast, though.
View on GitHub
The job x86_64-gnu-miri failed! Check out the build log: (web) (plain enhanced) (plain)
Click to see the possible cause of the failure (guessed by this bot)
tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs ... ok
tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs ... ok
FAILED TEST: tests/fail/dangling_pointers/dangling_primitive.rs
command: MIRI_ENV_VAR_TEST="0" MIRI_TEMP="/tmp/miri-uitest-RuZtqQ" RUST_BACKTRACE="1" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/miri" "--error-format=json" "--sysroot=/checkout/obj/build/x86_64-unknown-linux-gnu/miri-sysroot" "-Dwarnings" "-Dunused" "-Ainternal_features" "-Zui-testing" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/tmp/miri_ui/0/tests/fail/dangling_pointers" "tests/fail/dangling_pointers/dangling_primitive.rs" "--edition" "2021"
error: actual output differed from expected
Execute `./miri test --bless` to update `tests/fail/dangling_pointers/dangling_primitive.stderr` to the actual output
--- tests/fail/dangling_pointers/dangling_primitive.stderr
+++ <stderr output>
---
FAILED TEST: tests/fail/function_calls/return_pointer_on_unwind.rs
command: MIRI_ENV_VAR_TEST="0" MIRI_TEMP="/tmp/miri-uitest-RuZtqQ" RUST_BACKTRACE="1" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/miri" "--error-format=json" "--sysroot=/checkout/obj/build/x86_64-unknown-linux-gnu/miri-sysroot" "-Dwarnings" "-Dunused" "-Ainternal_features" "-Zui-testing" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/tmp/miri_ui/0/tests/fail/function_calls" "tests/fail/function_calls/return_pointer_on_unwind.rs" "-Zmiri-disable-stacked-borrows" "--edition" "2021"
error: actual output differed from expected
Execute `./miri test --bless` to update `tests/fail/function_calls/return_pointer_on_unwind.stderr` to the actual output
--- tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ <stderr output>
thread 'main' ($TID) panicked at tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC:
... 5 lines skipped ...
|
LL | dbg!(x.0);
- | ^^^^^^^^^ Undefined Behavior occurred here
+ | ^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
... 17 lines skipped ...
error: aborting due to 1 previous error
Full unnormalized output:
thread 'main' (1000) panicked at tests/fail/function_calls/return_pointer_on_unwind.rs:26:5:
explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
error: Undefined Behavior: reading memory at alloc110[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
##[error] --> tests/fail/function_calls/return_pointer_on_unwind.rs:53:10
|
LL | dbg!(x.0);
| ^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Uninitialized memory occurred at alloc110[0x0..0x4], in this allocation:
alloc110 (stack variable, size: 132, align: 4) {
0x00 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x10 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x20 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x30 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x40 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x50 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x60 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x70 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x80 │ __ __ __ __ │ ░░░░
}
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
---
thread 'main' (1000) panicked at tests/fail/function_calls/return_pointer_on_unwind.rs:26:5:
explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
error: Undefined Behavior: reading memory at alloc110[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
##[error] --> tests/fail/function_calls/return_pointer_on_unwind.rs:53:10
|
LL | dbg!(x.0);
| ^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Uninitialized memory occurred at alloc110[0x0..0x4], in this allocation:
alloc110 (stack variable, size: 132, align: 4) {
0x00 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x10 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x20 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x30 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x40 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x50 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x60 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x70 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
0x80 │ __ __ __ __ │ ░░░░
}
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
---
Location:
/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ui_test-0.30.3/src/lib.rs:365
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1: <color_eyre[166ce3d2f184b345]::config::EyreHook>::into_eyre_hook::{closure#0}<unknown>
at <unknown source file>:<unknown line>
2: <eyre[b9d8df6361ee71a2]::Report>::from_adhoc::<&str><unknown>
at <unknown source file>:<unknown line>
3: ui_test[2369805698cdcd5e]::run_tests_generic::<ui_test[2369805698cdcd5e]::default_file_filter, ui[b63c38d913af32db]::run_tests::{closure#1}, alloc[4350eba2b84ef921]::boxed::Box<dyn ui_test[2369805698cdcd5e]::status_emitter::StatusEmitter>><unknown>
at <unknown source file>:<unknown line>
4: ui[b63c38d913af32db]::ui<unknown>
at <unknown source file>:<unknown line>
5: ui[b63c38d913af32db]::main<unknown>
at <unknown source file>:<unknown line>
6: std[4befc14855dadcd7]::sys::backtrace::__rust_begin_short_backtrace::<fn() -> core[bf71407d4732122b]::result::Result<(), eyre[b9d8df6361ee71a2]::Report>, core[bf71407d4732122b]::result::Result<(), eyre[b9d8df6361ee71a2]::Report>><unknown>
at <unknown source file>:<unknown line>
7: std[4befc14855dadcd7]::rt::lang_start::<core[bf71407d4732122b]::result::Result<(), eyre[b9d8df6361ee71a2]::Report>>::{closure#0}<unknown>
at <unknown source file>:<unknown line>
8: std[4befc14855dadcd7]::rt::lang_start_internal<unknown>
at <unknown source file>:<unknown line>
9: main<unknown>
at <unknown source file>:<unknown line>
10: __libc_start_main<unknown>
at <unknown source file>:<unknown line>
11: _start<unknown>
at <unknown source file>:<unknown line>
Run with COLORBT_SHOW_HIDDEN=1 environment variable to disable frame filtering.
Run with RUST_BACKTRACE=full to include source snippets.
error: test failed, to rerun pass `--test ui`
Caused by:
process didn't exit successfully: `/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-tools/x86_64-unknown-linux-gnu/release/deps/ui-967339ea3dcc1e54` (exit status: 1)
Bootstrap failed while executing `test --stage 2 src/tools/miri src/tools/miri/cargo-miri`
Command `/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo test -Zwarnings --target x86_64-unknown-linux-gnu -Zbinary-dep-depinfo -j 4 -Zroot-dir=/checkout --locked --color=always --profile=release --manifest-path /checkout/src/tools/miri/Cargo.toml -- [workdir=/checkout]` failed with exit code 1
Created at: src/bootstrap/src/core/build_steps/tool.rs:191:21
Executed at: src/bootstrap/src/core/build_steps/test.rs:739:19
Command has failed. Rerun with -v to see more details.
Build completed unsuccessfully in 0:41:00
local time: Thu Mar 19 04:40:14 UTC 2026
network time: Thu, 19 Mar 2026 04:40:15 GMT
##[error]Process completed with exit code 1.
##[group]Run echo "disk usage:"
Generally this looks fine to me. I'm also not sure where (or whether they exist) there's more tests, but I think as long as you confirmed the one you added fails CI if the changes are reverted it feels OK to me.
r=me, I think it would probably make sense to revert #149869 as the beta backport but I'll leave that up to T-libs backport decision making.
tests/ui/borrowck/dbg-issue-120327.stderr
| 9 | 9 | | ^ value used here after move |
| 10 | 10 | | |
| 11 | help: consider borrowing instead of transferring ownership |
|
| 11 | help: consider cloning the value if the performance cost is acceptable |
This feels like an unfortunate regression (fine for this PR but maybe file an issue for it)? It seems highly likely that borrowing is better here.
Apparently that suggestion was actually specifically meant for dbg!; I'd assumed it was generic. Maybe it's worth handling it in this PR to keep its expectation of what dbg! looks like in sync with reality—same as the clippy lint change? That test is specifically for the diagnostic too; if I'd taken care to read the linked issue (or if the test was documented or tested for the suggestion message) I'd have updated the diagnostic from the start ^^
I've added a fix, but I can move it to its own PR if that'd be better.
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs · outdated
This already only worked for expansions, so I'd like to keep its scope narrow for now. If a more general "borrow here" suggestion would be helpful, I think it should be designed intentionally.
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs · outdated
Here's why it only worked for expansions: self.expr_span was the move_span, which previously pointed to the match's pattern (specifically a by-move binding), which is normally separate from the match scrutinee (where dbg!'s args expand).
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
| 581 | path @ Path { segments: [seg], .. }, |
|
| 582 | )) = &arg.kind |
|
| 583 | && seg.ident.name == self.arg_name |
|
| 584 | && self.move_span.source_equal(arg.span) |
This incidentally fixes a minor bug: if given dbg!(a, a); a;, it would produce two suggestions to borrow the second argument rather than one for each. Now that the move_span points to the user-provided argument, we can check the scrutinee for it to find the correct argument for each suggestion.
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
| 572 | && e.span.macro_backtrace().any(|expn| { |
|
| 573 | expn.macro_def_id.is_some_and(|macro_def_id| { |
|
| 574 | self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id) |
|
| 575 | }) |
This could avoid iterating by making dbg_internal! a diagnostic item. I figured it'd be cleaner to reuse the dbg_macro diagnostic item instead since efficiency isn't a priority, but I'm not sure.
I think as-is this will also apply to e.g. dbg!(some_other_macro!(a)), right? Not sure that's desirable. But I think it's probably fairly rare that someone does that, so we can go with this for now.
I don't think so? I could be wrong, but I don't think the argument to dbg! would have the dbg! expansion in its macro backtrace, since it's not part of dbg!. I tested the spans of the moved-from arguments to dbg! in dbg-issue-120327.rs; they all have empty macro backtraces.
tests/ui/borrowck/dbg-issue-120327.rs
| 59 | 35 | |
| 60 | 36 | fn get_expr(_s: String) {} |
| 61 | 37 | |
| 38 | // The suggestion is purely syntactic; applying it here will result in a type error. |
This was a pre-existing issue; I consider fixing it out-of-scope. I think other suggestions to borrow often produce incorrect code, but if we want to be extra-safe, it could only fire when the dbg! invocation is on a statement of its own.
Re-adding the T-compiler label since I've added in a diagnostic fix. I'll re-remove it if it'd be better as its own PR; I can add a FIXME to the diagnostic and its test in that case.
Also, it looks like my self-review notes didn't all end up rendering properly? Oops. Hopefully they still make sense.
@bors r+
I think my sense is that especially with compiler changes also involved we should land the revert on beta rather than trying to backport this.
View on GitHub
The job pr-check-2 failed! Check out the build log: (web) (plain enhanced) (plain)
Click to see the possible cause of the failure (guessed by this bot)
##[group]Linting stage2 bootstrap (stage1 -> stage2, x86_64-unknown-linux-gnu)
error: failed to run `rustc` to learn about target-specific information
Caused by:
process didn't exit successfully: `/checkout/obj/build/bootstrap/debug/rustc /checkout/obj/build/x86_64-unknown-linux-gnu/stage1/bin/clippy-driver /checkout/obj/build/bootstrap/debug/rustc - --crate-name ___ --print=file-names --sysroot /checkout/obj/build/x86_64-unknown-linux-gnu/stage1 --cfg=windows_raw_dylib -Csymbol-mangling-version=v0 -Zannotate-moves -Zunstable-options -Zforce-unstable-if-unmarked -Zmacro-backtrace -Csplit-debuginfo=off -Clink-args=-Wl,-z,origin '-Clink-args=-Wl,-rpath,$ORIGIN/../lib' -Zunstable-options --target x86_64-unknown-linux-gnu --crate-type bin --crate-type rlib --crate-type dylib --crate-type cdylib --crate-type staticlib --crate-type proc-macro --print=sysroot --print=split-debuginfo --print=crate-name --print=cfg -Wwarnings` (exit status: 101)
--- stderr
thread 'rustc' (32336) panicked at compiler/rustc_span/src/symbol.rs:2697:13:
duplicate symbols in the rustc symbol list and the extra symbols added by the driver: ["dbg_macro"]
stack backtrace:
0: __rustc::rust_begin_unwind
1: core::panicking::panic_fmt
2: <rustc_span::symbol::Interner>::with_extra_symbols
3: <rustc_span::SessionGlobals>::new
---
warning: the ICE couldn't be written to `/checkout/rustc-ice-2026-03-29T16_45_08-32335.txt`: Read-only file system (os error 30)
note: rustc 1.96.0-nightly (44c447024 2026-03-29) running on x86_64-unknown-linux-gnu
note: compiler flags: -C symbol-mangling-version=v0 -Z annotate-moves -Z unstable-options -Z force-unstable-if-unmarked -Z macro-backtrace -C split-debuginfo=off -C link-args=-Wl,-z,origin -C link-args=-Wl,-rpath,$ORIGIN/../lib -Z unstable-options --crate-type bin --crate-type rlib --crate-type dylib --crate-type cdylib --crate-type staticlib --crate-type proc-macro -Z on-broken-pipe=kill -Z tls-model=initial-exec -Z allow-features=binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic
query stack during panic:
end of query stack
note: Clippy version: clippy 0.1.96 (44c4470244 2026-03-29)
Forgot to re-test clippy 😔 @bors r-
Easy fix, at least.
@bors r=Mark-Simulacrum
View all comments
Fixes #153850
Credit to @theemathas for help with macro engineering ^^
r? libs