xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- LibCxxVariant.cpp -------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "LibCxxVariant.h"
10*f6aab3d8Srobert #include "LibCxx.h"
11061da546Spatrick #include "lldb/DataFormatters/FormattersHelpers.h"
12*f6aab3d8Srobert #include "lldb/Symbol/CompilerType.h"
13*f6aab3d8Srobert #include "lldb/Utility/LLDBAssert.h"
14061da546Spatrick 
15061da546Spatrick #include "llvm/ADT/ScopeExit.h"
16*f6aab3d8Srobert #include <optional>
17061da546Spatrick 
18061da546Spatrick using namespace lldb;
19061da546Spatrick using namespace lldb_private;
20061da546Spatrick 
21061da546Spatrick // libc++ variant implementation contains two members that we care about both
22061da546Spatrick // are contained in the __impl member.
23061da546Spatrick // - __index which tells us which of the variadic template types is the active
24061da546Spatrick //   type for the variant
25061da546Spatrick // - __data is a variadic union which recursively contains itself as member
26061da546Spatrick //   which refers to the tailing variadic types.
27061da546Spatrick //   - __head which refers to the leading non pack type
28061da546Spatrick //     - __value refers to the actual value contained
29061da546Spatrick //   - __tail which refers to the remaining pack types
30061da546Spatrick //
31061da546Spatrick // e.g. given std::variant<int,double,char> v1
32061da546Spatrick //
33061da546Spatrick // (lldb) frame var -R v1.__impl.__data
34061da546Spatrick //(... __union<... 0, int, double, char>) v1.__impl.__data = {
35061da546Spatrick // ...
36061da546Spatrick //  __head = {
37061da546Spatrick //    __value = ...
38061da546Spatrick //  }
39061da546Spatrick //  __tail = {
40061da546Spatrick //  ...
41061da546Spatrick //    __head = {
42061da546Spatrick //      __value = ...
43061da546Spatrick //    }
44061da546Spatrick //    __tail = {
45061da546Spatrick //    ...
46061da546Spatrick //      __head = {
47061da546Spatrick //        __value = ...
48061da546Spatrick //  ...
49061da546Spatrick //
50061da546Spatrick // So given
51061da546Spatrick // - __index equal to 0 the active value is contained in
52061da546Spatrick //
53061da546Spatrick //     __data.__head.__value
54061da546Spatrick //
55061da546Spatrick // - __index equal to 1 the active value is contained in
56061da546Spatrick //
57061da546Spatrick //     __data.__tail.__head.__value
58061da546Spatrick //
59061da546Spatrick // - __index equal to 2 the active value is contained in
60061da546Spatrick //
61061da546Spatrick //      __data.__tail.__tail.__head.__value
62061da546Spatrick //
63061da546Spatrick 
64061da546Spatrick namespace {
65061da546Spatrick // libc++ std::variant index could have one of three states
66dda28197Spatrick // 1) Valid, we can obtain it and its not variant_npos
67dda28197Spatrick // 2) Invalid, we can't obtain it or it is not a type we expect
68dda28197Spatrick // 3) NPos, its value is variant_npos which means the variant has no value
69dda28197Spatrick enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };
70061da546Spatrick 
VariantNposValue(uint64_t index_byte_size)71*f6aab3d8Srobert uint64_t VariantNposValue(uint64_t index_byte_size) {
72*f6aab3d8Srobert   switch (index_byte_size) {
73*f6aab3d8Srobert   case 1:
74*f6aab3d8Srobert     return static_cast<uint8_t>(-1);
75*f6aab3d8Srobert   case 2:
76*f6aab3d8Srobert     return static_cast<uint16_t>(-1);
77*f6aab3d8Srobert   case 4:
78*f6aab3d8Srobert     return static_cast<uint32_t>(-1);
79*f6aab3d8Srobert   }
80*f6aab3d8Srobert   lldbassert(false && "Unknown index type size");
81*f6aab3d8Srobert   return static_cast<uint32_t>(-1); // Fallback to stable ABI type.
82*f6aab3d8Srobert }
83*f6aab3d8Srobert 
84061da546Spatrick LibcxxVariantIndexValidity
LibcxxVariantGetIndexValidity(ValueObjectSP & impl_sp)85061da546Spatrick LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
86061da546Spatrick   ValueObjectSP index_sp(
87061da546Spatrick       impl_sp->GetChildMemberWithName(ConstString("__index"), true));
88061da546Spatrick 
89061da546Spatrick   if (!index_sp)
90dda28197Spatrick     return LibcxxVariantIndexValidity::Invalid;
91061da546Spatrick 
92*f6aab3d8Srobert   // In the stable ABI, the type of __index is just int.
93*f6aab3d8Srobert   // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is
94*f6aab3d8Srobert   // enabled, the type can either be unsigned char/short/int depending on
95*f6aab3d8Srobert   // how many variant types there are.
96*f6aab3d8Srobert   // We only need to do this here when comparing against npos, because npos is
97*f6aab3d8Srobert   // just `-1`, but that translates to different unsigned values depending on
98*f6aab3d8Srobert   // the byte size.
99*f6aab3d8Srobert   CompilerType index_type = index_sp->GetCompilerType();
100061da546Spatrick 
101*f6aab3d8Srobert   std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(nullptr);
102*f6aab3d8Srobert   if (!index_type_bytes)
103*f6aab3d8Srobert     return LibcxxVariantIndexValidity::Invalid;
104*f6aab3d8Srobert 
105*f6aab3d8Srobert   uint64_t npos_value = VariantNposValue(*index_type_bytes);
106*f6aab3d8Srobert   uint64_t index_value = index_sp->GetValueAsUnsigned(0);
107*f6aab3d8Srobert 
108*f6aab3d8Srobert   if (index_value == npos_value)
109dda28197Spatrick     return LibcxxVariantIndexValidity::NPos;
110061da546Spatrick 
111dda28197Spatrick   return LibcxxVariantIndexValidity::Valid;
112061da546Spatrick }
113061da546Spatrick 
LibcxxVariantIndexValue(ValueObjectSP & impl_sp)114*f6aab3d8Srobert std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
115061da546Spatrick   ValueObjectSP index_sp(
116061da546Spatrick       impl_sp->GetChildMemberWithName(ConstString("__index"), true));
117061da546Spatrick 
118061da546Spatrick   if (!index_sp)
119061da546Spatrick     return {};
120061da546Spatrick 
121061da546Spatrick   return {index_sp->GetValueAsUnsigned(0)};
122061da546Spatrick }
123061da546Spatrick 
LibcxxVariantGetNthHead(ValueObjectSP & impl_sp,uint64_t index)124061da546Spatrick ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
125061da546Spatrick   ValueObjectSP data_sp(
126061da546Spatrick       impl_sp->GetChildMemberWithName(ConstString("__data"), true));
127061da546Spatrick 
128061da546Spatrick   if (!data_sp)
129061da546Spatrick     return ValueObjectSP{};
130061da546Spatrick 
131061da546Spatrick   ValueObjectSP current_level = data_sp;
132061da546Spatrick   for (uint64_t n = index; n != 0; --n) {
133061da546Spatrick     ValueObjectSP tail_sp(
134061da546Spatrick         current_level->GetChildMemberWithName(ConstString("__tail"), true));
135061da546Spatrick 
136061da546Spatrick     if (!tail_sp)
137061da546Spatrick       return ValueObjectSP{};
138061da546Spatrick 
139061da546Spatrick     current_level = tail_sp;
140061da546Spatrick   }
141061da546Spatrick 
142061da546Spatrick   return current_level->GetChildMemberWithName(ConstString("__head"), true);
143061da546Spatrick }
144061da546Spatrick } // namespace
145061da546Spatrick 
146061da546Spatrick namespace lldb_private {
147061da546Spatrick namespace formatters {
LibcxxVariantSummaryProvider(ValueObject & valobj,Stream & stream,const TypeSummaryOptions & options)148061da546Spatrick bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
149061da546Spatrick                                   const TypeSummaryOptions &options) {
150061da546Spatrick   ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
151061da546Spatrick   if (!valobj_sp)
152061da546Spatrick     return false;
153061da546Spatrick 
154*f6aab3d8Srobert   ValueObjectSP impl_sp = GetChildMemberWithName(
155*f6aab3d8Srobert       *valobj_sp, {ConstString("__impl_"), ConstString("__impl")});
156061da546Spatrick 
157061da546Spatrick   if (!impl_sp)
158061da546Spatrick     return false;
159061da546Spatrick 
160061da546Spatrick   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
161061da546Spatrick 
162dda28197Spatrick   if (validity == LibcxxVariantIndexValidity::Invalid)
163061da546Spatrick     return false;
164061da546Spatrick 
165dda28197Spatrick   if (validity == LibcxxVariantIndexValidity::NPos) {
166061da546Spatrick     stream.Printf(" No Value");
167061da546Spatrick     return true;
168061da546Spatrick   }
169061da546Spatrick 
170061da546Spatrick   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
171061da546Spatrick 
172061da546Spatrick   if (!optional_index_value)
173061da546Spatrick     return false;
174061da546Spatrick 
175061da546Spatrick   uint64_t index_value = *optional_index_value;
176061da546Spatrick 
177061da546Spatrick   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
178061da546Spatrick 
179061da546Spatrick   if (!nth_head)
180061da546Spatrick     return false;
181061da546Spatrick 
182061da546Spatrick   CompilerType head_type = nth_head->GetCompilerType();
183061da546Spatrick 
184061da546Spatrick   if (!head_type)
185061da546Spatrick     return false;
186061da546Spatrick 
187061da546Spatrick   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
188061da546Spatrick 
189061da546Spatrick   if (!template_type)
190061da546Spatrick     return false;
191061da546Spatrick 
192dda28197Spatrick   stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
193061da546Spatrick 
194061da546Spatrick   return true;
195061da546Spatrick }
196061da546Spatrick } // namespace formatters
197061da546Spatrick } // namespace lldb_private
198061da546Spatrick 
199061da546Spatrick namespace {
200061da546Spatrick class VariantFrontEnd : public SyntheticChildrenFrontEnd {
201061da546Spatrick public:
VariantFrontEnd(ValueObject & valobj)202061da546Spatrick   VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
203061da546Spatrick     Update();
204061da546Spatrick   }
205061da546Spatrick 
GetIndexOfChildWithName(ConstString name)206061da546Spatrick   size_t GetIndexOfChildWithName(ConstString name) override {
207061da546Spatrick     return formatters::ExtractIndexFromString(name.GetCString());
208061da546Spatrick   }
209061da546Spatrick 
MightHaveChildren()210061da546Spatrick   bool MightHaveChildren() override { return true; }
211061da546Spatrick   bool Update() override;
CalculateNumChildren()212061da546Spatrick   size_t CalculateNumChildren() override { return m_size; }
213061da546Spatrick   ValueObjectSP GetChildAtIndex(size_t idx) override;
214061da546Spatrick 
215061da546Spatrick private:
216061da546Spatrick   size_t m_size = 0;
217061da546Spatrick };
218061da546Spatrick } // namespace
219061da546Spatrick 
Update()220061da546Spatrick bool VariantFrontEnd::Update() {
221061da546Spatrick   m_size = 0;
222*f6aab3d8Srobert   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
223*f6aab3d8Srobert       m_backend, {ConstString("__impl_"), ConstString("__impl")});
224061da546Spatrick   if (!impl_sp)
225061da546Spatrick     return false;
226061da546Spatrick 
227061da546Spatrick   LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
228061da546Spatrick 
229dda28197Spatrick   if (validity == LibcxxVariantIndexValidity::Invalid)
230061da546Spatrick     return false;
231061da546Spatrick 
232dda28197Spatrick   if (validity == LibcxxVariantIndexValidity::NPos)
233061da546Spatrick     return true;
234061da546Spatrick 
235061da546Spatrick   m_size = 1;
236061da546Spatrick 
237061da546Spatrick   return false;
238061da546Spatrick }
239061da546Spatrick 
GetChildAtIndex(size_t idx)240061da546Spatrick ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
241061da546Spatrick   if (idx >= m_size)
242*f6aab3d8Srobert     return {};
243061da546Spatrick 
244*f6aab3d8Srobert   ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
245*f6aab3d8Srobert       m_backend, {ConstString("__impl_"), ConstString("__impl")});
246*f6aab3d8Srobert   if (!impl_sp)
247*f6aab3d8Srobert     return {};
248061da546Spatrick 
249061da546Spatrick   auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
250061da546Spatrick 
251061da546Spatrick   if (!optional_index_value)
252*f6aab3d8Srobert     return {};
253061da546Spatrick 
254061da546Spatrick   uint64_t index_value = *optional_index_value;
255061da546Spatrick 
256061da546Spatrick   ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
257061da546Spatrick 
258061da546Spatrick   if (!nth_head)
259*f6aab3d8Srobert     return {};
260061da546Spatrick 
261061da546Spatrick   CompilerType head_type = nth_head->GetCompilerType();
262061da546Spatrick 
263061da546Spatrick   if (!head_type)
264*f6aab3d8Srobert     return {};
265061da546Spatrick 
266061da546Spatrick   CompilerType template_type = head_type.GetTypeTemplateArgument(1);
267061da546Spatrick 
268061da546Spatrick   if (!template_type)
269*f6aab3d8Srobert     return {};
270061da546Spatrick 
271061da546Spatrick   ValueObjectSP head_value(
272061da546Spatrick       nth_head->GetChildMemberWithName(ConstString("__value"), true));
273061da546Spatrick 
274061da546Spatrick   if (!head_value)
275*f6aab3d8Srobert     return {};
276061da546Spatrick 
277dda28197Spatrick   return head_value->Clone(ConstString("Value"));
278061da546Spatrick }
279061da546Spatrick 
280061da546Spatrick SyntheticChildrenFrontEnd *
LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)281061da546Spatrick formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
282061da546Spatrick                                          lldb::ValueObjectSP valobj_sp) {
283061da546Spatrick   if (valobj_sp)
284061da546Spatrick     return new VariantFrontEnd(*valobj_sp);
285061da546Spatrick   return nullptr;
286061da546Spatrick }
287