Skip to content

Commit e7f7ed2

Browse files
committedFeb 22, 2017
Add more ODR checking.
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
1 parent e67e162 commit e7f7ed2

File tree

7 files changed

+800
-13
lines changed

7 files changed

+800
-13
lines changed
 

‎clang/include/clang/AST/ODRHash.h

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//===-- ODRHash.h - Hashing to diagnose ODR failures ------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
///
10+
/// \file
11+
/// This file contains the declaration of the ODRHash class, which calculates
12+
/// a hash based on AST nodes, which is stable across different runs.
13+
///
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "clang/AST/DeclarationName.h"
17+
#include "clang/AST/Type.h"
18+
#include "clang/AST/TemplateBase.h"
19+
#include "llvm/ADT/DenseMap.h"
20+
#include "llvm/ADT/FoldingSet.h"
21+
#include "llvm/ADT/PointerUnion.h"
22+
#include "llvm/ADT/SmallVector.h"
23+
24+
namespace clang {
25+
26+
class Decl;
27+
class IdentifierInfo;
28+
class NestedNameSpecifer;
29+
class Stmt;
30+
class TemplateParameterList;
31+
32+
// ODRHash is used to calculate a hash based on AST node contents that
33+
// does not rely on pointer addresses. This allows the hash to not vary
34+
// between runs and is usable to detect ODR problems in modules. To use,
35+
// construct an ODRHash object, then call Add* methods over the nodes that
36+
// need to be hashed. Then call CalculateHash to get the hash value.
37+
// Typically, only one Add* call is needed. clear can be called to reuse the
38+
// object.
39+
class ODRHash {
40+
// Use DenseMaps to convert between Decl and Type pointers and an index value.
41+
llvm::DenseMap<const Decl*, unsigned> DeclMap;
42+
llvm::DenseMap<const Type*, unsigned> TypeMap;
43+
44+
// Save space by processing bools at the end.
45+
llvm::SmallVector<bool, 128> Bools;
46+
47+
llvm::FoldingSetNodeID ID;
48+
49+
public:
50+
ODRHash() {}
51+
52+
// Use this for ODR checking classes between modules. This method compares
53+
// more information than the AddDecl class.
54+
void AddCXXRecordDecl(const CXXRecordDecl *Record);
55+
56+
// Process SubDecls of the main Decl. This method calls the DeclVisitor
57+
// while AddDecl does not.
58+
void AddSubDecl(const Decl *D);
59+
60+
// Reset the object for reuse.
61+
void clear();
62+
63+
// Add booleans to ID and uses it to calculate the hash.
64+
unsigned CalculateHash();
65+
66+
// Add AST nodes that need to be processed.
67+
void AddDecl(const Decl *D);
68+
void AddType(const Type *T);
69+
void AddQualType(QualType T);
70+
void AddStmt(const Stmt *S);
71+
void AddIdentifierInfo(const IdentifierInfo *II);
72+
void AddNestedNameSpecifier(const NestedNameSpecifier *NNS);
73+
void AddTemplateName(TemplateName Name);
74+
void AddDeclarationName(DeclarationName Name);
75+
void AddTemplateArgument(TemplateArgument TA);
76+
void AddTemplateParameterList(const TemplateParameterList *TPL);
77+
78+
// Save booleans until the end to lower the size of data to process.
79+
void AddBoolean(bool value);
80+
81+
static bool isWhitelistedDecl(const Decl* D, const CXXRecordDecl *Record);
82+
};
83+
84+
} // end namespace clang

‎clang/include/clang/Basic/DiagnosticSerializationKinds.td

+9
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ def note_module_odr_violation_different_definitions : Note<
117117
def err_module_odr_violation_different_instantiations : Error<
118118
"instantiation of %q0 is different in different modules">;
119119

120+
def err_module_odr_violation_mismatch_decl : Error<
121+
"%q0 has different definitions in different modules; first difference is "
122+
"%select{definition in module '%2'|defined here}1 found "
123+
"%select{end of class|public access specifier|private access specifier|"
124+
"protected access specifier}3">;
125+
def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
126+
"%select{end of class|public access specifier|private access specifier|"
127+
"protected access specifier}1">;
128+
120129
def warn_module_uses_date_time : Warning<
121130
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
122131
InGroup<DiagGroup<"pch-date-time">>;

‎clang/lib/AST/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_clang_library(clangAST
4040
MicrosoftMangle.cpp
4141
NestedNameSpecifier.cpp
4242
NSAPI.cpp
43+
ODRHash.cpp
4344
OpenMPClause.cpp
4445
ParentMap.cpp
4546
RawCommentList.cpp

‎clang/lib/AST/DeclCXX.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/DeclTemplate.h"
1919
#include "clang/AST/Expr.h"
2020
#include "clang/AST/ExprCXX.h"
21+
#include "clang/AST/ODRHash.h"
2122
#include "clang/AST/TypeLoc.h"
2223
#include "clang/Basic/IdentifierTable.h"
2324
#include "llvm/ADT/STLExtras.h"
@@ -371,7 +372,15 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
371372
data().IsParsingBaseSpecifiers = false;
372373
}
373374

