1//===-- LibCxxVariant.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "LibCxxVariant.h"
10#include "LibCxx.h"
11#include "lldb/DataFormatters/FormattersHelpers.h"
12#include "lldb/Symbol/CompilerType.h"
13#include "lldb/Utility/LLDBAssert.h"
14
15#include "llvm/ADT/ScopeExit.h"
16#include <optional>
17
18using namespace lldb;
19using namespace lldb_private;
20
21// libc++ variant implementation contains two members that we care about both
22// are contained in the __impl member.
23// - __index which tells us which of the variadic template types is the active
24// type for the variant
25// - __data is a variadic union which recursively contains itself as member
26// which refers to the tailing variadic types.
27// - __head which refers to the leading non pack type
28// - __value refers to the actual value contained
29// - __tail which refers to the remaining pack types
30//
31// e.g. given std::variant<int,double,char> v1
32//
33// (lldb) frame var -R v1.__impl.__data
34//(... __union<... 0, int, double, char>) v1.__impl.__data = {
35// ...
36// __head = {
37// __value = ...
38// }
39// __tail = {
40// ...
41// __head = {
42// __value = ...
43// }
44// __tail = {
45// ...
46// __head = {
47// __value = ...
48// ...
49//
50// So given
51// - __index equal to 0 the active value is contained in
52//
53// __data.__head.__value
54//
55// - __index equal to 1 the active value is contained in
56//
57// __data.__tail.__head.__value
58//
59// - __index equal to 2 the active value is contained in
60//
61// __data.__tail.__tail.__head.__value
62//
63
64namespace {
65// libc++ std::variant index could have one of three states
66// 1) Valid, we can obtain it and its not variant_npos
67// 2) Invalid, we can't obtain it or it is not a type we expect
68// 3) NPos, its value is variant_npos which means the variant has no value
69enum class LibcxxVariantIndexValidity { Valid, Invalid, NPos };
70
71uint64_t VariantNposValue(uint64_t index_byte_size) {
72 switch (index_byte_size) {
73 case 1:
74 return static_cast<uint8_t>(-1);
75 case 2:
76 return static_cast<uint16_t>(-1);
77 case 4:
78 return static_cast<uint32_t>(-1);
79 }
80 lldbassert(false && "Unknown index type size");
81 return static_cast<uint32_t>(-1); // Fallback to stable ABI type.
82}
83
84LibcxxVariantIndexValidity
85LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
86 ValueObjectSP index_sp(impl_sp->GetChildMemberWithName(name: "__index"));
87
88 if (!index_sp)
89 return LibcxxVariantIndexValidity::Invalid;
90
91 // In the stable ABI, the type of __index is just int.
92 // In the unstable ABI, where _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION is
93 // enabled, the type can either be unsigned char/short/int depending on
94 // how many variant types there are.
95 // We only need to do this here when comparing against npos, because npos is
96 // just `-1`, but that translates to different unsigned values depending on
97 // the byte size.
98 CompilerType index_type = index_sp->GetCompilerType();
99
100 std::optional<uint64_t> index_type_bytes = index_type.GetByteSize(exe_scope: nullptr);
101 if (!index_type_bytes)
102 return LibcxxVariantIndexValidity::Invalid;
103
104 uint64_t npos_value = VariantNposValue(index_byte_size: *index_type_bytes);
105 uint64_t index_value = index_sp->GetValueAsUnsigned(fail_value: 0);
106
107 if (index_value == npos_value)
108 return LibcxxVariantIndexValidity::NPos;
109
110 return LibcxxVariantIndexValidity::Valid;
111}
112
113std::optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
114 ValueObjectSP index_sp(impl_sp->GetChildMemberWithName(name: "__index"));
115
116 if (!index_sp)
117 return {};
118
119 return {index_sp->GetValueAsUnsigned(fail_value: 0)};
120}
121
122ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
123 ValueObjectSP data_sp(impl_sp->GetChildMemberWithName(name: "__data"));
124
125 if (!data_sp)
126 return ValueObjectSP{};
127
128 ValueObjectSP current_level = data_sp;
129 for (uint64_t n = index; n != 0; --n) {
130 ValueObjectSP tail_sp(current_level->GetChildMemberWithName(name: "__tail"));
131
132 if (!tail_sp)
133 return ValueObjectSP{};
134
135 current_level = tail_sp;
136 }
137
138 return current_level->GetChildMemberWithName(name: "__head");
139}
140} // namespace
141
142namespace lldb_private {
143namespace formatters {
144bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
145 const TypeSummaryOptions &options) {
146 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
147 if (!valobj_sp)
148 return false;
149
150 ValueObjectSP impl_sp = GetChildMemberWithName(
151 obj&: *valobj_sp, alternative_names: {ConstString("__impl_"), ConstString("__impl")});
152
153 if (!impl_sp)
154 return false;
155
156 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
157
158 if (validity == LibcxxVariantIndexValidity::Invalid)
159 return false;
160
161 if (validity == LibcxxVariantIndexValidity::NPos) {
162 stream.Printf(format: " No Value");
163 return true;
164 }
165
166 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
167
168 if (!optional_index_value)
169 return false;
170
171 uint64_t index_value = *optional_index_value;
172
173 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index: index_value);
174
175 if (!nth_head)
176 return false;
177
178 CompilerType head_type = nth_head->GetCompilerType();
179
180 if (!head_type)
181 return false;
182
183 CompilerType template_type = head_type.GetTypeTemplateArgument(idx: 1);
184
185 if (!template_type)
186 return false;
187
188 stream << " Active Type = " << template_type.GetDisplayTypeName() << " ";
189
190 return true;
191}
192} // namespace formatters
193} // namespace lldb_private
194
195namespace {
196class VariantFrontEnd : public SyntheticChildrenFrontEnd {
197public:
198 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
199 Update();
200 }
201
202 size_t GetIndexOfChildWithName(ConstString name) override {
203 return formatters::ExtractIndexFromString(item_name: name.GetCString());
204 }
205
206 bool MightHaveChildren() override { return true; }
207 lldb::ChildCacheState Update() override;
208 llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
209 ValueObjectSP GetChildAtIndex(uint32_t idx) override;
210
211private:
212 size_t m_size = 0;
213};
214} // namespace
215
216lldb::ChildCacheState VariantFrontEnd::Update() {
217 m_size = 0;
218 ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
219 obj&: m_backend, alternative_names: {ConstString("__impl_"), ConstString("__impl")});
220 if (!impl_sp)
221 return lldb::ChildCacheState::eRefetch;
222
223 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
224
225 if (validity == LibcxxVariantIndexValidity::Invalid)
226 return lldb::ChildCacheState::eRefetch;
227
228 if (validity == LibcxxVariantIndexValidity::NPos)
229 return lldb::ChildCacheState::eReuse;
230
231 m_size = 1;
232
233 return lldb::ChildCacheState::eRefetch;
234}
235
236ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
237 if (idx >= m_size)
238 return {};
239
240 ValueObjectSP impl_sp = formatters::GetChildMemberWithName(
241 obj&: m_backend, alternative_names: {ConstString("__impl_"), ConstString("__impl")});
242 if (!impl_sp)
243 return {};
244
245 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
246
247 if (!optional_index_value)
248 return {};
249
250 uint64_t index_value = *optional_index_value;
251
252 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index: index_value);
253
254 if (!nth_head)
255 return {};
256
257 CompilerType head_type = nth_head->GetCompilerType();
258
259 if (!head_type)
260 return {};
261
262 CompilerType template_type = head_type.GetTypeTemplateArgument(idx: 1);
263
264 if (!template_type)
265 return {};
266
267 ValueObjectSP head_value(nth_head->GetChildMemberWithName(name: "__value"));
268
269 if (!head_value)
270 return {};
271
272 return head_value->Clone(new_name: ConstString("Value"));
273}
274
275SyntheticChildrenFrontEnd *
276formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
277 lldb::ValueObjectSP valobj_sp) {
278 if (valobj_sp)
279 return new VariantFrontEnd(*valobj_sp);
280 return nullptr;
281}
282

source code of lldb/source/Plugins/Language/CPlusPlus/LibCxxVariant.cpp