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> &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 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 &members(); + + DiscriminantValue &discriminant(); + +private: + std::vector _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 &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 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/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::None:32", + "core::option::Option::Some:32"]) + self.assertEqual(value.getCurrentValue().GetValue(), None) + self.assertEqual(value.getCurrentValue().GetType().GetDisplayTypeName(), + "core::option::Option::None") + + 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>") + # 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>") + # 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>::None >") 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), + E(EnumWithStructs), +} + +pub struct Struct1 { + field: i32, +} + +pub struct Struct2 { + field: u32, + inner: Struct1, +} + +pub struct NonNullHolder { + inner: Option>, +} + +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> is tricky type because it's optimized by compiler to hold None as 0 since + // Some(NonNull) 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 {} +}