VladimirMakaev updated this revision to Diff 540251.
VladimirMakaev added a comment.
Herald added a reviewer: sscalpone.
Rebased on master (this diff was quite a bit old)
Addressed review comments
- renamed method to ParseRustVariantPart
- added CU check
- added a good bunch of test covering cases I could think of using global
variabes.
- 2 tests for Option<NonNull<T>> couldn't be done with globals due to Rust
compiler strictness on UBs. Was able to create SBValue based on raw data
- addressed VariantMember::GetName()
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D149213/new/
https://reviews.llvm.org/D149213
Files:
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
lldb/test/API/lang/rust/enum-structs/RustEnumValue.py
lldb/test/API/lang/rust/enum-structs/TestRustEnumStructs.py
lldb/test/API/lang/rust/enum-structs/main.rs
Index: lldb/test/API/lang/rust/enum-structs/main.rs
===================================================================
--- /dev/null
+++ lldb/test/API/lang/rust/enum-structs/main.rs
@@ -0,0 +1,118 @@
+#![no_std]
+#![no_main]
+
+/// This file was manually compiled with rustc as object file
+/// obj2yaml tool was used to convert this to main.yaml
+/// This is done in order to make the test portable since LLVM codebase tests don't have setup to compile Rust programs
+/// no_std , no_main is used in order to make the object file as small as possible eliminating extra symbols from standard library
+/// static global variables are used because they can be inspected on object file without starting the process
+
+/// Command:
+/// rustc -g --emit=obj --crate-type=bin -C panic=abort -C link-arg=-nostdlib main.rs && obj2yaml main.o -o main.yaml
+use core::ptr::NonNull;
+
+enum CLikeEnumDefault {
+ A = 2,
+ B = 10,
+}
+
+#[repr(u8)]
+enum CLikeEnumReprU8 {
+ VariantA,
+ VariantB,
+ VariantC,
+}
+
+#[repr(u32)]
+enum CLikeEnumReprU32 {
+ VariantA = 1,
+ VariantB = 2,
+ VariantC = 3,
+}
+
+enum EnumWithTuples {
+ A(u8),
+ B(u16),
+ C(u32),
+ D(usize),
+ AA(u8, u8),
+ BB(u16, u16),
+ BC(u16, u32),
+ CC(u32, u32),
+ // no DD on purpose to have D = CC in size
+}
+
+enum EnumWithStructs {
+ A(Struct1),
+ B(Struct2),
+}
+
+#[repr(usize)]
+enum MixedEnum {
+ A,
+ B(i32),
+ C(u8, i32),
+ D(Option<Struct2>),
+ E(EnumWithStructs),
+}
+
+pub struct Struct1 {
+ field: i32,
+}
+
+pub struct Struct2 {
+ field: u32,
+ inner: Struct1,
+}
+
+pub struct NonNullHolder {
+ inner: Option<NonNull<u64>>,
+}
+
+static CLIKE_DEFAULT_A: CLikeEnumDefault = CLikeEnumDefault::A;
+static CLIKE_DEFAULT_B: CLikeEnumDefault = CLikeEnumDefault::B;
+
+static CLIKE_U8_A: CLikeEnumReprU8 = CLikeEnumReprU8::VariantA;
+static CLIKE_U8_B: CLikeEnumReprU8 = CLikeEnumReprU8::VariantB;
+static CLIKE_U8_C: CLikeEnumReprU8 = CLikeEnumReprU8::VariantC;
+
+static CLIKE_U32_A: CLikeEnumReprU32 = CLikeEnumReprU32::VariantA;
+static CLIKE_U32_B: CLikeEnumReprU32 = CLikeEnumReprU32::VariantB;
+static CLIKE_U32_C: CLikeEnumReprU32 = CLikeEnumReprU32::VariantC;
+
+static ENUM_WITH_TUPLES_A: EnumWithTuples = EnumWithTuples::A(13);
+static ENUM_WITH_TUPLES_AA: EnumWithTuples = EnumWithTuples::AA(13, 37);
+static ENUM_WITH_TUPLES_B: EnumWithTuples = EnumWithTuples::B(37);
+static ENUM_WITH_TUPLES_BB: EnumWithTuples = EnumWithTuples::BB(37, 5535);
+static ENUM_WITH_TUPLES_BC: EnumWithTuples = EnumWithTuples::BC(65000, 165000);
+static ENUM_WITH_TUPLES_C: EnumWithTuples = EnumWithTuples::C(31337);
+static ENUM_WITH_TUPLES_CC: EnumWithTuples = EnumWithTuples::CC(31337, 87236);
+static ENUM_WITH_TUPLES_D: EnumWithTuples = EnumWithTuples::D(123456789012345678);
+
+static MIXED_ENUM_A: MixedEnum = MixedEnum::A;
+static MIXED_ENUM_B: MixedEnum = MixedEnum::B(-10);
+static MIXED_ENUM_C: MixedEnum = MixedEnum::C(254, -254);
+static MIXED_ENUM_D_NONE: MixedEnum = MixedEnum::D(None);
+static MIXED_ENUM_D_SOME: MixedEnum = MixedEnum::D(Some(Struct2 {
+ field: 123456,
+ inner: Struct1 { field: 123 },
+}));
+
+#[no_mangle]
+pub extern "C" fn _start() -> ! {
+ loop {}
+}
+
+#[panic_handler]
+fn my_panic(_info: &core::panic::PanicInfo) -> ! {
+ // Option<NonNull<T>> is tricky type because it's optimized by compiler to hold None as 0 since
+ // Some(NonNull<T>) can never be 0
+ // This type also cannot be global due to Rust borrow checker rules and Send trait implementation
+ // this code is added in order to have compiler emit generic type specialization into symbols
+ let non_null = unsafe {
+ NonNullHolder {
+ inner: NonNull::new(1235 as *mut u64),
+ }
+ };
+ loop {}
+}
Index: lldb/test/API/lang/rust/enum-structs/TestRustEnumStructs.py
===================================================================
--- /dev/null
+++ lldb/test/API/lang/rust/enum-structs/TestRustEnumStructs.py
@@ -0,0 +1,187 @@
+"""Test that lldb recognizes enum structs emitted by Rust compiler """
+import logging
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from RustEnumValue import RustEnumValue
+
+
+class TestRustEnumStructs(TestBase):
+ def setUp(self):
+ TestBase.setUp(self)
+ src_dir = self.getSourceDir()
+ yaml_path = os.path.join(src_dir, "main.yaml")
+ obj_path = self.getBuildArtifact("main.o")
+ self.yaml2obj(yaml_path, obj_path)
+ self.dbg.CreateTarget(obj_path)
+
+ def getFromGlobal(self, name):
+ values = self.target().FindGlobalVariables(name, 1)
+ self.assertEqual(values.GetSize(), 1)
+ return RustEnumValue(values[0])
+
+ def test_clike_enums_are_represented_correctly(self):
+ # these type of enums are not using DW_TAG_variant_part.
+ all_values = [
+ self.target().FindFirstGlobalVariable("CLIKE_DEFAULT_A").GetValue(),
+ self.target().FindFirstGlobalVariable("CLIKE_DEFAULT_B").GetValue(),
+ self.target().FindFirstGlobalVariable("CLIKE_U8_A").GetValue(),
+ self.target().FindFirstGlobalVariable("CLIKE_U8_C").GetValue(),
+ self.target().FindFirstGlobalVariable("CLIKE_U32_A").GetValue(),
+ self.target().FindFirstGlobalVariable("CLIKE_U32_B").GetValue(),
+ ]
+ self.assertEqual(all_values, ['A', 'B', 'VariantA', 'VariantC', 'VariantA', 'VariantB'])
+
+ def test_enum_with_tuples_has_all_variants(self):
+ self.assertEqual(self.getFromGlobal("ENUM_WITH_TUPLES_A").getAllVariantTypes(),
+ ['main::EnumWithTuples::A:8',
+ 'main::EnumWithTuples::B:8',
+ 'main::EnumWithTuples::C:8',
+ 'main::EnumWithTuples::D:8',
+ 'main::EnumWithTuples::AA:8',
+ 'main::EnumWithTuples::BB:8',
+ 'main::EnumWithTuples::BC:8',
+ 'main::EnumWithTuples::CC:8'])
+
+ def test_enum_with_tuples_values_are_correct_a(self):
+ # static ENUM_WITH_TUPLES_A: EnumWithTuples = EnumWithTuples::A(13);
+ self.assertEqual(
+ self.getFromGlobal("ENUM_WITH_TUPLES_A").getCurrentValue().GetChildAtIndex(0).GetData().GetUnsignedInt8(
+ lldb.SBError(), 0),
+ 13)
+
+ def test_enum_with_tuples_values_are_correct_aa(self):
+ # static ENUM_WITH_TUPLES_AA: EnumWithTuples = EnumWithTuples::AA(13, 37);
+ value = self.getFromGlobal("ENUM_WITH_TUPLES_AA").getCurrentValue()
+ self.assertEqual(
+ (value.GetChildAtIndex(0).GetData().GetUnsignedInt8(
+ lldb.SBError(), 0),
+ value.GetChildAtIndex(1).GetData().GetUnsignedInt8(
+ lldb.SBError(), 0)),
+ (13, 37))
+
+ def test_enum_with_tuples_values_are_correct_b(self):
+ # static ENUM_WITH_TUPLES_B: EnumWithTuples = EnumWithTuples::B(37);
+ self.assertEqual(
+ self.getFromGlobal("ENUM_WITH_TUPLES_B").getCurrentValue().GetChildAtIndex(0).GetData().GetUnsignedInt16(
+ lldb.SBError(), 0),
+ 37)
+
+ def test_enum_with_tuples_values_are_correct_bb(self):
+ # static ENUM_WITH_TUPLES_BB: EnumWithTuples = EnumWithTuples::BB(37, 5535);
+ value = self.getFromGlobal("ENUM_WITH_TUPLES_BB").getCurrentValue()
+ self.assertEqual(
+ (value.GetChildAtIndex(0).GetData().GetUnsignedInt16(lldb.SBError(), 0),
+ value.GetChildAtIndex(1).GetData().GetUnsignedInt16(lldb.SBError(), 0)),
+ (37, 5535))
+
+ def test_enum_with_tuples_values_are_correct_bc(self):
+ # static ENUM_WITH_TUPLES_BC: EnumWithTuples = EnumWithTuples::BC(65000, 165000);
+ value = self.getFromGlobal("ENUM_WITH_TUPLES_BC").getCurrentValue()
+ self.assertEqual(
+ (value.GetChildAtIndex(0).GetData().GetUnsignedInt16(lldb.SBError(), 0),
+ value.GetChildAtIndex(1).GetData().GetUnsignedInt32(lldb.SBError(), 0)),
+ (65000, 165000))
+
+ def test_enum_with_tuples_values_are_correct_c(self):
+ # static ENUM_WITH_TUPLES_C: EnumWithTuples = EnumWithTuples::C(31337);
+ self.assertEqual(
+ self.getFromGlobal("ENUM_WITH_TUPLES_C").getCurrentValue().GetChildAtIndex(0).GetData().GetUnsignedInt32(
+ lldb.SBError(), 0),
+ 31337)
+
+ def test_enum_with_tuples_values_are_correct_cc(self):
+ # static ENUM_WITH_TUPLES_CC: EnumWithTuples = EnumWithTuples::CC(31337, 87236);
+ value = self.getFromGlobal("ENUM_WITH_TUPLES_CC").getCurrentValue()
+ self.assertEqual(
+ (value.GetChildAtIndex(0).GetData().GetUnsignedInt32(lldb.SBError(), 0),
+ value.GetChildAtIndex(1).GetData().GetUnsignedInt32(lldb.SBError(), 0)),
+ (31337, 87236))
+
+ def test_enum_with_tuples_values_are_correct_d(self):
+ # static ENUM_WITH_TUPLES_D: EnumWithTuples = EnumWithTuples::D(123456789012345678);
+ self.assertEqual(
+ self.getFromGlobal("ENUM_WITH_TUPLES_D").getCurrentValue().GetChildAtIndex(0).GetData().GetUnsignedInt64(
+ lldb.SBError(), 0),
+ 123456789012345678)
+
+ def test_mixed_enum_variants(self):
+ # static MIXED_ENUM_A: MixedEnum1 = MixedEnum1::A;
+ self.assertEqual(self.getFromGlobal("MIXED_ENUM_A").getAllVariantTypes(), ['main::MixedEnum::A:64',
+ 'main::MixedEnum::B:64',
+ 'main::MixedEnum::C:64',
+ 'main::MixedEnum::D:64',
+ 'main::MixedEnum::E:64'])
+
+ def test_mixed_enum_a(self):
+ # static MIXED_ENUM_A: MixedEnum = MixedEnum::A;
+ value = self.getFromGlobal("MIXED_ENUM_A").getCurrentValue()
+ self.assertEqual(value.GetType().GetDisplayTypeName(), "main::MixedEnum::A")
+ self.assertEqual(value.GetValue(), None)
+
+ def test_mixed_enum_c(self):
+ # static MIXED_ENUM_C: MixedEnum = MixedEnum::C(254, -254);
+ value = self.getFromGlobal("MIXED_ENUM_C").getCurrentValue()
+ self.assertEqual(
+ (value.GetChildAtIndex(0).GetData().GetUnsignedInt8(lldb.SBError(), 0),
+ value.GetChildAtIndex(1).GetData().GetSignedInt32(lldb.SBError(), 0)),
+ (254, -254))
+
+ def test_mixed_enum_d_none(self):
+ # static MIXED_ENUM_D_NONE: MixedEnum = MixedEnum::D(None);
+ value = RustEnumValue(self.getFromGlobal("MIXED_ENUM_D_NONE").getCurrentValue().GetChildAtIndex(0))
+ self.assertEqual(value.getAllVariantTypes(), ["core::option::Option<main::Struct2>::None<main::Struct2>:32",
+ "core::option::Option<main::Struct2>::Some<main::Struct2>:32"])
+ self.assertEqual(value.getCurrentValue().GetValue(), None)
+ self.assertEqual(value.getCurrentValue().GetType().GetDisplayTypeName(),
+ "core::option::Option<main::Struct2>::None<main::Struct2>")
+
+ def test_mixed_enum_d_some(self):
+ # static MIXED_ENUM_D_SOME: MixedEnum = MixedEnum::D(Some(Struct2 {
+ # field: 123456,
+ # inner: Struct1 { field: 123 },
+ # }));
+ variant_with_option = RustEnumValue(
+ self.getFromGlobal("MIXED_ENUM_D_SOME").getCurrentValue().GetChildAtIndex(0))
+
+ value_inside_option = variant_with_option.getCurrentValue().GetChildAtIndex(0)
+ self.assertEqual(
+ value_inside_option.GetChildMemberWithName("field").GetData().GetUnsignedInt32(lldb.SBError(), 0), 123456)
+
+ self.assertEqual(
+ value_inside_option.GetChildMemberWithName("inner").GetChildMemberWithName(
+ "field").GetData().GetSignedInt32(lldb.SBError(), 0),
+ 123)
+ self.assertEqual(value_inside_option.GetType().GetDisplayTypeName(), "main::Struct2")
+
+ def test_option_non_null_some_pointer(self):
+ type = self.target().FindFirstType("core::option::Option<core::ptr::non_null::NonNull<u64>>")
+ # this type is "optimized" by rust compiler so the discriminant isn't present on Some variant of option
+ data = [1337]
+ pointer_size = self.target().GetAddressByteSize()
+ byte_order = self.target().GetByteOrder()
+ value = RustEnumValue(self.target().CreateValueFromData("adhoc_value",
+ lldb.SBData.CreateDataFromUInt64Array(byte_order,
+ pointer_size,
+ data),
+ type))
+ self.assertEqual(value.getFields(), ["$variant$0", "$variant$"])
+ self.assertEqual(
+ value.getCurrentValue().GetChildAtIndex(0).GetChildMemberWithName("pointer").GetValueAsUnsigned(), 1337)
+
+ def test_option_non_null_none(self):
+ type = self.target().FindFirstType("core::option::Option<core::ptr::non_null::NonNull<u64>>")
+ # this type is "optimized" by rust compiler so the discriminant isn't present on Some variant of option
+ # in this test case 0 is used to represent 'None'
+ data = [0]
+ pointer_size = self.target().GetAddressByteSize()
+ byte_order = self.target().GetByteOrder()
+ value = RustEnumValue(self.target().CreateValueFromData("adhoc_value",
+ lldb.SBData.CreateDataFromUInt64Array(byte_order,
+ pointer_size,
+ data),
+ type))
+ self.assertEqual(value.getFields(), ["$variant$0", "$variant$"])
+ self.assertEqual(value.getCurrentValue().GetValue(), None)
+ self.assertEqual(value.getCurrentValue().GetType().GetDisplayTypeName(),
+ "core::option::Option<core::ptr::non_null::NonNull<u64>>::None<core::ptr::non_null::NonNull<unsigned long> >")
Index: lldb/test/API/lang/rust/enum-structs/RustEnumValue.py
===================================================================
--- /dev/null
+++ lldb/test/API/lang/rust/enum-structs/RustEnumValue.py
@@ -0,0 +1,63 @@
+"""Helper library to traverse data emitted for Rust enums """
+from lldbsuite.test.lldbtest import *
+
+DISCRIMINANT_MEMBER_NAME = "$discr$"
+VALUE_MEMBER_NAME = "value"
+
+class RustEnumValue:
+
+ def __init__(self, value: lldb.SBValue):
+ self.value = value
+
+ def getAllVariantTypes(self):
+ result = []
+ for i in range(self._inner().GetNumChildren()):
+ result.append(self.getVariantByIndex(i).GetDisplayTypeName())
+ return result
+
+ def _inner(self) -> lldb.SBValue:
+ return self.value.GetChildAtIndex(0)
+
+ def getVariantByIndex(self, index):
+ return self._inner().GetChildAtIndex(index).GetChildMemberWithName(VALUE_MEMBER_NAME)
+
+ @staticmethod
+ def _getDiscriminantValueAsUnsigned(discr_sbvalue: lldb.SBValue):
+ byte_size = discr_sbvalue.GetType().GetByteSize()
+ error = lldb.SBError()
+
+ # when discriminant is u16 Clang emits 'unsigned char'
+ # and LLDB seems to treat it as character type disalowing to call GetValueAsUnsigned
+ if byte_size == 1:
+ return discr_sbvalue.GetData().GetUnsignedInt8(error, 0)
+ elif byte_size == 2:
+ return discr_sbvalue.GetData().GetUnsignedInt16(error, 0)
+ elif byte_size == 4:
+ return discr_sbvalue.GetData().GetUnsignedInt32(error, 0)
+ elif byte_size == 8:
+ return discr_sbvalue.GetData().GetUnsignedInt64(error, 0)
+ else:
+ return discr_sbvalue.GetValueAsUnsigned()
+
+ def getCurrentVariantIndex(self):
+ default_index = 0
+ for i in range(self._inner().GetNumChildren()):
+ variant: lldb.SBValue = self._inner().GetChildAtIndex(i);
+ discr = variant.GetChildMemberWithName(DISCRIMINANT_MEMBER_NAME)
+ if discr.IsValid():
+ discr_unsigned_value = RustEnumValue._getDiscriminantValueAsUnsigned(discr)
+ if discr_unsigned_value < self._inner().GetNumChildren():
+ return discr_unsigned_value
+ else:
+ default_index = i
+ return default_index
+
+ def getFields(self):
+ result = []
+ for i in range(self._inner().GetNumChildren()):
+ type: lldb.SBType = self._inner().GetType()
+ result.append(type.GetFieldAtIndex(i).GetName())
+ return result
+
+ def getCurrentValue(self) -> lldb.SBValue:
+ return self.getVariantByIndex(self.getCurrentVariantIndex())
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -317,6 +317,21 @@
const lldb::ModuleSP &module_sp,
std::vector<std::unique_ptr<clang::CXXBaseSpecifier>> &base_classes,
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
+
+ /// Parses DW_TAG_variant_part DIE into a structure that encodes all variants
+ /// Note that this is currently being emitted by rustc and not Clang
+ /// \param die DW_TAG_variant_part DIE to parse
+ /// \param parent_die The parent DW_TAG_structure_type to parse
+ /// \param class_clang_type The Rust struct representing parent_die.
+ /// \param default_accesibility The default accessibility that is given to
+ /// base classes if they don't have an explicit accessibility set
+ /// \param layout_info The layout information that will be updated for
+ // base classes with the base offset
+ void
+ ParseRustVariantPart(DWARFDIE &die, const DWARFDIE &parent_die,
+ lldb_private::CompilerType &class_clang_type,
+ const lldb::AccessType default_accesibility,
+ lldb_private::ClangASTImporter::LayoutInfo &layout_info);
};
/// Parsed form of all attributes that are relevant for type reconstruction.
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2495,8 +2495,165 @@
/// \see clang::ObjCPropertyAttribute
uint32_t prop_attributes = 0;
};
+
+struct DiscriminantValue {
+ explicit DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp);
+
+ uint32_t byte_offset;
+ uint32_t byte_size;
+ DWARFFormValue type_ref;
+};
+
+struct VariantMember {
+ explicit VariantMember(DWARFDIE &die, ModuleSP module_sp);
+ bool IsDefault() const;
+
+ std::optional<u_int32_t> discr_value;
+ DWARFFormValue type_ref;
+ ConstString variant_name;
+ uint32_t byte_offset;
+ ConstString GetName() const;
+};
+
+struct VariantPart {
+ explicit VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
+ ModuleSP module_sp);
+
+ std::vector<VariantMember> &members();
+
+ DiscriminantValue &discriminant();
+
+private:
+ std::vector<VariantMember> _members;
+ DiscriminantValue _discriminant;
+};
+
} // namespace
+ConstString VariantMember::GetName() const {
+ return this->variant_name;
+}
+
+bool VariantMember::IsDefault() const { return !discr_value; }
+
+VariantMember::VariantMember(DWARFDIE &die, lldb::ModuleSP module_sp) {
+ assert(die.Tag() == llvm::dwarf::DW_TAG_variant);
+ this->discr_value =
+ die.GetAttributeValueAsOptionalUnsigned(DW_AT_discr_value);
+
+ for (auto child_die : die.children()) {
+ switch (child_die.Tag()) {
+ case llvm::dwarf::DW_TAG_member: {
+ DWARFAttributes attributes = child_die.GetAttributes();
+ for (std::size_t i = 0; i < attributes.Size(); ++i) {
+ DWARFFormValue form_value;
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_name:
+ variant_name = ConstString(form_value.AsCString());
+ break;
+ case DW_AT_type:
+ type_ref = form_value;
+ break;
+
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(
+ nullptr, // ExecutionContext *
+ nullptr, // RegisterContext *
+ module_sp,
+ DataExtractor(debug_info_data, block_offset,
+ block_length),
+ die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr,
+ memberOffset, nullptr)) {
+ byte_offset = memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ byte_offset = form_value.Unsigned();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+DiscriminantValue::DiscriminantValue(const DWARFDIE &die, ModuleSP module_sp) {
+ auto referenced_die = die.GetReferencedDIE(DW_AT_discr);
+ DWARFAttributes attributes = referenced_die.GetAttributes();
+ for (std::size_t i = 0; i < attributes.Size(); ++i) {
+ const dw_attr_t attr = attributes.AttributeAtIndex(i);
+ DWARFFormValue form_value;
+ if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ switch (attr) {
+ case DW_AT_type:
+ type_ref = form_value;
+ break;
+ case DW_AT_data_member_location:
+ if (form_value.BlockData()) {
+ Value initialValue(0);
+ Value memberOffset(0);
+ const DWARFDataExtractor &debug_info_data = die.GetData();
+ uint32_t block_length = form_value.Unsigned();
+ uint32_t block_offset =
+ form_value.BlockData() - debug_info_data.GetDataStart();
+ if (DWARFExpression::Evaluate(
+ nullptr, // ExecutionContext *
+ nullptr, // RegisterContext *
+ module_sp,
+ DataExtractor(debug_info_data, block_offset, block_length),
+ die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr,
+ memberOffset, nullptr)) {
+ byte_offset = memberOffset.ResolveValue(nullptr).UInt();
+ }
+ } else {
+ // With DWARF 3 and later, if the value is an integer constant,
+ // this form value is the offset in bytes from the beginning of
+ // the containing entity.
+ byte_offset = form_value.Unsigned();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+VariantPart::VariantPart(const DWARFDIE &die, const DWARFDIE &parent_die,
+ lldb::ModuleSP module_sp)
+ : _members(), _discriminant(die, module_sp) {
+
+ for (auto child : die.children()) {
+ if (child.Tag() == llvm::dwarf::DW_TAG_variant) {
+ _members.push_back(VariantMember(child, module_sp));
+ }
+ }
+}
+
+std::vector<VariantMember> &VariantPart::members() { return this->_members; }
+
+DiscriminantValue &VariantPart::discriminant() { return this->_discriminant; }
+
MemberAttributes::MemberAttributes(const DWARFDIE &die,
const DWARFDIE &parent_die,
ModuleSP module_sp) {
@@ -3022,6 +3179,13 @@
ParseObjCProperty(die, parent_die, class_clang_type, delayed_properties);
break;
+ case DW_TAG_variant_part:
+ if (die.GetCU()->GetDWARFLanguageType() == eLanguageTypeRust) {
+ ParseRustVariantPart(die, parent_die, class_clang_type,
+ default_accessibility, layout_info);
+ }
+ break;
+
case DW_TAG_member:
ParseSingleMember(die, parent_die, class_clang_type,
default_accessibility, layout_info, last_field_info);
@@ -3729,3 +3893,76 @@
return true;
}
+
+void DWARFASTParserClang::ParseRustVariantPart(
+ DWARFDIE &die, const DWARFDIE &parent_die, CompilerType &class_clang_type,
+ const lldb::AccessType default_accesibility,
+ ClangASTImporter::LayoutInfo &layout_info) {
+ assert(die.Tag() == llvm::dwarf::DW_TAG_variant_part);
+ assert(SymbolFileDWARF::GetLanguage(*die.GetCU()) ==
+ LanguageType::eLanguageTypeRust);
+
+ ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
+
+ VariantPart variants(die, parent_die, module_sp);
+
+ auto discriminant_type =
+ die.ResolveTypeUID(variants.discriminant().type_ref.Reference());
+
+ auto decl_context = m_ast.GetDeclContextForType(class_clang_type);
+
+ auto inner_holder = m_ast.CreateRecordType(
+ decl_context, OptionalClangModuleID(), lldb::eAccessPublic,
+ std::string(
+ llvm::formatv("{0}$Inner", class_clang_type.GetTypeName(false))),
+ clang::TTK_Union, lldb::eLanguageTypeRust);
+ m_ast.StartTagDeclarationDefinition(inner_holder);
+ m_ast.SetIsPacked(inner_holder);
+
+ for (auto member : variants.members()) {
+
+ auto has_discriminant = !member.IsDefault();
+
+ auto member_type = die.ResolveTypeUID(member.type_ref.Reference());
+
+ auto field_type = m_ast.CreateRecordType(
+ m_ast.GetDeclContextForType(inner_holder), OptionalClangModuleID(),
+ lldb::eAccessPublic,
+ std::string(llvm::formatv("{0}$Variant", member.GetName())),
+ clang::TTK_Struct, lldb::eLanguageTypeRust);
+
+ m_ast.StartTagDeclarationDefinition(field_type);
+ auto offset = member.byte_offset;
+
+ if (has_discriminant) {
+ m_ast.AddFieldToRecordType(
+ field_type, "$discr$", discriminant_type->GetFullCompilerType(),
+ lldb::eAccessPublic, variants.discriminant().byte_offset);
+ offset += discriminant_type->GetByteSize(nullptr).value_or(0);
+ }
+
+ m_ast.AddFieldToRecordType(field_type, "value",
+ member_type->GetFullCompilerType(),
+ lldb::eAccessPublic, offset * 8);
+
+ m_ast.CompleteTagDeclarationDefinition(field_type);
+
+ auto name = has_discriminant
+ ? llvm::formatv("$variant${0}", member.discr_value.value())
+ : std::string("$variant$");
+
+ auto variant_decl =
+ m_ast.AddFieldToRecordType(inner_holder, llvm::StringRef(name),
+ field_type, default_accesibility, 0);
+
+ layout_info.field_offsets.insert({variant_decl, 0});
+ }
+
+ auto inner_field = m_ast.AddFieldToRecordType(class_clang_type,
+ llvm::StringRef("$variants$"),
+ inner_holder, eAccessPublic, 0);
+
+ m_ast.CompleteTagDeclarationDefinition(inner_holder);
+
+ layout_info.field_offsets.insert({inner_field, 0});
+}
\ No newline at end of file
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits