Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
MsvcStl.cpp
MsvcStlSmartPointer.cpp
MsvcStlTuple.cpp
MsvcStlVariant.cpp
MsvcStlVector.cpp
MSVCUndecoratedNameParser.cpp

Expand Down
33 changes: 25 additions & 8 deletions lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1449,11 +1449,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
"^std::variant<.+>$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));

stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false);
Expand Down Expand Up @@ -1509,9 +1504,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
TypeSummaryImplSP(new ScriptSummaryFormat(
stl_summary_flags,
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
AddCXXSummary(cpp_category_sp, LibStdcppVariantSummaryProvider,
"libstdc++ std::variant summary provider", "^std::variant<.+>$",
stl_summary_flags, true);

AddCXXSynthetic(
cpp_category_sp,
Expand Down Expand Up @@ -1649,6 +1641,25 @@ GenericOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *children,
return LibStdcppOptionalSyntheticFrontEndCreator(children, valobj_sp);
}

static SyntheticChildrenFrontEnd *
GenericVariantSyntheticFrontEndCreator(CXXSyntheticChildren *children,
lldb::ValueObjectSP valobj_sp) {
if (!valobj_sp)
return nullptr;

if (IsMsvcStlVariant(*valobj_sp))
return MsvcStlVariantSyntheticFrontEndCreator(children, valobj_sp);
return new ScriptedSyntheticChildren::FrontEnd(
"lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider", *valobj_sp);
}

static bool GenericVariantSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options) {
if (IsMsvcStlVariant(valobj))
return MsvcStlVariantSummaryProvider(valobj, stream, options);
return LibStdcppVariantSummaryProvider(valobj, stream, options);
}

/// Load formatters that are formatting types from more than one STL
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
if (!cpp_category_sp)
Expand Down Expand Up @@ -1713,6 +1724,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
AddCXXSynthetic(cpp_category_sp, GenericForwardListSyntheticFrontEndCreator,
"std::forward_list synthetic children",
"^std::forward_list<.+>(( )?&)?$", stl_synth_flags, true);
AddCXXSynthetic(cpp_category_sp, GenericVariantSyntheticFrontEndCreator,
"std::variant synthetic children", "^std::variant<.*>$",
stl_synth_flags, true);

SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
stl_deref_flags.SetFrontEndWantsDereference();
Expand Down Expand Up @@ -1749,6 +1763,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
AddCXXSummary(cpp_category_sp, GenericOptionalSummaryProvider,
"MSVC STL/libstd++ std::optional summary provider",
"^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
AddCXXSummary(cpp_category_sp, GenericVariantSummaryProvider,
"MSVC STL/libstdc++ std::variant summary provider",
"^std::variant<.*>$", stl_summary_flags, true);
}

static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ SyntheticChildrenFrontEnd *
LibStdcppUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);

bool LibStdcppVariantSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options);

} // namespace formatters
} // namespace lldb_private

Expand Down
8 changes: 8 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ SyntheticChildrenFrontEnd *
MsvcStlOptionalSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);

// MSVC STL std::variant<>
bool IsMsvcStlVariant(ValueObject &valobj);
bool MsvcStlVariantSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options);
SyntheticChildrenFrontEnd *
MsvcStlVariantSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);

} // namespace formatters
} // namespace lldb_private

Expand Down
188 changes: 188 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/MsvcStlVariant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//===-- MsvcStlVariant.cpp-------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MsvcStl.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/Symbol/CompilerType.h"
#include <optional>

using namespace lldb;
using namespace lldb_private;

