Skip to content

Commit 401b5cc

Browse files
authored
[LLDB] Add formatters for MSVC STL std::variant (#148554)
Adds a summary and synthetic children for MSVC STL's `std::variant`. This one is a bit complicated because of DWARF vs PDB differences. I put the representations in comments. Being able to `GetChildMemberWithName` a member in an anonymous union would make this a lot simpler (`std::optional` will have something similar iirc). Towards #24834.
1 parent 43a829a commit 401b5cc

File tree

6 files changed

+231
-8
lines changed

6 files changed

+231
-8
lines changed

lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3636
MsvcStl.cpp
3737
MsvcStlSmartPointer.cpp
3838
MsvcStlTuple.cpp
39+
MsvcStlVariant.cpp
3940
MsvcStlVector.cpp
4041
MSVCUndecoratedNameParser.cpp
4142

lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,11 +1449,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14491449
SyntheticChildrenSP(new ScriptedSyntheticChildren(
14501450
stl_synth_flags,
14511451
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
1452-
cpp_category_sp->AddTypeSynthetic(
1453-
"^std::variant<.+>$", eFormatterMatchRegex,
1454-
SyntheticChildrenSP(new ScriptedSyntheticChildren(
1455-
stl_synth_flags,
1456-
"lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));
14571452

14581453
stl_summary_flags.SetDontShowChildren(false);
14591454
stl_summary_flags.SetSkipPointers(false);
@@ -1509,9 +1504,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15091504
TypeSummaryImplSP(new ScriptSummaryFormat(
15101505
stl_summary_flags,
15111506
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
1512-
AddCXXSummary(cpp_category_sp, LibStdcppVariantSummaryProvider,
1513-
"libstdc++ std::variant summary provider", "^std::variant<.+>$",
1514-
stl_summary_flags, true);
15151507

15161508
AddCXXSynthetic(
15171509
cpp_category_sp,
@@ -1649,6 +1641,25 @@ GenericOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *children,
16491641
return LibStdcppOptionalSyntheticFrontEndCreator(children, valobj_sp);
16501642
}
16511643

1644+
static SyntheticChildrenFrontEnd *
1645+
GenericVariantSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1646+
lldb::ValueObjectSP valobj_sp) {
1647+
if (!valobj_sp)
1648+
return nullptr;
1649+
1650+
if (IsMsvcStlVariant(*valobj_sp))
1651+
return MsvcStlVariantSyntheticFrontEndCreator(children, valobj_sp);
1652+
return new ScriptedSyntheticChildren::FrontEnd(
1653+
"lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider", *valobj_sp);
1654+
}
1655+
1656+
static bool GenericVariantSummaryProvider(ValueObject &valobj, Stream &stream,
1657+
const TypeSummaryOptions &options) {
1658+
if (IsMsvcStlVariant(valobj))
1659+
return MsvcStlVariantSummaryProvider(valobj, stream, options);
1660+
return LibStdcppVariantSummaryProvider(valobj, stream, options);
1661+
}
1662+
16521663
/// Load formatters that are formatting types from more than one STL
16531664
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16541665
if (!cpp_category_sp)
@@ -1713,6 +1724,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17131724
AddCXXSynthetic(cpp_category_sp, GenericForwardListSyntheticFrontEndCreator,
17141725
"std::forward_list synthetic children",
17151726
"^std::forward_list<.+>(( )?&)?$", stl_synth_flags, true);
1727+
AddCXXSynthetic(cpp_category_sp, GenericVariantSyntheticFrontEndCreator,
1728+
"std::variant synthetic children", "^std::variant<.*>$",
1729+
stl_synth_flags, true);
17161730

17171731
SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
17181732
stl_deref_flags.SetFrontEndWantsDereference();
@@ -1749,6 +1763,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17491763
AddCXXSummary(cpp_category_sp, GenericOptionalSummaryProvider,
17501764
"MSVC STL/libstd++ std::optional summary provider",
17511765
"^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
1766+
AddCXXSummary(cpp_category_sp, GenericVariantSummaryProvider,
1767+
"MSVC STL/libstdc++ std::variant summary provider",
1768+
"^std::variant<.*>$", stl_summary_flags, true);
17521769
}
17531770

17541771
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {

lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ SyntheticChildrenFrontEnd *
6161
LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
6262
lldb::ValueObjectSP);
6363

64+
bool LibStdcppVariantSummaryProvider(ValueObject &valobj, Stream &stream,
65+
const TypeSummaryOptions &options);
66+
6467
} // namespace formatters
6568
} // namespace lldb_private
6669

lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ SyntheticChildrenFrontEnd *
7171
MsvcStlOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *,
7272
lldb::ValueObjectSP valobj_sp);
7373

74+
// MSVC STL std::variant<>
75+
bool IsMsvcStlVariant(ValueObject &valobj);
76+
bool MsvcStlVariantSummaryProvider(ValueObject &valobj, Stream &stream,
77+
const TypeSummaryOptions &options);
78+
SyntheticChildrenFrontEnd *
79+
MsvcStlVariantSyntheticFrontEndCreator(CXXSyntheticChildren *,
80+
lldb::ValueObjectSP valobj_sp);
81+
7482
} // namespace formatters
7583
} // namespace lldb_private
7684

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
//===-- MsvcStlVariant.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 "MsvcStl.h"
10+
#include "lldb/DataFormatters/FormattersHelpers.h"
11+
#include "lldb/Symbol/CompilerType.h"
12+
#include <optional>
13+
14+
using namespace lldb;
15+
using namespace lldb_private;
16+
17+
namespace {
18+
19+
// A variant when using DWARF looks as follows:
20+
// (lldb) fr v -R v1
21+
// (std::variant<int, double, char>) v1 = {
22+
// std::_SMF_control<std::_Variant_base<int, double, char>, int, double, char>
23+
// = {
24+
// std::_Variant_storage<int, double, char> = {
25+
// = {
26+
// _Head = 0
27+
// _Tail = {
28+
// = {
29+
// _Head = 2
30+
// _Tail = {
31+
// = {
32+
// _Head = '\0'
33+
// _Tail = {}
34+
// }
35+
// }
36+
// }
37+
// }
38+
// }
39+
// }
40+
// _Which = '\x01'
41+
// }
42+
// }
43+
44+
ValueObjectSP GetStorageMember(ValueObject &valobj, llvm::StringRef name) {
45+
// Find the union
46+
ValueObjectSP union_sp = valobj.GetChildAtIndex(0);
47+
if (!union_sp)
48+
return nullptr;
49+
return union_sp->GetChildMemberWithName(name);
50+
}
51+
52+
ValueObjectSP GetHead(ValueObject &valobj) {
53+
return GetStorageMember(valobj, "_Head");
54+
}
55+
ValueObjectSP GetTail(ValueObject &valobj) {
56+
return GetStorageMember(valobj, "_Tail");
57+
}
58+
59+
std::optional<int64_t> GetIndexValue(ValueObject &valobj) {
60+
ValueObjectSP index_sp = valobj.GetChildMemberWithName("_Which");
61+
if (!index_sp)
62+
return std::nullopt;
63+
64+
return {index_sp->GetValueAsSigned(-1)};
65+
}
66+
67+
ValueObjectSP GetNthStorage(ValueObject &outer, int64_t index) {
68+
// We need to find the std::_Variant_storage base class.
69+
70+
// -> std::_SMF_control (typedef to std::_Variant_base)
71+
ValueObjectSP container_sp = outer.GetSP()->GetChildAtIndex(0);
72+
if (!container_sp)
73+
return nullptr;
74+
// -> std::_Variant_storage
75+
container_sp = container_sp->GetChildAtIndex(0);
76+
if (!container_sp)
77+
return nullptr;
78+
79+
for (int64_t i = 0; i < index; i++) {
80+
container_sp = GetTail(*container_sp);
81+
if (!container_sp)
82+
return nullptr;
83+
}
84+
return container_sp;
85+
}
86+
87+
} // namespace
88+
89+
bool formatters::IsMsvcStlVariant(ValueObject &valobj) {
90+
if (auto valobj_sp = valobj.GetNonSyntheticValue()) {
91+
return valobj_sp->GetChildMemberWithName("_Which") != nullptr;
92+
}
93+
return false;
94+
}
95+
96+
bool formatters::MsvcStlVariantSummaryProvider(
97+
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
98+
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
99+
if (!valobj_sp)
100+
return false;
101+
102+
auto index = GetIndexValue(*valobj_sp);
103+
if (!index)
104+
return false;
105+
106+
if (*index < 0) {
107+
stream.Printf(" No Value");
108+
return true;
109+
}
110+
111+
ValueObjectSP storage = GetNthStorage(*valobj_sp, *index);
112+
if (!storage)
113+
return false;
114+
CompilerType storage_type = storage->GetCompilerType();
115+
if (!storage_type)
116+
return false;
117+
// Resolve the typedef
118+
if (storage_type.IsTypedefType())
119+
storage_type = storage_type.GetTypedefedType();
120+
121+
CompilerType active_type = storage_type.GetTypeTemplateArgument(1, true);
122+
if (!active_type)
123+
return false;
124+
125+
stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
126+
return true;
127+
}
128+
129+
namespace {
130+
class VariantFrontEnd : public SyntheticChildrenFrontEnd {
131+
public:
132+
VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
133+
Update();
134+
}
135+
136+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
137+
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
138+
if (!optional_idx) {
139+
return llvm::createStringError("Type has no child named '%s'",
140+
name.AsCString());
141+
}
142+
return *optional_idx;
143+
}
144+
145+
lldb::ChildCacheState Update() override;
146+
llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
147+
ValueObjectSP GetChildAtIndex(uint32_t idx) override;
148+
149+
private:
150+
size_t m_size = 0;
151+
};
152+
} // namespace
153+
154+
lldb::ChildCacheState VariantFrontEnd::Update() {
155+
m_size = 0;
156+
157+
auto index = GetIndexValue(m_backend);
158+
if (index && *index >= 0)
159+
m_size = 1;
160+
161+
return lldb::ChildCacheState::eRefetch;
162+
}
163+
164+
ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
165+
if (idx >= m_size)
166+
return nullptr;
167+
168+
auto index = GetIndexValue(m_backend);
169+
if (!index)
170+
return nullptr;
171+
172+
ValueObjectSP storage_sp = GetNthStorage(m_backend, *index);
173+
if (!storage_sp)
174+
return nullptr;
175+
176+
ValueObjectSP head_sp = GetHead(*storage_sp);
177+
if (!head_sp)
178+
return nullptr;
179+
180+
return head_sp->Clone(ConstString("Value"));
181+
}
182+
183+
SyntheticChildrenFrontEnd *formatters::MsvcStlVariantSyntheticFrontEndCreator(
184+
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
185+
if (valobj_sp)
186+
return new VariantFrontEnd(*valobj_sp);
187+
return nullptr;
188+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/variant/TestDataFormatterStdVariant.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,9 @@ def test_libcxx(self):
8383
def test_libstdcxx(self):
8484
self.build(dictionary={"USE_LIBSTDCPP": 1})
8585
self.do_test()
86+
87+
@add_test_categories(["msvcstl"])
88+
def test_msvcstl(self):
89+
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
90+
self.build()
91+
self.do_test()

0 commit comments

Comments
 (0)