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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to