Skip to content

Commit

Permalink
Add more ODR checking.
Browse files Browse the repository at this point in the history
Add the basics for the ODRHash class, which will only process Decl's from
a whitelist, which currently only has AccessSpecDecl.  Different access
specifiers in merged classes can now be detected.

Differential Revision: https://reviews.llvm.org/D21675

llvm-svn: 295800
  • Loading branch information
Weverything committed Feb 22, 2017
1 parent e67e162 commit e7f7ed2
Show file tree
Hide file tree
Showing 7 changed files with 800 additions and 13 deletions.
84 changes: 84 additions & 0 deletions clang/include/clang/AST/ODRHash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the declaration of the ODRHash class, which calculates
/// a hash based on AST nodes, which is stable across different runs.
///
//===----------------------------------------------------------------------===//

#include "clang/AST/DeclarationName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TemplateBase.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"

namespace clang {

class Decl;
class IdentifierInfo;
class NestedNameSpecifer;
class Stmt;
class TemplateParameterList;

// ODRHash is used to calculate a hash based on AST node contents that
// does not rely on pointer addresses. This allows the hash to not vary
// between runs and is usable to detect ODR problems in modules. To use,
// construct an ODRHash object, then call Add* methods over the nodes that
// need to be hashed. Then call CalculateHash to get the hash value.
// Typically, only one Add* call is needed. clear can be called to reuse the
// object.
class ODRHash {
// Use DenseMaps to convert between Decl and Type pointers and an index value.
llvm::DenseMap<const Decl*, unsigned> DeclMap;
llvm::DenseMap<const Type*, unsigned> TypeMap;

// Save space by processing bools at the end.
llvm::SmallVector<bool, 128> Bools;

llvm::FoldingSetNodeID ID;

public:
ODRHash() {}

// Use this for ODR checking classes between modules. This method compares
// more information than the AddDecl class.
void AddCXXRecordDecl(const CXXRecordDecl *Record);

// Process SubDecls of the main Decl. This method calls the DeclVisitor
// while AddDecl does not.
void AddSubDecl(const Decl *D);

// Reset the object for reuse.
void clear();

// Add booleans to ID and uses it to calculate the hash.
unsigned CalculateHash();

// Add AST nodes that need to be processed.
void AddDecl(const Decl *D);
void AddType(const Type *T);
void AddQualType(QualType T);
void AddStmt(const Stmt *S);
void AddIdentifierInfo(const IdentifierInfo *II);
void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
void AddTemplateName(TemplateName Name);
void AddDeclarationName(DeclarationName Name);
void AddTemplateArgument(TemplateArgument TA);
void AddTemplateParameterList(const TemplateParameterList *TPL);

// Save booleans until the end to lower the size of data to process.
void AddBoolean(bool value);

static bool isWhitelistedDecl(const Decl* D, const CXXRecordDecl *Record);
};

} // end namespace clang
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticSerializationKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ def note_module_odr_violation_different_definitions : Note<
def err_module_odr_violation_different_instantiations : Error<
"instantiation of %q0 is different in different modules">;

def err_module_odr_violation_mismatch_decl : Error<
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "
"%select{end of class|public access specifier|private access specifier|"
"protected access specifier}3">;
def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
"%select{end of class|public access specifier|private access specifier|"
"protected access specifier}1">;

def warn_module_uses_date_time : Warning<
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
InGroup<DiagGroup<"pch-date-time">>;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_clang_library(clangAST
MicrosoftMangle.cpp
NestedNameSpecifier.cpp
NSAPI.cpp
ODRHash.cpp
OpenMPClause.cpp
ParentMap.cpp
RawCommentList.cpp
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/IdentifierTable.h"
#include "llvm/ADT/STLExtras.h"
Expand Down Expand Up @@ -371,7 +372,15 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
data().IsParsingBaseSpecifiers = false;
}

void CXXRecordDecl::computeODRHash() {}
void CXXRecordDecl::computeODRHash() {
if (!DefinitionData)
return;

ODRHash Hash;
Hash.AddCXXRecordDecl(this);

DefinitionData->ODRHash = Hash.CalculateHash();
}

