diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index eb19e427217..b647cfa5f4a 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 7d3fe43eeab..413ef0ba764 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 52e03e0ad3d..9fbc33d4b05 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 f16a903ad2c..488dbe3456b 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(super) const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; -pub const NO_SCOPE_METADATA: Option<&DIScope> = None; +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(); -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; +// 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]>; - // 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: (), - } +mod enums; +mod type_map; - /// 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), - } +pub(crate) use type_map::TypeMap; - 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: () }) - } - - /// 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::*; - -/// 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>>, -} - -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); - } - } - - 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() - } -} - -/// 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"), - }; - - 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, - ) - }; - - 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, - }, - ]; - - composite_type_metadata( + type_map::build_type_with_children( cx, - ptr_type, - &ptr_type_debuginfo_name, - unique_type_id, - member_descriptions, - NO_SCOPE_METADATA, + 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); + + // 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, + ) + }; + + 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), + ), + ] + }, + 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 name = compute_debuginfo_type_name(cx.tcx, t, false); - let (size, align) = cx.size_and_align_of(t); - create_struct_stub( + 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()); + }; + + 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, - 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() - } -} - -fn prepare_struct_metadata<'ll, 'tcx>( +/// 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, + flags: DIFlags, + 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, + ) + } +} + +/// 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>, -} - -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() - } -} - -fn prepare_tuple_metadata<'ll, 'tcx>( +/// 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 + ) + } + }; + + 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() +} + +/// 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, - ); + let tuple_type_and_layout = cx.layout_of(tuple_type); + let type_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false); - create_and_register_recursive_type_forward_declaration( + 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, -} - -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() - } -} - -fn prepare_union_metadata<'ll, 'tcx>( +/// Builds the debufinfo node for a closure environment. +fn build_closure_env_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() { - ty::Adt(def, _) => (def.did(), def.non_enum_variant()), - _ => bug!("prepare_union_metadata on a non-ADT"), +) -> 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); - let containing_scope = get_namespace_for_item(cx, union_def_id); - - 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::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![] + }, + ) +} + +/// Build the debuginfo node for a Rust `union` type. +fn build_union_type_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, +) -> 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!("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); + + type_map::build_type_with_children( + cx, + 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), - ); + 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, + }; - // 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, - }; + let field_offset = pointer_size * index as u64; - 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 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 00000000000..e9772cd78d7 --- /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 00000000000..e41f11b34c8 --- /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 00000000000..12b8cfb4812 --- /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 00000000000..26f0647f888 --- /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 34013b5f737..6cb8f7863a9 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>, +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: 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>, + 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 fa75463067f..fe9851cfa56 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 d1a0cf78d6a..6e3f4f0b8ef 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 67df64e9b1b..6a122addf22 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 a90c85a5449..b10e662b5bb 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 8fbd2765fd7..9f6058a71b3 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 fb8b9e09fd2..a6e56a6bd57 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 e777fe3af63..3ec860f2cbc 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 642694355ad..a153a9a4228 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 c9692128562..52841d50f64 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