Skip to main content

rustc_codegen_llvm/debuginfo/metadata/enums/
mod.rs

1use std::borrow::Cow;
2
3use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants};
4use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo};
5use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
6use rustc_codegen_ssa::traits::MiscCodegenMethods;
7use rustc_hir::def::CtorKind;
8use rustc_index::IndexSlice;
9use rustc_middle::mir::CoroutineLayout;
10use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
11use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef};
12use rustc_span::{Span, Symbol, bug};
13
14use super::type_map::{DINodeCreationResult, UniqueTypeId};
15use super::{SmallVec, size_and_align_of};
16use crate::common::{AsCCharPtr, CodegenCx};
17use crate::debuginfo::metadata::type_map::{self, Stub};
18use crate::debuginfo::metadata::{
19    UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes,
20    file_metadata_from_def_id, type_di_node, unknown_file_metadata,
21};
22use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item};
23use crate::llvm::debuginfo::{DIFlags, DIType};
24use crate::llvm::{self, ToLlvmBool};
25
26mod cpp_like;
27mod native;
28
29pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
30    cx: &CodegenCx<'ll, 'tcx>,
31    unique_type_id: UniqueTypeId<'tcx>,
32    span: Span,
33) -> DINodeCreationResult<'ll> {
34    let enum_type = unique_type_id.expect_ty();
35    let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
36        bug_impl(None,
    format_args!("build_enum_type_di_node() called with non-enum type: `{0:?}`",
        enum_type), Location::caller())bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
