xref: /llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp (revision 624ea68cbc3ce422b3ee110c0c0af839eec2e278)
180814287SRaphael Isemann //===-- LibCxxVariant.cpp -------------------------------------------------===//
28306f76eSShafik Yaghmour //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68306f76eSShafik Yaghmour //
78306f76eSShafik Yaghmour //===----------------------------------------------------------------------===//
88306f76eSShafik Yaghmour 
98306f76eSShafik Yaghmour #include "LibCxxVariant.h"
102f4a66eeSAdrian Prantl #include "LibCxx.h"
118306f76eSShafik Yaghmour #include "lldb/DataFormatters/FormattersHelpers.h"
123c51ea36SJordan Rupprecht #include "lldb/Symbol/CompilerType.h"
133c51ea36SJordan Rupprecht #include "lldb/Utility/LLDBAssert.h"
148306f76eSShafik Yaghmour 
158306f76eSShafik Yaghmour #include "llvm/ADT/ScopeExit.h"
16f190ce62SKazu Hirata #include <optional>
178306f76eSShafik Yaghmour 
188306f76eSShafik Yaghmour using namespace lldb;
198306f76eSShafik Yaghmour using namespace lldb_private;
208306f76eSShafik Yaghmour 
218306f76eSShafik Yaghmour // libc++ variant implementation contains two members that we care about both
228306f76eSShafik Yaghmour // are contained in the __impl member.
238306f76eSShafik Yaghmour // - __index which tells us which of the variadic template types is the active
248306f76eSShafik Yaghmour //   type for the variant
258306f76eSShafik Yaghmour // - __data is a variadic union which recursively contains itself as member
268306f76eSShafik Yaghmour //   which refers to the tailing variadic types.
278306f76eSShafik Yaghmour //   - __head which refers to the leading non pack type
288306f76eSShafik Yaghmour //     - __value refers to the actual value contained
298306f76eSShafik Yaghmour //   - __tail which refers to the remaining pack types
308306f76eSShafik Yaghmour //
318306f76eSShafik Yaghmour // e.g. given std::variant<int,double,char> v1
328306f76eSShafik Yaghmour //
338306f76eSShafik Yaghmour // (lldb) frame var -R v1.__impl.__data
348306f76eSShafik Yaghmour //(... __union<... 0, int, double, char>) v1.__impl.__data = {
358306f76eSShafik Yaghmour // ...
368306f76eSShafik Yaghmour //  __head = {
378306f76eSShafik Yaghmour //    __value = ...
388306f76eSShafik Yaghmour //  }
398306f76eSShafik Yaghmour //  __tail = {
408306f76eSShafik Yaghmour //  ...
418306f76eSShafik Yaghmour //    __head = {
428306f76eSShafik Yaghmour //      __value = ...
438306f76eSShafik Yaghmour //    }
448306f76eSShafik Yaghmour //    __tail = {
458306f76eSShafik Yaghmour //    ...
468306f76eSShafik Yaghmour //      __head = {
478306f76eSShafik Yaghmour //        __value = ...
488306f76eSShafik Yaghmour //  ...
498306f76eSShafik Yaghmour //
508306f76eSShafik Yaghmour // So given
518306f76eSShafik Yaghmour // - __index equal to 0 the active value is contained in
528306f76eSShafik Yaghmour //
538306f76eSShafik Yaghmour //     __data.__head.__value
548306f76eSShafik Yaghmour //
558306f76eSShafik Yaghmour // - __index equal to 1 the active value is contained in
568306f76eSShafik Yaghmour //
578306f76eSShafik Yaghmour //     __data.__tail.__head.__value
588306f76eSShafik Yaghmour //
598306f76eSShafik Yaghmour // - __index equal to 2 the active value is contained in
608306f76eSShafik Yaghmour //
618306f76eSShafik Yaghmour //      __data.__tail.__tail.__head.__value
628306f76eSShafik Yaghmour //
638306f76eSShafik Yaghmour 
648306f76eSShafik Yaghmour namespace {
658306f76eSShafik Yaghmour // libc++ std::variant index could have one of three states
6697212121SRaphael Isemann // 1) Valid, we can obtain it and its not variant_npos
6797212121SRaphael Isemann // 2) Invalid, we can't obtain it or it is not a type we expect
6897212121SRaphael Isemann // 3) NPos, its value is variant_npos which means the variant has no value
6997212121SRaphael Isemann enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };
708306f76eSShafik Yaghmour 
VariantNposValue(uint64_t index_byte_size)713c51ea36SJordan Rupprecht uint64_t VariantNposValue(uint64_t index_byte_size) {
723c51ea36SJordan Rupprecht   switch (index_byte_size) {
733c51ea36SJordan Rupprecht   case 1:
743c51ea36SJordan Rupprecht     return static_cast<uint8_t>(-1);
753c51ea36SJordan Rupprecht   case 2:
763c51ea36SJordan Rupprecht     return static_cast<uint16_t>(-1);
773c51ea36SJordan Rupprecht   case 4:
783c51ea36SJordan Rupprecht     return static_cast<uint32_t>(-1);
793c51ea36SJordan Rupprecht   }
803c51ea36SJordan Rupprecht   lldbassert(false && "Unknown index type size");
813c51ea36SJordan Rupprecht   return static_cast<uint32_t>(-1); // Fallback to stable ABI type.
823c51ea36SJordan Rupprecht }
833c51ea36SJordan Rupprecht 
848306f76eSShafik Yaghmour LibcxxVariantIndexValidity
LibcxxVariantGetIndexValidity(ValueObjectSP & impl_sp)858306f76eSShafik Yaghmour LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
867d4fcd41SDave Lee   ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
878306f76eSShafik Yaghmour 
888306f76eSShafik Yaghmour   if (!index_sp)
8997212121SRaphael Isemann     return LibcxxVariantIndexValidity::Invalid;
908306f76eSShafik Yaghmour 
913c51ea36SJordan Rupprecht   // In the stable ABI, the type of __index is just int.
923c51ea36SJordan Rupprecht   // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is
933c51ea36SJordan Rupprecht   // enabled, the type can either be unsigned char/short/int depending on
943c51ea36SJordan Rupprecht   // how many variant types there are.
953c51ea36SJordan Rupprecht   // We only need to do this here when comparing against npos, because npos is
963c51ea36SJordan Rupprecht   // just `-1`, but that translates to different unsigned values depending on
973c51ea36SJordan Rupprecht   // the byte size.
983c51ea36SJordan Rupprecht   CompilerType index_type = index_sp->GetCompilerType();
998306f76eSShafik Yaghmour 
1002fe83274SKazu Hirata   std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
1013c51ea36SJordan Rupprecht   if (!index_type_bytes)
1023c51ea36SJordan Rupprecht     return LibcxxVariantIndexValidity::Invalid;
1033c51ea36SJordan Rupprecht 
1043c51ea36SJordan Rupprecht   uint64_t npos_value = VariantNposValue(*index_type_bytes);
1053c51ea36SJordan Rupprecht   uint64_t index_value = index_sp->GetValueAsUnsigned(0);
1063c51ea36SJordan Rupprecht 
1073c51ea36SJordan Rupprecht   if (index_value == npos_value)
10897212121SRaphael Isemann     return LibcxxVariantIndexValidity::NPos;
1098306f76eSShafik Yaghmour 
11097212121SRaphael Isemann   return LibcxxVariantIndexValidity::Valid;
1118306f76eSShafik Yaghmour }
1128306f76eSShafik Yaghmour 
LibcxxVariantIndexValue(ValueObjectSP & impl_sp)1132fe83274SKazu Hirata std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
1147d4fcd41SDave Lee   ValueObjectSP index_sp(impl_sp->GetChildMemberWithName("__index"));
1158306f76eSShafik Yaghmour 
1168306f76eSShafik Yaghmour   if (!index_sp)
1178306f76eSShafik Yaghmour     return {};
1188306f76eSShafik Yaghmour 
1198306f76eSShafik Yaghmour   return {index_sp->GetValueAsUnsigned(0)};
1208306f76eSShafik Yaghmour }
1218306f76eSShafik Yaghmour 
LibcxxVariantGetNthHead(ValueObjectSP & impl_sp,uint64_t index)1228306f76eSShafik Yaghmour ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
1237d4fcd41SDave Lee   ValueObjectSP data_sp(impl_sp->GetChildMemberWithName("__data"));
1248306f76eSShafik Yaghmour 
1258306f76eSShafik Yaghmour   if (!data_sp)
1268306f76eSShafik Yaghmour     return ValueObjectSP{};
1278306f76eSShafik Yaghmour 
1288306f76eSShafik Yaghmour   ValueObjectSP current_level = data_sp;
1298306f76eSShafik Yaghmour   for (uint64_t n = index; n != 0; --n) {
1307d4fcd41SDave Lee     ValueObjectSP tail_sp(current_level->GetChildMemberWithName("__tail"));
1318306f76eSShafik Yaghmour 
1328306f76eSShafik Yaghmour     if (!tail_sp)
1338306f76eSShafik Yaghmour       return ValueObjectSP{};
1348306f76eSShafik Yaghmour 
1358306f76eSShafik Yaghmour     current_level = tail_sp;
1368306f76eSShafik Yaghmour   }
1378306f76eSShafik Yaghmour 
1387d4fcd41SDave Lee   return current_level->GetChildMemberWithName("__head");
139831be096SHenry Wong }
1408306f76eSShafik Yaghmour } // namespace
1418306f76eSShafik Yaghmour 
1428306f76eSShafik Yaghmour namespace lldb_private {
1438306f76eSShafik Yaghmour namespace formatters {
LibcxxVariantSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)1448306f76eSShafik Yaghmour bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
1458306f76eSShafik Yaghmour                                   const TypeSummaryOptions &options) {
1468306f76eSShafik Yaghmour   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
1478306f76eSShafik Yaghmour   if (!valobj_sp)
1488306f76eSShafik Yaghmour     return false;
1498306f76eSShafik Yaghmour 
1502f4a66eeSAdrian Prantl   ValueObjectSP impl_sp = GetChildMemberWithName(
1512f4a66eeSAdrian Prantl       *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
1528306f76eSShafik Yaghmour 
1538306f76eSShafik Yaghmour   if (!impl_sp)
1548306f76eSShafik Yaghmour     return false;
1558306f76eSShafik Yaghmour 
1568306f76eSShafik Yaghmour   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
1578306f76eSShafik Yaghmour 
15897212121SRaphael Isemann   if (validity == LibcxxVariantIndexValidity::Invalid)
1598306f76eSShafik Yaghmour     return false;
1608306f76eSShafik Yaghmour 
16197212121SRaphael Isemann   if (validity == LibcxxVariantIndexValidity::NPos) {
1628306f76eSShafik Yaghmour     stream.Printf(" No Value");
1638306f76eSShafik Yaghmour     return true;
1648306f76eSShafik Yaghmour   }
1658306f76eSShafik Yaghmour 
1668306f76eSShafik Yaghmour   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
1678306f76eSShafik Yaghmour 
1688306f76eSShafik Yaghmour   if (!optional_index_value)
1698306f76eSShafik Yaghmour     return false;
1708306f76eSShafik Yaghmour 
1718306f76eSShafik Yaghmour   uint64_t index_value = *optional_index_value;
1728306f76eSShafik Yaghmour 
1738306f76eSShafik Yaghmour   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
1748306f76eSShafik Yaghmour 
1758306f76eSShafik Yaghmour   if (!nth_head)
1768306f76eSShafik Yaghmour     return false;
1778306f76eSShafik Yaghmour 
1788306f76eSShafik Yaghmour   CompilerType head_type = nth_head->GetCompilerType();
1798306f76eSShafik Yaghmour 
1808306f76eSShafik Yaghmour   if (!head_type)
1818306f76eSShafik Yaghmour     return false;
1828306f76eSShafik Yaghmour 
1838306f76eSShafik Yaghmour   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
1848306f76eSShafik Yaghmour 
1858306f76eSShafik Yaghmour   if (!template_type)
1868306f76eSShafik Yaghmour     return false;
1878306f76eSShafik Yaghmour 
188785df616SRaphael Isemann   stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
1898306f76eSShafik Yaghmour 
1908306f76eSShafik Yaghmour   return true;
1918306f76eSShafik Yaghmour }
1928306f76eSShafik Yaghmour } // namespace formatters
1938306f76eSShafik Yaghmour } // namespace lldb_private
1948306f76eSShafik Yaghmour 
1958306f76eSShafik Yaghmour namespace {
1968306f76eSShafik Yaghmour class VariantFrontEnd : public SyntheticChildrenFrontEnd {
1978306f76eSShafik Yaghmour public:
VariantFrontEnd(ValueObject & valobj)1988306f76eSShafik Yaghmour   VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
1998306f76eSShafik Yaghmour     Update();
2008306f76eSShafik Yaghmour   }
2018306f76eSShafik Yaghmour 
GetIndexOfChildWithName(ConstString name)2020e4c4821SAdrian Prantl   size_t GetIndexOfChildWithName(ConstString name) override {
2038306f76eSShafik Yaghmour     return formatters::ExtractIndexFromString(name.GetCString());
2048306f76eSShafik Yaghmour   }
2058306f76eSShafik Yaghmour 
MightHaveChildren()2068306f76eSShafik Yaghmour   bool MightHaveChildren() override { return true; }
207d7fb94b6SMichael Buch   lldb::ChildCacheState Update() override;
CalculateNumChildren()208*624ea68cSAdrian Prantl   llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
209e710523eSAdrian Prantl   ValueObjectSP GetChildAtIndex(uint32_t idx) override;
2108306f76eSShafik Yaghmour 
2118306f76eSShafik Yaghmour private:
2128306f76eSShafik Yaghmour   size_t m_size = 0;
2138306f76eSShafik Yaghmour };
2148306f76eSShafik Yaghmour } // namespace
2158306f76eSShafik Yaghmour 
Update()216d7fb94b6SMichael Buch lldb::ChildCacheState VariantFrontEnd::Update() {
2178306f76eSShafik Yaghmour   m_size = 0;
2182f4a66eeSAdrian Prantl   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
2192f4a66eeSAdrian Prantl       m_backend, {ConstString("__impl_"), ConstString("__impl")});
2208306f76eSShafik Yaghmour   if (!impl_sp)
221d7fb94b6SMichael Buch     return lldb::ChildCacheState::eRefetch;
2228306f76eSShafik Yaghmour 
2238306f76eSShafik Yaghmour   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
2248306f76eSShafik Yaghmour 
22597212121SRaphael Isemann   if (validity == LibcxxVariantIndexValidity::Invalid)
226d7fb94b6SMichael Buch     return lldb::ChildCacheState::eRefetch;
2278306f76eSShafik Yaghmour 
22897212121SRaphael Isemann   if (validity == LibcxxVariantIndexValidity::NPos)
229d7fb94b6SMichael Buch     return lldb::ChildCacheState::eReuse;
2308306f76eSShafik Yaghmour 
2318306f76eSShafik Yaghmour   m_size = 1;
2328306f76eSShafik Yaghmour 
233d7fb94b6SMichael Buch   return lldb::ChildCacheState::eRefetch;
2348306f76eSShafik Yaghmour }
2358306f76eSShafik Yaghmour 
GetChildAtIndex(uint32_t idx)236e710523eSAdrian Prantl ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
2378306f76eSShafik Yaghmour   if (idx >= m_size)
2382f4a66eeSAdrian Prantl     return {};
2398306f76eSShafik Yaghmour 
2402f4a66eeSAdrian Prantl   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
2412f4a66eeSAdrian Prantl       m_backend, {ConstString("__impl_"), ConstString("__impl")});
2422f4a66eeSAdrian Prantl   if (!impl_sp)
2432f4a66eeSAdrian Prantl     return {};
2448306f76eSShafik Yaghmour 
2458306f76eSShafik Yaghmour   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
2468306f76eSShafik Yaghmour 
2478306f76eSShafik Yaghmour   if (!optional_index_value)
2482f4a66eeSAdrian Prantl     return {};
2498306f76eSShafik Yaghmour 
2508306f76eSShafik Yaghmour   uint64_t index_value = *optional_index_value;
2518306f76eSShafik Yaghmour 
2528306f76eSShafik Yaghmour   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
2538306f76eSShafik Yaghmour 
2548306f76eSShafik Yaghmour   if (!nth_head)
2552f4a66eeSAdrian Prantl     return {};
2568306f76eSShafik Yaghmour 
2578306f76eSShafik Yaghmour   CompilerType head_type = nth_head->GetCompilerType();
2588306f76eSShafik Yaghmour 
2598306f76eSShafik Yaghmour   if (!head_type)
2602f4a66eeSAdrian Prantl     return {};
2618306f76eSShafik Yaghmour 
2628306f76eSShafik Yaghmour   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
2638306f76eSShafik Yaghmour 
2648306f76eSShafik Yaghmour   if (!template_type)
2652f4a66eeSAdrian Prantl     return {};
2668306f76eSShafik Yaghmour 
2677d4fcd41SDave Lee   ValueObjectSP head_value(nth_head->GetChildMemberWithName("__value"));
2688306f76eSShafik Yaghmour 
2698306f76eSShafik Yaghmour   if (!head_value)
2702f4a66eeSAdrian Prantl     return {};
2718306f76eSShafik Yaghmour 
2720ed233c8SRaphael Isemann   return head_value->Clone(ConstString("Value"));
2738306f76eSShafik Yaghmour }
2748306f76eSShafik Yaghmour 
2758306f76eSShafik Yaghmour SyntheticChildrenFrontEnd *
LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)2768306f76eSShafik Yaghmour formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
2778306f76eSShafik Yaghmour                                          lldb::ValueObjectSP valobj_sp) {
2788306f76eSShafik Yaghmour   if (valobj_sp)
2798306f76eSShafik Yaghmour     return new VariantFrontEnd(*valobj_sp);
2808306f76eSShafik Yaghmour   return nullptr;
2818306f76eSShafik Yaghmour }
282