Skip to main content

rustc_codegen_llvm/debuginfo/metadata/enums/
native.rs

1use std::borrow::Cow;
2
3use libc::c_uint;
4use rustc_abi::{Size, TagEncoding, VariantIdx, Variants};
5use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name;
6use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo};
7use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods};
8use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
9use rustc_middle::{bug, ty};
10use smallvec::smallvec;
11
12use crate::common::{AsCCharPtr, CodegenCx};
13use crate::debuginfo::metadata::type_map::{self, Stub, StubInfo, UniqueTypeId};
14use crate::debuginfo::metadata::{
15    DINodeCreationResult, NO_GENERICS, SmallVec, UNKNOWN_LINE_NUMBER, create_member_type,
16    file_metadata, file_metadata_from_def_id, size_and_align_of, type_di_node,
17    unknown_file_metadata, visibility_di_flags,
18};
19use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item};
20use crate::llvm;
21use crate::llvm::debuginfo::{DIFile, DIFlags, DIType};
22
23/// Build the debuginfo node for an enum type. The listing below shows how such a
24/// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type`
25/// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant`
26/// for each variant of the enum. The variant-part also contains a single member
27/// describing the discriminant, and a nested struct type for each of the variants.
28///
29/// ```txt
30///  ---> DW_TAG_structure_type              (top-level type for enum)
31///         DW_TAG_variant_part              (variant part)
32///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
33///           DW_TAG_member                  (discriminant member)
34///           DW_TAG_variant                 (variant 1)
35///           DW_TAG_variant                 (variant 2)
36///           DW_TAG_variant                 (variant 3)
37///         DW_TAG_structure_type            (type of variant 1)
38///         DW_TAG_structure_type            (type of variant 2)
39///         DW_TAG_structure_type            (type of variant 3)
40/// ```
41pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
42    cx: &CodegenCx<'ll, 'tcx>,
43    unique_type_id: UniqueTypeId<'tcx>,
44) -> DINodeCreationResult<'ll> {
45    let enum_type = unique_type_id.expect_ty();
46    let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
47        ::rustc_middle::util::bug::bug_fmt(format_args!("build_enum_type_di_node() called with non-enum type: `{0:?}`",
        enum_type))bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
48    };
49
50    let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
51    let enum_type_and_layout = cx.layout_of(enum_type);
52    let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
53
54    let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did());
55
56    if !!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) {
    ::core::panicking::panic("assertion failed: !wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout)")
};assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout));
57
58    let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
59        Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did())))
60    } else {
61        None
62    };
63
64    type_map::build_type_with_children(
65        cx,
66        type_map::stub(
67            cx,
68            Stub::Struct,
69            unique_type_id,
70            &enum_type_name,
71            def_location,
72            size_and_align_of(enum_type_and_layout),
73            Some(containing_scope),
74            visibility_flags,
75        ),
76        |cx, enum_type_di_node| {
77            // Build the struct type for each variant. These will be referenced by the
78            // DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE.
79            // We also called the names for the corresponding DW_TAG_variant DIEs here.
80            let variant_member_infos: SmallVec<_> = enum_adt_def
81                .variant_range()
82                .map(|variant_index| VariantMemberInfo {
83                    variant_index,
84                    variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
85                    variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
86                        cx,
87                        enum_type_and_layout,
88                        enum_type_di_node,
89                        variant_index,
90                        enum_adt_def.variant(variant_index),
91                        enum_type_and_layout.for_variant(cx, variant_index),
92                        visibility_flags,
93                    ),
94                    source_info: if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
95                        Some(file_metadata_from_def_id(
96                            cx,
97                            Some(enum_adt_def.variant(variant_index).def_id),
98                        ))
99                    } else {
100                        None
101                    },
102                })
103                .collect();
104
105            let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
106                Some(enum_adt_def.did())
107            } else {
108                None
109            };
110            {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(build_enum_variant_part_di_node(cx, enum_type_and_layout,
                enum_type_di_node, enum_adt_def_id,
                &variant_member_infos[..]));
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [build_enum_variant_part_di_node(cx, enum_type_and_layout,
                                enum_type_di_node, enum_adt_def_id,
                                &variant_member_infos[..])])))
    }
}smallvec![build_enum_variant_part_di_node(
111                cx,
112                enum_type_and_layout,
113                enum_type_di_node,
114                enum_adt_def_id,
115                &variant_member_infos[..],
116            )]
117        },
118        // We don't seem to be emitting generic args on the enum type, it seems. Rather
119        // they get attached to the struct type of each variant.
120        NO_GENERICS,
121    )
122}
123
124/// Build the debuginfo node for a coroutine environment. It looks the same as the debuginfo for
125/// an enum. See [build_enum_type_di_node] for more information.
126///
127/// ```txt
128///
129///  ---> DW_TAG_structure_type              (top-level type for the coroutine)
130///         DW_TAG_variant_part              (variant part)
131///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
132///           DW_TAG_member                  (discriminant member)
133///           DW_TAG_variant                 (variant 1)
134///           DW_TAG_variant                 (variant 2)
135///           DW_TAG_variant                 (variant 3)
136///         DW_TAG_structure_type            (type of variant 1)
137///         DW_TAG_structure_type            (type of variant 2)
138///         DW_TAG_structure_type            (type of variant 3)
139///
140/// ```
141pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
142    cx: &CodegenCx<'ll, 'tcx>,
143    unique_type_id: UniqueTypeId<'tcx>,
144) -> DINodeCreationResult<'ll> {
145    let coroutine_type = unique_type_id.expect_ty();
146    let &ty::Coroutine(coroutine_def_id, coroutine_args) = coroutine_type.kind() else {
147        ::rustc_middle::util::bug::bug_fmt(format_args!("build_coroutine_di_node() called with non-coroutine type: `{0:?}`",
        coroutine_type))bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type)