37    };
38
39    let enum_type_and_layout = cx.spanned_layout_of(enum_type, span);
40
41    if wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) {
42        return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
43    }
44
45    if cpp_like_debuginfo(cx.tcx) {
46        cpp_like::build_enum_type_di_node(cx, unique_type_id)
47    } else {
48        native::build_enum_type_di_node(cx, unique_type_id)
49    }
50}
51
52pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
53    cx: &CodegenCx<'ll, 'tcx>,
54    unique_type_id: UniqueTypeId<'tcx>,
55) -> DINodeCreationResult<'ll> {
56    if cpp_like_debuginfo(cx.tcx) {
57        cpp_like::build_coroutine_di_node(cx, unique_type_id)
58    } else {
59        native::build_coroutine_di_node(cx, unique_type_id)
60    }
61}
62
63/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields.
64///
65/// The resulting debuginfo will be a DW_TAG_enumeration_type.
66fn build_c_style_enum_di_node<'ll, 'tcx>(
67    cx: &CodegenCx<'ll, 'tcx>,
68    enum_adt_def: AdtDef<'tcx>,
69    enum_type_and_layout: TyAndLayout<'tcx>,
70) -> DINodeCreationResult<'ll> {
71    let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
72    let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
73        Some(enum_adt_def.did())
74    } else {
75        None
76    };
77    DINodeCreationResult {
78        di_node: build_enumeration_type_di_node(
79            cx,
80            &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
81            tag_base_type(cx.tcx, enum_type_and_layout),
82            enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
83                let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
84                (name, discr.val)
85            }),
86            enum_adt_def_id,
87            containing_scope,
88        ),
89        already_stored_in_typemap: false,
90    }
91}
92
93/// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants.
94/// This is a helper function and does not register anything in the type map by itself.
95///
96/// `variants` is an iterator of (discr-value, variant-name).
97fn build_enumeration_type_di_node<'ll, 'tcx>(
98    cx: &CodegenCx<'ll, 'tcx>,
99    type_name: &str,
100    base_type: Ty<'tcx>,
101    enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>,
102    def_id: Option<rustc_span::def_id::DefId>,
103    containing_scope: &'ll DIType,
104) -> &'ll DIType {
105    let is_unsigned = match base_type.kind() {
106        ty::Int(_) => false,
107        ty::Uint(_) => true,
108        _ => bug_impl(None,
    format_args!("build_enumeration_type_di_node() called with non-integer tag type."),
    Location::caller())bug!("build_enumeration_type_di_node() called with non-integer tag type."),
109    };
110    let (size, align) = cx.size_and_align_of(base_type);
111
112    let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators
113        .map(|(name, value)| {
114            let value_words = [value as u64, (value >> 64) as u64];
115            let size_in_bits = size.bits();
116            // LLVM computes `NumWords = (SizeInBits + 63) / 64`.
117            if !((size_in_bits + 63) / 64 <= value_words.len() as u64) {
    ::core::panicking::panic("assertion failed: (size_in_bits + 63) / 64 <= value_words.len() as u64")
};assert!((size_in_bits + 63) / 64 <= value_words.len() as u64);
118
119            let enumerator = unsafe {
120                llvm::LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision(
121                    DIB(cx),
122                    name.as_ptr(),
123                    name.len(),
124                    size_in_bits,
125                    value_words.as_ptr(),
126                    is_unsigned.to_llvm_bool(),
127                )
128            };
129            Some(enumerator)
130        })
131        .collect();
132
133    let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
134    {
135        file_metadata_from_def_id(cx, def_id)
136    } else {
137        (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
138    };
139
140    unsafe {
141        llvm::LLVMRustDIBuilderCreateEnumerationType(
142            DIB(cx),
143            containing_scope,
144            type_name.as_c_char_ptr(),
145            type_name.len(),
146            file_metadata,
147            line_number,
148            size.bits(),
149            align.bits() as u32,
150            create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
151            type_di_node(cx, base_type),
152            true,
153        )
154    }
155}
156
157/// Build the debuginfo node for the struct type describing a single variant of an enum.
158///
159/// ```txt
160///       DW_TAG_structure_type              (top-level type for enum)
161///         DW_TAG_variant_part              (variant part)
162///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
163///           DW_TAG_member                  (discriminant member)
164///           DW_TAG_variant                 (variant 1)
165///           DW_TAG_variant                 (variant 2)
166///           DW_TAG_variant                 (variant 3)
167///  --->   DW_TAG_structure_type            (type of variant 1)
168///  --->   DW_TAG_structure_type            (type of variant 2)
169///  --->   DW_TAG_structure_type            (type of variant 3)
170/// ```
171///
172/// In CPP-like mode, we have the exact same descriptions for each variant too:
173///
174/// ```txt
175///       DW_TAG_union_type              (top-level type for enum)
176///         DW_TAG_member                    (member for variant 1)
177///         DW_TAG_member                    (member for variant 2)
178///         DW_TAG_member                    (member for variant 3)
179///  --->   DW_TAG_structure_type            (type of variant 1)
180///  --->   DW_TAG_structure_type            (type of variant 2)
181///  --->   DW_TAG_structure_type            (type of variant 3)
182///         DW_TAG_enumeration_type          (type of tag)
183/// ```
184///
185/// The node looks like:
186///
187/// ```txt
188/// DW_TAG_structure_type
189///   DW_AT_name                  <name-of-variant>
190///   DW_AT_byte_size             0x00000010
191///   DW_AT_alignment             0x00000008
192///   DW_TAG_member
193///     DW_AT_name                  <name-of-field-0>
194///     DW_AT_type                  <0x0000018e>
195///     DW_AT_alignment             0x00000004
196///     DW_AT_data_member_location  4
197///   DW_TAG_member
198///     DW_AT_name                  <name-of-field-1>
199///     DW_AT_type                  <0x00000195>
200///     DW_AT_alignment             0x00000008
201///     DW_AT_data_member_location  8
202///   ...
203/// ```
204///
205/// The type of a variant is always a struct type with the name of the variant
206/// and a DW_TAG_member for each field (but not the discriminant).
207fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
208    cx: &CodegenCx<'ll, 'tcx>,
209    enum_type_and_layout: TyAndLayout<'tcx>,
210    enum_type_di_node: &'ll DIType,
211    variant_index: VariantIdx,
212    variant_def: &VariantDef,
213    variant_layout: TyAndLayout<'tcx>,
214    di_flags: DIFlags,
215) -> &'ll DIType {
216    {
    match (&variant_layout.ty, &enum_type_and_layout.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
217
218    let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
219        Some(file_metadata_from_def_id(cx, Some(variant_def.def_id)))
220    } else {
221        None
222    };
223
224    type_map::build_type_with_children(
225        cx,
226        type_map::stub(
227            cx,
228            Stub::Struct,
229            UniqueTypeId::for_enum_variant_struct_type(
230                cx.tcx,
231                enum_type_and_layout.ty,
232                variant_index,
233            ),
234            variant_def.name.as_str(),
235            def_location,
236            // NOTE: We use size and align of enum_type, not from variant_layout:
237            size_and_align_of(enum_type_and_layout),
238            Some(enum_type_di_node),
239            di_flags,
240        ),
241        |cx, struct_type_di_node| {
242            (0..variant_layout.fields.count())
243                .map(|field_index| {
244                    let field_name = if variant_def.ctor_kind() != Some(CtorKind::Fn) {
245                        // Fields have names
246                        let field = &variant_def.fields[FieldIdx::from_usize(field_index)];
247                        Cow::from(field.name.as_str())
248                    } else {
249                        // Tuple-like
250                        super::tuple_field_name(field_index)
251                    };
252
253                    let field_layout = variant_layout.field(cx, field_index);
254
255                    build_field_di_node(
256                        cx,
257                        struct_type_di_node,
258                        &field_name,
259                        field_layout,
260                        variant_layout.fields.offset(field_index),
261                        di_flags,
262                        type_di_node(cx, field_layout.ty),
263                        None,
264                    )
265                })
266                .collect::<SmallVec<_>>()
267        },
268        |cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
269    )
270    .di_node
271}
272
273/// Build the struct type for describing a single coroutine state.
274/// See [build_coroutine_variant_struct_type_di_node].
275///
276/// ```txt
277///
278///       DW_TAG_structure_type              (top-level type for enum)
279///         DW_TAG_variant_part              (variant part)
280///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
281///           DW_TAG_member                  (discriminant member)
282///           DW_TAG_variant                 (variant 1)
283///           DW_TAG_variant                 (variant 2)
284///           DW_TAG_variant                 (variant 3)
285///  --->   DW_TAG_structure_type            (type of variant 1)
286///  --->   DW_TAG_structure_type            (type of variant 2)
287///  --->   DW_TAG_structure_type            (type of variant 3)
288///
289/// ```
290fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
291    cx: &CodegenCx<'ll, 'tcx>,
292    variant_index: VariantIdx,
293    coroutine_type_and_layout: TyAndLayout<'tcx>,
294    coroutine_type_di_node: &'ll DIType,
295    coroutine_layout: &CoroutineLayout<'tcx>,
296    common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
297) -> &'ll DIType {
298    let variant_name = CoroutineArgs::variant_name(variant_index);
299    let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
300        cx.tcx,
301        coroutine_type_and_layout.ty,
302        variant_index,
303    );
304
305    let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index);
306
307    let coroutine_args = match coroutine_type_and_layout.ty.kind() {
308        ty::Coroutine(_, args) => args.as_coroutine(),
309        _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
310    };
311
312    type_map::build_type_with_children(
313        cx,
314        type_map::stub(
315            cx,
316            Stub::Struct,
317            unique_type_id,
318            &variant_name,
319            None,
320            size_and_align_of(coroutine_type_and_layout),
321            Some(coroutine_type_di_node),
322            DIFlags::FlagZero,
323        ),
324        |cx, variant_struct_type_di_node| {
325            // Fields that just belong to this variant/state
326            let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
327                .map(|field_index| {
328                    let coroutine_saved_local = coroutine_layout.variant_fields[variant_index]
329                        [FieldIdx::from_usize(field_index)];
330                    let field_name_maybe =
331                        coroutine_layout.field_tys[coroutine_saved_local].debuginfo_name;
332                    let field_name = field_name_maybe
333                        .as_ref()
334                        .map(|s| Cow::from(s.as_str()))
335                        .unwrap_or_else(|| super::tuple_field_name(field_index));
336
337                    let field_type = variant_layout.field(cx, field_index).ty;
338
339                    build_field_di_node(
340                        cx,
341                        variant_struct_type_di_node,
342                        &field_name,
343                        cx.layout_of(field_type),
344                        variant_layout.fields.offset(field_index),
345                        DIFlags::FlagZero,
346                        type_di_node(cx, field_type),
347                        None,
348                    )
349                })
350                .collect();
351
352            // Fields that are common to all states
353            let common_fields: SmallVec<_> = coroutine_args
354                .upvar_tys()
355                .iter()
356                .zip(common_upvar_names)
357                .enumerate()
358                .map(|(index, (upvar_ty, upvar_name))| {
359                    build_field_di_node(
360                        cx,
361                        variant_struct_type_di_node,
362                        upvar_name.as_str(),
363                        cx.layout_of(upvar_ty),
364                        coroutine_type_and_layout.fields.offset(index),
365                        DIFlags::FlagZero,
366                        type_di_node(cx, upvar_ty),
367                        None,
368                    )
369                })
370                .collect();
371
372            state_specific_fields.into_iter().chain(common_fields).collect()
373        },
374        |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
375    )
376    .di_node
377}
378
379#[derive(#[automatically_derived]
impl ::core::marker::Copy for DiscrResult { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DiscrResult { }
#[automatically_derived]
impl ::core::clone::Clone for DiscrResult {
    #[inline]
    fn clone(&self) -> DiscrResult {
        let _: ::core::clone::AssertParamIsClone<u128>;
        *self
    }
}Clone)]
380enum DiscrResult {
381    NoDiscriminant,
382    Value(u128),
383    Range(u128, u128),
384}
385
386impl DiscrResult {
387    fn opt_single_val(&self) -> Option<u128> {
388        if let Self::Value(d) = *self { Some(d) } else { None }
389    }
390}
391
392/// Returns the discriminant value corresponding to the variant index.
393///
394/// Will return `None` if there is less than two variants (because then the enum won't have)
395/// a tag, and if this is the untagged variant of a niche-layout enum (because then there is no
396/// single discriminant value).
397fn compute_discriminant_value<'ll, 'tcx>(
398    cx: &CodegenCx<'ll, 'tcx>,
399    enum_type_and_layout: TyAndLayout<'tcx>,
400    variant_index: VariantIdx,
401) -> DiscrResult {
402    match enum_type_and_layout.layout.variants() {
403        &Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant,
404        &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
405            enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
406        ),
407        &Variants::Multiple {
408            tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant },
409            tag,
410            ..
411        } => {
412            if variant_index == untagged_variant {
413                let valid_range = enum_type_and_layout
414                    .for_variant(cx, variant_index)
415                    .largest_niche
416                    .as_ref()
417                    .unwrap()
418                    .valid_range;
419
420                let min = valid_range.start.min(valid_range.end);
421                let min = tag.size(cx).truncate(min);
422
423                let max = valid_range.start.max(valid_range.end);
424                let max = tag.size(cx).truncate(max);
425
426                DiscrResult::Range(min, max)
427            } else {
428                let value = (variant_index.as_u32() as u128)
429                    .wrapping_sub(niche_variants.start.as_u32() as u128)
430                    .wrapping_add(niche_start);
431                let value = tag.size(cx).truncate(value);
432                DiscrResult::Value(value)
433            }
434        }
435    }
436}