// 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();
#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"
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<EnumRecord>(cvt, m_cvr.er));
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<clang::CXXBaseSpecifier> base_spec =
m_ast_builder.clang().CreateBaseClassSpecifier(
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<ConstantSym>(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();
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<BitFieldRecord>(cvt, bfr));
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;
namespace llvm {
namespace pdb {
class TpiStream;
+class GlobalsStream;
}
} // namespace llvm
class CompilerType;
namespace npdb {
class PdbAstBuilder;
+class PdbIndex;
class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks {
using IndexedBase =
CompilerType &m_derived_ct;
clang::TagDecl &m_tag_decl;
PdbAstBuilder &m_ast_builder;
- llvm::pdb::TpiStream &m_tpi;
+ PdbIndex &m_index;
std::vector<IndexedBase> 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, \
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;
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<EnumType>(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,
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,
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;
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 {
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
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 &);
#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"
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;