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::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::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) -> DINodeCreationResult<'ll> {
34 let enum_type = unique_type_id.expect_ty();
35 let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
36 bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
37 };
38
39 let enum_type_and_layout = cx.layout_of(enum_type);
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
63fn 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
93fn 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!("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)| unsafe {
114 let value = [value as u64, (value >> 64) as u64];
115 Some(llvm::LLVMRustDIBuilderCreateEnumerator(
116 DIB(cx),
117 name.as_c_char_ptr(),
118 name.len(),
119 value.as_ptr(),
120 size.bits() as libc::c_uint,
121 is_unsigned,
122 ))
123 })
124 .collect();
125
126 let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers
127 {
128 file_metadata_from_def_id(cx, def_id)
129 } else {
130 (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)
131 };
132
133 unsafe {
134 llvm::LLVMRustDIBuilderCreateEnumerationType(
135 DIB(cx),
136 containing_scope,
137 type_name.as_c_char_ptr(),
138 type_name.len(),
139 file_metadata,
140 line_number,
141 size.bits(),
142 align.bits() as u32,
143 create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
144 type_di_node(cx, base_type),
145 true,
146 )
147 }
148}
149
150fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
201 cx: &CodegenCx<'ll, 'tcx>,
202 enum_type_and_layout: TyAndLayout<'tcx>,
203 enum_type_di_node: &'ll DIType,
204 variant_index: VariantIdx,
205 variant_def: &VariantDef,
206 variant_layout: TyAndLayout<'tcx>,
207 di_flags: DIFlags,
208) -> &'ll DIType {
209 assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
210
211 let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
212 Some(file_metadata_from_def_id(cx, Some(variant_def.def_id)))
213 } else {
214 None
215 };
216
217 type_map::build_type_with_children(
218 cx,
219 type_map::stub(
220 cx,
221 Stub::Struct,
222 UniqueTypeId::for_enum_variant_struct_type(
223 cx.tcx,
224 enum_type_and_layout.ty,
225 variant_index,
226 ),
227 variant_def.name.as_str(),
228 def_location,
229 size_and_align_of(enum_type_and_layout),
231 Some(enum_type_di_node),
232 di_flags,
233 ),
234 |cx, struct_type_di_node| {
235 (0..variant_layout.fields.count())
236 .map(|field_index| {
237 let field_name = if variant_def.ctor_kind() != Some(CtorKind::Fn) {
238 let field = &variant_def.fields[FieldIdx::from_usize(field_index)];
240 Cow::from(field.name.as_str())
241 } else {
242 super::tuple_field_name(field_index)
244 };
245
246 let field_layout = variant_layout.field(cx, field_index);
247
248 build_field_di_node(
249 cx,
250 struct_type_di_node,
251 &field_name,
252 (field_layout.size, field_layout.align.abi),
253 variant_layout.fields.offset(field_index),
254 di_flags,
255 type_di_node(cx, field_layout.ty),
256 None,
257 )
258 })
259 .collect::<SmallVec<_>>()
260 },
261 |cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
262 )
263 .di_node
264}
265
266fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>(
284 cx: &CodegenCx<'ll, 'tcx>,
285 variant_index: VariantIdx,
286 coroutine_type_and_layout: TyAndLayout<'tcx>,
287 coroutine_type_di_node: &'ll DIType,
288 coroutine_layout: &CoroutineLayout<'tcx>,
289 common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
290) -> &'ll DIType {
291 let variant_name = CoroutineArgs::variant_name(variant_index);
292 let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
293 cx.tcx,
294 coroutine_type_and_layout.ty,
295 variant_index,
296 );
297
298 let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index);
299
300 let coroutine_args = match coroutine_type_and_layout.ty.kind() {
301 ty::Coroutine(_, args) => args.as_coroutine(),
302 _ => unreachable!(),
303 };
304
305 type_map::build_type_with_children(
306 cx,
307 type_map::stub(
308 cx,
309 Stub::Struct,
310 unique_type_id,
311 &variant_name,
312 None,
313 size_and_align_of(coroutine_type_and_layout),
314 Some(coroutine_type_di_node),
315 DIFlags::FlagZero,
316 ),
317 |cx, variant_struct_type_di_node| {
318 let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
320 .map(|field_index| {
321 let coroutine_saved_local = coroutine_layout.variant_fields[variant_index]
322 [FieldIdx::from_usize(field_index)];
323 let field_name_maybe = coroutine_layout.field_names[coroutine_saved_local];
324 let field_name = field_name_maybe
325 .as_ref()
326 .map(|s| Cow::from(s.as_str()))
327 .unwrap_or_else(|| super::tuple_field_name(field_index));
328
329 let field_type = variant_layout.field(cx, field_index).ty;
330
331 build_field_di_node(
332 cx,
333 variant_struct_type_di_node,
334 &field_name,
335 cx.size_and_align_of(field_type),
336 variant_layout.fields.offset(field_index),
337 DIFlags::FlagZero,
338 type_di_node(cx, field_type),
339 None,
340 )
341 })
342 .collect();
343
344 let common_fields: SmallVec<_> = coroutine_args
346 .prefix_tys()
347 .iter()
348 .zip(common_upvar_names)
349 .enumerate()
350 .map(|(index, (upvar_ty, upvar_name))| {
351 build_field_di_node(
352 cx,
353 variant_struct_type_di_node,
354 upvar_name.as_str(),
355 cx.size_and_align_of(upvar_ty),
356 coroutine_type_and_layout.fields.offset(index),
357 DIFlags::FlagZero,
358 type_di_node(cx, upvar_ty),
359 None,
360 )
361 })
362 .collect();
363
364 state_specific_fields.into_iter().chain(common_fields).collect()
365 },
366 |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty),
367 )
368 .di_node
369}
370
371#[derive(Copy, Clone)]
372enum DiscrResult {
373 NoDiscriminant,
374 Value(u128),
375 Range(u128, u128),
376}
377
378impl DiscrResult {
379 fn opt_single_val(&self) -> Option<u128> {
380 if let Self::Value(d) = *self { Some(d) } else { None }
381 }
382}
383
384fn compute_discriminant_value<'ll, 'tcx>(
390 cx: &CodegenCx<'ll, 'tcx>,
391 enum_type_and_layout: TyAndLayout<'tcx>,
392 variant_index: VariantIdx,
393) -> DiscrResult {
394 match enum_type_and_layout.layout.variants() {
395 &Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant,
396 &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
397 enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
398 ),
399 &Variants::Multiple {
400 tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant },
401 tag,
402 ..
403 } => {
404 if variant_index == untagged_variant {
405 let valid_range = enum_type_and_layout
406 .for_variant(cx, variant_index)
407 .largest_niche
408 .as_ref()
409 .unwrap()
410 .valid_range;
411
412 let min = valid_range.start.min(valid_range.end);
413 let min = tag.size(cx).truncate(min);
414
415 let max = valid_range.start.max(valid_range.end);
416 let max = tag.size(cx).truncate(max);
417
418 DiscrResult::Range(min, max)
419 } else {
420 let value = (variant_index.as_u32() as u128)
421 .wrapping_sub(niche_variants.start().as_u32() as u128)
422 .wrapping_add(niche_start);
423 let value = tag.size(cx).truncate(value);
424 DiscrResult::Value(value)
425 }
426 }
427 }
428}