rustc_codegen_llvm/debuginfo/metadata/enums/
mod.rs1use 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::bug;
10use rustc_middle::mir::CoroutineLayout;
11use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
12use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef};
13use rustc_span::{Span, Symbol};
14
15use super::type_map::{DINodeCreationResult, UniqueTypeId};
16use super::{SmallVec, size_and_align_of};
17use crate::common::{AsCCharPtr, CodegenCx};
18use crate::debuginfo::metadata::type_map::{self, Stub};
19use crate::debuginfo::metadata::{
20 UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes,
21 file_metadata_from_def_id, type_di_node, unknown_file_metadata,
22};
23use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item};
24use crate::llvm::debuginfo::{DIFlags, DIType};
25use crate::llvm::{self};
26
27mod cpp_like;
28mod native;
29
30pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
31 cx: &CodegenCx<'ll, 'tcx>,
32 unique_type_id: UniqueTypeId<'tcx>,
33 span: Span,
34) -> DINodeCreationResult<'ll> {
35 let enum_type = unique_type_id.expect_ty();
36 let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
37 bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
38 };
39
40 let enum_type_and_layout = cx.spanned_layout_of(enum_type, span);
41
42 if wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) {
43 return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
44 }
45
46 if cpp_like_debuginfo(cx.tcx) {
47 cpp_like::build_enum_type_di_node(cx, unique_type_id)
48 } else {
49 native::build_enum_type_di_node(cx, unique_type_id)
50 }
51}
52
53pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
54 cx: &CodegenCx<'ll, 'tcx>,
55 unique_type_id: UniqueTypeId<'tcx>,
56) -> DINodeCreationResult<'ll> {
57 if cpp_like_debuginfo(cx.tcx) {
58 cpp_like::build_coroutine_di_node(cx, unique_type_id)
59 } else {
60 native::build_coroutine_di_node(cx, unique_type_id)
61 }
62}
63
64fn build_c_style_enum_di_node<'ll, 'tcx>(
68 cx: &CodegenCx<'ll, 'tcx>,
69 enum_adt_def: AdtDef<'tcx>,
70 enum_type_and_layout: TyAndLayout<'tcx>,
71) -> DINodeCreationResult<'ll> {
72 let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
73 let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
74 Some(enum_adt_def.did())
75 } else {
76 None
77 };
78 DINodeCreationResult {
79 di_node: build_enumeration_type_di_node(
80 cx,
81 &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
82 tag_base_type(cx.tcx, enum_type_and_layout),
83 enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
84 let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
85 (name, discr.val)
86 }),
87 enum_adt_def_id,
88 containing_scope,
89 ),
90 already_stored_in_typemap: false,
91 }
92}
93
94fn build_enumeration_type_di_node<'ll, 'tcx>(
99 cx: &CodegenCx<'ll, 'tcx>,
100 type_name: &str,
101 base_type: Ty<'tcx>,
102 enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>,
103 def_id: Option<rustc_span::def_id::DefId>,
104 containing_scope: &'ll DIType,
105) -> &'ll DIType {
106 let is_unsigned = match base_type.kind() {
107 ty::Int(_) => false,
108 ty::Uint(_) => true,
109 _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
110 };
111 let (size, align) = cx.size_and_align_of(base_type);
112
113 let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators
114 .map(|(name, value)| unsafe {
115 let value = [value as u64, (value >> 64) as u64];
116 Some(llvm::LLVMRustDIBuilderCreateEnumerator(
117 DIB(cx),
118 name.as_c_char_ptr(),
119 name.len(),
120 value.as_ptr(),
121 size.bits() as libc::c_uint,
122 is_unsigned,
123 ))
124 })
125 .collect();
126
127 let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
128 {
129 file_metadata_from_def_id(cx, def_id)
130 } else {
131 (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
132 };
133
134 unsafe {
135 llvm::LLVMRustDIBuilderCreateEnumerationType(
136 DIB(cx),
137 containing_scope,
138 type_name.as_c_char_ptr(),
139 type_name.len(),
140 file_metadata,
141 line_number,
142 size.bits(),
143 align.bits() as u32,
144 create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
145 type_di_node(cx, base_type),
146 true,
147 )
148 }
149}
150
151fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
202 cx: &CodegenCx<'ll, 'tcx>,
203 enum_type_and_layout: TyAndLayout<'tcx>,
204 enum_type_di_node: &'ll DIType,
205 variant_index: VariantIdx,
206 variant_def: &VariantDef,
207 variant_layout: TyAndLayout<'tcx>,
208 di_flags: DIFlags,
209) -> &'ll DIType {
210 assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
211
212 let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
213 Some(file_metadata_from_def_id(cx, Some(variant_def.def_id)))
214 } else {
215 None
216 };
217
218 type_map::build_type_with_children(
219 cx,
220 type_map::stub(
221 cx,
222 Stub::Struct,
223 UniqueTypeId::for_enum_variant_struct_type(
224 cx.tcx,
225 enum_type_and_layout.ty,
226 variant_index,
227 ),
228 variant_def.name.as_str(),
229 def_location,
230 size_and_align_of(enum_type_and_layout),
232 Some(enum_type_di_node),
233 di_flags,
234 ),
235 |cx, struct_type_di_node| {
236 (0..variant_layout.fields.count())
237 .map(|field_index| {
238 let field_name = if variant_def.ctor_kind() != Some(CtorKind::Fn) {
239 let field = &variant_def.fields[FieldIdx::from_usize(field_index)];
241 Cow::from(field.name.as_str())
242 } else {
243 super::tuple_field_name(field_index)
245 };
246
247 let field_layout = variant_layout.field(cx, field_index);
248
249 build_field_di_node(
250 cx,
251 struct_type_di_node,
252 &field_name,
253 field_layout,
254 variant_layout.fields.offset(field_index),
255 di_flags,
256 type_di_node(cx, field_layout.ty),
257 None,
258 )
259 })
260 .collect::<SmallVec<_>>()
261 },
262 |cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
263 )
264 .di_node
265}
266
267fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
285 cx: &CodegenCx<'ll, 'tcx>,
286 variant_index: VariantIdx,
287 coroutine_type_and_layout: TyAndLayout<'tcx>,
288 coroutine_type_di_node: &'ll DIType,
289 coroutine_layout: &CoroutineLayout<'tcx>,
290 common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
291) -> &'ll DIType {
292 let variant_name = CoroutineArgs::variant_name(variant_index);
293 let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
294 cx.tcx,
295 coroutine_type_and_layout.ty,
296 variant_index,
297 );
298
299 let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index);
300
301 let coroutine_args = match coroutine_type_and_layout.ty.kind() {
302 ty::Coroutine(_, args) => args.as_coroutine(),
303 _ => unreachable!(),
304 };
305
306 type_map::build_type_with_children(
307 cx,
308 type_map::stub(
309 cx,
310 Stub::Struct,
311 unique_type_id,
312 &variant_name,
313 None,
314 size_and_align_of(coroutine_type_and_layout),
315 Some(coroutine_type_di_node),
316 DIFlags::FlagZero,
317 ),
318 |cx, variant_struct_type_di_node| {
319 let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
321 .map(|field_index| {
322 let coroutine_saved_local = coroutine_layout.variant_fields[variant_index]
323 [FieldIdx::from_usize(field_index)];
324 let field_name_maybe = coroutine_layout.field_names[coroutine_saved_local];
325 let field_name = field_name_maybe
326 .as_ref()
327 .map(|s| Cow::from(s.as_str()))
328 .unwrap_or_else(|| super::tuple_field_name(field_index));
329
330 let field_type = variant_layout.field(cx, field_index).ty;
331
332 build_field_di_node(
333 cx,
334 variant_struct_type_di_node,
335 &field_name,
336 cx.layout_of(field_type),
337 variant_layout.fields.offset(field_index),
338 DIFlags::FlagZero,
339 type_di_node(cx, field_type),
340 None,
341 )
342 })
343 .collect();
344
345 let common_fields: SmallVec<_> = coroutine_args
347 .prefix_tys()
348 .iter()
349 .zip(common_upvar_names)
350 .enumerate()
351 .map(|(index, (upvar_ty, upvar_name))| {
352 build_field_di_node(
353 cx,
354 variant_struct_type_di_node,
355 upvar_name.as_str(),
356 cx.layout_of(upvar_ty),
357 coroutine_type_and_layout.fields.offset(index),
358 DIFlags::FlagZero,
359 type_di_node(cx, upvar_ty),
360 None,
361 )
362 })
363 .collect();
364
365 state_specific_fields.into_iter().chain(common_fields).collect()
366 },
367 |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
368 )
369 .di_node
370}
371
372#[derive(Copy, Clone)]
373enum DiscrResult {
374 NoDiscriminant,
375 Value(u128),
376 Range(u128, u128),
377}
378
379impl DiscrResult {
380 fn opt_single_val(&self) -> Option<u128> {
381 if let Self::Value(d) = *self { Some(d) } else { None }
382 }
383}
384
385fn compute_discriminant_value<'ll, 'tcx>(
391 cx: &CodegenCx<'ll, 'tcx>,
392 enum_type_and_layout: TyAndLayout<'tcx>,
393 variant_index: VariantIdx,
394) -> DiscrResult {
395 match enum_type_and_layout.layout.variants() {
396 &Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant,
397 &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
398 enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
399 ),
400 &Variants::Multiple {
401 tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant },
402 tag,
403 ..
404 } => {
405 if variant_index == untagged_variant {
406 let valid_range = enum_type_and_layout
407 .for_variant(cx, variant_index)
408 .largest_niche
409 .as_ref()
410 .unwrap()
411 .valid_range;
412
413 let min = valid_range.start.min(valid_range.end);
414 let min = tag.size(cx).truncate(min);
415
416 let max = valid_range.start.max(valid_range.end);
417 let max = tag.size(cx).truncate(max);
418
419 DiscrResult::Range(min, max)
420 } else {
421 let value = (variant_index.as_u32() as u128)
422 .wrapping_sub(niche_variants.start().as_u32() as u128)
423 .wrapping_add(niche_start);
424 let value = tag.size(cx).truncate(value);
425 DiscrResult::Value(value)
426 }
427 }
428 }
429}