148    };
149
150    let containing_scope = get_namespace_for_item(cx, coroutine_def_id);
151    let coroutine_type_and_layout = cx.layout_of(coroutine_type);
152
153    if !!wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout) {
    ::core::panicking::panic("assertion failed: !wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout)")
};assert!(!wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout));
154
155    let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false);
156
157    let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
158        Some(file_metadata_from_def_id(cx, Some(coroutine_def_id)))
159    } else {
160        None
161    };
162
163    type_map::build_type_with_children(
164        cx,
165        type_map::stub(
166            cx,
167            Stub::Struct,
168            unique_type_id,
169            &coroutine_type_name,
170            def_location,
171            size_and_align_of(coroutine_type_and_layout),
172            Some(containing_scope),
173            DIFlags::FlagZero,
174        ),
175        |cx, coroutine_type_di_node| {
176            let coroutine_layout =
177                cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args).unwrap();
178
179            let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } =
180                coroutine_type_and_layout.variants
181            else {
182                ::rustc_middle::util::bug::bug_fmt(format_args!("Encountered coroutine with non-direct-tag layout: {0:?}",
        coroutine_type_and_layout))bug!(
183                    "Encountered coroutine with non-direct-tag layout: {:?}",
184                    coroutine_type_and_layout
185                )
186            };
187
188            let common_upvar_names =
189                cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id);
190
191            // Build variant struct types
192            let variant_struct_type_di_nodes: SmallVec<_> = variants
193                .indices()
194                .map(|variant_index| {
195                    // FIXME: This is problematic because just a number is not a valid identifier.
196                    //        CoroutineArgs::variant_name(variant_index), would be consistent
197                    //        with enums?
198                    let variant_name = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}", variant_index.as_usize()))
    })format!("{}", variant_index.as_usize()).into();
