1 //===-- LibCxxVariant.cpp --------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "LibCxxVariant.h"
10 #include "lldb/DataFormatters/FormattersHelpers.h"
12 #include "llvm/ADT/Optional.h"
13 #include "llvm/ADT/ScopeExit.h"
16 using namespace lldb_private;
18 // libc++ variant implementation contains two members that we care about both
19 // are contained in the __impl member.
20 // - __index which tells us which of the variadic template types is the active
21 // type for the variant
22 // - __data is a variadic union which recursively contains itself as member
23 // which refers to the tailing variadic types.
24 // - __head which refers to the leading non pack type
25 // - __value refers to the actual value contained
26 // - __tail which refers to the remaining pack types
28 // e.g. given std::variant<int,double,char> v1
30 // (lldb) frame var -R v1.__impl.__data
31 //(... __union<... 0, int, double, char>) v1.__impl.__data = {
48 // - __index equal to 0 the active value is contained in
50 // __data.__head.__value
52 // - __index equal to 1 the active value is contained in
54 // __data.__tail.__head.__value
56 // - __index equal to 2 the active value is contained in
58 // __data.__tail.__tail.__head.__value
62 // libc++ std::variant index could have one of three states
63 // 1) VALID, we can obtain it and its not variant_npos
64 // 2) INVALID, we can't obtain it or it is not a type we expect
65 // 3) NPOS, its value is variant_npos which means the variant has no value
66 enum class LibcxxVariantIndexValidity { VALID, INVALID, NPOS };
68 LibcxxVariantIndexValidity
69 LibcxxVariantGetIndexValidity(ValueObjectSP &impl_sp) {
70 ValueObjectSP index_sp(
71 impl_sp->GetChildMemberWithName(ConstString("__index"), true));
74 return LibcxxVariantIndexValidity::INVALID;
76 int64_t index_value = index_sp->GetValueAsSigned(0);
78 if (index_value == -1)
79 return LibcxxVariantIndexValidity::NPOS;
81 return LibcxxVariantIndexValidity::VALID;
84 llvm::Optional<uint64_t> LibcxxVariantIndexValue(ValueObjectSP &impl_sp) {
85 ValueObjectSP index_sp(
86 impl_sp->GetChildMemberWithName(ConstString("__index"), true));
91 return {index_sp->GetValueAsUnsigned(0)};
94 ValueObjectSP LibcxxVariantGetNthHead(ValueObjectSP &impl_sp, uint64_t index) {
95 ValueObjectSP data_sp(
96 impl_sp->GetChildMemberWithName(ConstString("__data"), true));
99 return ValueObjectSP{};
101 ValueObjectSP current_level = data_sp;
102 for (uint64_t n = index; n != 0; --n) {
103 ValueObjectSP tail_sp(
104 current_level->GetChildMemberWithName(ConstString("__tail"), true));
107 return ValueObjectSP{};
109 current_level = tail_sp;
112 return current_level->GetChildMemberWithName(ConstString("__head"), true);
116 namespace lldb_private {
117 namespace formatters {
118 bool LibcxxVariantSummaryProvider(ValueObject &valobj, Stream &stream,
119 const TypeSummaryOptions &options) {
120 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
124 ValueObjectSP impl_sp(
125 valobj_sp->GetChildMemberWithName(ConstString("__impl"), true));
130 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
132 if (validity == LibcxxVariantIndexValidity::INVALID)
135 if (validity == LibcxxVariantIndexValidity::NPOS) {
136 stream.Printf(" No Value");
140 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
142 if (!optional_index_value)
145 uint64_t index_value = *optional_index_value;
147 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
152 CompilerType head_type = nth_head->GetCompilerType();
157 CompilerType template_type = head_type.GetTypeTemplateArgument(1);
162 stream.Printf(" Active Type = %s ", template_type.GetTypeName().GetCString());
166 } // namespace formatters
167 } // namespace lldb_private
170 class VariantFrontEnd : public SyntheticChildrenFrontEnd {
172 VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
176 size_t GetIndexOfChildWithName(ConstString name) override {
177 return formatters::ExtractIndexFromString(name.GetCString());
180 bool MightHaveChildren() override { return true; }
181 bool Update() override;
182 size_t CalculateNumChildren() override { return m_size; }
183 ValueObjectSP GetChildAtIndex(size_t idx) override;
187 ValueObjectSP m_base_sp;
191 bool VariantFrontEnd::Update() {
193 ValueObjectSP impl_sp(
194 m_backend.GetChildMemberWithName(ConstString("__impl"), true));
198 LibcxxVariantIndexValidity validity = LibcxxVariantGetIndexValidity(impl_sp);
200 if (validity == LibcxxVariantIndexValidity::INVALID)
203 if (validity == LibcxxVariantIndexValidity::NPOS)
211 ValueObjectSP VariantFrontEnd::GetChildAtIndex(size_t idx) {
213 return ValueObjectSP();
215 ValueObjectSP impl_sp(
216 m_backend.GetChildMemberWithName(ConstString("__impl"), true));
218 auto optional_index_value = LibcxxVariantIndexValue(impl_sp);
220 if (!optional_index_value)
221 return ValueObjectSP();
223 uint64_t index_value = *optional_index_value;
225 ValueObjectSP nth_head = LibcxxVariantGetNthHead(impl_sp, index_value);
228 return ValueObjectSP();
230 CompilerType head_type = nth_head->GetCompilerType();
233 return ValueObjectSP();
235 CompilerType template_type = head_type.GetTypeTemplateArgument(1);
238 return ValueObjectSP();
240 ValueObjectSP head_value(
241 nth_head->GetChildMemberWithName(ConstString("__value"), true));
244 return ValueObjectSP();
246 return head_value->Clone(ConstString(ConstString("Value").AsCString()));
249 SyntheticChildrenFrontEnd *
250 formatters::LibcxxVariantFrontEndCreator(CXXSyntheticChildren *,
251 lldb::ValueObjectSP valobj_sp) {
253 return new VariantFrontEnd(*valobj_sp);