From 07ebc13d87796622bb6e4ac46a7e6a054bc4c680 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 11:15:25 +0100 Subject: [PATCH 1/9] debuginfo: Refactor debuginfo generation for types This commit - changes names to use di_node instead of metadata - uniformly names all functions that build new debuginfo nodes build_xyz_di_node - renames CrateDebugContext to CodegenUnitDebugContext (which is more accurate) - moves TypeMap and functions that work directly work with it to a new type_map module - moves and reimplements enum related builder functions to a new enums module - splits enum debuginfo building for the native and cpp-like cases, since they are mostly separate - uses SmallVec instead of Vec in many places - removes the old infrastructure for dealing with recursion cycles (create_and_register_recursive_type_forward_declaration(), RecursiveTypeDescription, set_members_of_composite_type(), MemberDescription, MemberDescriptionFactory, prepare_xyz_metadata(), etc) - adds type_map::build_type_with_children() as a replacement for dealing with recursion cycles - adds many (doc-)comments explaining what's going on - changes cpp-like naming for C-Style enums so they don't get a enum$<...> name (because the NatVis visualizer does not apply to them) - fixes detection of what is a C-style enum because some enums where classified as C-style even though they have fields - changes the position of discriminant debuginfo node so it is consistently nested inside the top-level union instead of, sometimes, next to it --- compiler/rustc_codegen_llvm/src/allocator.rs | 4 +- compiler/rustc_codegen_llvm/src/consts.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 10 +- .../src/debuginfo/metadata.rs | 2225 ++++------------- .../src/debuginfo/metadata/enums/cpp_like.rs | 515 ++++ .../src/debuginfo/metadata/enums/mod.rs | 428 ++++ .../src/debuginfo/metadata/enums/native.rs | 441 ++++ .../src/debuginfo/metadata/type_map.rs | 271 ++ .../rustc_codegen_llvm/src/debuginfo/mod.rs | 58 +- .../rustc_codegen_llvm/src/debuginfo/utils.rs | 4 +- .../rustc_codegen_ssa/src/debuginfo/mod.rs | 32 + .../src/debuginfo/type_names.rs | 8 +- src/test/codegen/async-fn-debug-msvc.rs | 7 +- src/test/codegen/async-fn-debug.rs | 4 +- src/test/codegen/generator-debug-msvc.rs | 5 +- src/test/codegen/generator-debug.rs | 4 +- src/test/debuginfo/msvc-pretty-enums.rs | 14 +- src/test/debuginfo/type-names.rs | 25 +- 18 files changed, 2293 insertions(+), 1764 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs create mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs create mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs create mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index eb19e42721701..b647cfa5f4a6e 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -140,8 +140,8 @@ pub(crate) unsafe fn codegen( llvm::LLVMDisposeBuilder(llbuilder); if tcx.sess.opts.debuginfo != DebugInfo::None { - let dbg_cx = debuginfo::CrateDebugContext::new(llmod); - debuginfo::metadata::compile_unit_metadata(tcx, module_name, &dbg_cx); + let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod); + debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx); dbg_cx.finalize(tcx.sess); } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 7d3fe43eeab17..413ef0ba76464 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -428,7 +428,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { llvm::LLVMSetGlobalConstant(g, llvm::True); } - debuginfo::create_global_var_metadata(self, def_id, g); + debuginfo::build_global_var_di_node(self, def_id, g); if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { llvm::set_thread_local_mode(g, self.tls_model); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 52e03e0ad3d6b..9fbc33d4b05d4 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -95,7 +95,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub isize_ty: &'ll Type, pub coverage_cx: Option>, - pub dbg_cx: Option>, + pub dbg_cx: Option>, eh_personality: Cell>, eh_catch_typeinfo: Cell>, @@ -396,8 +396,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { }; let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None { - let dctx = debuginfo::CrateDebugContext::new(llmod); - debuginfo::metadata::compile_unit_metadata(tcx, codegen_unit.name().as_str(), &dctx); + let dctx = debuginfo::CodegenUnitDebugContext::new(llmod); + debuginfo::metadata::build_compile_unit_di_node( + tcx, + codegen_unit.name().as_str(), + &dctx, + ); Some(dctx) } else { None diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f16a903ad2c5b..488dbe3456be3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1,21 +1,22 @@ -use self::MemberDescriptionFactory::*; -use self::RecursiveTypeDescription::*; +use self::type_map::DINodeCreationResult; +use self::type_map::Stub; +use self::type_map::UniqueTypeId; use super::namespace::mangled_name_of_instance; use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name}; use super::utils::{ create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, DIB, }; -use super::CrateDebugContext; +use super::CodegenUnitDebugContext; use crate::abi; use crate::common::CodegenCx; +use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::fat_pointer_kind; use crate::debuginfo::utils::FatPtrKind; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, - DebugEmissionKind, + DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind, }; use crate::value::Value; @@ -23,30 +24,26 @@ use cstr::cstr; use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::fx::FxHashMap; use rustc_fs_util::path_to_c_string; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{self, GeneratorLayout}; -use rustc_middle::ty::layout::{self, IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{ - self, AdtKind, GeneratorSubsts, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES, -}; +use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; use rustc_span::FileNameDisplayPreference; use rustc_span::{self, SourceFile, SourceFileHash}; -use rustc_target::abi::{Abi, Align, HasDataLayout, Integer, TagEncoding}; -use rustc_target::abi::{Int, Pointer, F32, F64}; -use rustc_target::abi::{Primitive, Size, VariantIdx, Variants}; -use smallvec::SmallVec; +use rustc_target::abi::{Align, Size}; +use smallvec::smallvec; use tracing::debug; use libc::{c_longlong, c_uint}; -use std::cell::RefCell; +use std::borrow::Cow; use std::collections::hash_map::Entry; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; @@ -88,250 +85,51 @@ const DW_ATE_unsigned: c_uint = 0x07; #[allow(non_upper_case_globals)] const DW_ATE_UTF: c_uint = 0x10; -pub const UNKNOWN_LINE_NUMBER: c_uint = 0; -pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; - -pub const NO_SCOPE_METADATA: Option<&DIScope> = None; - -mod unique_type_id { - use rustc_data_structures::{ - fingerprint::Fingerprint, - stable_hasher::{HashStable, NodeIdHashingMode, StableHasher}, - }; - use rustc_middle::ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; - use rustc_target::abi::VariantIdx; - - // This type cannot be constructed outside of this module because - // it has a private field. We make use of this in order to prevent - // `UniqueTypeId` from being constructed directly, without asserting - // the preconditions. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] - pub struct HiddenZst { - _inaccessible: (), - } - - /// A unique identifier for anything that we create a debuginfo node for. - /// The types it contains are expected to already be normalized (which - /// is debug_asserted in the constructors). - /// - /// Note that there are some things that only show up in debuginfo, like - /// the separate type descriptions for each enum variant. These get an ID - /// too because they have their own debuginfo node in LLVM IR. - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] - pub(super) enum UniqueTypeId<'tcx> { - /// The ID of a regular type as it shows up at the language level. - Ty(Ty<'tcx>, HiddenZst), - /// The ID for the artificial struct type describing a single enum variant. - Variant(Ty<'tcx>, VariantIdx, HiddenZst), - /// The ID for the single DW_TAG_variant_part nested inside the top-level - /// DW_TAG_structure_type that describes enums and generators. - VariantPart(Ty<'tcx>, HiddenZst), - /// The ID of the artificial type we create for VTables. - VTableTy(Ty<'tcx>, Option>, HiddenZst), - } - - impl<'tcx> UniqueTypeId<'tcx> { - pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self { - debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)); - UniqueTypeId::Ty(t, HiddenZst { _inaccessible: () }) - } - - pub fn for_enum_variant( - tcx: TyCtxt<'tcx>, - enum_ty: Ty<'tcx>, - variant_idx: VariantIdx, - ) -> Self { - debug_assert_eq!( - enum_ty, - tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty) - ); - UniqueTypeId::Variant(enum_ty, variant_idx, HiddenZst { _inaccessible: () }) - } - - pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self { - debug_assert_eq!( - enum_ty, - tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty) - ); - UniqueTypeId::VariantPart(enum_ty, HiddenZst { _inaccessible: () }) - } - - pub fn for_vtable_ty( - tcx: TyCtxt<'tcx>, - self_type: Ty<'tcx>, - implemented_trait: Option>, - ) -> Self { - debug_assert_eq!( - self_type, - tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type) - ); - debug_assert_eq!( - implemented_trait, - tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait) - ); - UniqueTypeId::VTableTy(self_type, implemented_trait, HiddenZst { _inaccessible: () }) - } +pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; - /// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId` - /// argument of the various `LLVMRustDIBuilderCreate*Type()` methods. - /// - /// Right now this takes the form of a hex-encoded opaque hash value. - pub fn generate_unique_id_string(&self, tcx: TyCtxt<'tcx>) -> String { - let mut hasher = StableHasher::new(); - let mut hcx = tcx.create_stable_hashing_context(); - hcx.while_hashing_spans(false, |hcx| { - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - self.hash_stable(hcx, &mut hasher); - }); - }); - hasher.finish::().to_hex() - } - } -} -use unique_type_id::*; +const NO_SCOPE_METADATA: Option<&DIScope> = None; +/// A function that returns an empty list of generic parameter debuginfo nodes. +const NO_GENERICS: for<'ll> fn(&CodegenCx<'ll, '_>) -> SmallVec<&'ll DIType> = |_| SmallVec::new(); -/// The `TypeMap` is where the debug context holds the type metadata nodes -/// created so far. The metadata nodes are indexed by `UniqueTypeId`. -#[derive(Default)] -pub struct TypeMap<'ll, 'tcx> { - unique_id_to_metadata: RefCell, &'ll DIType>>, -} +// SmallVec is used quite a bit in this module, so create a shorthand. +// The actual number of elements is not so important. +pub type SmallVec = smallvec::SmallVec<[T; 16]>; -impl<'ll, 'tcx> TypeMap<'ll, 'tcx> { - /// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will - /// fail if the mapping already exists. - fn register_unique_id_with_metadata( - &self, - unique_type_id: UniqueTypeId<'tcx>, - metadata: &'ll DIType, - ) { - if self.unique_id_to_metadata.borrow_mut().insert(unique_type_id, metadata).is_some() { - bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id); - } - } +mod enums; +mod type_map; - fn find_metadata_for_unique_id( - &self, - unique_type_id: UniqueTypeId<'tcx>, - ) -> Option<&'ll DIType> { - self.unique_id_to_metadata.borrow().get(&unique_type_id).cloned() - } -} +pub(crate) use type_map::TypeMap; -/// A description of some recursive type. It can either be already finished (as -/// with `FinalMetadata`) or it is not yet finished, but contains all information -/// needed to generate the missing parts of the description. See the -/// documentation section on Recursive Types at the top of this file for more -/// information. -enum RecursiveTypeDescription<'ll, 'tcx> { - UnfinishedMetadata { - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId<'tcx>, - metadata_stub: &'ll DICompositeType, - member_holding_stub: &'ll DICompositeType, - member_description_factory: MemberDescriptionFactory<'ll, 'tcx>, - }, - FinalMetadata(&'ll DICompositeType), -} - -fn create_and_register_recursive_type_forward_declaration<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId<'tcx>, - metadata_stub: &'ll DICompositeType, - member_holding_stub: &'ll DICompositeType, - member_description_factory: MemberDescriptionFactory<'ll, 'tcx>, -) -> RecursiveTypeDescription<'ll, 'tcx> { - // Insert the stub into the `TypeMap` in order to allow for recursive references. - debug_context(cx).type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub); - - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - member_holding_stub, - member_description_factory, - } -} - -impl<'ll, 'tcx> RecursiveTypeDescription<'ll, 'tcx> { - /// Finishes up the description of the type in question (mostly by providing - /// descriptions of the fields of the given type) and returns the final type - /// metadata. - fn finalize(&self, cx: &CodegenCx<'ll, 'tcx>) -> MetadataCreationResult<'ll> { - match *self { - FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - member_holding_stub, - ref member_description_factory, - } => { - // Make sure that we have a forward declaration of the type in - // the TypeMap so that recursive references are possible. This - // will always be the case if the RecursiveTypeDescription has - // been properly created through the - // `create_and_register_recursive_type_forward_declaration()` - // function. - { - if debug_context(cx) - .type_map - .find_metadata_for_unique_id(unique_type_id) - .is_none() - { - bug!( - "Forward declaration of potentially recursive type \ - '{:?}' was not found in TypeMap!", - unfinished_type - ); - } - } - - // ... then create the member descriptions ... - let member_descriptions = member_description_factory.create_member_descriptions(cx); - let type_params = compute_type_parameters(cx, unfinished_type); - - // ... and attach them to the stub to complete it. - set_members_of_composite_type( - cx, - member_holding_stub, - member_descriptions, - None, - type_params, - ); - MetadataCreationResult::new(metadata_stub, true) - } - } - } -} - -/// Returns from the enclosing function if the type metadata with the given +/// Returns from the enclosing function if the type debuginfo node with the given /// unique ID can be found in the type map. -macro_rules! return_if_metadata_created_in_meantime { +macro_rules! return_if_di_node_created_in_meantime { ($cx: expr, $unique_type_id: expr) => { - if let Some(metadata) = - debug_context($cx).type_map.find_metadata_for_unique_id($unique_type_id) - { - return MetadataCreationResult::new(metadata, true); + if let Some(di_node) = debug_context($cx).type_map.di_node_for_unique_id($unique_type_id) { + return DINodeCreationResult::new(di_node, true); } }; } +/// Extract size and alignment from a TyAndLayout. +fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) { + (ty_and_layout.size, ty_and_layout.align.abi) +} + /// Creates debuginfo for a fixed size array (e.g. `[u64; 123]`). -/// For slices (that is, "arrays" of unknown size) use [slice_type_metadata]. -fn fixed_size_array_metadata<'ll, 'tcx>( +/// For slices (that is, "arrays" of unknown size) use [build_slice_type_di_node]. +fn build_fixed_size_array_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, array_type: Ty<'tcx>, -) -> MetadataCreationResult<'ll> { +) -> DINodeCreationResult<'ll> { let ty::Array(element_type, len) = array_type.kind() else { - bug!("fixed_size_array_metadata() called with non-ty::Array type `{:?}`", array_type) + bug!("build_fixed_size_array_di_node() called with non-ty::Array type `{:?}`", array_type) }; - let element_type_metadata = type_metadata(cx, *element_type); + let element_type_di_node = type_di_node(cx, *element_type); - return_if_metadata_created_in_meantime!(cx, unique_type_id); + return_if_di_node_created_in_meantime!(cx, unique_type_id); let (size, align) = cx.size_and_align_of(array_type); @@ -341,17 +139,17 @@ fn fixed_size_array_metadata<'ll, 'tcx>( unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; let subscripts = create_DIArray(DIB(cx), &[subrange]); - let metadata = unsafe { + let di_node = unsafe { llvm::LLVMRustDIBuilderCreateArrayType( DIB(cx), size.bits(), align.bits() as u32, - element_type_metadata, + element_type_di_node, subscripts, ) }; - MetadataCreationResult::new(metadata, false) + DINodeCreationResult::new(di_node, false) } /// Creates debuginfo for built-in pointer-like things: @@ -362,21 +160,21 @@ fn fixed_size_array_metadata<'ll, 'tcx>( /// /// At some point we might want to remove the special handling of Box /// and treat it the same as other smart pointers (like Rc, Arc, ...). -fn pointer_or_reference_metadata<'ll, 'tcx>( +fn build_pointer_or_reference_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ptr_type: Ty<'tcx>, pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> MetadataCreationResult<'ll> { - let pointee_type_metadata = type_metadata(cx, pointee_type); +) -> DINodeCreationResult<'ll> { + let pointee_type_di_node = type_di_node(cx, pointee_type); - return_if_metadata_created_in_meantime!(cx, unique_type_id); + return_if_di_node_created_in_meantime!(cx, unique_type_id); let (thin_pointer_size, thin_pointer_align) = cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.types.unit)); let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true); - let pointer_type_metadata = match fat_pointer_kind(cx, pointee_type) { + match fat_pointer_kind(cx, pointee_type) { None => { // This is a thin pointer. Create a regular pointer type and give it the correct name. debug_assert_eq!( @@ -387,87 +185,90 @@ fn pointer_or_reference_metadata<'ll, 'tcx>( pointee_type, ); - unsafe { + let di_node = unsafe { llvm::LLVMRustDIBuilderCreatePointerType( DIB(cx), - pointee_type_metadata, + pointee_type_di_node, thin_pointer_size.bits(), thin_pointer_align.bits() as u32, 0, // Ignore DWARF address space. ptr_type_debuginfo_name.as_ptr().cast(), ptr_type_debuginfo_name.len(), ) - } + }; + + DINodeCreationResult { di_node, already_stored_in_typemap: false } } Some(fat_pointer_kind) => { - let layout = cx.layout_of(ptr_type); - - let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); - let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); - - let (addr_field_name, extra_field_name) = match fat_pointer_kind { - FatPtrKind::Dyn => ("pointer", "vtable"), - FatPtrKind::Slice => ("data_ptr", "length"), - }; + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &ptr_type_debuginfo_name, + cx.size_and_align_of(ptr_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, owner| { + let layout = cx.layout_of(ptr_type); + let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); + let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); + + let (addr_field_name, extra_field_name) = match fat_pointer_kind { + FatPtrKind::Dyn => ("pointer", "vtable"), + FatPtrKind::Slice => ("data_ptr", "length"), + }; - debug_assert_eq!(abi::FAT_PTR_ADDR, 0); - debug_assert_eq!(abi::FAT_PTR_EXTRA, 1); + debug_assert_eq!(abi::FAT_PTR_ADDR, 0); + debug_assert_eq!(abi::FAT_PTR_EXTRA, 1); - // The data pointer type is a regular, thin pointer, regardless of whether this is a slice - // or a trait object. - let data_ptr_type_metadata = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - addr_field.size.bits(), - addr_field.align.abi.bits() as u32, - 0, // Ignore DWARF address space. - std::ptr::null(), - 0, - ) - }; + // The data pointer type is a regular, thin pointer, regardless of whether this + // is a slice or a trait object. + let data_ptr_type_di_node = unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_di_node, + addr_field.size.bits(), + addr_field.align.abi.bits() as u32, + 0, // Ignore DWARF address space. + std::ptr::null(), + 0, + ) + }; - let member_descriptions = vec![ - MemberDescription { - name: addr_field_name.into(), - type_metadata: data_ptr_type_metadata, - offset: layout.fields.offset(abi::FAT_PTR_ADDR), - size: addr_field.size, - align: addr_field.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - MemberDescription { - name: extra_field_name.into(), - type_metadata: type_metadata(cx, extra_field.ty), - offset: layout.fields.offset(abi::FAT_PTR_EXTRA), - size: extra_field.size, - align: extra_field.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, + smallvec![ + build_field_di_node( + cx, + owner, + addr_field_name, + (addr_field.size, addr_field.align.abi), + layout.fields.offset(abi::FAT_PTR_ADDR), + DIFlags::FlagZero, + data_ptr_type_di_node, + ), + build_field_di_node( + cx, + owner, + extra_field_name, + (extra_field.size, extra_field.align.abi), + layout.fields.offset(abi::FAT_PTR_EXTRA), + DIFlags::FlagZero, + type_di_node(cx, extra_field.ty), + ), + ] }, - ]; - - composite_type_metadata( - cx, - ptr_type, - &ptr_type_debuginfo_name, - unique_type_id, - member_descriptions, - NO_SCOPE_METADATA, + NO_GENERICS, ) } - }; - - MetadataCreationResult { metadata: pointer_type_metadata, already_stored_in_typemap: false } + } } -fn subroutine_type_metadata<'ll, 'tcx>( +fn build_subroutine_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> MetadataCreationResult<'ll> { +) -> DINodeCreationResult<'ll> { // It's possible to create a self-referential // type in Rust by using 'impl trait': // @@ -483,49 +284,46 @@ fn subroutine_type_metadata<'ll, 'tcx>( // Once that is created, we replace the marker in the typemap with the actual type. debug_context(cx) .type_map - .unique_id_to_metadata + .unique_id_to_di_node .borrow_mut() - .insert(unique_type_id, recursion_marker_type(cx)); - - let UniqueTypeId::Ty(fn_ty, _) = unique_type_id else { - bug!("subroutine_type_metadata() called with unexpected input type: {:?}", unique_type_id) - }; + .insert(unique_type_id, recursion_marker_type_di_node(cx)); + let fn_ty = unique_type_id.expect_ty(); let signature = cx .tcx .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), fn_ty.fn_sig(cx.tcx)); - let signature_metadata: SmallVec<[_; 32]> = iter::once( + let signature_di_nodes: SmallVec<_> = iter::once( // return type match signature.output().kind() { ty::Tuple(tys) if tys.is_empty() => { // this is a "void" function None } - _ => Some(type_metadata(cx, signature.output())), + _ => Some(type_di_node(cx, signature.output())), }, ) .chain( // regular arguments - signature.inputs().iter().map(|&argument_type| Some(type_metadata(cx, argument_type))), + signature.inputs().iter().map(|&argument_type| Some(type_di_node(cx, argument_type))), ) .collect(); - debug_context(cx).type_map.unique_id_to_metadata.borrow_mut().remove(&unique_type_id); + debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); - let fn_metadata = unsafe { + let fn_di_node = unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType( DIB(cx), - create_DIArray(DIB(cx), &signature_metadata[..]), + create_DIArray(DIB(cx), &signature_di_nodes[..]), ) }; // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); - let metadata = unsafe { + let di_node = unsafe { llvm::LLVMRustDIBuilderCreatePointerType( DIB(cx), - fn_metadata, + fn_di_node, cx.tcx.data_layout.pointer_size.bits(), cx.tcx.data_layout.pointer_align.abi.bits() as u32, 0, // Ignore DWARF address space. @@ -534,19 +332,32 @@ fn subroutine_type_metadata<'ll, 'tcx>( ) }; - MetadataCreationResult::new(metadata, false) + DINodeCreationResult::new(di_node, false) } /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs /// we with the correct type name (e.g. "dyn SomeTrait + Sync"). -fn dyn_type_metadata<'ll, 'tcx>( +fn build_dyn_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, dyn_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> &'ll DIType { +) -> DINodeCreationResult<'ll> { if let ty::Dynamic(..) = dyn_type.kind() { let type_name = compute_debuginfo_type_name(cx.tcx, dyn_type, true); - composite_type_metadata(cx, dyn_type, &type_name, unique_type_id, vec![], NO_SCOPE_METADATA) + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + cx.size_and_align_of(dyn_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |_, _| smallvec![], + NO_GENERICS, + ) } else { bug!("Only ty::Dynamic is valid for dyn_type_metadata(). Found {:?} instead.", dyn_type) } @@ -569,11 +380,11 @@ fn dyn_type_metadata<'ll, 'tcx>( /// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the /// slice is zero, then accessing `unsized_field` in the debugger would /// result in an out-of-bounds access. -fn slice_type_metadata<'ll, 'tcx>( +fn build_slice_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, slice_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> MetadataCreationResult<'ll> { +) -> DINodeCreationResult<'ll> { let element_type = match slice_type.kind() { ty::Slice(element_type) => *element_type, ty::Str => cx.tcx.types.u8, @@ -585,82 +396,67 @@ fn slice_type_metadata<'ll, 'tcx>( } }; - let element_type_metadata = type_metadata(cx, element_type); - return_if_metadata_created_in_meantime!(cx, unique_type_id); - MetadataCreationResult { metadata: element_type_metadata, already_stored_in_typemap: false } + let element_type_metadata = type_di_node(cx, element_type); + return_if_di_node_created_in_meantime!(cx, unique_type_id); + DINodeCreationResult { di_node: element_type_metadata, already_stored_in_typemap: false } } -pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { +/// Get the debuginfo node for the given type. +/// +/// This function will look up the debuginfo node in the TypeMap. If it can't find it, it +/// will create the node by dispatching to the corresponding `build_*_di_node()` function. +pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { let unique_type_id = UniqueTypeId::for_ty(cx.tcx, t); - if let Some(metadata) = debug_context(cx).type_map.find_metadata_for_unique_id(unique_type_id) { - return metadata; + if let Some(existing_di_node) = debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) + { + return existing_di_node; } - debug!("type_metadata: {:?}", t); + debug!("type_di_node: {:?}", t); - let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() { + let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() { ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) + DINodeCreationResult::new(build_basic_type_di_node(cx, t), false) } ty::Tuple(elements) if elements.is_empty() => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::Array(..) => fixed_size_array_metadata(cx, unique_type_id, t), - ty::Slice(_) | ty::Str => slice_type_metadata(cx, t, unique_type_id), - ty::Dynamic(..) => { - MetadataCreationResult::new(dyn_type_metadata(cx, t, unique_type_id), false) - } - ty::Foreign(..) => { - MetadataCreationResult::new(foreign_type_metadata(cx, t, unique_type_id), false) + DINodeCreationResult::new(build_basic_type_di_node(cx, t), false) } + ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t), + ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), + ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), + ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { - pointer_or_reference_metadata(cx, t, pointee_type, unique_type_id) + build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id) } // Box may have a non-ZST allocator A. In that case, we // cannot treat Box as just an owned alias of `*mut T`. ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => { - pointer_or_reference_metadata(cx, t, t.boxed_ty(), unique_type_id) - } - ty::FnDef(..) | ty::FnPtr(_) => subroutine_type_metadata(cx, unique_type_id), - ty::Closure(def_id, substs) => { - let upvar_tys: Vec<_> = substs.as_closure().upvar_tys().collect(); - let containing_scope = get_namespace_for_item(cx, def_id); - prepare_tuple_metadata(cx, t, &upvar_tys, unique_type_id, Some(containing_scope)) - .finalize(cx) - } - ty::Generator(def_id, substs, _) => { - let upvar_tys: Vec<_> = substs - .as_generator() - .prefix_tys() - .map(|t| cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) - .collect(); - prepare_enum_metadata(cx, t, def_id, unique_type_id, upvar_tys).finalize(cx) + build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id) } + ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id), + ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id), + ty::Generator(..) => enums::build_generator_di_node(cx, unique_type_id), ty::Adt(def, ..) => match def.adt_kind() { - AdtKind::Struct => prepare_struct_metadata(cx, t, unique_type_id).finalize(cx), - AdtKind::Union => prepare_union_metadata(cx, t, unique_type_id).finalize(cx), - AdtKind::Enum => { - prepare_enum_metadata(cx, t, def.did(), unique_type_id, vec![]).finalize(cx) - } + AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), + AdtKind::Union => build_union_type_di_node(cx, unique_type_id), + AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id), }, - ty::Tuple(tys) => { - prepare_tuple_metadata(cx, t, tys, unique_type_id, NO_SCOPE_METADATA).finalize(cx) - } + ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), // Type parameters from polymorphized functions. - ty::Param(_) => MetadataCreationResult::new(param_type_metadata(cx, t), false), + ty::Param(_) => build_param_type_di_node(cx, t), _ => bug!("debuginfo: unexpected type in type_metadata: {:?}", t), }; { if already_stored_in_typemap { // Make sure that we really do have a `TypeMap` entry for the unique type ID. - let metadata_for_uid = - match debug_context(cx).type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => metadata, + let di_node_for_uid = + match debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) { + Some(di_node) => di_node, None => { bug!( - "expected type metadata for unique \ + "expected type di_node for unique \ type ID '{:?}' to already be in \ the `debuginfo::TypeMap` but it \ was not.", @@ -669,16 +465,17 @@ pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll } }; - debug_assert_eq!(metadata_for_uid as *const _, metadata as *const _); + debug_assert_eq!(di_node_for_uid as *const _, di_node as *const _); } else { - debug_context(cx).type_map.register_unique_id_with_metadata(unique_type_id, metadata); + debug_context(cx).type_map.insert(unique_type_id, di_node); } } - metadata + di_node } -fn recursion_marker_type<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType { +// FIXME(mw): Cache this via a regular UniqueTypeId instead of an extra field in the debug context. +fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType { *debug_context(cx).recursion_marker_type.get_or_init(move || { unsafe { // The choice of type here is pretty arbitrary - @@ -823,8 +620,8 @@ impl MsvcBasicName for ty::FloatTy { } } -fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { - debug!("basic_type_metadata: {:?}", t); +fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { + debug!("build_basic_type_di_node: {:?}", t); // When targeting MSVC, emit MSVC style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) @@ -841,10 +638,10 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed), ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned), ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float), - _ => bug!("debuginfo::basic_type_metadata - `t` is invalid type"), + _ => bug!("debuginfo::build_basic_type_di_node - `t` is invalid type"), }; - let ty_metadata = unsafe { + let ty_di_node = unsafe { llvm::LLVMRustDIBuilderCreateBasicType( DIB(cx), name.as_ptr().cast(), @@ -855,20 +652,20 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l }; if !cpp_like_debuginfo { - return ty_metadata; + return ty_di_node; } let typedef_name = match t.kind() { ty::Int(int_ty) => int_ty.name_str(), ty::Uint(uint_ty) => uint_ty.name_str(), ty::Float(float_ty) => float_ty.name_str(), - _ => return ty_metadata, + _ => return ty_di_node, }; - let typedef_metadata = unsafe { + let typedef_di_node = unsafe { llvm::LLVMRustDIBuilderCreateTypedef( DIB(cx), - ty_metadata, + ty_di_node, typedef_name.as_ptr().cast(), typedef_name.len(), unknown_file_metadata(cx), @@ -877,48 +674,60 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l ) }; - typedef_metadata + typedef_di_node } -fn foreign_type_metadata<'ll, 'tcx>( +fn build_foreign_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> &'ll DIType { - debug!("foreign_type_metadata: {:?}", t); +) -> DINodeCreationResult<'ll> { + debug!("build_foreign_type_di_node: {:?}", t); + + let &ty::Foreign(def_id) = unique_type_id.expect_ty().kind() else { + bug!("build_foreign_type_di_node() called with unexpected type: {:?}", unique_type_id.expect_ty()); + }; - let name = compute_debuginfo_type_name(cx.tcx, t, false); - let (size, align) = cx.size_and_align_of(t); - create_struct_stub( + build_type_with_children( cx, - size, - align, - &name, - unique_type_id, - NO_SCOPE_METADATA, - DIFlags::FlagZero, - None, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &compute_debuginfo_type_name(cx.tcx, t, false), + cx.size_and_align_of(t), + Some(get_namespace_for_item(cx, def_id)), + DIFlags::FlagZero, + ), + |_, _| smallvec![], + NO_GENERICS, ) } -fn param_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { - debug!("param_type_metadata: {:?}", t); +fn build_param_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + t: Ty<'tcx>, +) -> DINodeCreationResult<'ll> { + debug!("build_param_type_di_node: {:?}", t); let name = format!("{:?}", t); - unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_ptr().cast(), - name.len(), - Size::ZERO.bits(), - DW_ATE_unsigned, - ) + DINodeCreationResult { + di_node: unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr().cast(), + name.len(), + Size::ZERO.bits(), + DW_ATE_unsigned, + ) + }, + already_stored_in_typemap: false, } } -pub fn compile_unit_metadata<'ll, 'tcx>( +pub fn build_compile_unit_di_node<'ll, 'tcx>( tcx: TyCtxt<'tcx>, codegen_unit_name: &str, - debug_context: &CrateDebugContext<'ll, 'tcx>, + debug_context: &CodegenUnitDebugContext<'ll, 'tcx>, ) -> &'ll DIDescriptor { let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { Some(ref path) => path.clone(), @@ -1075,165 +884,86 @@ pub fn compile_unit_metadata<'ll, 'tcx>( } } -struct MetadataCreationResult<'ll> { - metadata: &'ll DIType, - already_stored_in_typemap: bool, -} - -impl<'ll> MetadataCreationResult<'ll> { - fn new(metadata: &'ll DIType, already_stored_in_typemap: bool) -> Self { - MetadataCreationResult { metadata, already_stored_in_typemap } - } -} - -#[derive(Debug)] -struct SourceInfo<'ll> { - file: &'ll DIFile, - line: u32, -} - -/// Description of a type member, which can either be a regular field (as in -/// structs or tuples) or an enum variant. -#[derive(Debug)] -struct MemberDescription<'ll> { - name: String, - type_metadata: &'ll DIType, +/// Creates a `DW_TAG_member` entry inside the DIE represented by the given `type_di_node`. +fn build_field_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + owner: &'ll DIScope, + name: &str, + size_and_align: (Size, Align), offset: Size, - size: Size, - align: Align, flags: DIFlags, - discriminant: Option, - source_info: Option>, -} - -impl<'ll> MemberDescription<'ll> { - fn into_metadata( - self, - cx: &CodegenCx<'ll, '_>, - composite_type_metadata: &'ll DIScope, - ) -> &'ll DIType { - let (file, line) = self - .source_info - .map(|info| (info.file, info.line)) - .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); - unsafe { - llvm::LLVMRustDIBuilderCreateVariantMemberType( - DIB(cx), - composite_type_metadata, - self.name.as_ptr().cast(), - self.name.len(), - file, - line, - self.size.bits(), - self.align.bits() as u32, - self.offset.bits(), - self.discriminant.map(|v| cx.const_u64(v)), - self.flags, - self.type_metadata, - ) - } - } -} - -/// A factory for `MemberDescription`s. It produces a list of member descriptions -/// for some record-like type. `MemberDescriptionFactory`s are used to defer the -/// creation of type member descriptions in order to break cycles arising from -/// recursive type definitions. -enum MemberDescriptionFactory<'ll, 'tcx> { - StructMDF(StructMemberDescriptionFactory<'tcx>), - TupleMDF(TupleMemberDescriptionFactory<'tcx>), - EnumMDF(EnumMemberDescriptionFactory<'ll, 'tcx>), - UnionMDF(UnionMemberDescriptionFactory<'tcx>), - VariantMDF(VariantMemberDescriptionFactory<'tcx>), -} - -impl<'ll, 'tcx> MemberDescriptionFactory<'ll, 'tcx> { - fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec> { - match *self { - StructMDF(ref this) => this.create_member_descriptions(cx), - TupleMDF(ref this) => this.create_member_descriptions(cx), - EnumMDF(ref this) => this.create_member_descriptions(cx), - UnionMDF(ref this) => this.create_member_descriptions(cx), - VariantMDF(ref this) => this.create_member_descriptions(cx), - } - } -} - -//=----------------------------------------------------------------------------- -// Structs -//=----------------------------------------------------------------------------- - -/// Creates `MemberDescription`s for the fields of a struct. -struct StructMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, -} - -impl<'tcx> StructMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'ll>( - &self, - cx: &CodegenCx<'ll, 'tcx>, - ) -> Vec> { - let layout = cx.layout_of(self.ty); - self.variant - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let name = if self.variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - f.name.to_string() - }; - let field = layout.field(cx, i); - MemberDescription { - name, - type_metadata: type_metadata(cx, field.ty), - offset: layout.fields.offset(i), - size: field.size, - align: field.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - } - }) - .collect() + type_di_node: &'ll DIType, +) -> &'ll DIType { + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + owner, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size_and_align.0.bits(), + size_and_align.1.bits() as u32, + offset.bits(), + flags, + type_di_node, + ) } } -fn prepare_struct_metadata<'ll, 'tcx>( +/// Creates the debuginfo node for a Rust struct type. Maybe be a regular struct or a tuple-struct. +fn build_struct_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - struct_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> RecursiveTypeDescription<'ll, 'tcx> { - let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false); - - let (struct_def_id, variant) = match struct_type.kind() { - ty::Adt(def, _) => (def.did(), def.non_enum_variant()), - _ => bug!("prepare_struct_metadata on a non-ADT"), +) -> DINodeCreationResult<'ll> { + let struct_type = unique_type_id.expect_ty(); + let ty::Adt(adt_def, _) = struct_type.kind() else { + bug!("build_struct_type_di_node() called with non-struct-type: {:?}", struct_type); }; + debug_assert!(adt_def.is_struct()); + let containing_scope = get_namespace_for_item(cx, adt_def.did()); + let struct_type_and_layout = cx.layout_of(struct_type); + let variant_def = adt_def.non_enum_variant(); - let containing_scope = get_namespace_for_item(cx, struct_def_id); - let (size, align) = cx.size_and_align_of(struct_type); - - let struct_metadata_stub = create_struct_stub( + type_map::build_type_with_children( cx, - size, - align, - &struct_name, - unique_type_id, - Some(containing_scope), - DIFlags::FlagZero, - None, - ); - - create_and_register_recursive_type_forward_declaration( - cx, - struct_type, - unique_type_id, - struct_metadata_stub, - struct_metadata_stub, - StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant }), + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &compute_debuginfo_type_name(cx.tcx, struct_type, false), + size_and_align_of(struct_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| { + variant_def + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field_name = if variant_def.ctor_kind == CtorKind::Fn { + // This is a tuple struct + tuple_field_name(i) + } else { + // This is struct with named fields + Cow::Borrowed(f.name.as_str()) + }; + let field_layout = struct_type_and_layout.field(cx, i); + build_field_di_node( + cx, + owner, + &field_name[..], + (field_layout.size, field_layout.align.abi), + struct_type_and_layout.fields.offset(i), + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + |cx| build_generic_type_param_di_nodes(cx, struct_type), ) } @@ -1246,7 +976,9 @@ fn prepare_struct_metadata<'ll, 'tcx>( /// Here are some examples: /// - `name__field1__field2` when the upvar is captured by value. /// - `_ref__name__field` when the upvar is captured by reference. -fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> Vec { +/// +/// For generators this only contains upvars that are shared by all states. +fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec { let body = tcx.optimized_mir(def_id); body.var_debug_info @@ -1263,145 +995,184 @@ fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> let prefix = if is_ref { "_ref__" } else { "" }; Some(prefix.to_owned() + var.name.as_str()) }) - .collect::>() + .collect() } -/// Creates `MemberDescription`s for the fields of a tuple. -struct TupleMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - component_types: Vec>, -} +/// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator. +/// For a generator, this will handle upvars shared by all states. +fn build_upvar_field_di_nodes<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + closure_or_generator_ty: Ty<'tcx>, + closure_or_generator_metadata: &'ll DIType, +) -> SmallVec<&'ll DIType> { + let (&def_id, up_var_tys) = match closure_or_generator_ty.kind() { + ty::Generator(def_id, substs, _) => { + let upvar_tys: SmallVec<_> = substs.as_generator().prefix_tys().collect(); + (def_id, upvar_tys) + } + ty::Closure(def_id, substs) => { + let upvar_tys: SmallVec<_> = substs.as_closure().upvar_tys().collect(); + (def_id, upvar_tys) + } + _ => { + bug!( + "new_upvar_member_descriptions() called with non-closure-or-generator-type: {:?}", + closure_or_generator_ty + ) + } + }; -impl<'tcx> TupleMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'ll>( - &self, - cx: &CodegenCx<'ll, 'tcx>, - ) -> Vec> { - let mut capture_names = match *self.ty.kind() { - ty::Generator(def_id, ..) | ty::Closure(def_id, ..) => { - Some(closure_saved_names_of_captured_variables(cx.tcx, def_id).into_iter()) - } - _ => None, - }; - let layout = cx.layout_of(self.ty); - self.component_types - .iter() - .enumerate() - .map(|(i, &component_type)| { - let (size, align) = cx.size_and_align_of(component_type); - let name = if let Some(names) = capture_names.as_mut() { - names.next().unwrap() - } else { - format!("__{}", i) - }; - MemberDescription { - name, - type_metadata: type_metadata(cx, component_type), - offset: layout.fields.offset(i), - size, - align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - } - }) - .collect() - } + debug_assert!(up_var_tys + .iter() + .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t))); + + let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id); + let layout = cx.layout_of(closure_or_generator_ty); + + up_var_tys + .into_iter() + .zip(capture_names.iter()) + .enumerate() + .map(|(index, (up_var_ty, capture_name))| { + build_field_di_node( + cx, + closure_or_generator_metadata, + capture_name, + cx.size_and_align_of(up_var_ty), + layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, up_var_ty), + ) + }) + .collect() } -fn prepare_tuple_metadata<'ll, 'tcx>( +/// Builds the DW_TAG_structure_type debuginfo node for a Rust tuple type. +fn build_tuple_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - tuple_type: Ty<'tcx>, - component_types: &[Ty<'tcx>], unique_type_id: UniqueTypeId<'tcx>, - containing_scope: Option<&'ll DIScope>, -) -> RecursiveTypeDescription<'ll, 'tcx> { - let (size, align) = cx.size_and_align_of(tuple_type); - let tuple_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false); +) -> DINodeCreationResult<'ll> { + let tuple_type = unique_type_id.expect_ty(); + let &ty::Tuple(component_types) = tuple_type.kind() else { + bug!("build_tuple_type_di_node() called with non-tuple-type: {:?}", tuple_type) + }; - let struct_stub = create_struct_stub( - cx, - size, - align, - &tuple_name[..], - unique_type_id, - containing_scope, - DIFlags::FlagZero, - None, - ); - - create_and_register_recursive_type_forward_declaration( + let tuple_type_and_layout = cx.layout_of(tuple_type); + let type_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false); + + type_map::build_type_with_children( cx, - tuple_type, - unique_type_id, - struct_stub, - struct_stub, - TupleMDF(TupleMemberDescriptionFactory { - ty: tuple_type, - component_types: component_types.to_vec(), - }), + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + size_and_align_of(tuple_type_and_layout), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + // Fields: + |cx, tuple_metadata| { + component_types + .into_iter() + .enumerate() + .map(|(index, component_type)| { + build_field_di_node( + cx, + tuple_metadata, + &tuple_field_name(index), + cx.size_and_align_of(component_type), + tuple_type_and_layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, component_type), + ) + }) + .collect() + }, + NO_GENERICS, ) } -//=----------------------------------------------------------------------------- -// Unions -//=----------------------------------------------------------------------------- - -struct UnionMemberDescriptionFactory<'tcx> { - layout: TyAndLayout<'tcx>, - variant: &'tcx ty::VariantDef, -} +/// Builds the debufinfo node for a closure environment. +fn build_closure_env_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let closure_env_type = unique_type_id.expect_ty(); + let &ty::Closure(def_id, _substs) = closure_env_type.kind() else { + bug!("new_closure_env_metadata() called with non-closure-type: {:?}", closure_env_type) + }; + let containing_scope = get_namespace_for_item(cx, def_id); + let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false); -impl<'tcx> UnionMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'ll>( - &self, - cx: &CodegenCx<'ll, 'tcx>, - ) -> Vec> { - self.variant - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = self.layout.field(cx, i); - MemberDescription { - name: f.name.to_string(), - type_metadata: type_metadata(cx, field.ty), - offset: Size::ZERO, - size: field.size, - align: field.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - } - }) - .collect() - } + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &type_name, + cx.size_and_align_of(closure_env_type), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| build_upvar_field_di_nodes(cx, closure_env_type, owner), + // Generics: + |_| { + // FIXME(mw): Should we specify generic parameters for closures? + smallvec![] + }, + ) } -fn prepare_union_metadata<'ll, 'tcx>( +/// Build the debuginfo node for a Rust `union` type. +fn build_union_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - union_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, -) -> RecursiveTypeDescription<'ll, 'tcx> { - let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false); - - let (union_def_id, variant) = match union_type.kind() { +) -> DINodeCreationResult<'ll> { + let union_type = unique_type_id.expect_ty(); + let (union_def_id, variant_def) = match union_type.kind() { ty::Adt(def, _) => (def.did(), def.non_enum_variant()), - _ => bug!("prepare_union_metadata on a non-ADT"), + _ => bug!("build_union_type_di_node on a non-ADT"), }; - let containing_scope = get_namespace_for_item(cx, union_def_id); + let union_ty_and_layout = cx.layout_of(union_type); + let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); - let union_metadata_stub = - create_union_stub(cx, union_type, &union_name, unique_type_id, containing_scope); - - create_and_register_recursive_type_forward_declaration( + type_map::build_type_with_children( cx, - union_type, - unique_type_id, - union_metadata_stub, - union_metadata_stub, - UnionMDF(UnionMemberDescriptionFactory { layout: cx.layout_of(union_type), variant }), + type_map::stub( + cx, + Stub::Union, + unique_type_id, + &type_name, + size_and_align_of(union_ty_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + // Fields: + |cx, owner| { + variant_def + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field_layout = union_ty_and_layout.field(cx, i); + build_field_di_node( + cx, + owner, + f.name.as_str(), + size_and_align_of(field_layout), + Size::ZERO, + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + // Generics: + |cx| build_generic_type_param_di_nodes(cx, union_type), ) } @@ -1446,890 +1217,30 @@ fn generator_layout_and_saved_local_names<'tcx>( (generator_layout, generator_saved_local_names) } -/// Describes the members of an enum value; an enum is described as a union of -/// structs in DWARF. This `MemberDescriptionFactory` provides the description for -/// the members of this union; so for every variant of the given enum, this -/// factory will produce one `MemberDescription` (all with no name and a fixed -/// offset of zero bytes). -struct EnumMemberDescriptionFactory<'ll, 'tcx> { - enum_type: Ty<'tcx>, - layout: TyAndLayout<'tcx>, - tag_type_metadata: Option<&'ll DIType>, - common_members: Vec>, -} - -impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> { - fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec> { - let generator_variant_info_data = match *self.enum_type.kind() { - ty::Generator(def_id, ..) => { - Some(generator_layout_and_saved_local_names(cx.tcx, def_id)) - } - _ => None, - }; - - let variant_info_for = |index: VariantIdx| match *self.enum_type.kind() { - ty::Adt(adt, _) => VariantInfo::Adt(&adt.variant(index), index), - ty::Generator(def_id, _, _) => { - let (generator_layout, generator_saved_local_names) = - generator_variant_info_data.as_ref().unwrap(); - VariantInfo::Generator { - def_id, - generator_layout: *generator_layout, - generator_saved_local_names, - variant_index: index, - } - } - _ => bug!(), - }; - - // While LLVM supports generating debuginfo for variant types (enums), it doesn't support - // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting - // msvc, then we need to use a different, fallback encoding of the debuginfo. - let fallback = cpp_like_debuginfo(cx.tcx); - // This will always find the metadata in the type map. - let self_metadata = type_metadata(cx, self.enum_type); - - match self.layout.variants { - Variants::Single { index } => { - if let ty::Adt(adt, _) = self.enum_type.kind() { - if adt.variants().is_empty() { - return vec![]; - } - } - - let variant_info = variant_info_for(index); - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, self.layout, variant_info, self_metadata); - - let member_descriptions = member_description_factory.create_member_descriptions(cx); - let type_params = compute_type_parameters(cx, self.enum_type); - - set_members_of_composite_type( - cx, - variant_type_metadata, - member_descriptions, - Some(&self.common_members), - type_params, - ); - vec![MemberDescription { - name: variant_info.variant_name(), - type_metadata: variant_type_metadata, - offset: Size::ZERO, - size: self.layout.size, - align: self.layout.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: variant_info.source_info(cx), - }] - } - Variants::Multiple { - tag_encoding: TagEncoding::Direct, - tag_field, - ref variants, - .. - } => { - let fallback_discr_variant = if fallback { - // For MSVC, we generate a union of structs for each variant and an - // explicit discriminant field roughly equivalent to the following C: - // ```c - // union enum$<{name}> { - // struct {variant 0 name} { - // - // } variant0; - // - // {name} discriminant; - // } - // ``` - // The natvis in `intrinsic.natvis` then matches on `this.discriminant` to - // determine which variant is active and then displays it. - let enum_layout = self.layout; - let offset = enum_layout.fields.offset(tag_field); - let discr_ty = enum_layout.field(cx, tag_field).ty; - let (size, align) = cx.size_and_align_of(discr_ty); - Some(MemberDescription { - name: "discriminant".into(), - type_metadata: self.tag_type_metadata.unwrap(), - offset, - size, - align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }) - } else { - None - }; - - variants - .iter_enumerated() - .map(|(i, _)| { - let variant = self.layout.for_variant(cx, i); - let variant_info = variant_info_for(i); - let (variant_type_metadata, member_desc_factory) = - describe_enum_variant(cx, variant, variant_info, self_metadata); - - let member_descriptions = - member_desc_factory.create_member_descriptions(cx); - let type_params = compute_type_parameters(cx, self.enum_type); - - set_members_of_composite_type( - cx, - variant_type_metadata, - member_descriptions, - Some(&self.common_members), - type_params, - ); - - MemberDescription { - name: if fallback { - format!("variant{}", i.as_u32()) - } else { - variant_info.variant_name() - }, - type_metadata: variant_type_metadata, - offset: Size::ZERO, - size: self.layout.size, - align: self.layout.align.abi, - flags: DIFlags::FlagZero, - discriminant: Some( - self.layout.ty.discriminant_for_variant(cx.tcx, i).unwrap().val - as u64, - ), - source_info: variant_info.source_info(cx), - } - }) - .chain(fallback_discr_variant.into_iter()) - .collect() - } - Variants::Multiple { - tag_encoding: - TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, - tag, - ref variants, - tag_field, - } => { - let calculate_niche_value = |i: VariantIdx| { - if i == dataful_variant { - None - } else { - let value = (i.as_u32() as u128) - .wrapping_sub(niche_variants.start().as_u32() as u128) - .wrapping_add(niche_start); - let value = tag.value.size(cx).truncate(value); - // NOTE(eddyb) do *NOT* remove this assert, until - // we pass the full 128-bit value to LLVM, otherwise - // truncation will be silent and remain undetected. - assert_eq!(value as u64 as u128, value); - Some(value as u64) - } - }; - - // For MSVC, we will generate a union of two fields, one for the dataful variant - // and one that just points to the discriminant. We also create an enum that - // contains tag values for the non-dataful variants and make the discriminant field - // that type. We then use natvis to render the enum type correctly in Windbg/VS. - // This will generate debuginfo roughly equivalent to the following C: - // ```c - // union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> { - // struct { - // - // } dataful_variant; - // enum Discriminant$ { - // - // } discriminant; - // } - // ``` - // The natvis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>` - // and evaluates `this.discriminant`. If the value is between the min niche and max - // niche, then the enum is in the dataful variant and `this.dataful_variant` is - // rendered. Otherwise, the enum is in one of the non-dataful variants. In that - // case, we just need to render the name of the `this.discriminant` enum. - if fallback { - let dataful_variant_layout = self.layout.for_variant(cx, dataful_variant); - - let mut discr_enum_ty = tag.value.to_ty(cx.tcx); - // If the niche is the NULL value of a reference, then `discr_enum_ty` will be a RawPtr. - // CodeView doesn't know what to do with enums whose base type is a pointer so we fix this up - // to just be `usize`. - if let ty::RawPtr(_) = discr_enum_ty.kind() { - discr_enum_ty = cx.tcx.types.usize; - } - - let tags: Vec<_> = variants - .iter_enumerated() - .filter_map(|(variant_idx, _)| { - calculate_niche_value(variant_idx).map(|tag| { - let variant = variant_info_for(variant_idx); - let name = variant.variant_name(); - - Some(unsafe { - llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr().cast(), - name.len(), - tag as i64, - !discr_enum_ty.is_signed(), - ) - }) - }) - }) - .collect(); - - let discr_enum = unsafe { - llvm::LLVMRustDIBuilderCreateEnumerationType( - DIB(cx), - self_metadata, - "Discriminant$".as_ptr().cast(), - "Discriminant$".len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - tag.value.size(cx).bits(), - tag.value.align(cx).abi.bits() as u32, - create_DIArray(DIB(cx), &tags), - type_metadata(cx, discr_enum_ty), - true, - ) - }; - - let variant_info = variant_info_for(dataful_variant); - let (variant_type_metadata, member_desc_factory) = describe_enum_variant( - cx, - dataful_variant_layout, - variant_info, - self_metadata, - ); - - let member_descriptions = member_desc_factory.create_member_descriptions(cx); - let type_params = compute_type_parameters(cx, self.enum_type); - - set_members_of_composite_type( - cx, - variant_type_metadata, - member_descriptions, - Some(&self.common_members), - type_params, - ); - - let (size, align) = - cx.size_and_align_of(dataful_variant_layout.field(cx, tag_field).ty); - - vec![ - MemberDescription { - // Name the dataful variant so that we can identify it for natvis - name: "dataful_variant".to_string(), - type_metadata: variant_type_metadata, - offset: Size::ZERO, - size: self.layout.size, - align: self.layout.align.abi, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: variant_info.source_info(cx), - }, - MemberDescription { - name: "discriminant".into(), - type_metadata: discr_enum, - offset: dataful_variant_layout.fields.offset(tag_field), - size, - align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }, - ] - } else { - variants - .iter_enumerated() - .map(|(i, _)| { - let variant = self.layout.for_variant(cx, i); - let variant_info = variant_info_for(i); - let (variant_type_metadata, member_desc_factory) = - describe_enum_variant(cx, variant, variant_info, self_metadata); - - let member_descriptions = - member_desc_factory.create_member_descriptions(cx); - let type_params = compute_type_parameters(cx, self.enum_type); - - set_members_of_composite_type( - cx, - variant_type_metadata, - member_descriptions, - Some(&self.common_members), - type_params, - ); - - let niche_value = calculate_niche_value(i); - - MemberDescription { - name: variant_info.variant_name(), - type_metadata: variant_type_metadata, - offset: Size::ZERO, - size: self.layout.size, - align: self.layout.align.abi, - flags: DIFlags::FlagZero, - discriminant: niche_value, - source_info: variant_info.source_info(cx), - } - }) - .collect() - } - } - } - } -} - -// Creates `MemberDescription`s for the fields of a single enum variant. -struct VariantMemberDescriptionFactory<'tcx> { - /// Cloned from the `layout::Struct` describing the variant. - offsets: Vec, - args: Vec<(String, Ty<'tcx>)>, -} - -impl<'tcx> VariantMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'ll>( - &self, - cx: &CodegenCx<'ll, 'tcx>, - ) -> Vec> { - self.args - .iter() - .enumerate() - .map(|(i, &(ref name, ty))| { - let (size, align) = cx.size_and_align_of(ty); - MemberDescription { - name: name.to_string(), - type_metadata: type_metadata(cx, ty), - offset: self.offsets[i], - size, - align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - } - }) - .collect() - } -} - -#[derive(Copy, Clone)] -enum VariantInfo<'a, 'tcx> { - Adt(&'tcx ty::VariantDef, VariantIdx), - Generator { - def_id: DefId, - generator_layout: &'tcx GeneratorLayout<'tcx>, - generator_saved_local_names: &'a IndexVec>, - variant_index: VariantIdx, - }, -} - -impl<'tcx> VariantInfo<'_, 'tcx> { - fn variant_idx(&self) -> VariantIdx { - match self { - VariantInfo::Adt(_, variant_index) | VariantInfo::Generator { variant_index, .. } => { - *variant_index - } - } - } - - fn map_struct_name(&self, f: impl FnOnce(&str) -> R) -> R { - match self { - VariantInfo::Adt(variant, _) => f(variant.name.as_str()), - VariantInfo::Generator { variant_index, .. } => { - f(&GeneratorSubsts::variant_name(*variant_index)) - } - } - } - - fn variant_name(&self) -> String { - match self { - VariantInfo::Adt(variant, _) => variant.name.to_string(), - VariantInfo::Generator { variant_index, .. } => { - // Since GDB currently prints out the raw discriminant along - // with every variant, make each variant name be just the value - // of the discriminant. The struct name for the variant includes - // the actual variant description. - format!("{}", variant_index.as_usize()) - } - } - } - - fn field_name(&self, i: usize) -> String { - let field_name = match *self { - VariantInfo::Adt(variant, _) if variant.ctor_kind != CtorKind::Fn => { - Some(variant.fields[i].name) - } - VariantInfo::Generator { - generator_layout, - generator_saved_local_names, - variant_index, - .. - } => { - generator_saved_local_names - [generator_layout.variant_fields[variant_index][i.into()]] - } - _ => None, - }; - field_name.map(|name| name.to_string()).unwrap_or_else(|| format!("__{}", i)) - } - - fn source_info<'ll>(&self, cx: &CodegenCx<'ll, 'tcx>) -> Option> { - if let VariantInfo::Generator { def_id, variant_index, .. } = self { - let span = - cx.tcx.generator_layout(*def_id).unwrap().variant_source_info[*variant_index].span; - if !span.is_dummy() { - let loc = cx.lookup_debug_loc(span.lo()); - return Some(SourceInfo { file: file_metadata(cx, &loc.file), line: loc.line }); - } - } - None - } -} - -/// Returns a tuple of (1) `type_metadata_stub` of the variant, (2) a -/// `MemberDescriptionFactory` for producing the descriptions of the -/// fields of the variant. This is a rudimentary version of a full -/// `RecursiveTypeDescription`. -fn describe_enum_variant<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - layout: layout::TyAndLayout<'tcx>, - variant: VariantInfo<'_, 'tcx>, - containing_scope: &'ll DIScope, -) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) { - let metadata_stub = variant.map_struct_name(|variant_name| { - let unique_type_id = - UniqueTypeId::for_enum_variant(cx.tcx, layout.ty, variant.variant_idx()); - - let (size, align) = cx.size_and_align_of(layout.ty); - - create_struct_stub( - cx, - size, - align, - variant_name, - unique_type_id, - Some(containing_scope), - DIFlags::FlagZero, - None, - ) - }); - - let offsets = (0..layout.fields.count()).map(|i| layout.fields.offset(i)).collect(); - let args = (0..layout.fields.count()) - .map(|i| (variant.field_name(i), layout.field(cx, i).ty)) - .collect(); - - let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { offsets, args }); - - (metadata_stub, member_description_factory) -} - -fn prepare_enum_metadata<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - enum_type: Ty<'tcx>, - enum_def_id: DefId, - unique_type_id: UniqueTypeId<'tcx>, - outer_field_tys: Vec>, -) -> RecursiveTypeDescription<'ll, 'tcx> { - let tcx = cx.tcx; - let enum_name = compute_debuginfo_type_name(tcx, enum_type, false); - - let containing_scope = get_namespace_for_item(cx, enum_def_id); - // FIXME: This should emit actual file metadata for the enum, but we - // currently can't get the necessary information when it comes to types - // imported from other crates. Formerly we violated the ODR when performing - // LTO because we emitted debuginfo for the same type with varying file - // metadata, so as a workaround we pretend that the type comes from - // - let file_metadata = unknown_file_metadata(cx); - - let discriminant_type_metadata = |discr: Primitive| { - let enumerators_metadata: Vec<_> = match enum_type.kind() { - ty::Adt(def, _) => iter::zip(def.discriminants(tcx), def.variants()) - .map(|((_, discr), v)| { - let name = v.name.as_str(); - let is_unsigned = match discr.ty.kind() { - ty::Int(_) => false, - ty::Uint(_) => true, - _ => bug!("non integer discriminant"), - }; - unsafe { - Some(llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr().cast(), - name.len(), - // FIXME: what if enumeration has i128 discriminant? - discr.val as i64, - is_unsigned, - )) - } - }) - .collect(), - ty::Generator(_, substs, _) => substs - .as_generator() - .variant_range(enum_def_id, tcx) - .map(|variant_index| { - debug_assert_eq!(tcx.types.u32, substs.as_generator().discr_ty(tcx)); - let name = GeneratorSubsts::variant_name(variant_index); - unsafe { - Some(llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr().cast(), - name.len(), - // Generators use u32 as discriminant type, verified above. - variant_index.as_u32().into(), - true, // IsUnsigned - )) - } - }) - .collect(), - _ => bug!(), - }; - - let disr_type_key = (enum_def_id, discr); - let cached_discriminant_type_metadata = - debug_context(cx).created_enum_disr_types.borrow().get(&disr_type_key).cloned(); - match cached_discriminant_type_metadata { - Some(discriminant_type_metadata) => discriminant_type_metadata, - None => { - let (discriminant_size, discriminant_align) = (discr.size(cx), discr.align(cx)); - let discriminant_base_type_metadata = type_metadata(cx, discr.to_ty(tcx)); - - let item_name; - let discriminant_name = match enum_type.kind() { - ty::Adt(..) => { - item_name = tcx.item_name(enum_def_id); - item_name.as_str() - } - ty::Generator(..) => enum_name.as_str(), - _ => bug!(), - }; - - let discriminant_type_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateEnumerationType( - DIB(cx), - containing_scope, - discriminant_name.as_ptr().cast(), - discriminant_name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - discriminant_size.bits(), - discriminant_align.abi.bits() as u32, - create_DIArray(DIB(cx), &enumerators_metadata), - discriminant_base_type_metadata, - true, - ) - }; - - debug_context(cx) - .created_enum_disr_types - .borrow_mut() - .insert(disr_type_key, discriminant_type_metadata); - - discriminant_type_metadata - } - } - }; - - let layout = cx.layout_of(enum_type); - - if let (Abi::Scalar(_), Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. }) = - (layout.abi, &layout.variants) - { - return FinalMetadata(discriminant_type_metadata(tag.value)); - } - - // While LLVM supports generating debuginfo for variant types (enums), it doesn't support - // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting - // msvc, then we need to use a different encoding of the debuginfo. - if cpp_like_debuginfo(tcx) { - let discriminant_type_metadata = match layout.variants { - Variants::Single { .. } => None, - Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } - | Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => { - Some(discriminant_type_metadata(tag.value)) - } - }; - - let enum_metadata = { - let unique_type_id_str = unique_type_id.generate_unique_id_string(tcx); - - unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - None, - enum_name.as_ptr().cast(), - enum_name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - layout.size.bits(), - layout.align.abi.bits() as u32, - DIFlags::FlagZero, - None, - 0, // RuntimeLang - unique_type_id_str.as_ptr().cast(), - unique_type_id_str.len(), - ) - } - }; - - return create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - enum_metadata, - enum_metadata, - EnumMDF(EnumMemberDescriptionFactory { - enum_type, - layout, - tag_type_metadata: discriminant_type_metadata, - common_members: vec![], - }), - ); - } - - let discriminator_name = match enum_type.kind() { - ty::Generator(..) => "__state", - _ => "", - }; - let discriminator_metadata = match layout.variants { - // A single-variant enum has no discriminant. - Variants::Single { .. } => None, - - Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, tag_field, .. } => { - // Find the integer type of the correct size. - let size = tag.value.size(cx); - let align = tag.value.align(cx); - - let tag_type = match tag.value { - Int(t, _) => t, - F32 => Integer::I32, - F64 => Integer::I64, - Pointer => cx.data_layout().ptr_sized_integer(), - } - .to_ty(cx.tcx, false); - - let tag_metadata = basic_type_metadata(cx, tag_type); - unsafe { - Some(llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - containing_scope, - discriminator_name.as_ptr().cast(), - discriminator_name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - size.bits(), - align.abi.bits() as u32, - layout.fields.offset(tag_field).bits(), - DIFlags::FlagArtificial, - tag_metadata, - )) - } - } - - Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, tag_field, .. } => { - let discr_type = tag.value.to_ty(cx.tcx); - let (size, align) = cx.size_and_align_of(discr_type); - - let discr_metadata = basic_type_metadata(cx, discr_type); - unsafe { - Some(llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - containing_scope, - discriminator_name.as_ptr().cast(), - discriminator_name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - size.bits(), - align.bits() as u32, - layout.fields.offset(tag_field).bits(), - DIFlags::FlagArtificial, - discr_metadata, - )) - } - } - }; - - let outer_fields = match layout.variants { - Variants::Single { .. } => vec![], - Variants::Multiple { .. } => { - let tuple_mdf = - TupleMemberDescriptionFactory { ty: enum_type, component_types: outer_field_tys }; - tuple_mdf - .create_member_descriptions(cx) - .into_iter() - .map(|desc| Some(desc.into_metadata(cx, containing_scope))) - .collect() - } - }; - - let variant_part_unique_type_id_str = - UniqueTypeId::for_enum_variant_part(tcx, enum_type).generate_unique_id_string(tcx); - - let empty_array = create_DIArray(DIB(cx), &[]); - let name = ""; - let variant_part = unsafe { - llvm::LLVMRustDIBuilderCreateVariantPart( - DIB(cx), - containing_scope, - name.as_ptr().cast(), - name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - layout.size.bits(), - layout.align.abi.bits() as u32, - DIFlags::FlagZero, - discriminator_metadata, - empty_array, - variant_part_unique_type_id_str.as_ptr().cast(), - variant_part_unique_type_id_str.len(), - ) - }; - - let struct_wrapper = { - // The variant part must be wrapped in a struct according to DWARF. - // All fields except the discriminant (including `outer_fields`) - // should be put into structures inside the variant part, which gives - // an equivalent layout but offers us much better integration with - // debuggers. - let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]); - let unique_type_id_str = unique_type_id.generate_unique_id_string(tcx); - - unsafe { - llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - Some(containing_scope), - enum_name.as_ptr().cast(), - enum_name.len(), - file_metadata, - UNKNOWN_LINE_NUMBER, - layout.size.bits(), - layout.align.abi.bits() as u32, - DIFlags::FlagZero, - None, - type_array, - 0, - None, - unique_type_id_str.as_ptr().cast(), - unique_type_id_str.len(), - ) - } - }; - - create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - struct_wrapper, - variant_part, - EnumMDF(EnumMemberDescriptionFactory { - enum_type, - layout, - tag_type_metadata: None, - common_members: outer_fields, - }), - ) -} - -/// Creates debug information for a composite type, that is, anything that -/// results in a LLVM struct. -/// -/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - composite_type: Ty<'tcx>, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId<'tcx>, - member_descriptions: Vec>, - containing_scope: Option<&'ll DIScope>, -) -> &'ll DICompositeType { - let (size, align) = cx.size_and_align_of(composite_type); - - // Create the (empty) struct metadata node ... - let composite_type_metadata = create_struct_stub( - cx, - size, - align, - composite_type_name, - composite_type_unique_id, - containing_scope, - DIFlags::FlagZero, - None, - ); - - // ... and immediately create and add the member descriptions. - set_members_of_composite_type( - cx, - composite_type_metadata, - member_descriptions, - None, - compute_type_parameters(cx, composite_type), - ); - - composite_type_metadata -} - -fn set_members_of_composite_type<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - composite_type_metadata: &'ll DICompositeType, - member_descriptions: Vec>, - common_members: Option<&Vec>>, - type_params: &'ll DIArray, -) { - // In some rare cases LLVM metadata uniquing would lead to an existing type - // description being used instead of a new one created in - // create_struct_stub. This would cause a hard to trace assertion in - // DICompositeType::SetTypeArray(). The following check makes sure that we - // get a better error message if this should happen again due to some - // regression. - { - let mut composite_types_completed = - debug_context(cx).composite_types_completed.borrow_mut(); - if !composite_types_completed.insert(composite_type_metadata) { - bug!( - "debuginfo::set_members_of_composite_type() - \ - Already completed forward declaration re-encountered." - ); - } - } - - let mut member_metadata: Vec<_> = member_descriptions - .into_iter() - .map(|desc| Some(desc.into_metadata(cx, composite_type_metadata))) - .collect(); - if let Some(other_members) = common_members { - member_metadata.extend(other_members.iter()); - } - - unsafe { - let field_array = create_DIArray(DIB(cx), &member_metadata); - llvm::LLVMRustDICompositeTypeReplaceArrays( - DIB(cx), - composite_type_metadata, - Some(field_array), - Some(type_params), - ); - } -} - /// Computes the type parameters for a type, if any, for the given metadata. -fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> &'ll DIArray { +fn build_generic_type_param_di_nodes<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + ty: Ty<'tcx>, +) -> SmallVec<&'ll DIType> { if let ty::Adt(def, substs) = *ty.kind() { if substs.types().next().is_some() { let generics = cx.tcx.generics_of(def.did()); let names = get_parameter_names(cx, generics); - let template_params: Vec<_> = iter::zip(substs, names) + let template_params: SmallVec<_> = iter::zip(substs, names) .filter_map(|(kind, name)| { if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = type_metadata(cx, actual_type); + let actual_type_metadata = type_di_node(cx, actual_type); let name = name.as_str(); Some(unsafe { - Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( DIB(cx), None, name.as_ptr().cast(), name.len(), actual_type_metadata, - )) + ) }) } else { None @@ -2337,10 +1248,11 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) - }) .collect(); - return create_DIArray(DIB(cx), &template_params); + return template_params; } } - return create_DIArray(DIB(cx), &[]); + + return smallvec![]; fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { let mut names = generics @@ -2351,89 +1263,10 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) - } } -/// A convenience wrapper around `LLVMRustDIBuilderCreateStructType()`. Does not do -/// any caching, does not add any fields to the struct. This can be done later -/// with `set_members_of_composite_type()`. -fn create_struct_stub<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - size: Size, - align: Align, - type_name: &str, - unique_type_id: UniqueTypeId<'tcx>, - containing_scope: Option<&'ll DIScope>, - flags: DIFlags, - vtable_holder: Option<&'ll DIType>, -) -> &'ll DICompositeType { - let unique_type_id = unique_type_id.generate_unique_id_string(cx.tcx); - - let metadata_stub = unsafe { - // `LLVMRustDIBuilderCreateStructType()` wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in `llvm/lib/IR/Value.cpp`. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - containing_scope, - type_name.as_ptr().cast(), - type_name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - size.bits(), - align.bits() as u32, - flags, - None, - empty_array, - 0, - vtable_holder, - unique_type_id.as_ptr().cast(), - unique_type_id.len(), - ) - }; - - metadata_stub -} - -fn create_union_stub<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - union_type: Ty<'tcx>, - union_type_name: &str, - unique_type_id: UniqueTypeId<'tcx>, - containing_scope: &'ll DIScope, -) -> &'ll DICompositeType { - let (union_size, union_align) = cx.size_and_align_of(union_type); - let unique_type_id = unique_type_id.generate_unique_id_string(cx.tcx); - - let metadata_stub = unsafe { - // `LLVMRustDIBuilderCreateUnionType()` wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in `llvm/lib/IR/Value.cpp`. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - Some(containing_scope), - union_type_name.as_ptr().cast(), - union_type_name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - union_size.bits(), - union_align.bits() as u32, - DIFlags::FlagZero, - Some(empty_array), - 0, // RuntimeLang - unique_type_id.as_ptr().cast(), - unique_type_id.len(), - ) - }; - - metadata_stub -} - /// Creates debug information for the given global variable. /// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, global: &'ll Value) { +/// Adds the created debuginfo nodes directly to the crate's IR. +pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, global: &'ll Value) { if cx.dbg_cx.is_none() { return; } @@ -2459,7 +1292,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g let is_local_to_unit = is_node_local_to_unit(cx, def_id); let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all()); - let type_metadata = type_metadata(cx, variable_type); + let type_di_node = type_di_node(cx, variable_type); let var_name = tcx.item_name(def_id); let var_name = var_name.as_str(); let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name; @@ -2479,7 +1312,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g linkage_name.len(), file_metadata, line_number, - type_metadata, + type_di_node, is_local_to_unit, global, None, @@ -2497,7 +1330,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g /// the name of the method they implement. This can be implemented in the future once there /// is a proper disambiguation scheme for dealing with methods from different traits that have /// the same name. -fn vtable_type_metadata<'ll, 'tcx>( +fn build_vtable_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, poly_trait_ref: Option>, @@ -2516,8 +1349,8 @@ fn vtable_type_metadata<'ll, 'tcx>( // All function pointers are described as opaque pointers. This could be improved in the future // by describing them as actual function pointers. let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit); - let void_pointer_type_debuginfo = type_metadata(cx, void_pointer_ty); - let usize_debuginfo = type_metadata(cx, tcx.types.usize); + let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty); + let usize_di_node = type_di_node(cx, tcx.types.usize); let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty); // If `usize` is not pointer-sized and -aligned then the size and alignment computations // for the vtable as a whole would be wrong. Let's make sure this holds even on weird @@ -2531,67 +1364,66 @@ fn vtable_type_metadata<'ll, 'tcx>( // This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate // the vtable to the type it is for. - let vtable_holder = type_metadata(cx, ty); + let vtable_holder = type_di_node(cx, ty); - let vtable_type_metadata = create_struct_stub( + build_type_with_children( cx, - size, - pointer_align, - &vtable_type_name, - unique_type_id, - NO_SCOPE_METADATA, - DIFlags::FlagArtificial, - Some(vtable_holder), - ); - - // Create a field for each entry in the vtable. - let fields: Vec<_> = vtable_entries - .iter() - .enumerate() - .filter_map(|(index, vtable_entry)| { - let (field_name, field_type) = match vtable_entry { - ty::VtblEntry::MetadataDropInPlace => { - ("drop_in_place".to_string(), void_pointer_type_debuginfo) - } - ty::VtblEntry::Method(_) => { - // Note: This code does not try to give a proper name to each method - // because there might be multiple methods with the same name - // (coming from different traits). - (format!("__method{}", index), void_pointer_type_debuginfo) - } - ty::VtblEntry::TraitVPtr(_) => { - // Note: In the future we could try to set the type of this pointer - // to the type that we generate for the corresponding vtable. - (format!("__super_trait_ptr{}", index), void_pointer_type_debuginfo) - } - ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_debuginfo), - ty::VtblEntry::MetadataSize => ("size".to_string(), usize_debuginfo), - ty::VtblEntry::Vacant => return None, - }; + type_map::stub( + cx, + Stub::VtableTy { vtable_holder }, + unique_type_id, + &vtable_type_name, + (size, pointer_align), + NO_SCOPE_METADATA, + DIFlags::FlagArtificial, + ), + |cx, vtable_type_di_node| { + vtable_entries + .iter() + .enumerate() + .filter_map(|(index, vtable_entry)| { + let (field_name, field_type_di_node) = match vtable_entry { + ty::VtblEntry::MetadataDropInPlace => { + ("drop_in_place".to_string(), void_pointer_type_di_node) + } + ty::VtblEntry::Method(_) => { + // Note: This code does not try to give a proper name to each method + // because their might be multiple methods with the same name + // (coming from different traits). + (format!("__method{}", index), void_pointer_type_di_node) + } + ty::VtblEntry::TraitVPtr(_) => { + (format!("__super_trait_ptr{}", index), void_pointer_type_di_node) + } + ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_di_node), + ty::VtblEntry::MetadataSize => ("size".to_string(), usize_di_node), + ty::VtblEntry::Vacant => return None, + }; - Some(MemberDescription { - name: field_name, - type_metadata: field_type, - offset: pointer_size * index as u64, - size: pointer_size, - align: pointer_align, - flags: DIFlags::FlagZero, - discriminant: None, - source_info: None, - }) - }) - .collect(); + let field_offset = pointer_size * index as u64; - let type_params = create_DIArray(DIB(cx), &[]); - set_members_of_composite_type(cx, vtable_type_metadata, fields, None, type_params); - vtable_type_metadata + Some(build_field_di_node( + cx, + vtable_type_di_node, + &field_name, + (pointer_size, pointer_align), + field_offset, + DIFlags::FlagZero, + field_type_di_node, + )) + }) + .collect() + }, + NO_GENERICS, + ) + .di_node } /// Creates debug information for the given vtable, which is for the /// given type. /// /// Adds the created metadata nodes directly to the crate's IR. -pub fn create_vtable_metadata<'ll, 'tcx>( +pub fn create_vtable_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, poly_trait_ref: Option>, @@ -2608,7 +1440,7 @@ pub fn create_vtable_metadata<'ll, 'tcx>( let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable); - let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref); + let vtable_type_di_node = build_vtable_type_di_node(cx, ty, poly_trait_ref); let linkage_name = ""; unsafe { @@ -2621,7 +1453,7 @@ pub fn create_vtable_metadata<'ll, 'tcx>( linkage_name.len(), unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, - vtable_type, + vtable_type_di_node, true, vtable, None, @@ -2639,3 +1471,14 @@ pub fn extend_scope_to_file<'ll>( let file_metadata = file_metadata(cx, file); unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } } + +pub fn tuple_field_name(field_index: usize) -> Cow<'static, str> { + const TUPLE_FIELD_NAMES: [&'static str; 16] = [ + "__0", "__1", "__2", "__3", "__4", "__5", "__6", "__7", "__8", "__9", "__10", "__11", + "__12", "__13", "__14", "__15", + ]; + TUPLE_FIELD_NAMES + .get(field_index) + .map(|s| Cow::from(*s)) + .unwrap_or_else(|| Cow::from(format!("__{}", field_index))) +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs new file mode 100644 index 0000000000000..e9772cd78d762 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -0,0 +1,515 @@ +use std::borrow::Cow; + +use libc::c_uint; +use rustc_codegen_ssa::debuginfo::{ + type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo, +}; +use rustc_middle::{ + bug, + ty::{ + self, + layout::{LayoutOf, TyAndLayout}, + util::Discr, + AdtDef, GeneratorSubsts, + }, +}; +use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants}; +use smallvec::smallvec; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + build_field_di_node, closure_saved_names_of_captured_variables, + enums::tag_base_type, + file_metadata, generator_layout_and_saved_local_names, size_and_align_of, + type_map::{self, UniqueTypeId}, + unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA, + UNKNOWN_LINE_NUMBER, + }, + utils::DIB, + }, + llvm::{ + self, + debuginfo::{DIFile, DIFlags, DIType}, + }, +}; + +/// In CPP-like mode, we generate a union of structs for each variant and an +/// explicit discriminant field roughly equivalent to the following C/C++ code: +/// +/// ```c +/// union enum$<{fully-qualified-name}> { +/// struct {variant 0 name} { +/// +/// } variant0; +/// +/// {name} discriminant; +/// } +/// ``` +/// +/// As you can see, the type name is wrapped `enum$`. This way we can have a +/// single NatVis rule for handling all enums. +/// +/// At the LLVM IR level this looks like +/// +/// ```txt +/// DW_TAG_union_type (top-level type for enum) +/// DW_TAG_member (member for variant 1) +/// DW_TAG_member (member for variant 2) +/// DW_TAG_member (member for variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// DW_TAG_enumeration_type (type of tag) +/// ``` +/// +/// The above encoding applies for enums with a direct tag. For niche-tag we have to do things +/// differently in order to allow a NatVis visualizer to extract all the information needed: +/// We generate a union of two fields, one for the dataful variant +/// and one that just points to the discriminant (which is some field within the dataful variant). +/// We also create a DW_TAG_enumeration_type DIE that contains tag values for the non-dataful +/// variants and make the discriminant field that type. We then use NatVis to render the enum type +/// correctly in Windbg/VS. This will generate debuginfo roughly equivalent to the following C: +/// +/// ```c +/// union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> { +/// struct { +/// +/// } dataful_variant; +/// enum Discriminant$ { +/// +/// } discriminant; +/// } +/// ``` +/// +/// The NatVis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>` +/// and evaluates `this.discriminant`. If the value is between the min niche and max +/// niche, then the enum is in the dataful variant and `this.dataful_variant` is +/// rendered. Otherwise, the enum is in one of the non-dataful variants. In that +/// case, we just need to render the name of the `this.discriminant` enum. +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let enum_type_and_layout = cx.layout_of(enum_type); + let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + type_map::Stub::Union, + unique_type_id, + &enum_type_name, + cx.size_and_align_of(enum_type), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, enum_type_di_node| { + match enum_type_and_layout.variants { + Variants::Single { index: variant_index } => { + if enum_adt_def.variants().is_empty() { + // Uninhabited enums have Variants::Single. We don't generate + // any members for them. + return smallvec![]; + } + + build_single_variant_union_fields( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + variant_index, + ) + } + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + ref variants, + tag_field, + .. + } => build_union_fields_for_direct_tag_enum( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + &mut variants.indices(), + tag_field, + ), + Variants::Multiple { + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + ref variants, + tag_field, + .. + } => build_union_fields_for_niche_tag_enum( + cx, + enum_adt_def, + enum_type_and_layout, + enum_type_di_node, + dataful_variant, + &mut variants.indices(), + tag_field, + ), + } + }, + NO_GENERICS, + ) +} + +/// A generator debuginfo node looks the same as a that of an enum type. +/// +/// See [build_enum_type_di_node] for more information. +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let generator_type = unique_type_id.expect_ty(); + let generator_type_and_layout = cx.layout_of(generator_type); + let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + type_map::Stub::Union, + unique_type_id, + &generator_type_name, + size_and_align_of(generator_type_and_layout), + NO_SCOPE_METADATA, + DIFlags::FlagZero, + ), + |cx, generator_type_di_node| match generator_type_and_layout.variants { + Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => { + build_union_fields_for_direct_tag_generator( + cx, + generator_type_and_layout, + generator_type_di_node, + ) + } + Variants::Single { .. } + | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => { + bug!( + "Encountered generator with non-direct-tag layout: {:?}", + generator_type_and_layout + ) + } + }, + NO_GENERICS, + ) +} + +fn build_single_variant_union_fields<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_index: VariantIdx, +) -> SmallVec<&'ll DIType> { + let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); + let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + variant_layout, + ); + + // NOTE: The field name of the union is the same as the variant name, not "variant0". + let variant_name = enum_adt_def.variant(variant_index).name.as_str(); + + smallvec![build_field_di_node( + cx, + enum_type_di_node, + variant_name, + // NOTE: We use the size and align of the entire type, not from variant_layout + // since the later is sometimes smaller (if it has fewer fields). + size_and_align_of(enum_type_and_layout), + Size::ZERO, + DIFlags::FlagZero, + variant_struct_type_di_node, + )] +} + +fn build_union_fields_for_direct_tag_enum<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_indices: &mut dyn Iterator, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let variant_field_infos: SmallVec> = variant_indices + .map(|variant_index| { + let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); + + VariantFieldInfo { + variant_index, + variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + variant_layout, + ), + source_info: None, + } + }) + .collect(); + + let discr_type_name = cx.tcx.item_name(enum_adt_def.did()); + let tag_base_type = super::tag_base_type(cx, enum_type_and_layout); + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + discr_type_name.as_str(), + tag_base_type, + &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { + (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str())) + }), + enum_type_di_node, + ); + + build_union_fields_for_direct_tag_enum_or_generator( + cx, + enum_type_and_layout, + enum_type_di_node, + &variant_field_infos, + discr_type_di_node, + tag_field, + ) +} + +fn build_union_fields_for_niche_tag_enum<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + dataful_variant_index: VariantIdx, + variant_indices: &mut dyn Iterator, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let dataful_variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node( + cx, + enum_type_and_layout.ty, + enum_type_di_node, + dataful_variant_index, + &enum_adt_def.variant(dataful_variant_index), + enum_type_and_layout.for_variant(cx, dataful_variant_index), + ); + + let tag_base_type = super::tag_base_type(cx, enum_type_and_layout); + // Create an DW_TAG_enumerator for each variant except the dataful one. + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + "Discriminant$", + tag_base_type, + &mut variant_indices.filter_map(|variant_index| { + if let Some(discr_val) = + super::compute_discriminant_value(cx, enum_type_and_layout, variant_index) + { + let discr = Discr { val: discr_val as u128, ty: tag_base_type }; + let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); + Some((discr, variant_name)) + } else { + debug_assert_eq!(variant_index, dataful_variant_index); + None + } + }), + enum_type_di_node, + ); + + smallvec![ + build_field_di_node( + cx, + enum_type_di_node, + "dataful_variant", + size_and_align_of(enum_type_and_layout), + Size::ZERO, + DIFlags::FlagZero, + dataful_variant_struct_type_di_node, + ), + build_field_di_node( + cx, + enum_type_di_node, + "discriminant", + cx.size_and_align_of(tag_base_type), + enum_type_and_layout.fields.offset(tag_field), + DIFlags::FlagZero, + discr_type_di_node, + ), + ] +} + +fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + generator_type_and_layout: TyAndLayout<'tcx>, + generator_type_di_node: &'ll DIType, +) -> SmallVec<&'ll DIType> { + let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } = generator_type_and_layout.variants else { + bug!("This function only supports layouts with direcly encoded tags.") + }; + + let (generator_def_id, generator_substs) = match generator_type_and_layout.ty.kind() { + &ty::Generator(def_id, substs, _) => (def_id, substs.as_generator()), + _ => unreachable!(), + }; + + let (generator_layout, state_specific_upvar_names) = + generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + + let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx); + + // Build the type node for each field. + let variant_field_infos: SmallVec> = variant_range + .clone() + .map(|variant_index| { + let variant_struct_type_di_node = super::build_generator_variant_struct_type_di_node( + cx, + variant_index, + generator_type_and_layout, + generator_type_di_node, + generator_layout, + &state_specific_upvar_names, + &common_upvar_names, + ); + + let span = generator_layout.variant_source_info[variant_index].span; + let source_info = if !span.is_dummy() { + let loc = cx.lookup_debug_loc(span.lo()); + Some((file_metadata(cx, &loc.file), loc.line as c_uint)) + } else { + None + }; + + VariantFieldInfo { variant_index, variant_struct_type_di_node, source_info } + }) + .collect(); + + let tag_base_type = tag_base_type(cx, generator_type_and_layout); + let discr_type_name = "Discriminant$"; + let discr_type_di_node = super::build_enumeration_type_di_node( + cx, + discr_type_name, + tag_base_type, + &mut generator_substs + .discriminants(generator_def_id, cx.tcx) + .map(|(variant_index, discr)| (discr, GeneratorSubsts::variant_name(variant_index))), + generator_type_di_node, + ); + + build_union_fields_for_direct_tag_enum_or_generator( + cx, + generator_type_and_layout, + generator_type_di_node, + &variant_field_infos[..], + discr_type_di_node, + tag_field, + ) +} + +/// This is a helper function shared between enums and generators that makes sure fields have the +/// expect names. +fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_field_infos: &[VariantFieldInfo<'ll>], + discr_type_di_node: &'ll DIType, + tag_field: usize, +) -> SmallVec<&'ll DIType> { + let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1); + + // We create a field in the union for each variant ... + unions_fields.extend(variant_field_infos.into_iter().map(|variant_member_info| { + let (file_di_node, line_number) = variant_member_info + .source_info + .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); + + let field_name = variant_union_field_name(variant_member_info.variant_index); + let (size, align) = size_and_align_of(enum_type_and_layout); + + // We use LLVMRustDIBuilderCreateMemberType() member type directly because + // the build_field_di_node() function does not support specifying a source location, + // which is something that we don't do anywhere else. + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + enum_type_di_node, + field_name.as_ptr().cast(), + field_name.len(), + file_di_node, + line_number, + // NOTE: We use the size and align of the entire type, not from variant_layout + // since the later is sometimes smaller (if it has fewer fields). + size.bits(), + align.bits() as u32, + // Union fields are always at offset zero + Size::ZERO.bits(), + DIFlags::FlagZero, + variant_member_info.variant_struct_type_di_node, + ) + } + })); + + debug_assert_eq!( + cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), + cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout)) + ); + + // ... and a field for the discriminant. + unions_fields.push(build_field_di_node( + cx, + enum_type_di_node, + "discriminant", + cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), + enum_type_and_layout.fields.offset(tag_field), + DIFlags::FlagZero, + discr_type_di_node, + )); + + unions_fields +} + +/// Information about a single field of the top-level DW_TAG_union_type. +struct VariantFieldInfo<'ll> { + variant_index: VariantIdx, + variant_struct_type_di_node: &'ll DIType, + source_info: Option<(&'ll DIFile, c_uint)>, +} + +fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> { + const PRE_ALLOCATED: [&str; 16] = [ + "variant0", + "variant1", + "variant2", + "variant3", + "variant4", + "variant5", + "variant6", + "variant7", + "variant8", + "variant9", + "variant10", + "variant11", + "variant12", + "variant13", + "variant14", + "variant15", + ]; + + PRE_ALLOCATED + .get(variant_index.as_usize()) + .map(|&s| Cow::from(s)) + .unwrap_or_else(|| format!("variant{}", variant_index.as_usize()).into()) +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs new file mode 100644 index 0000000000000..e41f11b34c894 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -0,0 +1,428 @@ +use rustc_codegen_ssa::debuginfo::{ + type_names::{compute_debuginfo_type_name, cpp_like_debuginfo}, + wants_c_like_enum_debuginfo, +}; +use rustc_hir::def::CtorKind; +use rustc_index::vec::IndexVec; +use rustc_middle::{ + bug, + mir::{Field, GeneratorLayout, GeneratorSavedLocal}, + ty::{ + self, + layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}, + util::Discr, + AdtDef, GeneratorSubsts, Ty, VariantDef, + }, +}; +use rustc_span::Symbol; +use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants}; +use std::borrow::Cow; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + build_field_di_node, build_generic_type_param_di_nodes, type_di_node, + type_map::{self, Stub}, + unknown_file_metadata, UNKNOWN_LINE_NUMBER, + }, + utils::{create_DIArray, get_namespace_for_item, DIB}, + }, + llvm::{ + self, + debuginfo::{DIFlags, DIType}, + }, +}; + +use super::{ + size_and_align_of, + type_map::{DINodeCreationResult, UniqueTypeId}, + SmallVec, +}; + +mod cpp_like; +mod native; + +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let enum_type_and_layout = cx.layout_of(enum_type); + + if wants_c_like_enum_debuginfo(enum_type_and_layout) { + return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout); + } + + if cpp_like_debuginfo(cx.tcx) { + cpp_like::build_enum_type_di_node(cx, unique_type_id) + } else { + native::build_enum_type_di_node(cx, unique_type_id) + } +} + +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + if cpp_like_debuginfo(cx.tcx) { + cpp_like::build_generator_di_node(cx, unique_type_id) + } else { + native::build_generator_di_node(cx, unique_type_id) + } +} + +/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields. +/// +/// The resulting debuginfo will be a DW_TAG_enumeration_type. +fn build_c_style_enum_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_adt_def: AdtDef<'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, +) -> DINodeCreationResult<'ll> { + let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); + DINodeCreationResult { + di_node: build_enumeration_type_di_node( + cx, + &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false), + tag_base_type(cx, enum_type_and_layout), + &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { + (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str())) + }), + containing_scope, + ), + already_stored_in_typemap: false, + } +} + +/// Extract the type with which we want to describe the tag of the given enum or generator. +fn tag_base_type<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, +) -> Ty<'tcx> { + debug_assert!(match enum_type_and_layout.ty.kind() { + ty::Generator(..) => true, + ty::Adt(adt_def, _) => adt_def.is_enum(), + _ => false, + }); + + // FIXME(mw): Why are niche and regular tags treated differently? Because we want to preserve + // the sign? + match enum_type_and_layout.layout.variants() { + // A single-variant enum has no discriminant. + Variants::Single { .. } => { + bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout) + } + + Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => { + match tag.value { + Primitive::Int(t, _) => t, + Primitive::F32 => Integer::I32, + Primitive::F64 => Integer::I64, + Primitive::Pointer => { + // If the niche is the NULL value of a reference, then `discr_enum_ty` will be + // a RawPtr. CodeView doesn't know what to do with enums whose base type is a + // pointer so we fix this up to just be `usize`. + cx.data_layout().ptr_sized_integer() + } + } + .to_ty(cx.tcx, false) + } + + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => { + tag.value.to_ty(cx.tcx) + } + } +} + +/// This is a helper function. FIXME: elaborate docs. +fn build_enumeration_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + type_name: &str, + base_type: Ty<'tcx>, + variants: &mut dyn Iterator, Cow<'tcx, str>)>, + containing_scope: &'ll DIType, +) -> &'ll DIType { + let enumerator_di_nodes: SmallVec> = variants + .map(|(discr, variant_name)| { + let is_unsigned = match discr.ty.kind() { + ty::Int(_) => false, + ty::Uint(_) => true, + _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), + }; + unsafe { + Some(llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + variant_name.as_ptr().cast(), + variant_name.len(), + // FIXME: what if enumeration has i128 discriminant? + discr.val as i64, + is_unsigned, + )) + } + }) + .collect(); + + let (size, align) = cx.size_and_align_of(base_type); + + unsafe { + llvm::LLVMRustDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + type_name.as_ptr().cast(), + type_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + create_DIArray(DIB(cx), &enumerator_di_nodes[..]), + type_di_node(cx, base_type), + true, + ) + } +} + +/// Build the debuginfo node for the struct type describing a single variant of an enum. +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// ``` +/// +/// In CPP-like mode, we have the exact same descriptions for each variant too: +/// +/// ```txt +/// DW_TAG_union_type (top-level type for enum) +/// DW_TAG_member (member for variant 1) +/// DW_TAG_member (member for variant 2) +/// DW_TAG_member (member for variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// DW_TAG_enumeration_type (type of tag) +/// ``` +/// +/// The node looks like: +/// +/// ```txt +/// DW_TAG_structure_type +/// DW_AT_name +/// DW_AT_byte_size 0x00000010 +/// DW_AT_alignment 0x00000008 +/// DW_TAG_member +/// DW_AT_name +/// DW_AT_type <0x0000018e> +/// DW_AT_alignment 0x00000004 +/// DW_AT_data_member_location 4 +/// DW_TAG_member +/// DW_AT_name +/// DW_AT_type <0x00000195> +/// DW_AT_alignment 0x00000008 +/// DW_AT_data_member_location 8 +/// ... +/// ``` +/// +/// The type of a variant is always a struct type with the name of the variant +/// and a DW_TAG_member for each field (but not the discriminant). +fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type: Ty<'tcx>, + enum_type_di_node: &'ll DIType, + variant_index: VariantIdx, + variant_def: &VariantDef, + variant_layout: TyAndLayout<'tcx>, +) -> &'ll DIType { + debug_assert_eq!(variant_layout.ty, enum_type); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index), + variant_def.name.as_str(), + // NOTE: We use size and align of enum_type, not from variant_layout: + cx.size_and_align_of(enum_type), + Some(enum_type_di_node), + DIFlags::FlagZero, + ), + |cx, struct_type_di_node| { + (0..variant_layout.fields.count()) + .map(|field_index| { + let field_name = if variant_def.ctor_kind != CtorKind::Fn { + // Fields have names + Cow::from(variant_def.fields[field_index].name.as_str()) + } else { + // Tuple-like + super::tuple_field_name(field_index) + }; + + let field_layout = variant_layout.field(cx, field_index); + + build_field_di_node( + cx, + struct_type_di_node, + &field_name, + (field_layout.size, field_layout.align.abi), + variant_layout.fields.offset(field_index), + DIFlags::FlagZero, + type_di_node(cx, field_layout.ty), + ) + }) + .collect() + }, + |cx| build_generic_type_param_di_nodes(cx, enum_type), + ) + .di_node +} + +/// Build the struct type for describing a single generator state. +/// See [build_generator_variant_struct_type_di_node]. +/// +/// ```txt +/// +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// ---> DW_TAG_structure_type (type of variant 1) +/// ---> DW_TAG_structure_type (type of variant 2) +/// ---> DW_TAG_structure_type (type of variant 3) +/// +/// ``` +pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + variant_index: VariantIdx, + generator_type_and_layout: TyAndLayout<'tcx>, + generator_type_di_node: &'ll DIType, + generator_layout: &GeneratorLayout<'tcx>, + state_specific_upvar_names: &IndexVec>, + common_upvar_names: &[String], +) -> &'ll DIType { + let variant_name = GeneratorSubsts::variant_name(variant_index); + let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( + cx.tcx, + generator_type_and_layout.ty, + variant_index, + ); + + let variant_layout = generator_type_and_layout.for_variant(cx, variant_index); + + let generator_substs = match generator_type_and_layout.ty.kind() { + ty::Generator(_, substs, _) => substs.as_generator(), + _ => unreachable!(), + }; + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &variant_name, + size_and_align_of(generator_type_and_layout), + Some(generator_type_di_node), + DIFlags::FlagZero, + ), + |cx, variant_struct_type_di_node| { + // Fields that just belong to this variant/state + let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count()) + .map(|field_index| { + let generator_saved_local = generator_layout.variant_fields[variant_index] + [Field::from_usize(field_index)]; + let field_name_maybe = state_specific_upvar_names[generator_saved_local]; + let field_name = field_name_maybe + .as_ref() + .map(|s| Cow::from(s.as_str())) + .unwrap_or_else(|| super::tuple_field_name(field_index)); + + let field_type = variant_layout.field(cx, field_index).ty; + + build_field_di_node( + cx, + variant_struct_type_di_node, + &field_name, + cx.size_and_align_of(field_type), + variant_layout.fields.offset(field_index), + DIFlags::FlagZero, + type_di_node(cx, field_type), + ) + }) + .collect(); + + // Fields that are common to all states + let common_fields: SmallVec<_> = generator_substs + .prefix_tys() + .enumerate() + .map(|(index, upvar_ty)| { + build_field_di_node( + cx, + variant_struct_type_di_node, + &common_upvar_names[index], + cx.size_and_align_of(upvar_ty), + generator_type_and_layout.fields.offset(index), + DIFlags::FlagZero, + type_di_node(cx, upvar_ty), + ) + }) + .collect(); + + state_specific_fields.into_iter().chain(common_fields.into_iter()).collect() + }, + |cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty), + ) + .di_node +} + +/// Returns the discriminant value corresponding to the variant index. +/// +/// Will return `None` if there is less than two variants (because then the enum won't have) +/// a tag, and if this is the dataful variant of a niche-layout enum (because then there is no +/// single discriminant value). +fn compute_discriminant_value<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + variant_index: VariantIdx, +) -> Option { + match enum_type_and_layout.layout.variants() { + &Variants::Single { .. } => None, + &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some( + enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val + as u64, + ), + &Variants::Multiple { + tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, + tag, + .. + } => { + if variant_index == dataful_variant { + None + } else { + let value = (variant_index.as_u32() as u128) + .wrapping_sub(niche_variants.start().as_u32() as u128) + .wrapping_add(niche_start); + let value = tag.value.size(cx).truncate(value); + // NOTE(eddyb) do *NOT* remove this assert, until + // we pass the full 128-bit value to LLVM, otherwise + // truncation will be silent and remain undetected. + assert_eq!(value as u64 as u128, value); + Some(value as u64) + } + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs new file mode 100644 index 0000000000000..12b8cfb481253 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -0,0 +1,441 @@ +use std::borrow::Cow; + +use crate::{ + common::CodegenCx, + debuginfo::{ + metadata::{ + closure_saved_names_of_captured_variables, + enums::tag_base_type, + file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node, + type_map::{self, Stub, StubInfo, UniqueTypeId}, + unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, + UNKNOWN_LINE_NUMBER, + }, + utils::{create_DIArray, get_namespace_for_item, DIB}, + }, + llvm::{ + self, + debuginfo::{DIFile, DIFlags, DIType}, + }, +}; +use libc::c_uint; +use rustc_codegen_ssa::{ + debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo}, + traits::ConstMethods, +}; +use rustc_middle::{ + bug, + ty::{ + self, + layout::{LayoutOf, TyAndLayout}, + }, +}; +use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants}; +use smallvec::smallvec; + +/// Build the debuginfo node for an enum type. The listing below shows how such a +/// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type` +/// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant` +/// for each variant of the enum. The variant-part also contains a single member +/// describing the discriminant, and a nested struct type for each of the variants. +/// +/// ```txt +/// ---> DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +pub(super) fn build_enum_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let enum_type = unique_type_id.expect_ty(); + let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { + bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) + }; + + let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); + let enum_type_and_layout = cx.layout_of(enum_type); + let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); + + debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &enum_type_name, + size_and_align_of(enum_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + |cx, enum_type_di_node| { + // Build the struct type for each variant. These will be referenced by the + // DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE. + // We also called the names for the corresponding DW_TAG_variant DIEs here. + let variant_member_infos: SmallVec<_> = enum_adt_def + .variant_range() + .map(|variant_index| VariantMemberInfo { + variant_index, + variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()), + variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node( + cx, + enum_type, + enum_type_di_node, + variant_index, + enum_adt_def.variant(variant_index), + enum_type_and_layout.for_variant(cx, variant_index), + ), + source_info: None, + }) + .collect(); + + smallvec![build_enum_variant_part_di_node( + cx, + enum_type_and_layout, + enum_type_di_node, + &variant_member_infos[..], + )] + }, + // We don't seem to be emitting generic args on the enum type, it seems. Rather + // they get attached to the struct type of each variant. + NO_GENERICS, + ) +} + +/// Build the debuginfo node for a generator environment. It looks the same as the debuginfo for +/// an enum. See [build_enum_type_di_node] for more information. +/// +/// ```txt +/// +/// ---> DW_TAG_structure_type (top-level type for the generator) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// +/// ``` +pub(super) fn build_generator_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> DINodeCreationResult<'ll> { + let generator_type = unique_type_id.expect_ty(); + let &ty::Generator(generator_def_id, _, _ ) = generator_type.kind() else { + bug!("build_generator_di_node() called with non-generator type: `{:?}`", generator_type) + }; + + let containing_scope = get_namespace_for_item(cx, generator_def_id); + let generator_type_and_layout = cx.layout_of(generator_type); + + debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout)); + + let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false); + + type_map::build_type_with_children( + cx, + type_map::stub( + cx, + Stub::Struct, + unique_type_id, + &generator_type_name, + size_and_align_of(generator_type_and_layout), + Some(containing_scope), + DIFlags::FlagZero, + ), + |cx, generator_type_di_node| { + let (generator_layout, state_specific_upvar_names) = + generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + + let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else { + bug!( + "Encountered generator with non-direct-tag layout: {:?}", + generator_type_and_layout + ) + }; + + let common_upvar_names = + closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + + // Build variant struct types + let variant_struct_type_di_nodes: SmallVec<_> = variants + .indices() + .map(|variant_index| { + // FIXME: This is problematic because just a number is not a valid identifier. + // GeneratorSubsts::variant_name(variant_index), would be consistent + // with enums? + let variant_name = format!("{}", variant_index.as_usize()).into(); + + let span = generator_layout.variant_source_info[variant_index].span; + let source_info = if !span.is_dummy() { + let loc = cx.lookup_debug_loc(span.lo()); + Some((file_metadata(cx, &loc.file), loc.line)) + } else { + None + }; + + VariantMemberInfo { + variant_index, + variant_name, + variant_struct_type_di_node: + super::build_generator_variant_struct_type_di_node( + cx, + variant_index, + generator_type_and_layout, + generator_type_di_node, + generator_layout, + &state_specific_upvar_names, + &common_upvar_names, + ), + source_info, + } + }) + .collect(); + + smallvec![build_enum_variant_part_di_node( + cx, + generator_type_and_layout, + generator_type_di_node, + &variant_struct_type_di_nodes[..], + )] + }, + // We don't seem to be emitting generic args on the generator type, it seems. Rather + // they get attached to the struct type of each variant. + NO_GENERICS, + ) +} + +/// Builds the DW_TAG_variant_part of an enum or generator debuginfo node: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// ---> DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +fn build_enum_variant_part_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + enum_type_di_node: &'ll DIType, + variant_member_infos: &[VariantMemberInfo<'_, 'll>], +) -> &'ll DIType { + let tag_member_di_node = + build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node); + + let variant_part_unique_type_id = + UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty); + + let stub = StubInfo::new( + cx, + variant_part_unique_type_id, + |cx, variant_part_unique_type_id_str| unsafe { + let variant_part_name = ""; + llvm::LLVMRustDIBuilderCreateVariantPart( + DIB(cx), + enum_type_di_node, + variant_part_name.as_ptr().cast(), + variant_part_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + enum_type_and_layout.size.bits(), + enum_type_and_layout.align.abi.bits() as u32, + DIFlags::FlagZero, + tag_member_di_node, + create_DIArray(DIB(cx), &[]), + variant_part_unique_type_id_str.as_ptr().cast(), + variant_part_unique_type_id_str.len(), + ) + }, + ); + + type_map::build_type_with_children( + cx, + stub, + |cx, variant_part_di_node| { + variant_member_infos + .iter() + .map(|variant_member_info| { + build_enum_variant_member_di_node( + cx, + enum_type_and_layout, + variant_part_di_node, + variant_member_info, + ) + }) + .collect() + }, + NO_GENERICS, + ) + .di_node +} + +/// Builds the DW_TAG_member describing where we can find the tag of an enum. +/// Returns `None` if the enum does not have a tag. +/// +/// ```txt +/// +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// ---> DW_TAG_member (discriminant member) +/// DW_TAG_variant (variant 1) +/// DW_TAG_variant (variant 2) +/// DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// +/// ``` +fn build_discr_member_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_or_generator_type_and_layout: TyAndLayout<'tcx>, + enum_or_generator_type_di_node: &'ll DIType, +) -> Option<&'ll DIType> { + let tag_name = match enum_or_generator_type_and_layout.ty.kind() { + ty::Generator(..) => "__state", + _ => "", + }; + + // NOTE: This is actually wrong. This will become a member of + // of the DW_TAG_variant_part. But, due to LLVM's API, that + // can only be constructed with this DW_TAG_member already in created. + // In LLVM IR the wrong scope will be listed but when DWARF is + // generated from it, the DW_TAG_member will be a child the + // DW_TAG_variant_part. + let containing_scope = enum_or_generator_type_di_node; + + match enum_or_generator_type_and_layout.layout.variants() { + // A single-variant enum has no discriminant. + &Variants::Single { .. } => None, + + &Variants::Multiple { tag_field, .. } => { + let tag_base_type = tag_base_type(cx, enum_or_generator_type_and_layout); + let (size, align) = cx.size_and_align_of(tag_base_type); + + unsafe { + Some(llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + containing_scope, + tag_name.as_ptr().cast(), + tag_name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + enum_or_generator_type_and_layout.fields.offset(tag_field).bits(), + DIFlags::FlagArtificial, + type_di_node(cx, tag_base_type), + )) + } + } + } +} + +/// Build the debuginfo node for `DW_TAG_variant`: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// ---> DW_TAG_variant (variant 1) +/// ---> DW_TAG_variant (variant 2) +/// ---> DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +/// ``` +/// +/// This node looks like: +/// +/// ```txt +/// DW_TAG_variant +/// DW_AT_discr_value 0 +/// DW_TAG_member +/// DW_AT_name None +/// DW_AT_type <0x000002a1> +/// DW_AT_alignment 0x00000002 +/// DW_AT_data_member_location 0 +/// ``` +/// +/// The DW_AT_discr_value is optional, and is omitted if +/// - This is the only variant of a univariant enum (i.e. their is no discriminant) +/// - This is the "dataful" variant of a niche-layout enum +/// (where only the other variants are identified by a single value) +/// +/// There is only ever a single member, the type of which is a struct that describes the +/// fields of the variant (excluding the discriminant). The name of the member is the name +/// of the variant as given in the source code. The DW_AT_data_member_location is always +/// zero. +/// +/// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree +/// (including the DW_TAG_member) is built by a single call to +/// `LLVMRustDIBuilderCreateVariantMemberType()`. +fn build_enum_variant_member_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + enum_type_and_layout: TyAndLayout<'tcx>, + variant_part_di_node: &'ll DIType, + variant_member_info: &VariantMemberInfo<'_, 'll>, +) -> &'ll DIType { + let variant_index = variant_member_info.variant_index; + let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index); + + let (file_di_node, line_number) = variant_member_info + .source_info + .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); + + unsafe { + llvm::LLVMRustDIBuilderCreateVariantMemberType( + DIB(cx), + variant_part_di_node, + variant_member_info.variant_name.as_ptr().cast(), + variant_member_info.variant_name.len(), + file_di_node, + line_number, + enum_type_and_layout.size.bits(), // FIXME: Unused? + enum_type_and_layout.align.abi.bits() as u32, // FIXME: Unused? + Size::ZERO.bits(), // FIXME: Unused? + discr_value.map(|v| cx.const_u64(v)), + DIFlags::FlagZero, + variant_member_info.variant_struct_type_di_node, + ) + } +} + +/// Information needed for building a `DW_TAG_variant`: +/// +/// ```txt +/// DW_TAG_structure_type (top-level type for enum) +/// DW_TAG_variant_part (variant part) +/// DW_AT_discr (reference to discriminant DW_TAG_member) +/// DW_TAG_member (discriminant member) +/// ---> DW_TAG_variant (variant 1) +/// ---> DW_TAG_variant (variant 2) +/// ---> DW_TAG_variant (variant 3) +/// DW_TAG_structure_type (type of variant 1) +/// DW_TAG_structure_type (type of variant 2) +/// DW_TAG_structure_type (type of variant 3) +struct VariantMemberInfo<'a, 'll> { + variant_index: VariantIdx, + variant_name: Cow<'a, str>, + variant_struct_type_di_node: &'ll DIType, + source_info: Option<(&'ll DIFile, c_uint)>, +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs new file mode 100644 index 0000000000000..26f0647f888a2 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -0,0 +1,271 @@ +use std::cell::RefCell; + +use rustc_data_structures::{ + fingerprint::Fingerprint, + fx::FxHashMap, + stable_hasher::{HashStable, NodeIdHashingMode, StableHasher}, +}; +use rustc_middle::{ + bug, + ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}, +}; +use rustc_target::abi::{Align, Size, VariantIdx}; + +use crate::{ + common::CodegenCx, + debuginfo::utils::{create_DIArray, debug_context, DIB}, + llvm::{ + self, + debuginfo::{DIFlags, DIScope, DIType}, + }, +}; + +use super::{unknown_file_metadata, SmallVec, UNKNOWN_LINE_NUMBER}; + +mod private { + // This type cannot be constructed outside of this module because + // it has a private field. We make use of this in order to prevent + // `UniqueTypeId` from being constructed directly, without asserting + // the preconditions. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] + pub struct HiddenZst; +} + +/// A unique identifier for anything that we create a debuginfo node for. +/// The types it contains are expected to already be normalized (which +/// is debug_asserted in the constructors). +/// +/// Note that there are some things that only show up in debuginfo, like +/// the separate type descriptions for each enum variant. These get an ID +/// too because they have their own debuginfo node in LLVM IR. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)] +pub(super) enum UniqueTypeId<'tcx> { + /// The ID of a regular type as it shows up at the language level. + Ty(Ty<'tcx>, private::HiddenZst), + /// The ID for the single DW_TAG_variant_part nested inside the top-level + /// DW_TAG_structure_type that describes enums and generators. + VariantPart(Ty<'tcx>, private::HiddenZst), + /// The ID for the artificial struct type describing a single enum variant. + VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst), + /// The ID of the artificial type we create for VTables. + VTableTy(Ty<'tcx>, Option>, private::HiddenZst), +} + +impl<'tcx> UniqueTypeId<'tcx> { + pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self { + debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)); + UniqueTypeId::Ty(t, private::HiddenZst) + } + + pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self { + debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)); + UniqueTypeId::VariantPart(enum_ty, private::HiddenZst) + } + + pub fn for_enum_variant_struct_type( + tcx: TyCtxt<'tcx>, + enum_ty: Ty<'tcx>, + variant_idx: VariantIdx, + ) -> Self { + debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)); + UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst) + } + + pub fn for_vtable_ty( + tcx: TyCtxt<'tcx>, + self_type: Ty<'tcx>, + implemented_trait: Option>, + ) -> Self { + debug_assert_eq!( + self_type, + tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type) + ); + debug_assert_eq!( + implemented_trait, + tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait) + ); + UniqueTypeId::VTableTy(self_type, implemented_trait, private::HiddenZst) + } + + /// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId` + /// argument of the various `LLVMRustDIBuilderCreate*Type()` methods. + /// + /// Right now this takes the form of a hex-encoded opaque hash value. + pub fn generate_unique_id_string(self, tcx: TyCtxt<'tcx>) -> String { + let mut hasher = StableHasher::new(); + let mut hcx = tcx.create_stable_hashing_context(); + hcx.while_hashing_spans(false, |hcx| { + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + self.hash_stable(hcx, &mut hasher); + }); + }); + hasher.finish::().to_hex() + } + + pub fn expect_ty(self) -> Ty<'tcx> { + match self { + UniqueTypeId::Ty(ty, _) => ty, + _ => bug!("Expected `UniqueTypeId::Ty` but found `{:?}`", self), + } + } +} + +/// The `TypeMap` is where the debug context holds the type metadata nodes +/// created so far. The debuginfo nodes are identified by `UniqueTypeId`. +#[derive(Default)] +pub(crate) struct TypeMap<'ll, 'tcx> { + pub(super) unique_id_to_di_node: RefCell, &'ll DIType>>, +} + +impl<'ll, 'tcx> TypeMap<'ll, 'tcx> { + /// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will + /// fail if the mapping already exists. + pub(super) fn insert(&self, unique_type_id: UniqueTypeId<'tcx>, metadata: &'ll DIType) { + if self.unique_id_to_di_node.borrow_mut().insert(unique_type_id, metadata).is_some() { + bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id); + } + } + + pub(super) fn di_node_for_unique_id( + &self, + unique_type_id: UniqueTypeId<'tcx>, + ) -> Option<&'ll DIType> { + self.unique_id_to_di_node.borrow().get(&unique_type_id).cloned() + } +} + +pub struct DINodeCreationResult<'ll> { + pub di_node: &'ll DIType, + pub already_stored_in_typemap: bool, +} + +impl<'ll> DINodeCreationResult<'ll> { + pub fn new(di_node: &'ll DIType, already_stored_in_typemap: bool) -> Self { + DINodeCreationResult { di_node, already_stored_in_typemap } + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Stub<'ll> { + Struct, + Union, + VtableTy { vtable_holder: &'ll DIType }, +} + +pub struct StubInfo<'ll, 'tcx> { + metadata: &'ll DIType, + unique_type_id: UniqueTypeId<'tcx>, +} + +impl<'ll, 'tcx> StubInfo<'ll, 'tcx> { + pub(super) fn new( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, + build: impl FnOnce(&CodegenCx<'ll, 'tcx>, /* unique_type_id_str: */ &str) -> &'ll DIType, + ) -> StubInfo<'ll, 'tcx> { + let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); + let di_node = build(cx, &unique_type_id_str); + StubInfo { metadata: di_node, unique_type_id } + } +} + +/// Create a stub debuginfo node onto which fields and nested types can be attached. +pub(super) fn stub<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + kind: Stub<'ll>, + unique_type_id: UniqueTypeId<'tcx>, + name: &str, + (size, align): (Size, Align), + containing_scope: Option<&'ll DIScope>, + flags: DIFlags, +) -> StubInfo<'ll, 'tcx> { + let empty_array = create_DIArray(DIB(cx), &[]); + let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); + + let metadata = match kind { + Stub::Struct | Stub::VtableTy { .. } => { + let vtable_holder = match kind { + Stub::VtableTy { vtable_holder } => Some(vtable_holder), + _ => None, + }; + unsafe { + llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + flags, + None, + empty_array, + 0, + vtable_holder, + unique_type_id_str.as_ptr().cast(), + unique_type_id_str.len(), + ) + } + } + Stub::Union => unsafe { + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + name.as_ptr().cast(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + size.bits(), + align.bits() as u32, + flags, + Some(empty_array), + 0, + unique_type_id_str.as_ptr().cast(), + unique_type_id_str.len(), + ) + }, + }; + StubInfo { metadata, unique_type_id } +} + +/// This function enables creating debuginfo nodes that can recursively refer to themselves. +/// It will first insert the given stub into the type map and only then execute the `members` +/// and `generics` closures passed in. These closures have access to the stub so they can +/// directly attach fields to them. If build the type of a field transitively refers back +/// to the type currently being built, the stub will already be found in the type map, +/// which effectively breaks the recursion cycle. +pub(super) fn build_type_with_children<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + stub_info: StubInfo<'ll, 'tcx>, + members: impl FnOnce(&CodegenCx<'ll, 'tcx>, &'ll DIType) -> SmallVec<&'ll DIType>, + generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec<&'ll DIType>, +) -> DINodeCreationResult<'ll> { + debug_assert_eq!( + debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), + None + ); + + debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata); + + let members: SmallVec<_> = + members(cx, stub_info.metadata).into_iter().map(|node| Some(node)).collect(); + let generics: SmallVec> = + generics(cx).into_iter().map(|node| Some(node)).collect(); + + if !(members.is_empty() && generics.is_empty()) { + unsafe { + let members_array = create_DIArray(DIB(cx), &members[..]); + let generics_array = create_DIArray(DIB(cx), &generics[..]); + llvm::LLVMRustDICompositeTypeReplaceArrays( + DIB(cx), + stub_info.metadata, + Some(members_array), + Some(generics_array), + ); + } + } + + DINodeCreationResult { di_node: stub_info.metadata, already_stored_in_typemap: true } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 34013b5f737c9..6cb8f7863a9d8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -2,7 +2,7 @@ use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; -use self::metadata::{file_metadata, type_metadata, TypeMap}; +use self::metadata::{file_metadata, type_di_node}; use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; use self::namespace::mangled_name_of_instance; use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; @@ -20,7 +20,7 @@ use crate::value::Value; use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_index::vec::IndexVec; @@ -32,7 +32,7 @@ use rustc_session::config::{self, DebugInfo}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span}; -use rustc_target::abi::{Primitive, Size}; +use rustc_target::abi::Size; use libc::c_uint; use smallvec::SmallVec; @@ -48,7 +48,7 @@ mod namespace; mod utils; pub use self::create_scope_map::compute_mir_scopes; -pub use self::metadata::create_global_var_metadata; +pub use self::metadata::build_global_var_di_node; pub use self::metadata::extend_scope_to_file; #[allow(non_upper_case_globals)] @@ -57,24 +57,18 @@ const DW_TAG_auto_variable: c_uint = 0x100; const DW_TAG_arg_variable: c_uint = 0x101; /// A context object for maintaining all state needed by the debuginfo module. -pub struct CrateDebugContext<'a, 'tcx> { - llcontext: &'a llvm::Context, - llmod: &'a llvm::Module, - builder: &'a mut DIBuilder<'a>, - created_files: RefCell, Option), &'a DIFile>>, - created_enum_disr_types: RefCell>, - - type_map: TypeMap<'a, 'tcx>, - namespace_map: RefCell>, - - recursion_marker_type: OnceCell<&'a DIType>, - - // This collection is used to assert that composite types (structs, enums, - // ...) have their members only set once: - composite_types_completed: RefCell>, +pub struct CodegenUnitDebugContext<'ll, 'tcx> { + llcontext: &'ll llvm::Context, + llmod: &'ll llvm::Module, + builder: &'ll mut DIBuilder<'ll>, + created_files: RefCell, Option), &'ll DIFile>>, + + type_map: metadata::TypeMap<'ll, 'tcx>, + namespace_map: RefCell>, + recursion_marker_type: OnceCell<&'ll DIType>, } -impl Drop for CrateDebugContext<'_, '_> { +impl Drop for CodegenUnitDebugContext<'_, '_> { fn drop(&mut self) { unsafe { llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _)); @@ -82,22 +76,20 @@ impl Drop for CrateDebugContext<'_, '_> { } } -impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> { - pub fn new(llmod: &'a llvm::Module) -> Self { - debug!("CrateDebugContext::new"); +impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { + pub fn new(llmod: &'ll llvm::Module) -> Self { + debug!("CodegenUnitDebugContext::new"); let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; // DIBuilder inherits context from the module, so we'd better use the same one let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - CrateDebugContext { + CodegenUnitDebugContext { llcontext, llmod, builder, created_files: Default::default(), - created_enum_disr_types: Default::default(), type_map: Default::default(), namespace_map: RefCell::new(Default::default()), recursion_marker_type: OnceCell::new(), - composite_types_completed: Default::default(), } } @@ -415,7 +407,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { signature.push(if fn_abi.ret.is_ignore() { None } else { - Some(type_metadata(cx, fn_abi.ret.layout.ty)) + Some(type_di_node(cx, fn_abi.ret.layout.ty)) }); // Arguments types @@ -440,11 +432,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { } _ => t, }; - Some(type_metadata(cx, t)) + Some(type_di_node(cx, t)) })); } else { signature - .extend(fn_abi.args.iter().map(|arg| Some(type_metadata(cx, arg.layout.ty)))); + .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty)))); } create_DIArray(DIB(cx), &signature[..]) @@ -467,7 +459,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = type_metadata(cx, actual_type); + let actual_type_metadata = type_di_node(cx, actual_type); let name = name.as_str(); Some(unsafe { Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( @@ -522,7 +514,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.needs_subst() { - Some(type_metadata(cx, impl_self_ty)) + Some(type_di_node(cx, impl_self_ty)) } else { Some(namespace::item_namespace(cx, def.did())) } @@ -569,7 +561,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { trait_ref: Option>, vtable: Self::Value, ) { - metadata::create_vtable_metadata(self, ty, trait_ref, vtable) + metadata::create_vtable_di_node(self, ty, trait_ref, vtable) } fn extend_scope_to_file( @@ -597,7 +589,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { let loc = self.lookup_debug_loc(span.lo()); let file_metadata = file_metadata(self, &loc.file); - let type_metadata = type_metadata(self, variable_type); + let type_metadata = type_di_node(self, variable_type); let (argument_index, dwarf_tag) = match variable_kind { ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index fa75463067f47..fe9851cfa5612 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -1,7 +1,7 @@ // Utility Functions. use super::namespace::item_namespace; -use super::CrateDebugContext; +use super::CodegenUnitDebugContext; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; @@ -35,7 +35,7 @@ pub fn create_DIArray<'ll>( #[inline] pub fn debug_context<'a, 'll, 'tcx>( cx: &'a CodegenCx<'ll, 'tcx>, -) -> &'a CrateDebugContext<'ll, 'tcx> { +) -> &'a CodegenUnitDebugContext<'ll, 'tcx> { cx.dbg_cx.as_ref().unwrap() } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs index d1a0cf78d6a2e..6e3f4f0b8ef01 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs @@ -1,2 +1,34 @@ +use rustc_middle::ty::{self, layout::TyAndLayout}; +use rustc_target::abi::Size; + // FIXME(eddyb) find a place for this (or a way to replace it). pub mod type_names; + +/// Returns true if we want to generate a DW_TAG_enumeration_type description for +/// this instead of a DW_TAG_struct_type with DW_TAG_variant_part. +/// +/// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single +/// fieldless variant, we generate DW_TAG_struct_type, although a +/// DW_TAG_enumeration_type would be a better fit. +pub fn wants_c_like_enum_debuginfo<'tcx>(enum_type_and_layout: TyAndLayout<'tcx>) -> bool { + match enum_type_and_layout.ty.kind() { + ty::Adt(adt_def, _) => { + if !adt_def.is_enum() { + return false; + } + + match adt_def.variants().len() { + 0 => false, + 1 => { + // Univariant enums unless they are zero-sized + enum_type_and_layout.size != Size::ZERO && adt_def.all_fields().count() == 0 + } + _ => { + // Enums with more than one variant if they have no fields + adt_def.all_fields().count() == 0 + } + } + } + _ => false, + } +} diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 67df64e9b1baa..6a122addf2268 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -18,13 +18,15 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt}; use rustc_query_system::ich::NodeIdHashingMode; use rustc_target::abi::{Integer, TagEncoding, Variants}; use smallvec::SmallVec; use std::fmt::Write; +use crate::debuginfo::wants_c_like_enum_debuginfo; + // Compute the name of the type as it should be stored in debuginfo. Does not do // any caching, i.e., calling the function twice with the same type will also do // the work twice. The `qualified` parameter only affects the first level of the @@ -71,7 +73,9 @@ fn push_debuginfo_type_name<'tcx>( ty::Float(float_ty) => output.push_str(float_ty.name_str()), ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output), ty::Adt(def, substs) => { - if def.is_enum() && cpp_like_debuginfo { + let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error"); + + if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) { msvc_enum_fallback(tcx, t, def, substs, output, visited); } else { push_item_name(tcx, def.did(), qualified, output); diff --git a/src/test/codegen/async-fn-debug-msvc.rs b/src/test/codegen/async-fn-debug-msvc.rs index a90c85a54491d..b10e662b5bbd6 100644 --- a/src/test/codegen/async-fn-debug-msvc.rs +++ b/src/test/codegen/async-fn-debug-msvc.rs @@ -16,8 +16,7 @@ async fn async_fn_test() { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test" -// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0" +// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}}, // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. // CHECK-SAME: file: [[FILE:![0-9]*]], line: 11, @@ -40,10 +39,10 @@ async fn async_fn_test() { // CHECK-SAME: baseType: [[VARIANT:![0-9]*]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], +// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]] +// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]], diff --git a/src/test/codegen/async-fn-debug.rs b/src/test/codegen/async-fn-debug.rs index 8fbd2765fd771..9f6058a71b343 100644 --- a/src/test/codegen/async-fn-debug.rs +++ b/src/test/codegen/async-fn-debug.rs @@ -18,7 +18,7 @@ async fn async_fn_test() { // CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test" // CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[ASYNC_FN]] -// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[ASYNC_FN]], +// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: discriminator: [[DISC:![0-9]*]] // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]], @@ -50,7 +50,7 @@ async fn async_fn_test() { // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[ASYNC_FN]], +// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]], // CHECK-SAME: flags: DIFlagArtificial fn main() { diff --git a/src/test/codegen/generator-debug-msvc.rs b/src/test/codegen/generator-debug-msvc.rs index fb8b9e09fd24d..a6e56a6bd579e 100644 --- a/src/test/codegen/generator-debug-msvc.rs +++ b/src/test/codegen/generator-debug-msvc.rs @@ -20,7 +20,6 @@ fn generator_test() -> impl Generator { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test" // CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0" // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. @@ -44,10 +43,10 @@ fn generator_test() -> impl Generator { // CHECK-SAME: baseType: [[VARIANT:![0-9]*]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], +// CHECK: [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]] +// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]], diff --git a/src/test/codegen/generator-debug.rs b/src/test/codegen/generator-debug.rs index e777fe3af63e0..3ec860f2cbc06 100644 --- a/src/test/codegen/generator-debug.rs +++ b/src/test/codegen/generator-debug.rs @@ -22,7 +22,7 @@ fn generator_test() -> impl Generator { // CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test" // CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{generator_env#0}", scope: [[GEN_FN]] -// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN_FN]], +// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]], // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: discriminator: [[DISC:![0-9]*]] // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]], @@ -54,7 +54,7 @@ fn generator_test() -> impl Generator { // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]] // CHECK-NOT: flags: DIFlagArtificial // CHECK-SAME: ) -// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN_FN]], +// CHECK: [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]], // CHECK-SAME: flags: DIFlagArtificial fn main() { diff --git a/src/test/debuginfo/msvc-pretty-enums.rs b/src/test/debuginfo/msvc-pretty-enums.rs index 642694355addf..a153a9a42289a 100644 --- a/src/test/debuginfo/msvc-pretty-enums.rs +++ b/src/test/debuginfo/msvc-pretty-enums.rs @@ -4,14 +4,14 @@ // cdb-command: g // cdb-command: dx a -// cdb-check:a : Some({...}) [Type: enum$ >, 2, 16, Some>] -// cdb-check: [] [Type: enum$ >, 2, 16, Some>] +// cdb-check:a : Some({...}) [Type: enum$, 2, 16, Some>] +// cdb-check: [] [Type: enum$, 2, 16, Some>] // cdb-check: [variant] : Some // cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum] // cdb-command: dx b -// cdb-check:b : None [Type: enum$ >, 2, 16, Some>] -// cdb-check: [] [Type: enum$ >, 2, 16, Some>] +// cdb-check:b : None [Type: enum$, 2, 16, Some>] +// cdb-check: [] [Type: enum$, 2, 16, Some>] // cdb-check: [variant] : None // cdb-command: dx c @@ -78,7 +78,7 @@ pub enum NicheLayoutEnum { Tag2, } -pub enum Empty { } +pub enum Empty {} fn main() { let a = Some(CStyleEnum::Low); @@ -97,4 +97,6 @@ fn main() { zzz(); // #break } -fn zzz() { () } +fn zzz() { + () +} diff --git a/src/test/debuginfo/type-names.rs b/src/test/debuginfo/type-names.rs index c96921285626a..52841d50f642f 100644 --- a/src/test/debuginfo/type-names.rs +++ b/src/test/debuginfo/type-names.rs @@ -33,10 +33,10 @@ // gdb-check:type = type_names::mod1::Enum2 // gdb-command:whatis generic_enum_1 -// gdb-check:type = type_names::mod1::mod2::Enum3 +// gdb-check:type = type_names::mod1::mod2::Enum3 // gdb-command:whatis generic_enum_2 -// gdb-check:type = type_names::mod1::mod2::Enum3 +// gdb-check:type = type_names::mod1::mod2::Enum3 // TUPLES // gdb-command:whatis tuple1 @@ -159,10 +159,10 @@ // FOREIGN TYPES // gdb-command:whatis foreign1 -// gdb-check:type = *mut ForeignType1 +// gdb-check:type = *mut type_names::{extern#0}::ForeignType1 // gdb-command:whatis foreign2 -// gdb-check:type = *mut ForeignType2 +// gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2 // === CDB TESTS ================================================================================== @@ -178,9 +178,9 @@ // cdb-command:dv /t *_enum_* // cdb-check:union enum$ simple_enum_1 = [...] // cdb-check:union enum$ simple_enum_2 = [...] -// cdb-check:type_names::mod1::Enum2 simple_enum_3 = [...] -// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_1 = [...] -// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_2 = [...] +// cdb-check:union enum$ simple_enum_3 = [...] +// cdb-check:union enum$ > generic_enum_1 = [...] +// cdb-check:union enum$ > generic_enum_2 = [...] // TUPLES // cdb-command:dv /t tuple* @@ -258,8 +258,8 @@ // FOREIGN TYPES // cdb-command:dv /t foreign* -// cdb-check:struct ForeignType2 * foreign2 = [...] -// cdb-check:struct ForeignType1 * foreign1 = [...] +// cdb-check:struct type_names::mod1::extern$0::ForeignType2 * foreign2 = [...] +// cdb-check:struct type_names::extern$0::ForeignType1 * foreign1 = [...] #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] @@ -283,7 +283,6 @@ extern "C" { } mod mod1 { - pub use self::Enum2::{Variant1, Variant2}; pub struct Struct2; pub enum Enum2 { @@ -367,14 +366,14 @@ fn main() { // Enums let simple_enum_1 = Variant1; let simple_enum_2 = Variant2(0); - let simple_enum_3 = mod1::Variant2(Struct1); + let simple_enum_3 = mod1::Enum2::Variant2(Struct1); let generic_enum_1: mod1::mod2::Enum3 = mod1::mod2::Variant1; let generic_enum_2 = mod1::mod2::Variant2(Struct1); // Tuples let tuple1 = (8u32, Struct1, mod1::mod2::Variant2(mod1::Struct2)); - let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Variant1, 'x'); + let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Enum2::Variant1, 'x'); // Box let box1 = (Box::new(1f32), 0i32); @@ -404,7 +403,7 @@ fn main() { let vec1 = vec![0_usize, 2, 3]; let slice1 = &*vec1; - let vec2 = vec![mod1::Variant2(Struct1)]; + let vec2 = vec![mod1::Enum2::Variant2(Struct1)]; let slice2 = &*vec2; // Trait Objects From 3ad299aa670face2085d2abec6e8481fa582068a Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 12:10:02 +0100 Subject: [PATCH 2/9] debuginfo: change cpp-like naming for generator environments so that NatVis works for them --- .../src/debuginfo/type_names.rs | 140 ++++++++++++------ src/test/codegen/async-fn-debug-msvc.rs | 2 +- src/test/codegen/generator-debug-msvc.rs | 2 +- src/test/debuginfo/generator-objects.rs | 31 ++++ 4 files changed, 125 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 6a122addf2268..ee0658f486a06 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -16,13 +16,14 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; -use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt}; use rustc_query_system::ich::NodeIdHashingMode; use rustc_target::abi::{Integer, TagEncoding, Variants}; use smallvec::SmallVec; +use std::borrow::Cow; use std::fmt::Write; use crate::debuginfo::wants_c_like_enum_debuginfo; @@ -76,7 +77,16 @@ fn push_debuginfo_type_name<'tcx>( let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error"); if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) { - msvc_enum_fallback(tcx, t, def, substs, output, visited); + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_item_name(tcx, def.did(), true, output); + push_generic_params_internal(tcx, substs, output, visited); + }, + output, + visited, + ); } else { push_item_name(tcx, def.did(), qualified, output); push_generic_params_internal(tcx, substs, output, visited); @@ -352,40 +362,26 @@ fn push_debuginfo_type_name<'tcx>( ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { // Name will be "{closure_env#0}", "{generator_env#0}", or // "{async_fn_env#0}", etc. - let def_key = tcx.def_key(def_id); - - if qualified { - let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; - push_item_name(tcx, parent_def_id, true, output); - output.push_str("::"); + // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of + // an artificial `enum$<>` type, as defined in msvc_enum_fallback(). + if cpp_like_debuginfo && matches!(t.kind(), ty::Generator(..)) { + let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap(); + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_closure_or_generator_name(tcx, def_id, substs, true, output, visited); + }, + output, + visited, + ); + } else { + push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited); } - - let mut label = String::with_capacity(20); - write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap(); - - push_disambiguated_special_name( - &label, - def_key.disambiguated_data.disambiguator, - cpp_like_debuginfo, - output, - ); - - // We also need to add the generic arguments of the async fn/generator or - // the enclosing function (for closures or async blocks), so that we end - // up with a unique name for every instantiation. - - // Find the generics of the enclosing function, as defined in the source code. - let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); - let generics = tcx.generics_of(enclosing_fn_def_id); - - // Truncate the substs to the length of the above generics. This will cut off - // anything closure- or generator-specific. - let substs = substs.truncate_to(tcx, generics); - push_generic_params_internal(tcx, substs, output, visited); } // Type parameters from polymorphized functions. ty::Param(_) => { - output.push_str(&format!("{:?}", t)); + write!(output, "{:?}", t).unwrap(); } ty::Error(_) | ty::Infer(_) @@ -408,24 +404,32 @@ fn push_debuginfo_type_name<'tcx>( // `EnumMemberDescriptionFactor::create_member_descriptions`. fn msvc_enum_fallback<'tcx>( tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - def: AdtDef<'tcx>, - substs: SubstsRef<'tcx>, + ty_and_layout: TyAndLayout<'tcx>, + push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet>), output: &mut String, visited: &mut FxHashSet>, ) { - let layout = tcx.layout_of(tcx.param_env(def.did()).and(ty)).expect("layout error"); + debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout)); + let ty = ty_and_layout.ty; output.push_str("enum$<"); - push_item_name(tcx, def.did(), true, output); - push_generic_params_internal(tcx, substs, output, visited); + push_inner(output, visited); + + let variant_name = |variant_index| match ty.kind() { + ty::Adt(adt_def, _) => { + debug_assert!(adt_def.is_enum()); + Cow::from(adt_def.variant(variant_index).name.as_str()) + } + ty::Generator(..) => GeneratorSubsts::variant_name(variant_index), + _ => unreachable!(), + }; if let Variants::Multiple { tag_encoding: TagEncoding::Niche { dataful_variant, .. }, tag, variants, .. - } = &layout.variants + } = &ty_and_layout.variants { let dataful_variant_layout = &variants[*dataful_variant]; @@ -439,16 +443,13 @@ fn push_debuginfo_type_name<'tcx>( let max = dataful_discriminant_range.end; let max = tag.value.size(&tcx).truncate(max); - let dataful_variant_name = def.variant(*dataful_variant).name.as_str(); - - output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name)); - } else if let Variants::Single { index: variant_idx } = &layout.variants { + let dataful_variant_name = variant_name(*dataful_variant); + write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap(); + } else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants { // Uninhabited enums can't be constructed and should never need to be visualized so // skip this step for them. - if def.variants().len() != 0 { - let variant = def.variant(*variant_idx).name.as_str(); - - output.push_str(&format!(", {}", variant)); + if !ty_and_layout.abi.is_uninhabited() { + write!(output, ", {}", variant_name(*variant_idx)).unwrap(); } } push_close_angle_bracket(true, output); @@ -700,6 +701,49 @@ pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, out push_generic_params_internal(tcx, substs, output, &mut visited); } +fn push_closure_or_generator_name<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + qualified: bool, + output: &mut String, + visited: &mut FxHashSet>, +) { + // Name will be "{closure_env#0}", "{generator_env#0}", or + // "{async_fn_env#0}", etc. + let def_key = tcx.def_key(def_id); + let generator_kind = tcx.generator_kind(def_id); + + if qualified { + let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; + push_item_name(tcx, parent_def_id, true, output); + output.push_str("::"); + } + + let mut label = String::with_capacity(20); + write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap(); + + push_disambiguated_special_name( + &label, + def_key.disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); + + // We also need to add the generic arguments of the async fn/generator or + // the enclosing function (for closures or async blocks), so that we end + // up with a unique name for every instantiation. + + // Find the generics of the enclosing function, as defined in the source code. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + let generics = tcx.generics_of(enclosing_fn_def_id); + + // Truncate the substs to the length of the above generics. This will cut off + // anything closure- or generator-specific. + let substs = substs.truncate_to(tcx, generics); + push_generic_params_internal(tcx, substs, output, visited); +} + fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { // MSVC debugger always treats `>>` as a shift, even when parsing templates, // so add a space to avoid confusion. diff --git a/src/test/codegen/async-fn-debug-msvc.rs b/src/test/codegen/async-fn-debug-msvc.rs index b10e662b5bbd6..8995605e3dd72 100644 --- a/src/test/codegen/async-fn-debug-msvc.rs +++ b/src/test/codegen/async-fn-debug-msvc.rs @@ -16,7 +16,7 @@ async fn async_fn_test() { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}}, +// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$", // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. // CHECK-SAME: file: [[FILE:![0-9]*]], line: 11, diff --git a/src/test/codegen/generator-debug-msvc.rs b/src/test/codegen/generator-debug-msvc.rs index a6e56a6bd579e..74b1eb948b0f7 100644 --- a/src/test/codegen/generator-debug-msvc.rs +++ b/src/test/codegen/generator-debug-msvc.rs @@ -20,7 +20,7 @@ fn generator_test() -> impl Generator { // FIXME: No way to reliably check the filename. -// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0" +// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum$" // CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]], // For brevity, we only check the struct name and members of the last variant. // CHECK-SAME: file: [[FILE:![0-9]*]], line: 14, diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index aee19736e7e1d..a972943d58e02 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -37,6 +37,37 @@ // lldb-command:print b // lldbg-check:(generator_objects::main::{generator_env#0}) $3 = +// === CDB TESTS =================================================================================== + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Unresumed [Type: enum$] +// cdb-check: [variant] : Unresumed +// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Suspend0 [Type: enum$] +// cdb-check: [variant] : Suspend0 +// cdb-check: [+0x008] c : 6 [Type: int] +// cdb-check: [+0x00c] d : 7 [Type: int] +// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Suspend1 [Type: enum$] +// cdb-check: [variant] : Suspend1 +// cdb-check: [+0x008] c : 7 [Type: int] +// cdb-check: [+0x00c] d : 8 [Type: int] +// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] + +// cdb-command: g +// cdb-command: dx b +// cdb-check: b : Returned [Type: enum$] +// cdb-check: [] [Type: enum$] +// cdb-check: [variant] : Returned +// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] + #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section] From abe854f9850f281889b76c76265dbe4178aa8c5f Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 12:34:38 +0100 Subject: [PATCH 3/9] compiletest: Don't update PDB files of test cases in-place. --- src/tools/compiletest/src/runtest.rs | 57 +++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8431aa7b818df..cf9f5e1332ffa 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -263,11 +263,19 @@ impl<'test> TestCx<'test> { Ui | MirOpt => false, mode => panic!("unimplemented for mode {:?}", mode), }; - if test_should_run { self.run_if_enabled() } else { WillExecute::No } + if test_should_run { + self.run_if_enabled() + } else { + WillExecute::No + } } fn run_if_enabled(&self) -> WillExecute { - if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled } + if self.config.run_enabled() { + WillExecute::Yes + } else { + WillExecute::Disabled + } } fn should_run_successfully(&self, pm: Option) -> bool { @@ -661,6 +669,19 @@ impl<'test> TestCx<'test> { } fn run_debuginfo_cdb_test_no_opt(&self) { + let exe_file = self.make_exe_name(); + + // Existing PDB files are update in-place. When changing the debuginfo + // the compiler generates for something, this can lead to the situation + // where both the old and the new version of the debuginfo for the same + // type is present in the PDB, which is very confusing. + // Therefore we delete any existing PDB file before compiling the test + // case. + let pdb_file = exe_file.with_extension(".pdb"); + if pdb_file.exists() { + std::fs::remove_file(pdb_file).unwrap(); + } + // compile test file (it should have 'compile-flags:-g' in the header) let should_run = self.run_if_enabled(); let compile_result = self.compile_test(should_run, EmitMetadata::No); @@ -671,8 +692,6 @@ impl<'test> TestCx<'test> { return; } - let exe_file = self.make_exe_name(); - let prefixes = { static PREFIXES: &[&str] = &["cdb", "cdbg"]; // No "native rust support" variation for CDB yet. @@ -2010,7 +2029,11 @@ impl<'test> TestCx<'test> { Some(ref s) => s .split(' ') .filter_map(|s| { - if s.chars().all(|c| c.is_whitespace()) { None } else { Some(s.to_owned()) } + if s.chars().all(|c| c.is_whitespace()) { + None + } else { + Some(s.to_owned()) + } }) .collect(), None => Vec::new(), @@ -2069,7 +2092,11 @@ impl<'test> TestCx<'test> { /// The revision, ignored for incremental compilation since it wants all revisions in /// the same directory. fn safe_revision(&self) -> Option<&str> { - if self.config.mode == Incremental { None } else { self.revision } + if self.config.mode == Incremental { + None + } else { + self.revision + } } /// Gets the absolute path to the directory where all output for the given @@ -2224,7 +2251,11 @@ impl<'test> TestCx<'test> { fn charset() -> &'static str { // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset - if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" } + if cfg!(target_os = "freebsd") { + "ISO-8859-1" + } else { + "UTF-8" + } } fn run_rustdoc_test(&self) { @@ -3014,7 +3045,11 @@ impl<'test> TestCx<'test> { let (stderr_kind, stdout_kind) = match output_kind { TestOutput::Compile => ( { - if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR } + if self.props.stderr_per_bitwidth { + &stderr_bits + } else { + UI_STDERR + } }, UI_STDOUT, ), @@ -3711,7 +3746,11 @@ impl<'test> TestCx<'test> { for output_file in files { println!("Actual {} saved to {}", kind, output_file.display()); } - if self.config.bless { 0 } else { 1 } + if self.config.bless { + 0 + } else { + 1 + } } fn prune_duplicate_output(&self, mode: CompareMode, kind: &str, canon_content: &str) { From 5144661d6bebdfcff7571c5251796f1b00447506 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 12:35:55 +0100 Subject: [PATCH 4/9] Remove out-dated information from rustc_codegen_llvm/src/debuginfo/doc.md --- .../rustc_codegen_llvm/src/debuginfo/doc.md | 59 ++----------------- 1 file changed, 5 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md index f983d09203904..22d7f3775095e 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -34,7 +34,7 @@ The function will take care of probing the cache for an existing node for that exact file path. All private state used by the module is stored within either the -CrateDebugContext struct (owned by the CodegenCx) or the +CodegenUnitDebugContext struct (owned by the CodegenCx) or the FunctionDebugContext (owned by the FunctionCx). This file consists of three conceptual sections: @@ -72,7 +72,7 @@ describe(t = List) ... ``` -To break cycles like these, we use "forward declarations". That is, when +To break cycles like these, we use "stubs". That is, when the algorithm encounters a possibly recursive type (any struct or enum), it immediately creates a type description node and inserts it into the cache *before* describing the members of the type. This type description is just @@ -80,13 +80,8 @@ a stub (as type members are not described and added to it yet) but it allows the algorithm to already refer to the type. After the stub is inserted into the cache, the algorithm continues as before. If it now encounters a recursive reference, it will hit the cache and does not try to -describe the type anew. - -This behavior is encapsulated in the 'RecursiveTypeDescription' enum, -which represents a kind of continuation, storing all state needed to -continue traversal at the type members after the type has been registered -with the cache. (This implementation approach might be a tad over- -engineered and may change in the future) +describe the type anew. This behavior is encapsulated in the +`type_map::build_type_with_children()` function. ## Source Locations and Line Information @@ -133,48 +128,4 @@ in the middle of the prologue, however, they are ignored by LLVM's prologue detection. The `create_argument_metadata()` and related functions take care of linking the `llvm.dbg.declare` instructions to the correct source locations even while source location emission is still disabled, so there -is no need to do anything special with source location handling here. - -## Unique Type Identification - -In order for link-time optimization to work properly, LLVM needs a unique -type identifier that tells it across compilation units which types are the -same as others. This type identifier is created by -`TypeMap::get_unique_type_id_of_type()` using the following algorithm: - -1. Primitive types have their name as ID - -2. Structs, enums and traits have a multipart identifier - - 1. The first part is the SVH (strict version hash) of the crate they - were originally defined in - - 2. The second part is the ast::NodeId of the definition in their - original crate - - 3. The final part is a concatenation of the type IDs of their concrete - type arguments if they are generic types. - -3. Tuple-, pointer-, and function types are structurally identified, which - means that they are equivalent if their component types are equivalent - (i.e., `(i32, i32)` is the same regardless in which crate it is used). - -This algorithm also provides a stable ID for types that are defined in one -crate but instantiated from metadata within another crate. We just have to -take care to always map crate and `NodeId`s back to the original crate -context. - -As a side-effect these unique type IDs also help to solve a problem arising -from lifetime parameters. Since lifetime parameters are completely omitted -in debuginfo, more than one `Ty` instance may map to the same debuginfo -type metadata, that is, some struct `Struct<'a>` may have N instantiations -with different concrete substitutions for `'a`, and thus there will be N -`Ty` instances for the type `Struct<'a>` even though it is not generic -otherwise. Unfortunately this means that we cannot use `ty::type_id()` as -cheap identifier for type metadata -- we have done this in the past, but it -led to unnecessary metadata duplication in the best case and LLVM -assertions in the worst. However, the unique type ID as described above -*can* be used as identifier. Since it is comparatively expensive to -construct, though, `ty::type_id()` is still used additionally as an -optimization for cases where the exact same type has been seen before -(which is most of the time). +is no need to do anything special with source location handling here. \ No newline at end of file From 07a1194edf01d0a070506f69dc7d70bdc025cdf9 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 12:45:35 +0100 Subject: [PATCH 5/9] debuginfo: Refactor debuginfo generation for types -- Run x.py fmt --- .../rustc_codegen_llvm/src/debuginfo/doc.md | 2 +- .../src/debuginfo/metadata.rs | 8 ++-- src/tools/compiletest/src/runtest.rs | 42 ++++--------------- 3 files changed, 13 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md index 22d7f3775095e..5a8976c61662b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -128,4 +128,4 @@ in the middle of the prologue, however, they are ignored by LLVM's prologue detection. The `create_argument_metadata()` and related functions take care of linking the `llvm.dbg.declare` instructions to the correct source locations even while source location emission is still disabled, so there -is no need to do anything special with source location handling here. \ No newline at end of file +is no need to do anything special with source location handling here. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 488dbe3456be3..3e2beaecc31ed 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1022,9 +1022,11 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( } }; - debug_assert!(up_var_tys - .iter() - .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t))); + debug_assert!( + up_var_tys + .iter() + .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) + ); let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id); let layout = cx.layout_of(closure_or_generator_ty); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index cf9f5e1332ffa..4740a9e8e9d54 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -263,19 +263,11 @@ impl<'test> TestCx<'test> { Ui | MirOpt => false, mode => panic!("unimplemented for mode {:?}", mode), }; - if test_should_run { - self.run_if_enabled() - } else { - WillExecute::No - } + if test_should_run { self.run_if_enabled() } else { WillExecute::No } } fn run_if_enabled(&self) -> WillExecute { - if self.config.run_enabled() { - WillExecute::Yes - } else { - WillExecute::Disabled - } + if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled } } fn should_run_successfully(&self, pm: Option) -> bool { @@ -2029,11 +2021,7 @@ impl<'test> TestCx<'test> { Some(ref s) => s .split(' ') .filter_map(|s| { - if s.chars().all(|c| c.is_whitespace()) { - None - } else { - Some(s.to_owned()) - } + if s.chars().all(|c| c.is_whitespace()) { None } else { Some(s.to_owned()) } }) .collect(), None => Vec::new(), @@ -2092,11 +2080,7 @@ impl<'test> TestCx<'test> { /// The revision, ignored for incremental compilation since it wants all revisions in /// the same directory. fn safe_revision(&self) -> Option<&str> { - if self.config.mode == Incremental { - None - } else { - self.revision - } + if self.config.mode == Incremental { None } else { self.revision } } /// Gets the absolute path to the directory where all output for the given @@ -2251,11 +2235,7 @@ impl<'test> TestCx<'test> { fn charset() -> &'static str { // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset - if cfg!(target_os = "freebsd") { - "ISO-8859-1" - } else { - "UTF-8" - } + if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" } } fn run_rustdoc_test(&self) { @@ -3045,11 +3025,7 @@ impl<'test> TestCx<'test> { let (stderr_kind, stdout_kind) = match output_kind { TestOutput::Compile => ( { - if self.props.stderr_per_bitwidth { - &stderr_bits - } else { - UI_STDERR - } + if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR } }, UI_STDOUT, ), @@ -3746,11 +3722,7 @@ impl<'test> TestCx<'test> { for output_file in files { println!("Actual {} saved to {}", kind, output_file.display()); } - if self.config.bless { - 0 - } else { - 1 - } + if self.config.bless { 0 } else { 1 } } fn prune_duplicate_output(&self, mode: CompareMode, kind: &str, canon_content: &str) { From 19707b0ff2c0beb80f77eaa7274f4588a506309d Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 3 Mar 2022 14:43:17 +0100 Subject: [PATCH 6/9] debuginfo: Refactor debuginfo generation for types -- Address outstanding FIXMEs. --- .../src/debuginfo/metadata.rs | 10 +------- .../src/debuginfo/metadata/enums/mod.rs | 23 ++++++++++++------- .../src/debuginfo/metadata/enums/native.rs | 6 ++--- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 3e2beaecc31ed..ac634aa015f74 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1120,11 +1120,7 @@ fn build_closure_env_di_node<'ll, 'tcx>( ), // Fields: |cx, owner| build_upvar_field_di_nodes(cx, closure_env_type, owner), - // Generics: - |_| { - // FIXME(mw): Should we specify generic parameters for closures? - smallvec![] - }, + NO_GENERICS, ) } @@ -1178,10 +1174,6 @@ fn build_union_type_di_node<'ll, 'tcx>( ) } -//=----------------------------------------------------------------------------- -// Enums -//=----------------------------------------------------------------------------- - // FIXME(eddyb) maybe precompute this? Right now it's computed once // per generator monomorphization, but it doesn't depend on substs. fn generator_layout_and_saved_local_names<'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index e41f11b34c894..19f0ce54250d0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -110,8 +110,6 @@ fn tag_base_type<'ll, 'tcx>( _ => false, }); - // FIXME(mw): Why are niche and regular tags treated differently? Because we want to preserve - // the sign? match enum_type_and_layout.layout.variants() { // A single-variant enum has no discriminant. Variants::Single { .. } => { @@ -119,6 +117,7 @@ fn tag_base_type<'ll, 'tcx>( } Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => { + // Niche tags are always normalized to unsized integers of the correct size. match tag.value { Primitive::Int(t, _) => t, Primitive::F32 => Integer::I32, @@ -134,12 +133,19 @@ fn tag_base_type<'ll, 'tcx>( } Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => { + // Direct tags preserve the sign. tag.value.to_ty(cx.tcx) } } } -/// This is a helper function. FIXME: elaborate docs. +/// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants. +/// This is a helper function and does not register anything in the type map by itself. +/// +/// `variants` is an iterator of (discr-value, variant-name). +/// +// NOTE: Handling of discriminant values is somewhat inconsistent. They can appear as u128, +// u64, and i64. Here everything gets mapped to i64 because that's what LLVM's API expects. fn build_enumeration_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, type_name: &str, @@ -147,13 +153,14 @@ fn build_enumeration_type_di_node<'ll, 'tcx>( variants: &mut dyn Iterator, Cow<'tcx, str>)>, containing_scope: &'ll DIType, ) -> &'ll DIType { + let is_unsigned = match base_type.kind() { + ty::Int(_) => false, + ty::Uint(_) => true, + _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), + }; + let enumerator_di_nodes: SmallVec> = variants .map(|(discr, variant_name)| { - let is_unsigned = match discr.ty.kind() { - ty::Int(_) => false, - ty::Uint(_) => true, - _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), - }; unsafe { Some(llvm::LLVMRustDIBuilderCreateEnumerator( DIB(cx), diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 12b8cfb481253..f1935e0ec31af 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -410,9 +410,9 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( variant_member_info.variant_name.len(), file_di_node, line_number, - enum_type_and_layout.size.bits(), // FIXME: Unused? - enum_type_and_layout.align.abi.bits() as u32, // FIXME: Unused? - Size::ZERO.bits(), // FIXME: Unused? + enum_type_and_layout.size.bits(), + enum_type_and_layout.align.abi.bits() as u32, + Size::ZERO.bits(), discr_value.map(|v| cx.const_u64(v)), DIFlags::FlagZero, variant_member_info.variant_struct_type_di_node, From 9580a7115d2d75514f0b329a29fafaa3796bd950 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 14 Mar 2022 17:11:58 +0100 Subject: [PATCH 7/9] debuginfo: Refactor debuginfo generation for types -- Address review comments. --- .../src/debuginfo/metadata.rs | 35 ++++++++++--------- .../src/debuginfo/metadata/enums/mod.rs | 2 ++ .../src/debuginfo/metadata/type_map.rs | 3 +- .../src/debuginfo/type_names.rs | 2 +- src/tools/compiletest/src/runtest.rs | 2 ++ 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index ac634aa015f74..f02c7b2d2e11a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -359,7 +359,10 @@ fn build_dyn_type_di_node<'ll, 'tcx>( NO_GENERICS, ) } else { - bug!("Only ty::Dynamic is valid for dyn_type_metadata(). Found {:?} instead.", dyn_type) + bug!( + "Only ty::Dynamic is valid for build_dyn_type_di_node(). Found {:?} instead.", + dyn_type + ) } } @@ -390,15 +393,15 @@ fn build_slice_type_di_node<'ll, 'tcx>( ty::Str => cx.tcx.types.u8, _ => { bug!( - "Only ty::Slice is valid for slice_type_metadata(). Found {:?} instead.", + "Only ty::Slice is valid for build_slice_type_di_node(). Found {:?} instead.", slice_type ) } }; - let element_type_metadata = type_di_node(cx, element_type); + let element_type_di_node = type_di_node(cx, element_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); - DINodeCreationResult { di_node: element_type_metadata, already_stored_in_typemap: false } + DINodeCreationResult { di_node: element_type_di_node, already_stored_in_typemap: false } } /// Get the debuginfo node for the given type. @@ -445,7 +448,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), // Type parameters from polymorphized functions. ty::Param(_) => build_param_type_di_node(cx, t), - _ => bug!("debuginfo: unexpected type in type_metadata: {:?}", t), + _ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t), }; { @@ -456,7 +459,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D Some(di_node) => di_node, None => { bug!( - "expected type di_node for unique \ + "expected type debuginfo node for unique \ type ID '{:?}' to already be in \ the `debuginfo::TypeMap` but it \ was not.", @@ -754,7 +757,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( name_in_debuginfo.push("@"); name_in_debuginfo.push(codegen_unit_name); - debug!("compile_unit_metadata: {:?}", name_in_debuginfo); + debug!("build_compile_unit_di_node: {:?}", name_in_debuginfo); let rustc_producer = format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION"),); // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. @@ -1003,7 +1006,7 @@ fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> fn build_upvar_field_di_nodes<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, closure_or_generator_ty: Ty<'tcx>, - closure_or_generator_metadata: &'ll DIType, + closure_or_generator_di_node: &'ll DIType, ) -> SmallVec<&'ll DIType> { let (&def_id, up_var_tys) = match closure_or_generator_ty.kind() { ty::Generator(def_id, substs, _) => { @@ -1016,7 +1019,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( } _ => { bug!( - "new_upvar_member_descriptions() called with non-closure-or-generator-type: {:?}", + "build_upvar_field_di_nodes() called with non-closure-or-generator-type: {:?}", closure_or_generator_ty ) } @@ -1038,7 +1041,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( .map(|(index, (up_var_ty, capture_name))| { build_field_di_node( cx, - closure_or_generator_metadata, + closure_or_generator_di_node, capture_name, cx.size_and_align_of(up_var_ty), layout.fields.offset(index), @@ -1074,14 +1077,14 @@ fn build_tuple_type_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), // Fields: - |cx, tuple_metadata| { + |cx, tuple_di_node| { component_types .into_iter() .enumerate() .map(|(index, component_type)| { build_field_di_node( cx, - tuple_metadata, + tuple_di_node, &tuple_field_name(index), cx.size_and_align_of(component_type), tuple_type_and_layout.fields.offset(index), @@ -1095,14 +1098,14 @@ fn build_tuple_type_di_node<'ll, 'tcx>( ) } -/// Builds the debufinfo node for a closure environment. +/// Builds the debuginfo node for a closure environment. fn build_closure_env_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { let closure_env_type = unique_type_id.expect_ty(); let &ty::Closure(def_id, _substs) = closure_env_type.kind() else { - bug!("new_closure_env_metadata() called with non-closure-type: {:?}", closure_env_type) + bug!("build_closure_env_di_node() called with non-closure-type: {:?}", closure_env_type) }; let containing_scope = get_namespace_for_item(cx, def_id); let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false); @@ -1225,7 +1228,7 @@ fn build_generic_type_param_di_nodes<'ll, 'tcx>( if let GenericArgKind::Type(ty) = kind.unpack() { let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = type_di_node(cx, actual_type); + let actual_type_di_node = type_di_node(cx, actual_type); let name = name.as_str(); Some(unsafe { llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( @@ -1233,7 +1236,7 @@ fn build_generic_type_param_di_nodes<'ll, 'tcx>( None, name.as_ptr().cast(), name.len(), - actual_type_metadata, + actual_type_di_node, ) }) } else { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 19f0ce54250d0..1eafa9501c47f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -126,6 +126,8 @@ fn tag_base_type<'ll, 'tcx>( // If the niche is the NULL value of a reference, then `discr_enum_ty` will be // a RawPtr. CodeView doesn't know what to do with enums whose base type is a // pointer so we fix this up to just be `usize`. + // DWARF might be able to deal with this but with an integer type we are on + // the safe side there too. cx.data_layout().ptr_sized_integer() } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 26f0647f888a2..8ce44ada88773 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -145,7 +145,6 @@ impl<'ll> DINodeCreationResult<'ll> { } } -#[allow(dead_code)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Stub<'ll> { Struct, @@ -233,7 +232,7 @@ pub(super) fn stub<'ll, 'tcx>( /// This function enables creating debuginfo nodes that can recursively refer to themselves. /// It will first insert the given stub into the type map and only then execute the `members` /// and `generics` closures passed in. These closures have access to the stub so they can -/// directly attach fields to them. If build the type of a field transitively refers back +/// directly attach fields to them. If the type of a field transitively refers back /// to the type currently being built, the stub will already be found in the type map, /// which effectively breaks the recursion cycle. pub(super) fn build_type_with_children<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index ee0658f486a06..fc2921fbd3fa0 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -364,7 +364,7 @@ fn push_debuginfo_type_name<'tcx>( // "{async_fn_env#0}", etc. // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of // an artificial `enum$<>` type, as defined in msvc_enum_fallback(). - if cpp_like_debuginfo && matches!(t.kind(), ty::Generator(..)) { + if cpp_like_debuginfo && t.is_generator() { let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap(); msvc_enum_fallback( tcx, diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 4740a9e8e9d54..6b27d1ecbf550 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -669,6 +669,8 @@ impl<'test> TestCx<'test> { // type is present in the PDB, which is very confusing. // Therefore we delete any existing PDB file before compiling the test // case. + // FIXME: If can reliably detect that MSVC's link.exe is used, then + // passing `/INCREMENTAL:NO` might be a cleaner way to do this. let pdb_file = exe_file.with_extension(".pdb"); if pdb_file.exists() { std::fs::remove_file(pdb_file).unwrap(); From 584855e03d5b42d34cc259ab4ef3d01a637ad1c6 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 14 Mar 2022 17:18:30 +0100 Subject: [PATCH 8/9] debuginfo: Refactor debuginfo generation for types -- Rename DebugInfoMethods::create_vtable_metadata() to DebugInfoMethods::create_vtable_debuginfo() --- compiler/rustc_codegen_gcc/src/debuginfo.rs | 2 +- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/meth.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/debuginfo.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 31959fa19c588..266759ed6cfa1 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -31,7 +31,7 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { } impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { - fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _trait_ref: Option>, _vtable: Self::Value) { + fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option>, _vtable: Self::Value) { // TODO(antoyo) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 6cb8f7863a9d8..4e6d3f88e6719 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -555,7 +555,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } } - fn create_vtable_metadata( + fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, trait_ref: Option>, diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 6ab429669c881..00f101595f27a 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -78,7 +78,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let align = cx.data_layout().pointer_align.abi; let vtable = cx.static_addr_of(vtable_const, align, Some("vtable")); - cx.create_vtable_metadata(ty, trait_ref, vtable); + cx.create_vtable_debuginfo(ty, trait_ref, vtable); cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable } diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index e700afc448f28..f310789d1449c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -7,7 +7,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::Size; pub trait DebugInfoMethods<'tcx>: BackendTypes { - fn create_vtable_metadata( + fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, trait_ref: Option>, From aa2408aa84cb3f758fbf572b1000ceea27fddb7f Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 15 Mar 2022 11:45:51 +0100 Subject: [PATCH 9/9] debuginfo: Refactor debuginfo generation for types -- Make generator-objects test case architecture independent. --- src/test/debuginfo/generator-objects.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index a972943d58e02..d6d7e5b44aafc 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -43,30 +43,30 @@ // cdb-command: dx b // cdb-check: b : Unresumed [Type: enum$] // cdb-check: [variant] : Unresumed -// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] +// cdb-check: [+0x[...]] _ref__a : 0x[...] : 5 [Type: int *] // cdb-command: g // cdb-command: dx b // cdb-check: b : Suspend0 [Type: enum$] // cdb-check: [variant] : Suspend0 -// cdb-check: [+0x008] c : 6 [Type: int] -// cdb-check: [+0x00c] d : 7 [Type: int] -// cdb-check: [+0x000] _ref__a : 0x[...] : 5 [Type: int *] +// cdb-check: [+0x[...]] c : 6 [Type: int] +// cdb-check: [+0x[...]] d : 7 [Type: int] +// cdb-check: [+0x[...]] _ref__a : 0x[...] : 5 [Type: int *] // cdb-command: g // cdb-command: dx b // cdb-check: b : Suspend1 [Type: enum$] // cdb-check: [variant] : Suspend1 -// cdb-check: [+0x008] c : 7 [Type: int] -// cdb-check: [+0x00c] d : 8 [Type: int] -// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] +// cdb-check: [+0x[...]] c : 7 [Type: int] +// cdb-check: [+0x[...]] d : 8 [Type: int] +// cdb-check: [+0x[...]] _ref__a : 0x[...] : 6 [Type: int *] // cdb-command: g // cdb-command: dx b // cdb-check: b : Returned [Type: enum$] // cdb-check: [] [Type: enum$] // cdb-check: [variant] : Returned -// cdb-check: [+0x000] _ref__a : 0x[...] : 6 [Type: int *] +// cdb-check: [+0x[...]] _ref__a : 0x[...] : 6 [Type: int *] #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section] @@ -97,6 +97,7 @@ fn main() { _zzz(); // #break } +#[inline(never)] fn _zzz() { () }