use std::borrow::Cow;
use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo};
use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
use rustc_hir::def::CtorKind;
use rustc_index::IndexSlice;
use rustc_middle::bug;
use rustc_middle::mir::CoroutineLayout;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef};
use rustc_span::Symbol;
use rustc_target::abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
use super::type_map::{DINodeCreationResult, UniqueTypeId};
use super::{size_and_align_of, SmallVec};
use crate::common::CodegenCx;
use crate::debuginfo::metadata::type_map::{self, Stub};
use crate::debuginfo::metadata::{
build_field_di_node, build_generic_type_param_di_nodes, type_di_node, unknown_file_metadata,
UNKNOWN_LINE_NUMBER,
};
use crate::debuginfo::utils::{create_DIArray, get_namespace_for_item, DIB};
use crate::llvm::debuginfo::{DIFlags, DIType};
use crate::llvm::{self};
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(cx.tcx, 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_coroutine_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
) -> DINodeCreationResult<'ll> {
if cpp_like_debuginfo(cx.tcx) {
cpp_like::build_coroutine_di_node(cx, unique_type_id)
} else {
native::build_coroutine_di_node(cx, unique_type_id)
}
}
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.tcx, enum_type_and_layout),
enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
(name, discr.val)
}),
containing_scope,
),
already_stored_in_typemap: false,
}
}
fn build_enumeration_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
type_name: &str,
base_type: Ty<'tcx>,
enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>,
containing_scope: &'ll DIType,
) -> &'ll DIType {
let is_unsigned = match base_type.kind() {
ty::Int(_) => false,
ty::Uint(_) => true,
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
};
let (size, align) = cx.size_and_align_of(base_type);
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators
.map(|(name, value)| unsafe {
let value = [value as u64, (value >> 64) as u64];
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
DIB(cx),
name.as_ptr().cast(),
name.len(),
value.as_ptr(),
size.bits() as libc::c_uint,
is_unsigned,
))
})
.collect();
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,
)
}
}
fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
enum_type_and_layout: TyAndLayout<'tcx>,
enum_type_di_node: &'ll DIType,
variant_index: VariantIdx,
variant_def: &VariantDef,
variant_layout: TyAndLayout<'tcx>,
di_flags: DIFlags,
) -> &'ll DIType {
assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
type_map::build_type_with_children(
cx,
type_map::stub(
cx,
Stub::Struct,
UniqueTypeId::for_enum_variant_struct_type(
cx.tcx,
enum_type_and_layout.ty,
variant_index,
),
variant_def.name.as_str(),
size_and_align_of(enum_type_and_layout),
Some(enum_type_di_node),
di_flags,
),
|cx, struct_type_di_node| {
(0..variant_layout.fields.count())
.map(|field_index| {
let field_name = if variant_def.ctor_kind() != Some(CtorKind::Fn) {
let field = &variant_def.fields[FieldIdx::from_usize(field_index)];
Cow::from(field.name.as_str())
} else {
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),
di_flags,
type_di_node(cx, field_layout.ty),
)
})
.collect::<SmallVec<_>>()
},
|cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
)
.di_node
}
fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
variant_index: VariantIdx,
coroutine_type_and_layout: TyAndLayout<'tcx>,
coroutine_type_di_node: &'ll DIType,
coroutine_layout: &CoroutineLayout<'tcx>,
common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
) -> &'ll DIType {
let variant_name = CoroutineArgs::variant_name(variant_index);
let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
cx.tcx,
coroutine_type_and_layout.ty,
variant_index,
);
let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index);
let coroutine_args = match coroutine_type_and_layout.ty.kind() {
ty::Coroutine(_, args) => args.as_coroutine(),
_ => unreachable!(),
};
type_map::build_type_with_children(
cx,
type_map::stub(
cx,
Stub::Struct,
unique_type_id,
&variant_name,
size_and_align_of(coroutine_type_and_layout),
Some(coroutine_type_di_node),
DIFlags::FlagZero,
),
|cx, variant_struct_type_di_node| {
let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
.map(|field_index| {
let coroutine_saved_local = coroutine_layout.variant_fields[variant_index]
[FieldIdx::from_usize(field_index)];
let field_name_maybe = coroutine_layout.field_names[coroutine_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();
let common_fields: SmallVec<_> = coroutine_args
.prefix_tys()
.iter()
.zip(common_upvar_names)
.enumerate()
.map(|(index, (upvar_ty, upvar_name))| {
build_field_di_node(
cx,
variant_struct_type_di_node,
upvar_name.as_str(),
cx.size_and_align_of(upvar_ty),
coroutine_type_and_layout.fields.offset(index),
DIFlags::FlagZero,
type_di_node(cx, upvar_ty),
)
})
.collect();
state_specific_fields.into_iter().chain(common_fields).collect()
},
|cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
)
.di_node
}
#[derive(Copy, Clone)]
enum DiscrResult {
NoDiscriminant,
Value(u128),
Range(u128, u128),
}
impl DiscrResult {
fn opt_single_val(&self) -> Option<u128> {
if let Self::Value(d) = *self { Some(d) } else { None }
}
}
fn compute_discriminant_value<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
enum_type_and_layout: TyAndLayout<'tcx>,
variant_index: VariantIdx,
) -> DiscrResult {
match enum_type_and_layout.layout.variants() {
&Variants::Single { .. } => DiscrResult::NoDiscriminant,
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
),
&Variants::Multiple {
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant },
tag,
..
} => {
if variant_index == untagged_variant {
let valid_range = enum_type_and_layout
.for_variant(cx, variant_index)
.largest_niche
.as_ref()
.unwrap()
.valid_range;
let min = valid_range.start.min(valid_range.end);
let min = tag.size(cx).truncate(min);
let max = valid_range.start.max(valid_range.end);
let max = tag.size(cx).truncate(max);
DiscrResult::Range(min, max)
} 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.size(cx).truncate(value);
DiscrResult::Value(value)
}
}
}
}