void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
// C++11 [class.copy]p11:
Expand Down
152 changes: 152 additions & 0 deletions clang/lib/AST/ODRHash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements the ODRHash class, which calculates a hash based
/// on AST nodes, which is stable across different runs.
///
//===----------------------------------------------------------------------===//

#include "clang/AST/ODRHash.h"

#include "clang/AST/DeclVisitor.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeVisitor.h"

using namespace clang;

void ODRHash::AddStmt(const Stmt *S) {}
void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {}
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
void ODRHash::AddTemplateName(TemplateName Name) {}
void ODRHash::AddDeclarationName(DeclarationName Name) {}
void ODRHash::AddTemplateArgument(TemplateArgument TA) {}
void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {}

void ODRHash::clear() {
DeclMap.clear();
TypeMap.clear();
Bools.clear();
ID.clear();
}

unsigned ODRHash::CalculateHash() {
// Append the bools to the end of the data segment backwards. This allows
// for the bools data to be compressed 32 times smaller compared to using
// ID.AddBoolean
const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT;
const unsigned size = Bools.size();
const unsigned remainder = size % unsigned_bits;
const unsigned loops = size / unsigned_bits;
auto I = Bools.rbegin();
unsigned value = 0;
for (unsigned i = 0; i < remainder; ++i) {
value <<= 1;
value |= *I;
++I;
}
ID.AddInteger(value);

for (unsigned i = 0; i < loops; ++i) {
value = 0;
for (unsigned j = 0; j < unsigned_bits; ++j) {
value <<= 1;
value |= *I;
++I;
}
ID.AddInteger(value);
}

assert(I == Bools.rend());
Bools.clear();
return ID.ComputeHash();
}

// Process a Decl pointer. Add* methods call back into ODRHash while Visit*
// methods process the relevant parts of the Decl.
class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
llvm::FoldingSetNodeID &ID;
ODRHash &Hash;

public:
ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
: ID(ID), Hash(Hash) {}

void Visit(const Decl *D) {
ID.AddInteger(D->getKind());
Inherited::Visit(D);
}

void VisitAccessSpecDecl(const AccessSpecDecl *D) {
ID.AddInteger(D->getAccess());
Inherited::VisitAccessSpecDecl(D);
}
};

// Only allow a small portion of Decl's to be processed. Remove this once
// all Decl's can be handled.
bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) {
if (D->isImplicit()) return false;
if (D->getDeclContext() != Parent) return false;

switch (D->getKind()) {
default:
return false;
case Decl::AccessSpec:
return true;
}
}

void ODRHash::AddSubDecl(const Decl *D) {
assert(D && "Expecting non-null pointer.");
AddDecl(D);

ODRDeclVisitor(ID, *this).Visit(D);
}

void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
assert(Record && Record->hasDefinition() &&
"Expected non-null record to be a definition.");
AddDecl(Record);

// Filter out sub-Decls which will not be processed in order to get an
// accurate count of Decl's.
llvm::SmallVector<const Decl *, 16> Decls;
for (const Decl *SubDecl : Record->decls()) {
if (isWhitelistedDecl(SubDecl, Record)) {
Decls.push_back(SubDecl);
}
}

ID.AddInteger(Decls.size());
for (auto SubDecl : Decls) {
AddSubDecl(SubDecl);
}
}

void ODRHash::AddDecl(const Decl *D) {
assert(D && "Expecting non-null pointer.");
auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size()));
ID.AddInteger(Result.first->second);
// On first encounter of a Decl pointer, process it. Every time afterwards,
// only the index value is needed.
if (!Result.second) {
return;
}

ID.AddInteger(D->getKind());
}

void ODRHash::AddType(const Type *T) {}
void ODRHash::AddQualType(QualType T) {}
void ODRHash::AddBoolean(bool Value) {
Bools.push_back(Value);
}
Loading

0 comments on commit e7f7ed2

Please sign in to comment.