199
200                    let span = coroutine_layout.variant_source_info[variant_index].span;
201                    let source_info = if !span.is_dummy() {
202                        let loc = cx.lookup_debug_loc(span.lo());
203                        Some((file_metadata(cx, &loc.file), loc.line))
204                    } else {
205                        None
206                    };
207
208                    VariantMemberInfo {
209                        variant_index,
210                        variant_name,
211                        variant_struct_type_di_node:
212                            super::build_coroutine_variant_struct_type_di_node(
213                                cx,
214                                variant_index,
215                                coroutine_type_and_layout,
216                                coroutine_type_di_node,
217                                coroutine_layout,
218                                common_upvar_names,
219                            ),
220                        source_info,
221                    }
222                })
223                .collect();
224
225            let coroutine_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
226                Some(coroutine_def_id)
227            } else {
228                None
229            };
230            {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(build_enum_variant_part_di_node(cx,
                coroutine_type_and_layout, coroutine_type_di_node,
                coroutine_def_id, &variant_struct_type_di_nodes[..]));
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [build_enum_variant_part_di_node(cx,
                                coroutine_type_and_layout, coroutine_type_di_node,
                                coroutine_def_id, &variant_struct_type_di_nodes[..])])))
    }
}smallvec![build_enum_variant_part_di_node(
231                cx,
232                coroutine_type_and_layout,
233                coroutine_type_di_node,
234                coroutine_def_id,
235                &variant_struct_type_di_nodes[..],
236            )]
237        },
238        // We don't seem to be emitting generic args on the coroutine type, it seems. Rather
239        // they get attached to the struct type of each variant.
240        NO_GENERICS,
241    )
242}
243
244/// Builds the DW_TAG_variant_part of an enum or coroutine debuginfo node:
245///
246/// ```txt
247///       DW_TAG_structure_type              (top-level type for enum)
248/// --->    DW_TAG_variant_part              (variant part)
249///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
250///           DW_TAG_member                  (discriminant member)
251///           DW_TAG_variant                 (variant 1)
252///           DW_TAG_variant                 (variant 2)
253///           DW_TAG_variant                 (variant 3)
254///         DW_TAG_structure_type            (type of variant 1)
255///         DW_TAG_structure_type            (type of variant 2)
256///         DW_TAG_structure_type            (type of variant 3)
257/// ```
258fn build_enum_variant_part_di_node<'ll, 'tcx>(
259    cx: &CodegenCx<'ll, 'tcx>,
260    enum_type_and_layout: TyAndLayout<'tcx>,
261    enum_type_di_node: &'ll DIType,
262    enum_type_def_id: Option<rustc_span::def_id::DefId>,
263    variant_member_infos: &[VariantMemberInfo<'_, 'll>],
264) -> &'ll DIType {
265    let tag_member_di_node =
266        build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node);
267
268    let variant_part_unique_type_id =
269        UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty);
270
271    let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
272    {
273        file_metadata_from_def_id(cx, enum_type_def_id)
274    } else {
275        (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
276    };
277
278    let stub = StubInfo::new(
279        cx,
280        variant_part_unique_type_id,
281        |cx, variant_part_unique_type_id_str| unsafe {
282            let variant_part_name = "";
283            llvm::LLVMRustDIBuilderCreateVariantPart(
284                DIB(cx),
285                enum_type_di_node,
286                variant_part_name.as_c_char_ptr(),
287                variant_part_name.len(),
288                file_metadata,
289                line_number,
290                enum_type_and_layout.size.bits(),
291                enum_type_and_layout.align.bits() as u32,
292                DIFlags::FlagZero,
293                tag_member_di_node,
294                create_DIArray(DIB(cx), &[]),
295                variant_part_unique_type_id_str.as_c_char_ptr(),
296                variant_part_unique_type_id_str.len(),
297            )
298        },
299    );
300
301    type_map::build_type_with_children(
302        cx,
303        stub,
304        |cx, variant_part_di_node| {
305            variant_member_infos
306                .iter()
307                .map(|variant_member_info| {
308                    build_enum_variant_member_di_node(
309                        cx,
310                        enum_type_and_layout,
311                        variant_part_di_node,
312                        variant_member_info,
313                    )
314                })
315                .collect()
316        },
317        NO_GENERICS,
318    )
319    .di_node
320}
321
322/// Builds the DW_TAG_member describing where we can find the tag of an enum.
323/// Returns `None` if the enum does not have a tag.
324///
325/// ```txt
326///
327///       DW_TAG_structure_type              (top-level type for enum)
328///         DW_TAG_variant_part              (variant part)
329///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
330/// --->      DW_TAG_member                  (discriminant member)
331///           DW_TAG_variant                 (variant 1)
332///           DW_TAG_variant                 (variant 2)
333///           DW_TAG_variant                 (variant 3)
334///         DW_TAG_structure_type            (type of variant 1)
335///         DW_TAG_structure_type            (type of variant 2)
336///         DW_TAG_structure_type            (type of variant 3)
337///
338/// ```
339fn build_discr_member_di_node<'ll, 'tcx>(
340    cx: &CodegenCx<'ll, 'tcx>,
341    enum_or_coroutine_type_and_layout: TyAndLayout<'tcx>,
342    enum_or_coroutine_type_di_node: &'ll DIType,
343) -> Option<&'ll DIType> {
344    let tag_name = match enum_or_coroutine_type_and_layout.ty.kind() {
345        ty::Coroutine(..) => "__state",
346        _ => "",
347    };
348
349    // NOTE: This is actually wrong. This will become a member of
350    //       of the DW_TAG_variant_part. But, due to LLVM's API, that
351    //       can only be constructed with this DW_TAG_member already in created.
352    //       In LLVM IR the wrong scope will be listed but when DWARF is
353    //       generated from it, the DW_TAG_member will be a child the
354    //       DW_TAG_variant_part.
355    let containing_scope = enum_or_coroutine_type_di_node;
356
357    match enum_or_coroutine_type_and_layout.layout.variants() {
358        // A single-variant or no-variant enum has no discriminant.
359        &Variants::Single { .. } | &Variants::Empty => None,
360
361        &Variants::Multiple { tag_field, .. } => {
362            let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
363            let ty = type_di_node(cx, tag_base_type);
364            let file = unknown_file_metadata(cx);
365
366            let layout = cx.layout_of(tag_base_type);
367
368            Some(create_member_type(
369                cx,
370                containing_scope,
371                &tag_name,
372                file,
373                UNKNOWN_LINE_NUMBER,
374                layout,
375                enum_or_coroutine_type_and_layout.fields.offset(tag_field.as_usize()),
376                DIFlags::FlagArtificial,
377                ty,
378            ))
379        }
380    }
381}
382
383/// Build the debuginfo node for `DW_TAG_variant`:
384///
385/// ```txt
386///       DW_TAG_structure_type              (top-level type for enum)
387///         DW_TAG_variant_part              (variant part)
388///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
389///           DW_TAG_member                  (discriminant member)
390///  --->     DW_TAG_variant                 (variant 1)
391///  --->     DW_TAG_variant                 (variant 2)
392///  --->     DW_TAG_variant                 (variant 3)
393///         DW_TAG_structure_type            (type of variant 1)
394///         DW_TAG_structure_type            (type of variant 2)
395///         DW_TAG_structure_type            (type of variant 3)
396/// ```
397///
398/// This node looks like:
399///
400/// ```txt
401/// DW_TAG_variant
402///   DW_AT_discr_value           0
403///   DW_TAG_member
404///     DW_AT_name                  None
405///     DW_AT_type                  <0x000002a1>
406///     DW_AT_alignment             0x00000002
407///     DW_AT_data_member_location  0
408/// ```
409///
410/// The DW_AT_discr_value is optional, and is omitted if
411///   - This is the only variant of a univariant enum (i.e. their is no discriminant)
412///   - This is the "untagged" variant of a niche-layout enum
413///     (where only the other variants are identified by a single value)
414///
415/// There is only ever a single member, the type of which is a struct that describes the
416/// fields of the variant (excluding the discriminant). The name of the member is the name
417/// of the variant as given in the source code. The DW_AT_data_member_location is always
418/// zero.
419///
420/// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree
421/// (including the DW_TAG_member) is built by a single call to
422/// `LLVMRustDIBuilderCreateVariantMemberType()`.
423fn build_enum_variant_member_di_node<'ll, 'tcx>(
424    cx: &CodegenCx<'ll, 'tcx>,
425    enum_type_and_layout: TyAndLayout<'tcx>,
426    variant_part_di_node: &'ll DIType,
427    variant_member_info: &VariantMemberInfo<'_, 'll>,
428) -> &'ll DIType {
429    let variant_index = variant_member_info.variant_index;
430    let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index);
431
432    let (file_di_node, line_number) = variant_member_info
433        .source_info
434        .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
435
436    let discr = discr_value.opt_single_val().map(|value| {
437        let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout);
438        let size = cx.size_of(tag_base_type);
439        cx.const_uint_big(cx.type_ix(size.bits()), value)
440    });
441
442    unsafe {
443        llvm::LLVMRustDIBuilderCreateVariantMemberType(
444            DIB(cx),
445            variant_part_di_node,
446            variant_member_info.variant_name.as_c_char_ptr(),
447            variant_member_info.variant_name.len(),
448            file_di_node,
449            line_number,
450            enum_type_and_layout.size.bits(),
451            enum_type_and_layout.align.bits() as u32,
452            Size::ZERO.bits(),
453            discr,
454            DIFlags::FlagZero,
455            variant_member_info.variant_struct_type_di_node,
456        )
457    }
458}
459
460/// Information needed for building a `DW_TAG_variant`:
461///
462/// ```txt
463///       DW_TAG_structure_type              (top-level type for enum)
464///         DW_TAG_variant_part              (variant part)
465///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
466///           DW_TAG_member                  (discriminant member)
467///  --->     DW_TAG_variant                 (variant 1)
468///  --->     DW_TAG_variant                 (variant 2)
469///  --->     DW_TAG_variant                 (variant 3)
470///         DW_TAG_structure_type            (type of variant 1)
471///         DW_TAG_structure_type            (type of variant 2)
472///         DW_TAG_structure_type            (type of variant 3)
473/// ```
474struct VariantMemberInfo<'a, 'll> {
475    variant_index: VariantIdx,
476    variant_name: Cow<'a, str>,
477    variant_struct_type_di_node: &'ll DIType,
478    source_info: Option<(&'ll DIFile, c_uint)>,
479}