diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -681,7 +681,7 @@ // Visit all members of this class, then perform any finalization necessary // to complete the class. CompilerType ct = ToCompilerType(tag_qt); - UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index.tpi()); + UdtRecordCompleter completer(best_ti, ct, tag, *this, m_index); auto error = llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer); completer.complete(); diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -25,6 +25,7 @@ namespace llvm { namespace pdb { class TpiStream; +class GlobalsStream; } } // namespace llvm @@ -33,6 +34,7 @@ class CompilerType; namespace npdb { class PdbAstBuilder; +class PdbIndex; class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks { using IndexedBase = @@ -49,14 +51,14 @@ CompilerType &m_derived_ct; clang::TagDecl &m_tag_decl; PdbAstBuilder &m_ast_builder; - llvm::pdb::TpiStream &m_tpi; + PdbIndex &m_index; std::vector m_bases; ClangASTImporter::LayoutInfo m_layout; public: UdtRecordCompleter(PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder, - llvm::pdb::TpiStream &tpi); + PdbIndex &index); #define MEMBER_RECORD(EnumName, EnumVal, Name) \ llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \ diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -6,14 +6,17 @@ #include "PdbUtil.h" #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Symbol/Type.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" @@ -29,10 +32,10 @@ CompilerType &derived_ct, clang::TagDecl &tag_decl, PdbAstBuilder &ast_builder, - TpiStream &tpi) + PdbIndex &index) : m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl), - m_ast_builder(ast_builder), m_tpi(tpi) { - CVType cvt = m_tpi.getType(m_id.index); + m_ast_builder(ast_builder), m_index(index) { + CVType cvt = m_index.tpi().getType(m_id.index); switch (cvt.kind()) { case LF_ENUM: llvm::cantFail(TypeDeserializer::deserializeAs(cvt, m_cvr.er)); @@ -55,7 +58,7 @@ PdbTypeSymId type_id(ti); clang::QualType qt = m_ast_builder.GetOrCreateType(type_id); - CVType udt_cvt = m_tpi.getType(ti); + CVType udt_cvt = m_index.tpi().getType(ti); std::unique_ptr base_spec = m_ast_builder.clang().CreateBaseClassSpecifier( @@ -128,9 +131,70 @@ lldb::AccessType access = TranslateMemberAccess(static_data_member.getAccess()); - TypeSystemClang::AddVariableToRecordType( + auto decl = TypeSystemClang::AddVariableToRecordType( m_derived_ct, static_data_member.Name, member_ct, access); + // Static constant members may be a const[expr] declaration. + // Query the symbol's value as the variable initializer if valid. + if (member_ct.IsConst()) { + std::string qual_name = decl->getQualifiedNameAsString(); + + auto results = + m_index.globals().findRecordsByName(qual_name, m_index.symrecords()); + + for (const auto &result : results) { + if (result.second.kind() == SymbolKind::S_CONSTANT) { + ConstantSym constant(SymbolRecordKind::ConstantSym); + cantFail(SymbolDeserializer::deserializeAs(result.second, + constant)); + + clang::QualType qual_type = decl->getType(); + unsigned type_width = decl->getASTContext().getIntWidth(qual_type); + unsigned constant_width = constant.Value.getBitWidth(); + + if (qual_type->isIntegralOrEnumerationType()) { + if (type_width >= constant_width) { + TypeSystemClang::SetIntegerInitializerForVariable( + decl, constant.Value.extOrTrunc(type_width)); + } else { + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a wider constant value ({4} bits). " + "Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + } else { + lldb::BasicType basic_type_enum = member_ct.GetBasicTypeEnumeration(); + switch (basic_type_enum) { + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + if (type_width == constant_width) { + TypeSystemClang::SetFloatingInitializerForVariable( + decl, basic_type_enum == lldb::eBasicTypeFloat + ? llvm::APFloat(constant.Value.bitsToFloat()) + : llvm::APFloat(constant.Value.bitsToDouble())); + decl->setConstexpr(true); + } else { + LLDB_LOG( + GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a constant value of mismatched width " + "({4} bits). Ignoring constant.", + m_derived_ct.GetTypeName(), static_data_member.Name, + member_ct.GetTypeName(), type_width, constant_width); + } + break; + default: + break; + } + } + break; + } + } + } + // FIXME: Add a PdbSymUid namespace for field list members and update // the m_uid_to_decl map with this decl. return Error::success(); @@ -149,7 +213,7 @@ TypeIndex ti(data_member.Type); if (!ti.isSimple()) { - CVType cvt = m_tpi.getType(ti); + CVType cvt = m_index.tpi().getType(ti); if (cvt.kind() == LF_BITFIELD) { BitFieldRecord bfr; llvm::cantFail(TypeDeserializer::deserializeAs(cvt, bfr)); @@ -187,7 +251,7 @@ OverloadedMethodRecord &overloaded) { TypeIndex method_list_idx = overloaded.MethodList; - CVType method_list_type = m_tpi.getType(method_list_idx); + CVType method_list_type = m_index.tpi().getType(method_list_idx); assert(method_list_type.kind() == LF_METHODLIST); MethodOverloadListRecord method_list; diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp --- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -1265,6 +1265,52 @@ if (!decl) continue; + // Static constant members may be a const[expr] declaration. + // Query the symbol's value as the variable initializer if valid. + if (member_comp_type.IsConst()) { + auto value = member->getValue(); + clang::QualType qual_type = decl->getType(); + unsigned type_width = m_ast.getASTContext().getIntWidth(qual_type); + unsigned constant_width = value.getBitWidth(); + + if (qual_type->isIntegralOrEnumerationType()) { + if (type_width >= constant_width) { + TypeSystemClang::SetIntegerInitializerForVariable( + decl, value.toAPSInt().extOrTrunc(type_width)); + } else { + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} bits) " + "which resolves to a wider constant value ({4} bits). " + "Ignoring constant.", + record_type.GetTypeName(), member_name, + member_comp_type.GetTypeName(), type_width, + constant_width); + } + } else { + switch (member_comp_type.GetBasicTypeEnumeration()) { + case lldb::eBasicTypeFloat: + case lldb::eBasicTypeDouble: + case lldb::eBasicTypeLongDouble: + if (type_width == constant_width) { + TypeSystemClang::SetFloatingInitializerForVariable( + decl, value.toAPFloat()); + decl->setConstexpr(true); + } else { + LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_AST), + "Class '{0}' has a member '{1}' of type '{2}' ({3} " + "bits) which resolves to a constant value of mismatched " + "width ({4} bits). Ignoring constant.", + record_type.GetTypeName(), member_name, + member_comp_type.GetTypeName(), type_width, + constant_width); + } + break; + default: + break; + } + } + } + m_uid_to_decl[member->getSymIndexId()] = decl; break; 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 @@ -873,6 +873,24 @@ const CompilerType &var_type, lldb::AccessType access); + /// Initializes a variable with an integer value. + /// \param var The variable to initialize. Must not already have an + /// initializer and must have an integer or enum type. + /// \param init_value The integer value that the variable should be + /// initialized to. Has to match the bit width of the + /// variable type. + static void SetIntegerInitializerForVariable(clang::VarDecl *var, + const llvm::APInt &init_value); + + /// Initializes a variable with a floating point value. + /// \param var The variable to initialize. Must not already have an + /// initializer and must have a floating point type. + /// \param init_value The float value that the variable should be + /// initialized to. + static void + SetFloatingInitializerForVariable(clang::VarDecl *var, + const llvm::APFloat &init_value); + clang::CXXMethodDecl *AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, const char *mangled_name, const CompilerType &method_type, 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 @@ -7316,6 +7316,35 @@ return var_decl; } +void TypeSystemClang::SetIntegerInitializerForVariable( + VarDecl *var, const llvm::APInt &init_value) { + assert(!var->hasInit() && "variable already initialized"); + + clang::ASTContext &ast = var->getASTContext(); + QualType qt = var->getType(); + assert(qt->isIntegralOrEnumerationType() && + "only integer or enum types supported"); + // If the variable is an enum type, take the underlying integer type as + // the type of the integer literal. + if (const EnumType *enum_type = llvm::dyn_cast(qt.getTypePtr())) { + const EnumDecl *enum_decl = enum_type->getDecl(); + qt = enum_decl->getIntegerType(); + } + var->setInit(IntegerLiteral::Create(ast, init_value, qt.getUnqualifiedType(), + SourceLocation())); +} + +void TypeSystemClang::SetFloatingInitializerForVariable( + clang::VarDecl *var, const llvm::APFloat &init_value) { + assert(!var->hasInit() && "variable already initialized"); + + clang::ASTContext &ast = var->getASTContext(); + QualType qt = var->getType(); + assert(qt->isFloatingType() && "only floating point types supported"); + var->setInit(FloatingLiteral::Create( + ast, init_value, true, qt.getUnqualifiedType(), SourceLocation())); +} + clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, const char *mangled_name, const CompilerType &method_clang_type, diff --git a/lldb/test/Shell/SymbolFile/PDB/Inputs/AstRestoreTest.cpp b/lldb/test/Shell/SymbolFile/PDB/Inputs/AstRestoreTest.cpp --- a/lldb/test/Shell/SymbolFile/PDB/Inputs/AstRestoreTest.cpp +++ b/lldb/test/Shell/SymbolFile/PDB/Inputs/AstRestoreTest.cpp @@ -3,6 +3,7 @@ namespace { enum Enum { Enum_0 = 1, Enum_1 = 2, Enum_2 = 4, Enum_3 = 8 }; +enum class ScopedEnum { Enum_0 = 1, Enum_1 = 2, Enum_2 = 4, Enum_3 = 8 }; } Enum Global = Enum_3; @@ -22,6 +23,13 @@ const Enum m_ce; static int ClassStatic; + static const int ClassStaticConst = 8; + static constexpr int ClassStaticConstexpr = 9; + static constexpr float ClassStaticConstexprFloat = 10.f; + static constexpr double ClassStaticConstexprDouble = 11.0; + static constexpr long double ClassStaticConstexprLongDouble = 12.0; + static const Enum ClassStaticConstEnum = Enum_3; + static const ScopedEnum ClassStaticConstScopedEnum = ScopedEnum::Enum_2; private: struct Inner { diff --git a/lldb/test/Shell/SymbolFile/PDB/ast-restore.test b/lldb/test/Shell/SymbolFile/PDB/ast-restore.test --- a/lldb/test/Shell/SymbolFile/PDB/ast-restore.test +++ b/lldb/test/Shell/SymbolFile/PDB/ast-restore.test @@ -4,7 +4,8 @@ RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=ENUM %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=GLOBAL %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=BASE %s -RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s +RUN: env LLDB_USE_NATIVE_PDB_READER=0 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s +RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=INNER %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=TEMPLATE %s RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=FOO %s @@ -46,6 +47,13 @@ CLASS: class Class : public N0::N1::Base { CLASS-DAG: const N0::N1::(anonymous namespace)::Enum m_ce; CLASS-DAG: static int ClassStatic; +CLASS-DAG: static const int ClassStaticConst = 8; +CLASS-DAG: static const int ClassStaticConstexpr = 9; +CLASS-DAG: static constexpr float ClassStaticConstexprFloat = 10.F; +CLASS-DAG: static constexpr double ClassStaticConstexprDouble = 11.; +CLASS-DAG: static constexpr double ClassStaticConstexprLongDouble = 12.; +CLASS-DAG: static const N0::N1::(anonymous namespace)::Enum ClassStaticConstEnum = 8; +CLASS-DAG: static const N0::N1::(anonymous namespace)::ScopedEnum ClassStaticConstScopedEnum = 4; CLASS-DAG: N0::N1::Class::Inner m_inner; CLASS-DAG: {{(inline )?}}Class(N0::N1::(anonymous namespace)::Enum); CLASS-DAG: static {{(inline )?}}int StaticFunc(const N0::N1::Class &); diff --git a/llvm/include/llvm/DebugInfo/PDB/PDBTypes.h b/llvm/include/llvm/DebugInfo/PDB/PDBTypes.h --- a/llvm/include/llvm/DebugInfo/PDB/PDBTypes.h +++ b/llvm/include/llvm/DebugInfo/PDB/PDBTypes.h @@ -9,6 +9,7 @@ #ifndef LLVM_DEBUGINFO_PDB_PDBTYPES_H #define LLVM_DEBUGINFO_PDB_PDBTYPES_H +#include "llvm/ADT/APFloat.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBFrameData.h" @@ -464,6 +465,88 @@ char *String; } Value; + bool isIntegralType() const { + switch (Type) { + case Bool: + case Int8: + case Int16: + case Int32: + case Int64: + case UInt8: + case UInt16: + case UInt32: + case UInt64: + return true; + default: + return false; + } + } + +#define VARIANT_WIDTH(Enum, NumBits) \ + case PDB_VariantType::Enum: \ + return NumBits; + + unsigned getBitWidth() const { + switch (Type) { + VARIANT_WIDTH(Bool, 1u) + VARIANT_WIDTH(Int8, 8u) + VARIANT_WIDTH(Int16, 16u) + VARIANT_WIDTH(Int32, 32u) + VARIANT_WIDTH(Int64, 64u) + VARIANT_WIDTH(Single, 32u) + VARIANT_WIDTH(Double, 64u) + VARIANT_WIDTH(UInt8, 8u) + VARIANT_WIDTH(UInt16, 16u) + VARIANT_WIDTH(UInt32, 32u) + VARIANT_WIDTH(UInt64, 64u) + default: + assert(false && "Variant::toAPSInt called on non-numeric type"); + return 0u; + } + } + +#undef VARIANT_WIDTH + +#define VARIANT_APSINT(Enum, NumBits, IsUnsigned) \ + case PDB_VariantType::Enum: \ + return APSInt(APInt(NumBits, Value.Enum), IsUnsigned); + + APSInt toAPSInt() const { + switch (Type) { + VARIANT_APSINT(Bool, 1u, true) + VARIANT_APSINT(Int8, 8u, false) + VARIANT_APSINT(Int16, 16u, false) + VARIANT_APSINT(Int32, 32u, false) + VARIANT_APSINT(Int64, 64u, false) + VARIANT_APSINT(UInt8, 8u, true) + VARIANT_APSINT(UInt16, 16u, true) + VARIANT_APSINT(UInt32, 32u, true) + VARIANT_APSINT(UInt64, 64u, true) + default: + assert(false && "Variant::toAPSInt called on non-integral type"); + return APSInt(); + } + } + +#undef VARIANT_APSINT + + APFloat toAPFloat() const { + // Float constants may be tagged as integers. + switch (Type) { + case PDB_VariantType::Single: + case PDB_VariantType::UInt32: + case PDB_VariantType::Int32: + return APFloat(Value.Single); + case PDB_VariantType::Double: + case PDB_VariantType::UInt64: + case PDB_VariantType::Int64: + return APFloat(Value.Double); + default: + assert(false && "Variant::toAPFloat called on non-floating-point type"); + return APFloat::getZero(APFloat::IEEEsingle()); + } + } + #define VARIANT_EQUAL_CASE(Enum) \ case PDB_VariantType::Enum: \ return Value.Enum == Other.Value.Enum;