374-
void CXXRecordDecl::computeODRHash() {}
375+
void CXXRecordDecl::computeODRHash() {
376+
if (!DefinitionData)
377+
return;
378+
379+
ODRHash Hash;
380+
Hash.AddCXXRecordDecl(this);
381+
382+
DefinitionData->ODRHash = Hash.CalculateHash();
383+
}
375384

376385
void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
377386
// C++11 [class.copy]p11:

‎clang/lib/AST/ODRHash.cpp

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
///
10+
/// \file
11+
/// This file implements the ODRHash class, which calculates a hash based
12+
/// on AST nodes, which is stable across different runs.
13+
///
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "clang/AST/ODRHash.h"
17+
18+
#include "clang/AST/DeclVisitor.h"
19+
#include "clang/AST/NestedNameSpecifier.h"
20+
#include "clang/AST/StmtVisitor.h"
21+
#include "clang/AST/TypeVisitor.h"
22+
23+
using namespace clang;
24+
25+
void ODRHash::AddStmt(const Stmt *S) {}
26+
void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {}
27+
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
28+
void ODRHash::AddTemplateName(TemplateName Name) {}
29+
void ODRHash::AddDeclarationName(DeclarationName Name) {}
30+
void ODRHash::AddTemplateArgument(TemplateArgument TA) {}
31+
void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {}
32+
33+
void ODRHash::clear() {
34+
DeclMap.clear();
35+
TypeMap.clear();
36+
Bools.clear();
37+
ID.clear();
38+
}
39+
40+
unsigned ODRHash::CalculateHash() {
41+
// Append the bools to the end of the data segment backwards. This allows
42+
// for the bools data to be compressed 32 times smaller compared to using
43+
// ID.AddBoolean
44+
const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT;
45+
const unsigned size = Bools.size();
46+
const unsigned remainder = size % unsigned_bits;
47+
const unsigned loops = size / unsigned_bits;
48+
auto I = Bools.rbegin();
49+
unsigned value = 0;
50+
for (unsigned i = 0; i < remainder; ++i) {
51+
value <<= 1;
52+
value |= *I;
53+
++I;
54+
}
55+
ID.AddInteger(value);
56+
57+
for (unsigned i = 0; i < loops; ++i) {
58+
value = 0;
59+
for (unsigned j = 0; j < unsigned_bits; ++j) {
60+
value <<= 1;
61+
value |= *I;
62+
++I;
63+
}
64+
ID.AddInteger(value);
65+
}
66+
67+
assert(I == Bools.rend());
68+
Bools.clear();
69+
return ID.ComputeHash();
70+
}
71+
72+
// Process a Decl pointer. Add* methods call back into ODRHash while Visit*
73+
// methods process the relevant parts of the Decl.
74+
class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
75+
typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
76+
llvm::FoldingSetNodeID &ID;
77+
ODRHash &Hash;
78+
79+
public:
80+
ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
81+
: ID(ID), Hash(Hash) {}
82+
83+
void Visit(const Decl *D) {
84+
ID.AddInteger(D->getKind());
85+
Inherited::Visit(D);
86+
}
87+
88+
void VisitAccessSpecDecl(const AccessSpecDecl *D) {
89+
ID.AddInteger(D->getAccess());
90+
Inherited::VisitAccessSpecDecl(D);
91+
}
92+
};
93+
94+
// Only allow a small portion of Decl's to be processed. Remove this once
95+
// all Decl's can be handled.
96+
bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) {
97+
if (D->isImplicit()) return false;
98+
if (D->getDeclContext() != Parent) return false;
99+
100+
switch (D->getKind()) {
101+
default:
102+
return false;
103+
case Decl::AccessSpec:
104+
return true;
105+
}
106+
}
107+
108+
void ODRHash::AddSubDecl(const Decl *D) {
109+
assert(D && "Expecting non-null pointer.");
110+
AddDecl(D);
111+
112+
ODRDeclVisitor(ID, *this).Visit(D);
113+
}
114+
115+
void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
116+
assert(Record && Record->hasDefinition() &&
117+
"Expected non-null record to be a definition.");
118+
AddDecl(Record);
119+
120+
// Filter out sub-Decls which will not be processed in order to get an
121+
// accurate count of Decl's.
122+
llvm::SmallVector<const Decl *, 16> Decls;
123+
for (const Decl *SubDecl : Record->decls()) {
124+
if (isWhitelistedDecl(SubDecl, Record)) {
125+
Decls.push_back(SubDecl);
126+
}
127+
}
128+
129+
ID.AddInteger(Decls.size());
130+
for (auto SubDecl : Decls) {
131+
AddSubDecl(SubDecl);
132+
}
133+
}
134+
135+
void ODRHash::AddDecl(const Decl *D) {
136+
assert(D && "Expecting non-null pointer.");
137+
auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size()));
138+
ID.AddInteger(Result.first->second);
139+
// On first encounter of a Decl pointer, process it. Every time afterwards,
140+
// only the index value is needed.
141+
if (!Result.second) {
142+
return;
143+
}
144+
145+
ID.AddInteger(D->getKind());
146+
}
147+
148+
void ODRHash::AddType(const Type *T) {}
149+
void ODRHash::AddQualType(QualType T) {}
150+
void ODRHash::AddBoolean(bool Value) {
151+
Bools.push_back(Value);
152+
}

0 commit comments

Comments
 (0)
Please sign in to comment.