From 9c64c42c001a897664bfca3d2f896a378c267bd6 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.rs | 3 ++ .../src/encode_cross_crate.rs | 1 + .../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 | 11 ++++++ compiler/rustc_feature/src/builtin_attrs.rs | 5 +++ compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_parse/src/validate_attr.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 | 33 ++++++++++------ src/tools/tidy/src/features.rs | 1 + tests/debuginfo/debuginfo-attrs.rs | 39 +++++++++++++++++++ tests/debuginfo/strings-and-strs.rs | 5 +-- 19 files changed, 115 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_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 3157b18b63515..b55b9981b0050 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -293,6 +293,9 @@ pub enum AttributeKind { /// Represents `#[coverage]`. Coverage(Span, CoverageStatus), + /// Represents `#[debuginfo_transparent]`. + DebuginfoTransparent(Span), + ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 86d9ddba4d230..ff9fed4dc13c7 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/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_attr_parsing/src/attributes/debuginfo.rs b/compiler/rustc_attr_parsing/src/attributes/debuginfo.rs new file mode 100644 index 0000000000000..2b5e20d9d3365 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/debuginfo.rs @@ -0,0 +1,12 @@ +use rustc_attr_data_structures::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 200f138196029..b5af104e2a4d8 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 4d692d9562c1e..69f7cbd61b28a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -22,6 +22,7 @@ use crate::attributes::codegen_attrs::{ 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}; @@ -167,6 +168,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..e55f7849af6d8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -7,6 +7,7 @@ use std::{iter, ptr}; use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Size}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; use rustc_hir::def::{CtorKind, DefKind}; @@ -1070,6 +1071,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 74872504b79f9..6e2bcd7926735 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -666,6 +666,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 e985e04ba3307..5d4ff2dc4012a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -462,6 +462,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_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index a476f0db37e0d..26431dba970a5 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -319,6 +319,7 @@ pub fn check_builtin_meta_item( | sym::no_implicit_prelude | sym::automatically_derived | sym::coverage + | sym::debuginfo_transparent ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3ec6a1124a620..c1e88dc3aed5d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -170,6 +170,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 d28a73bc13978..35439eae8e55c 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 729de5ffc8f6e..d3aa7a76b334d 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 b92561c9e356d..95a6c93a41e18 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 c069314ff7d23..ddf03f6fb79f6 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] // Lang item used experimentally by Miri to define the semantics of `Unique`. #[lang = "ptr_unique"] pub struct Unique { diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index c8f4a32cb17e2..bd8897188b2a7 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,8 @@ 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 +435,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 7d550408bec37..9c5ac4eefe203 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -7,7 +7,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" @@ -19,7 +19,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 @@ -38,7 +38,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)] #![feature(omit_gdb_pretty_printer_section)] #![omit_gdb_pretty_printer_section]