diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -18,6 +18,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" @@ -109,6 +110,16 @@ /// \see ClangASTImporter::DeportType clang::Decl *DeportDecl(clang::ASTContext *dst_ctx, clang::Decl *decl); + clang::PrintingCallbacks::TriState + IsTemplateArgumentDefaulted(clang::ClassTemplateSpecializationDecl const *D, + size_t ArgIndex) const; + + void + SetTemplateArgumentDefaults(clang::ClassTemplateSpecializationDecl const *D, + llvm::BitVector defaults) { + m_template_decl_defaults_map[D] = std::move(defaults); + } + /// Sets the layout for the given RecordDecl. The layout will later be /// used by Clang's during code generation. Not calling this function for /// a RecordDecl will cause that Clang's codegen tries to layout the @@ -454,6 +465,10 @@ RecordDeclToLayoutMap; RecordDeclToLayoutMap m_record_decl_to_layout_map; + + llvm::DenseMap + m_template_decl_defaults_map; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -806,6 +806,20 @@ md->removeOriginsWithContext(src_ast); } +PrintingCallbacks::TriState ClangASTImporter::IsTemplateArgumentDefaulted( + clang::ClassTemplateSpecializationDecl const *D, size_t ArgIndex) const { + using TriState = PrintingCallbacks::TriState; + + auto it = m_template_decl_defaults_map.find(D); + if (it == m_template_decl_defaults_map.end()) + return TriState::kUnknown; + + auto const &bits = it->getSecond(); + assert(bits.size() > ArgIndex); + + return bits.test(ArgIndex) ? TriState::kYes : TriState::kNo; +} + ClangASTImporter::MapCompleter::~MapCompleter() = default; llvm::Expected diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1806,6 +1806,15 @@ m_ast.CreateClassTemplateSpecializationDecl( decl_ctx, GetOwningClangModule(die), class_template_decl, tag_decl_kind, template_param_infos); + + llvm::BitVector defaults(template_param_infos.Size(), false); + for (size_t i = 0; i < template_param_infos.Size(); ++i) + defaults[i] = template_param_infos.IsDefaultArg(i); + + if (!defaults.empty()) + GetClangASTImporter().SetTemplateArgumentDefaults( + class_specialization_decl, std::move(defaults)); + clang_type = m_ast.CreateClassTemplateSpecializationType( class_specialization_decl); clang_type_was_created = true; @@ -2020,6 +2029,7 @@ CompilerType clang_type; uint64_t uval64 = 0; bool uval64_valid = false; + bool is_default_template_arg = false; if (num_attributes > 0) { DWARFFormValue form_value; for (size_t i = 0; i < num_attributes; ++i) { @@ -2050,6 +2060,11 @@ uval64 = form_value.Unsigned(); } break; + case DW_AT_default_value: + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + is_default_template_arg = form_value.Boolean(); + } + break; default: break; } @@ -2075,16 +2090,18 @@ template_param_infos.InsertArg( name, clang::TemplateArgument(ast, llvm::APSInt(apint, !is_signed), - ClangUtil::GetQualType(clang_type))); + ClangUtil::GetQualType(clang_type)), + is_default_template_arg); } else { template_param_infos.InsertArg( - name, - clang::TemplateArgument(ClangUtil::GetQualType(clang_type))); + name, clang::TemplateArgument(ClangUtil::GetQualType(clang_type)), + is_default_template_arg); } } else { auto *tplt_type = m_ast.CreateTemplateTemplateParmDecl(template_name); template_param_infos.InsertArg( - name, clang::TemplateArgument(clang::TemplateName(tplt_type))); + name, clang::TemplateArgument(clang::TemplateName(tplt_type)), + is_default_template_arg); } } } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -21,6 +21,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTFwd.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" #include "clang/Basic/TargetInfo.h" @@ -332,6 +333,7 @@ public: struct ArgumentMetadata { char const *const name = nullptr; + bool const is_defaulted = false; }; TemplateParameterInfos() = default; @@ -371,9 +373,15 @@ return args.front(); } - void InsertArg(char const *name, clang::TemplateArgument arg) { + bool IsDefaultArg(size_t idx) const { + assert(args_metadata.size() > idx); + return args_metadata[idx].is_defaulted; + } + + void InsertArg(char const *name, clang::TemplateArgument arg, + bool is_defaulted = false) { args.emplace_back(std::move(arg)); - args_metadata.push_back({name}); + args_metadata.push_back({name, is_defaulted}); } // Parameter pack related @@ -530,6 +538,24 @@ void CompleteObjCInterfaceDecl(clang::ObjCInterfaceDecl *); + class PrintingCallbacks : public clang::PrintingCallbacks { + public: + PrintingCallbacks(TypeSystemClang const &ts) : m_ast(ts) {} + + virtual ~PrintingCallbacks() = default; + + TriState + IsTemplateArgumentDefaulted(clang::ClassTemplateSpecializationDecl const *D, + size_t ArgIndex) const override { + return m_ast.IsTemplateArgumentDefaulted(D, ArgIndex); + } + + private: + TypeSystemClang const &m_ast; + }; + + lldb_private::ClangASTImporter *GetClangASTImporter() const; + bool LayoutRecordType( const clang::RecordDecl *record_decl, uint64_t &size, uint64_t &alignment, llvm::DenseMap &field_offsets, @@ -538,6 +564,10 @@ llvm::DenseMap &vbase_offsets); + PrintingCallbacks::TriState + IsTemplateArgumentDefaulted(clang::ClassTemplateSpecializationDecl const *D, + size_t ArgIndex) const; + /// Creates a CompilerDecl from the given Decl with the current /// TypeSystemClang instance as its typesystem. /// The Decl has to come from the ASTContext of this @@ -1192,6 +1222,8 @@ /// ASTContext wasn't created by parsing source code. clang::Sema *m_sema = nullptr; + PrintingCallbacks m_printing_callbacks = {*this}; + // For TypeSystemClang only TypeSystemClang(const TypeSystemClang &); const TypeSystemClang &operator=(const TypeSystemClang &); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -1392,14 +1392,43 @@ TemplateArgument const &targ = args[i]; if (IsValueParam(targ)) { QualType template_param_type = targ.getIntegralType(); - template_param_decls.push_back(NonTypeTemplateParmDecl::Create( + auto *decl = NonTypeTemplateParmDecl::Create( ast, decl_context, SourceLocation(), SourceLocation(), depth, i, identifier_info, template_param_type, parameter_pack, - ast.getTrivialTypeSourceInfo(template_param_type))); + ast.getTrivialTypeSourceInfo(template_param_type)); + + if (template_param_infos.IsDefaultArg(i)) { + if (targ.getKind() == TemplateArgument::Integral) { + Expr *LiteralExpr = nullptr; + if (template_param_type->isBooleanType()) { + LiteralExpr = CXXBoolLiteralExpr::Create( + ast, !targ.getAsIntegral().isZero(), template_param_type, {}); + } else { + LiteralExpr = IntegerLiteral::Create(ast, targ.getAsIntegral(), + template_param_type, {}); + } + + decl->setDefaultArgument(LiteralExpr); + } + } + + template_param_decls.push_back(decl); } else { - template_param_decls.push_back(TemplateTypeParmDecl::Create( + auto *decl = TemplateTypeParmDecl::Create( ast, decl_context, SourceLocation(), SourceLocation(), depth, i, - identifier_info, is_typename, parameter_pack)); + identifier_info, is_typename, parameter_pack); + + if (template_param_infos.IsDefaultArg(i)) { + if (targ.getKind() == TemplateArgument::Type) { + decl->setDefaultArgument( + ast.getTrivialTypeSourceInfo(targ.getAsType())); + } else if (targ.getKind() == TemplateArgument::Template) { + decl->setDefaultArgument(ast.getTemplateSpecializationTypeInfo( + targ.getAsTemplate(), {}, {})); + } + } + + template_param_decls.push_back(decl); } } @@ -3822,6 +3851,18 @@ return ConstString(qual_type.getAsString(GetTypePrintingPolicy())); } +lldb_private::ClangASTImporter *TypeSystemClang::GetClangASTImporter() const { + lldb_private::ClangASTImporter *importer = nullptr; + if (m_dwarf_ast_parser_up) + importer = &m_dwarf_ast_parser_up->GetClangASTImporter(); + if (!importer && m_pdb_ast_parser_up) + importer = &m_pdb_ast_parser_up->GetClangASTImporter(); + if (!importer && m_native_pdb_ast_parser_up) + importer = &m_native_pdb_ast_parser_up->GetClangASTImporter(); + + return importer; +} + ConstString TypeSystemClang::GetDisplayTypeName(lldb::opaque_compiler_type_t type) { if (!type) @@ -3833,6 +3874,7 @@ printing_policy.SuppressScope = false; printing_policy.SuppressUnwrittenScope = true; printing_policy.SuppressInlineNamespace = true; + printing_policy.Callbacks = &m_printing_callbacks; return ConstString(qual_type.getAsString(printing_policy)); } @@ -9423,13 +9465,8 @@ &base_offsets, llvm::DenseMap &vbase_offsets) { - lldb_private::ClangASTImporter *importer = nullptr; - if (m_dwarf_ast_parser_up) - importer = &m_dwarf_ast_parser_up->GetClangASTImporter(); - if (!importer && m_pdb_ast_parser_up) - importer = &m_pdb_ast_parser_up->GetClangASTImporter(); - if (!importer && m_native_pdb_ast_parser_up) - importer = &m_native_pdb_ast_parser_up->GetClangASTImporter(); + + auto *importer = GetClangASTImporter(); if (!importer) return false; @@ -9437,6 +9474,16 @@ field_offsets, base_offsets, vbase_offsets); } +PrintingCallbacks::TriState TypeSystemClang::IsTemplateArgumentDefaulted( + clang::ClassTemplateSpecializationDecl const *D, size_t ArgIndex) const { + lldb_private::ClangASTImporter *importer = GetClangASTImporter(); + + if (!importer) + return PrintingCallbacks::TriState::kUnknown; + + return importer->IsTemplateArgumentDefaulted(D, ArgIndex); +} + // CompilerDecl override functions ConstString TypeSystemClang::DeclGetName(void *opaque_decl) { diff --git a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py --- a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py +++ b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py @@ -31,22 +31,16 @@ ValueCheck(value="2"), ] - # The debug info vector type doesn't know about the default template - # arguments, so they will be printed. - debug_info_vector_type = "std::vector >" - - # The C++ module vector knows its default template arguments and will - # suppress them. - module_vector_type = "std::vector" + vector_type = "std::vector" # First muddy the scratch AST with non-C++ module types. - self.expect_expr("a", result_type=debug_info_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) self.expect_expr("dbg_info_vec", result_type="std::vector >", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=debug_info_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) @@ -54,7 +48,7 @@ # Enable the C++ module import and get the module vector type. self.runCmd("settings set target.import-std-module true") - self.expect_expr("a", result_type=module_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) # Test mixed debug info/module types @@ -62,7 +56,7 @@ result_type="std::vector", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=module_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) @@ -71,7 +65,7 @@ # Turn off the C++ module import and use debug info types again. self.runCmd("settings set target.import-std-module false") - self.expect_expr("a", result_type=debug_info_vector_type, + self.expect_expr("a", result_type=vector_type, result_children=children) # Test the types that were previoiusly mixed debug info/module types. @@ -79,7 +73,7 @@ result_type="std::vector >", result_children=[ ValueCheck(type="DbgInfoClass", children=[ - ValueCheck(name="ints", type=debug_info_vector_type, children=[ + ValueCheck(name="ints", type=vector_type, children=[ ValueCheck(value="1") ]) ]) diff --git a/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py b/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py --- a/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py +++ b/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py @@ -15,7 +15,7 @@ lldb.SBFileSpec("main.cpp")) # Test printing the vector before enabling any C++ module setting. - self.expect_expr("a", result_type="std::vector >") + self.expect_expr("a", result_type="std::vector") # Set loading the import-std-module to 'fallback' which loads the module # and retries when an expression fails to parse. @@ -25,7 +25,7 @@ # as before as this shouldn't use a C++ module type (the C++ module type # is hiding the second template parameter as it's equal to the default # argument which the C++ module has type info for). - self.expect_expr("a", result_type="std::vector >") + self.expect_expr("a", result_type="std::vector") # This expression can only parse with a C++ module. LLDB should # automatically fall back to import the C++ module to get this working. diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py @@ -60,7 +60,7 @@ valobj = self.expect_var_path( "sp_str", - type="std::shared_ptr, std::allocator > >", + type="std::shared_ptr >", children=[ValueCheck(name="__ptr_", summary='"hello"')], ) self.assertRegex(valobj.summary, r'^"hello"( strong=1)? weak=1$') diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string/TestDataFormatterLibcxxString.py @@ -70,8 +70,7 @@ '(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns, # FIXME: This should have a 'U' prefix. '(%s::u32string) u32_empty = ""'%ns, - '(%s::basic_string, ' - '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::basic_string) uchar = "aaaaa"'%(ns), '(%s::string *) null_str = nullptr'%ns, ]) @@ -108,8 +107,7 @@ '(%s::u16string) u16_string = u"ß水氶"'%ns, '(%s::u32string) u32_string = U"🍄🍅🍆🍌"'%ns, '(%s::u32string) u32_empty = ""'%ns, - '(%s::basic_string, ' - '%s::allocator >) uchar = "aaaaa"'%(ns,ns,ns), + '(%s::basic_string) uchar = "aaaaa"'%(ns), '(%s::string *) null_str = nullptr'%ns, ]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/string_view/TestDataFormatterLibcxxStringView.py @@ -96,10 +96,10 @@ type='std::u32string_view', summary='""') self.expect_var_path('uchar_source', - type='std::basic_string, std::allocator >', + type='std::basic_string', summary='"aaaaaaaaaa"') self.expect_var_path('uchar', - type='std::basic_string_view >', + type='std::basic_string_view', summary='"aaaaa"') self.expect_var_path('oops', type='std::string_view', @@ -172,10 +172,10 @@ type='std::u32string_view', summary='""') self.expect_var_path('uchar_source', - type='std::basic_string, std::allocator >', + type='std::basic_string', summary='"aaaaaaaaaa"') self.expect_var_path('uchar', - type='std::basic_string_view >', + type='std::basic_string_view', summary='"aaaaa"') self.runCmd('cont') diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py @@ -22,7 +22,7 @@ valobj = self.expect_var_path( "up_empty", - type="std::unique_ptr >", + type="std::unique_ptr", summary="nullptr", children=[ValueCheck(name="__value_")], ) @@ -36,7 +36,7 @@ valobj = self.expect_var_path( "up_int", - type="std::unique_ptr >", + type="std::unique_ptr", summary="10", children=[ValueCheck(name="__value_")], ) @@ -44,7 +44,7 @@ valobj = self.expect_var_path( "up_int_ref", - type="std::unique_ptr > &", + type="std::unique_ptr &", summary="10", children=[ValueCheck(name="__value_")], ) @@ -52,7 +52,7 @@ valobj = self.expect_var_path( "up_int_ref_ref", - type="std::unique_ptr > &&", + type="std::unique_ptr &&", summary="10", children=[ValueCheck(name="__value_")], ) @@ -60,13 +60,13 @@ valobj = self.expect_var_path( "up_str", - type="std::unique_ptr, std::allocator >, std::default_delete, std::allocator > > >", + type="std::unique_ptr >", summary='"hello"', children=[ValueCheck(name="__value_", summary='"hello"')], ) valobj = self.expect_var_path( - "up_user", type="std::unique_ptr >" + "up_user", type="std::unique_ptr" ) self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$") self.assertNotEqual(valobj.child[0].unsigned, 0) diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp --- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp @@ -6,11 +6,14 @@ // //===----------------------------------------------------------------------===// +#include "Plugins/Platform/MacOSX/PlatformMacOSX.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" #include "Plugins/SymbolFile/DWARF/DWARFCompileUnit.h" #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" #include "TestingSupport/Symbol/ClangTestUtils.h" #include "TestingSupport/Symbol/YAMLModuleTester.h" +#include "lldb/Core/Debugger.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -19,7 +22,23 @@ using namespace lldb_private::dwarf; namespace { -class DWARFASTParserClangTests : public testing::Test {}; +static std::once_flag debugger_initialize_flag; + +class DWARFASTParserClangTests : public testing::Test { + void SetUp() override { + HostInfo::Initialize(); + PlatformMacOSX::Initialize(); + std::call_once(debugger_initialize_flag, + []() { Debugger::Initialize(nullptr); }); + ArchSpec arch("x86_64-apple-macosx-"); + Platform::SetHostPlatform( + PlatformRemoteMacOSX::CreateInstance(true, &arch)); + } + void TearDown() override { + PlatformMacOSX::Terminate(); + HostInfo::Terminate(); + } +}; class DWARFASTParserClangStub : public DWARFASTParserClang { public: @@ -404,3 +423,380 @@ EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2), llvm::Failed()); } + +TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) { + // Tests parsing DW_AT_default_value for template parameters. + + // template + // class foo {}; + // + // template