From 43317dab8eab59cc0d500783794de529409941f2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:08:40 +0000 Subject: [PATCH] Add debuginfo_transparent attribute for structs This attribute causes the struct to be unwrapped at the debuginfo level the same way that repr(transparent) unwraps it at the ABI level. This is useful for preventing types like NonNull and Unique from making the debuginfo harder to read when pretty printers aren't used. --- .../src/attributes/debuginfo.rs | 12 ++++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 + .../src/debuginfo/metadata.rs | 12 ++++++ compiler/rustc_feature/src/builtin_attrs.rs | 5 +++ compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 3 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 8 ++++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/lib.rs | 1 + library/core/src/num/niche_types.rs | 1 + library/core/src/ptr/non_null.rs | 1 + library/core/src/ptr/unique.rs | 1 + src/etc/gdb_providers.py | 34 ++++++++++------ src/tools/tidy/src/features.rs | 1 + tests/debuginfo/debuginfo-attrs.rs | 39 +++++++++++++++++++ tests/debuginfo/strings-and-strs.rs | 5 +-- 18 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/debuginfo.rs create mode 100644 tests/debuginfo/debuginfo-attrs.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/debuginfo.rs b/compiler/rustc_attr_parsing/src/attributes/debuginfo.rs new file mode 100644 index 0000000000000..bbb718c8bf43e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/debuginfo.rs @@ -0,0 +1,12 @@ +use rustc_hir::attrs::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; + +pub(crate) struct DebuginfoTransparentParser; +impl NoArgsAttributeParser for DebuginfoTransparentParser { + const PATH: &[Symbol] = &[sym::debuginfo_transparent]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::DebuginfoTransparent; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index c574ef78bdf79..28a5d7cba31e2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -30,6 +30,7 @@ pub(crate) mod cfg; pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; +pub(crate) mod debuginfo; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index c6599f20c2d1f..478465703dce7 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -21,6 +21,7 @@ use crate::attributes::codegen_attrs::{ TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; +use crate::attributes::debuginfo::DebuginfoTransparentParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -184,6 +185,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0e9dbfba658d2..4b5780738126f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -9,8 +9,10 @@ use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Size}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::layout::{ HasTypingEnv, LayoutOf, TyAndLayout, WIDE_PTR_ADDR, WIDE_PTR_EXTRA, @@ -1070,6 +1072,16 @@ fn build_struct_type_di_node<'ll, 'tcx>( None }; + if find_attr!(cx.tcx.get_all_attrs(adt_def.did()), AttributeKind::DebuginfoTransparent(..)) { + let ty = struct_type_and_layout.non_1zst_field(cx).unwrap().1.ty; + + let di_node = type_di_node(cx, ty); + + return_if_di_node_created_in_meantime!(cx, unique_type_id); + + return DINodeCreationResult::new(di_node, false); + } + type_map::build_type_with_children( cx, type_map::stub( diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5c63d4808db2f..a412ddb0b50d5 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -667,6 +667,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, loop_match, experimental!(loop_match) ), + gated!( + debuginfo_transparent, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, debuginfo_attrs, experimental!(debuginfo_transparent) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1303b3317e05a..65d6858f1fbe3 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -460,6 +460,8 @@ declare_features! ( (unstable, custom_inner_attributes, "1.30.0", Some(54726)), /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. (unstable, custom_test_frameworks, "1.30.0", Some(50297)), + /// Allows `debuginfo_*` attributes. + (unstable, debuginfo_attrs, "CURRENT_RUSTC_VERSION", None), /// Allows declarative macros 2.0 (`macro`). (unstable, decl_macro, "1.17.0", Some(39412)), /// Allows the use of default values on struct definitions and the construction of struct diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 80618422b56d6..d6bdc03c69645 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -300,6 +300,9 @@ pub enum AttributeKind { /// Represents `#[coverage(..)]`. Coverage(Span, CoverageAttrKind), + /// Represents `#[debuginfo_transparent]`. + DebuginfoTransparent(Span), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 9644a597a3117..082ccdab7a870 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -29,6 +29,7 @@ impl AttributeKind { ConstStabilityIndirect => No, ConstTrait(..) => No, Coverage(..) => No, + DebuginfoTransparent { .. } => Yes, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2663d5fe99c7a..0bcdc7056e81f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -184,6 +184,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { target, Target::Impl { of_trait: true }, ), + Attribute::Parsed(AttributeKind::DebuginfoTransparent(attr_span)) => self + .check_generic_attr( + hir_id, + sym::debuginfo_transparent, + *attr_span, + target, + Target::Struct, + ), Attribute::Parsed( AttributeKind::Stability { span: attr_span, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d54175548e30e..bc88839ff7fcc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -805,6 +805,8 @@ symbols! { debug_tuple, debug_tuple_fields_finish, debugger_visualizer, + debuginfo_attrs, + debuginfo_transparent, decl_macro, declare_lint_pass, decode, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3c33f4b136844..99d1d6a800911 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -108,6 +108,7 @@ #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] +#![feature(debuginfo_attrs)] #![feature(disjoint_bitor)] #![feature(internal_impls_macro)] #![feature(ip)] diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs index d57b1d433e51c..0b74f874dd335 100644 --- a/library/core/src/num/niche_types.rs +++ b/library/core/src/num/niche_types.rs @@ -18,6 +18,7 @@ macro_rules! define_valid_range_type { #[repr(transparent)] #[rustc_layout_scalar_valid_range_start($low)] #[rustc_layout_scalar_valid_range_end($high)] + #[debuginfo_transparent] $(#[$m])* $vis struct $name($int); diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 62da6567cca75..48d515b6641ac 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -72,6 +72,7 @@ use crate::{fmt, hash, intrinsics, mem, ptr}; #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonNull"] +#[debuginfo_transparent] pub struct NonNull { // Remember to use `.as_ptr()` instead of `.pointer`, as field projecting to // this is banned by . diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index e9e13f9e97f84..3d295d353a274 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -32,6 +32,7 @@ use crate::ptr::NonNull; )] #[doc(hidden)] #[repr(transparent)] +#[debuginfo_transparent] pub struct Unique { pointer: NonNull, // NOTE: this marker has no consequences for variance, but is necessary diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index c8f4a32cb17e2..005c9957eb3a1 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -10,12 +10,15 @@ def unwrap_unique_or_non_null(unique_or_nonnull): - # BACKCOMPAT: rust 1.32 - # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 - # BACKCOMPAT: rust 1.60 - # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f - ptr = unique_or_nonnull["pointer"] - return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]] + if unique_or_nonnull.type.code != gdb.TYPE_CODE_PTR: + # BACKCOMPAT: rust 1.32 + # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 + # BACKCOMPAT: rust 1.60 + # https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f + # BACKCOMPAT: rust 1.89 + ptr = unique_or_nonnull["pointer"] + return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]] + return unique_or_nonnull # GDB 14 has a tag class that indicates that extension methods are ok @@ -255,12 +258,16 @@ def __init__(self, valobj): field = list(fields)[0] inner_valobj = valobj[field.name] + if inner_valobj.type.code != gdb.TYPE_CODE_INT: + # BACKCOMPAT: rust 1.89 + inner_fields = inner_valobj.type.fields() + assert len(inner_fields) == 1 + inner_field = list(inner_fields)[0] - inner_fields = inner_valobj.type.fields() - assert len(inner_fields) == 1 - inner_field = list(inner_fields)[0] + self._value = str(inner_valobj[inner_field.name]) + return - self._value = str(inner_valobj[inner_field.name]) + self._value = inner_valobj def to_string(self): return self._value @@ -277,7 +284,9 @@ def cast_to_internal(node): internal_type = gdb.lookup_type(internal_type_name) return node.cast(internal_type.pointer()) - if node_ptr.type.name.startswith("alloc::collections::btree::node::BoxedNode<"): + if node_ptr.type.name is not None and node_ptr.type.name.startswith( + "alloc::collections::btree::node::BoxedNode<" + ): # BACKCOMPAT: rust 1.49 node_ptr = node_ptr["ptr"] node_ptr = unwrap_unique_or_non_null(node_ptr) @@ -427,7 +436,8 @@ def __init__(self, valobj, show_values=True): table = self._table() table_inner = table["table"] capacity = int(table_inner["bucket_mask"]) + 1 - ctrl = table_inner["ctrl"]["pointer"] + # BACKCOMPAT: rust 1.89 + ctrl = unwrap_unique_or_non_null(table_inner["ctrl"]) self._size = int(table_inner["items"]) self._pair_type = table.type.template_argument(0).strip_typedefs() diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index fb00b3a943f87..c17762c9eceb6 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -100,6 +100,7 @@ pub fn check( walk_many( &[ + &tests_path.join("debuginfo"), &tests_path.join("ui"), &tests_path.join("ui-fulldeps"), &tests_path.join("rustdoc-ui"), diff --git a/tests/debuginfo/debuginfo-attrs.rs b/tests/debuginfo/debuginfo-attrs.rs new file mode 100644 index 0000000000000..a309e38571d29 --- /dev/null +++ b/tests/debuginfo/debuginfo-attrs.rs @@ -0,0 +1,39 @@ +//@ compile-flags:-g + +// gate-test-debuginfo_attrs +// Tests the `#[debuginfo_transparent]` attribute. + +// === CDB TESTS ================================================================================== +// cdb-command: g + +// cdb-command: dx transparent +// cdb-check:transparent : 1 [Type: u32] +// cdb-check: [] [Type: u32] + +// === GDB TESTS =================================================================================== + +// gdb-command:run + +// gdb-command:print transparent +// gdb-check:[...]$1 = 1 + +// === LLDB TESTS ================================================================================== + +// lldb-command:run + +// lldb-command:v transparent +// lldb-check:[...] 1 + +#![feature(debuginfo_attrs)] + +#[repr(transparent)] +#[debuginfo_transparent] +struct Transparent(u32); + +fn main() { + let transparent = Transparent(1); + + zzz(); // #break +} + +fn zzz() {} diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 392cf697e110b..bac75dbc60872 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -8,7 +8,7 @@ // gdb-command:run // gdb-command:print plain_string -// gdb-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {inner: alloc::raw_vec::RawVecInner {ptr: core::ptr::unique::Unique {pointer: core::ptr::non_null::NonNull {pointer: 0x[...]}, _marker: core::marker::PhantomData}, cap: core::num::niche_types::UsizeNoHighBit (5), alloc: alloc::alloc::Global}, _marker: core::marker::PhantomData}, len: 5}} +// gdb-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {inner: alloc::raw_vec::RawVecInner {ptr: 0x[...], cap: 5, alloc: alloc::alloc::Global}, _marker: core::marker::PhantomData}, len: 5}} // gdb-command:print plain_str // gdb-check:$2 = "Hello" @@ -20,7 +20,7 @@ // gdb-check:$4 = ("Hello", "World") // gdb-command:print str_in_rc -// gdb-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull> {pointer: 0x[...]}, phantom: core::marker::PhantomData>, alloc: alloc::alloc::Global} +// gdb-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: 0x[...], alloc: alloc::alloc::Global} // === LLDB TESTS ================================================================================== // lldb-command:run @@ -39,7 +39,6 @@ // lldb-command:v str_in_rc // lldb-check:(alloc::rc::Rc<&str, alloc::alloc::Global>) str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } - #![allow(unused_variables)] pub struct Foo<'a> {