namespace {

// A variant when using DWARF looks as follows:
// (lldb) fr v -R v1
// (std::variant<int, double, char>) v1 = {
// std::_SMF_control<std::_Variant_base<int, double, char>, int, double, char>
// = {
// std::_Variant_storage<int, double, char> = {
// = {
// _Head = 0
// _Tail = {
// = {
// _Head = 2
// _Tail = {
// = {
// _Head = '\0'
// _Tail = {}
// }
// }
// }
// }
// }
// }
// _Which = '\x01'
// }
// }

ValueObjectSP GetStorageMember(ValueObject &valobj, llvm::StringRef name) {
// Find the union
ValueObjectSP union_sp = valobj.GetChildAtIndex(0);
if (!union_sp)
return nullptr;
return union_sp->GetChildMemberWithName(name);
}

ValueObjectSP GetHead(ValueObject &valobj) {
return GetStorageMember(valobj, "_Head");
}
ValueObjectSP GetTail(ValueObject &valobj) {
return GetStorageMember(valobj, "_Tail");
}

std::optional<int64_t> GetIndexValue(ValueObject &valobj) {
ValueObjectSP index_sp = valobj.GetChildMemberWithName("_Which");
if (!index_sp)
return std::nullopt;

return {index_sp->GetValueAsSigned(-1)};
}

ValueObjectSP GetNthStorage(ValueObject &outer, int64_t index) {
// We need to find the std::_Variant_storage base class.

// -> std::_SMF_control (typedef to std::_Variant_base)
ValueObjectSP container_sp = outer.GetSP()->GetChildAtIndex(0);
if (!container_sp)
return nullptr;
// -> std::_Variant_storage
container_sp = container_sp->GetChildAtIndex(0);
if (!container_sp)
return nullptr;

for (int64_t i = 0; i < index; i++) {
container_sp = GetTail(*container_sp);
if (!container_sp)
return nullptr;
}
return container_sp;
}

} // namespace

bool formatters::IsMsvcStlVariant(ValueObject &valobj) {
if (auto valobj_sp = valobj.GetNonSyntheticValue()) {
return valobj_sp->GetChildMemberWithName("_Which") != nullptr;
}
return false;
}

bool formatters::MsvcStlVariantSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
if (!valobj_sp)
return false;

auto index = GetIndexValue(*valobj_sp);
if (!index)
return false;

if (*index < 0) {
stream.Printf(" No Value");
return true;
}

ValueObjectSP storage = GetNthStorage(*valobj_sp, *index);
if (!storage)
return false;
CompilerType storage_type = storage->GetCompilerType();
if (!storage_type)
return false;
// Resolve the typedef
if (storage_type.IsTypedefType())
storage_type = storage_type.GetTypedefedType();

CompilerType active_type = storage_type.GetTypeTemplateArgument(1, true);
if (!active_type)
return false;

stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
return true;
}

namespace {
class VariantFrontEnd : public SyntheticChildrenFrontEnd {
public:
VariantFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
Update();
}

llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
if (!optional_idx) {
return llvm::createStringError("Type has no child named '%s'",
name.AsCString());
}
return *optional_idx;
}

lldb::ChildCacheState Update() override;
llvm::Expected<uint32_t> CalculateNumChildren() override { return m_size; }
ValueObjectSP GetChildAtIndex(uint32_t idx) override;

private:
size_t m_size = 0;
};
} // namespace

lldb::ChildCacheState VariantFrontEnd::Update() {
m_size = 0;

auto index = GetIndexValue(m_backend);
if (index && *index >= 0)
m_size = 1;

return lldb::ChildCacheState::eRefetch;
}

ValueObjectSP VariantFrontEnd::GetChildAtIndex(uint32_t idx) {
if (idx >= m_size)
return nullptr;

auto index = GetIndexValue(m_backend);
if (!index)
return nullptr;

ValueObjectSP storage_sp = GetNthStorage(m_backend, *index);
if (!storage_sp)
return nullptr;

ValueObjectSP head_sp = GetHead(*storage_sp);
if (!head_sp)
return nullptr;

return head_sp->Clone(ConstString("Value"));
}

SyntheticChildrenFrontEnd *formatters::MsvcStlVariantSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
if (valobj_sp)
return new VariantFrontEnd(*valobj_sp);
return nullptr;
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,9 @@ def test_libcxx(self):
def test_libstdcxx(self):
self.build(dictionary={"USE_LIBSTDCPP": 1})
self.do_test()

@add_test_categories(["msvcstl"])
def test_msvcstl(self):
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
self.build()
self.do_test()
Loading