[ODRHash] static_cast and Stmt hashing.

Add support for static_cast in classes.  Add pointer-independent profiling for
Stmt's, sharing most of the logic with Stmt::Profile.  This is the first of the
deep sub-Decl diffing for error messages.

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

llvm-svn: 295890
This commit is contained in:
Richard Trieu 2017-02-22 22:22:42 +00:00
parent fccbda967a
commit 639d7b68d6
6 changed files with 366 additions and 91 deletions

View File

@ -39,6 +39,7 @@ namespace clang {
class Expr;
class IdentifierInfo;
class LabelDecl;
class ODRHash;
class ParmVarDecl;
class PrinterHelper;
struct PrintingPolicy;
@ -436,6 +437,15 @@ public:
/// written in the source.
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool Canonical) const;
/// \brief Calculate a unique representation for a statement that is
/// stable across compiler invocations.
///
/// \param ID profile information will be stored in ID.
///
/// \param Hash an ODRHash object which will be called where pointers would
/// have been used in the Profile function.
void ProcessODRHash(llvm::FoldingSetNodeID &ID, ODRHash& Hash) const;
};
/// DeclStmt - Adaptor class for mixing declarations with statements and

View File

@ -121,10 +121,24 @@ 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">;
"protected access specifier|static assert}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">;
"protected access specifier|static assert}1">;
def err_module_odr_violation_mismatch_decl_diff : Error<
"%q0 has different definitions in different modules; first difference is "
"%select{definition in module '%2'|defined here}1 found "
"%select{"
"static assert with condition|"
"static assert with message|"
"static assert with %select{|no }4message}3">;
def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
"%select{"
"static assert with different condition|"
"static assert with different message|"
"static assert with %select{|no }2message}1">;
def warn_module_uses_date_time : Warning<
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,

View File

@ -22,7 +22,10 @@
using namespace clang;
void ODRHash::AddStmt(const Stmt *S) {}
void ODRHash::AddStmt(const Stmt *S) {
assert(S && "Expecting non-null pointer.");
S->ProcessODRHash(ID, *this);
}
void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {}
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
void ODRHash::AddTemplateName(TemplateName Name) {}
@ -74,10 +77,18 @@ unsigned ODRHash::CalculateHash() {
class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
llvm::FoldingSetNodeID &ID;
ODRHash &Hash;
public:
ODRDeclVisitor(llvm::FoldingSetNodeID &ID)
: ID(ID) {}
ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
: ID(ID), Hash(Hash) {}
void AddStmt(const Stmt *S) {
Hash.AddBoolean(S);
if (S) {
Hash.AddStmt(S);
}
}
void Visit(const Decl *D) {
ID.AddInteger(D->getKind());
@ -88,6 +99,13 @@ public:
ID.AddInteger(D->getAccess());
Inherited::VisitAccessSpecDecl(D);
}
void VisitStaticAssertDecl(const StaticAssertDecl *D) {
AddStmt(D->getAssertExpr());
AddStmt(D->getMessage());
Inherited::VisitStaticAssertDecl(D);
}
};
// Only allow a small portion of Decl's to be processed. Remove this once
@ -100,6 +118,7 @@ bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) {
default:
return false;
case Decl::AccessSpec:
case Decl::StaticAssert:
return true;
}
}
@ -108,7 +127,7 @@ void ODRHash::AddSubDecl(const Decl *D) {
assert(D && "Expecting non-null pointer.");
AddDecl(D);
ODRDeclVisitor(ID).Visit(D);
ODRDeclVisitor(ID, *this).Visit(D);
}
void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {

View File

@ -19,20 +19,22 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/StmtVisitor.h"
#include "llvm/ADT/FoldingSet.h"
using namespace clang;
namespace {
class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
protected:
llvm::FoldingSetNodeID &ID;
const ASTContext &Context;
bool Canonical;
public:
StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool Canonical)
: ID(ID), Context(Context), Canonical(Canonical) { }
StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical)
: ID(ID), Canonical(Canonical) {}
virtual ~StmtProfiler() {}
void VisitStmt(const Stmt *S);
@ -41,22 +43,25 @@ namespace {
/// \brief Visit a declaration that is referenced within an expression
/// or statement.
void VisitDecl(const Decl *D);
virtual void VisitDecl(const Decl *D) = 0;
/// \brief Visit a type that is referenced within an expression or
/// statement.
void VisitType(QualType T);
virtual void VisitType(QualType T) = 0;
/// \brief Visit a name that occurs within an expression or statement.
void VisitName(DeclarationName Name);
virtual void VisitName(DeclarationName Name) = 0;
/// \brief Visit identifiers that are not in Decl's or Type's.
virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0;
/// \brief Visit a nested-name-specifier that occurs within an expression
/// or statement.
void VisitNestedNameSpecifier(NestedNameSpecifier *NNS);
virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0;
/// \brief Visit a template name that occurs within an expression or
/// statement.
void VisitTemplateName(TemplateName Name);
virtual void VisitTemplateName(TemplateName Name) = 0;
/// \brief Visit template arguments that occur within an expression or
/// statement.
@ -66,6 +71,127 @@ namespace {
/// \brief Visit a single template argument.
void VisitTemplateArgument(const TemplateArgument &Arg);
};
class StmtProfilerWithPointers : public StmtProfiler {
const ASTContext &Context;
public:
StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
const ASTContext &Context, bool Canonical)
: StmtProfiler(ID, Canonical), Context(Context) {}
private:
void VisitDecl(const Decl *D) override {
ID.AddInteger(D ? D->getKind() : 0);
if (Canonical && D) {
if (const NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(D)) {
ID.AddInteger(NTTP->getDepth());
ID.AddInteger(NTTP->getIndex());
ID.AddBoolean(NTTP->isParameterPack());
VisitType(NTTP->getType());
return;
}
if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
// The Itanium C++ ABI uses the type, scope depth, and scope
// index of a parameter when mangling expressions that involve
// function parameters, so we will use the parameter's type for
// establishing function parameter identity. That way, our
// definition of "equivalent" (per C++ [temp.over.link]) is at
// least as strong as the definition of "equivalent" used for
// name mangling.
VisitType(Parm->getType());
ID.AddInteger(Parm->getFunctionScopeDepth());
ID.AddInteger(Parm->getFunctionScopeIndex());
return;
}
if (const TemplateTypeParmDecl *TTP =
dyn_cast<TemplateTypeParmDecl>(D)) {
ID.AddInteger(TTP->getDepth());
ID.AddInteger(TTP->getIndex());
ID.AddBoolean(TTP->isParameterPack());
return;
}
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(D)) {
ID.AddInteger(TTP->getDepth());
ID.AddInteger(TTP->getIndex());
ID.AddBoolean(TTP->isParameterPack());
return;
}
}
ID.AddPointer(D ? D->getCanonicalDecl() : nullptr);
}
void VisitType(QualType T) override {
if (Canonical)
T = Context.getCanonicalType(T);
ID.AddPointer(T.getAsOpaquePtr());
}
void VisitName(DeclarationName Name) override {
ID.AddPointer(Name.getAsOpaquePtr());
}
void VisitIdentifierInfo(IdentifierInfo *II) override {
ID.AddPointer(II);
}
void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
if (Canonical)
NNS = Context.getCanonicalNestedNameSpecifier(NNS);
ID.AddPointer(NNS);
}
void VisitTemplateName(TemplateName Name) override {
if (Canonical)
Name = Context.getCanonicalTemplateName(Name);
Name.Profile(ID);
}
};
class StmtProfilerWithoutPointers : public StmtProfiler {
ODRHash &Hash;
public:
StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
: StmtProfiler(ID, false), Hash(Hash) {}
private:
void VisitType(QualType T) override {
Hash.AddQualType(T);
}
void VisitName(DeclarationName Name) override {
Hash.AddDeclarationName(Name);
}
void VisitIdentifierInfo(IdentifierInfo *II) override {
ID.AddBoolean(II);
if (II) {
Hash.AddIdentifierInfo(II);
}
}
void VisitDecl(const Decl *D) override {
ID.AddBoolean(D);
if (D) {
Hash.AddDecl(D);
}
}
void VisitTemplateName(TemplateName Name) override {
Hash.AddTemplateName(Name);
}
void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
ID.AddBoolean(NNS);
if (NNS) {
Hash.AddNestedNameSpecifier(NNS);
}
}
};
}
void StmtProfiler::VisitStmt(const Stmt *S) {
@ -853,7 +979,7 @@ void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) {
break;
case OffsetOfNode::Identifier:
ID.AddPointer(ON.getFieldName());
VisitIdentifierInfo(ON.getFieldName());
break;
case OffsetOfNode::Base:
@ -861,7 +987,7 @@ void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) {
break;
}
}
VisitExpr(S);
}
@ -1451,7 +1577,7 @@ StmtProfiler::VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) {
if (S->getDestroyedTypeInfo())
VisitType(S->getDestroyedType());
else
ID.AddPointer(S->getDestroyedTypeIdentifier());
VisitIdentifierInfo(S->getDestroyedTypeIdentifier());
}
void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
@ -1701,77 +1827,6 @@ void StmtProfiler::VisitObjCAvailabilityCheckExpr(
VisitExpr(S);
}
void StmtProfiler::VisitDecl(const Decl *D) {
ID.AddInteger(D? D->getKind() : 0);
if (Canonical && D) {
if (const NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(D)) {
ID.AddInteger(NTTP->getDepth());
ID.AddInteger(NTTP->getIndex());
ID.AddBoolean(NTTP->isParameterPack());
VisitType(NTTP->getType());
return;
}
if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
// The Itanium C++ ABI uses the type, scope depth, and scope
// index of a parameter when mangling expressions that involve
// function parameters, so we will use the parameter's type for
// establishing function parameter identity. That way, our
// definition of "equivalent" (per C++ [temp.over.link]) is at
// least as strong as the definition of "equivalent" used for
// name mangling.
VisitType(Parm->getType());
ID.AddInteger(Parm->getFunctionScopeDepth());
ID.AddInteger(Parm->getFunctionScopeIndex());
return;
}
if (const TemplateTypeParmDecl *TTP =
dyn_cast<TemplateTypeParmDecl>(D)) {
ID.AddInteger(TTP->getDepth());
ID.AddInteger(TTP->getIndex());
ID.AddBoolean(TTP->isParameterPack());
return;
}
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(D)) {
ID.AddInteger(TTP->getDepth());
ID.AddInteger(TTP->getIndex());
ID.AddBoolean(TTP->isParameterPack());
return;
}
}
ID.AddPointer(D? D->getCanonicalDecl() : nullptr);
}
void StmtProfiler::VisitType(QualType T) {
if (Canonical)
T = Context.getCanonicalType(T);
ID.AddPointer(T.getAsOpaquePtr());
}
void StmtProfiler::VisitName(DeclarationName Name) {
ID.AddPointer(Name.getAsOpaquePtr());
}
void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) {
if (Canonical)
NNS = Context.getCanonicalNestedNameSpecifier(NNS);
ID.AddPointer(NNS);
}
void StmtProfiler::VisitTemplateName(TemplateName Name) {
if (Canonical)
Name = Context.getCanonicalTemplateName(Name);
Name.Profile(ID);
}
void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args,
unsigned NumArgs) {
ID.AddInteger(NumArgs);
@ -1821,6 +1876,12 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) {
void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
bool Canonical) const {
StmtProfiler Profiler(ID, Context, Canonical);
StmtProfilerWithPointers Profiler(ID, Context, Canonical);
Profiler.Visit(this);
}
void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
class ODRHash &Hash) const {
StmtProfilerWithoutPointers Profiler(ID, Hash);
Profiler.Visit(this);
}

View File

@ -8955,6 +8955,7 @@ void ASTReader::diagnoseOdrViolations() {
PublicSpecifer,
PrivateSpecifer,
ProtectedSpecifer,
StaticAssert,
Other
} FirstDiffType = Other,
SecondDiffType = Other;
@ -8976,6 +8977,8 @@ void ASTReader::diagnoseOdrViolations() {
break;
}
llvm_unreachable("Invalid access specifier");
case Decl::StaticAssert:
return StaticAssert;
}
};
@ -9047,6 +9050,104 @@ void ASTReader::diagnoseOdrViolations() {
break;
}
assert(FirstDiffType == SecondDiffType);
// Used with err_module_odr_violation_mismatch_decl_diff and
// note_module_odr_violation_mismatch_decl_diff
enum ODRDeclDifference{
StaticAssertCondition,
StaticAssertMessage,
StaticAssertOnlyMessage,
};
// These lambdas have the common portions of the ODR diagnostics. This
// has the same return as Diag(), so addition parameters can be passed
// in with operator<<
auto ODRDiagError = [FirstRecord, &FirstModule, this](
SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff)
<< FirstRecord << FirstModule.empty() << FirstModule << Range
<< DiffType;
};
auto ODRDiagNote = [&SecondModule, this](
SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff)
<< SecondModule << Range << DiffType;
};
auto ComputeODRHash = [&Hash](const Stmt* S) {
assert(S);
Hash.clear();
Hash.AddStmt(S);
return Hash.CalculateHash();
};
switch (FirstDiffType) {
case Other:
case EndOfClass:
case PublicSpecifer:
case PrivateSpecifer:
case ProtectedSpecifer:
llvm_unreachable("Invalid diff type");
case StaticAssert: {
StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);
Expr *FirstExpr = FirstSA->getAssertExpr();
Expr *SecondExpr = SecondSA->getAssertExpr();
unsigned FirstODRHash = ComputeODRHash(FirstExpr);
unsigned SecondODRHash = ComputeODRHash(SecondExpr);
if (FirstODRHash != SecondODRHash) {
ODRDiagError(FirstExpr->getLocStart(), FirstExpr->getSourceRange(),
StaticAssertCondition);
ODRDiagNote(SecondExpr->getLocStart(),
SecondExpr->getSourceRange(), StaticAssertCondition);
Diagnosed = true;
break;
}
StringLiteral *FirstStr = FirstSA->getMessage();
StringLiteral *SecondStr = SecondSA->getMessage();
assert((FirstStr || SecondStr) && "Both messages cannot be empty");
if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
SourceLocation FirstLoc, SecondLoc;
SourceRange FirstRange, SecondRange;
if (FirstStr) {
FirstLoc = FirstStr->getLocStart();
FirstRange = FirstStr->getSourceRange();
} else {
FirstLoc = FirstSA->getLocStart();
FirstRange = FirstSA->getSourceRange();
}
if (SecondStr) {
SecondLoc = SecondStr->getLocStart();
SecondRange = SecondStr->getSourceRange();
} else {
SecondLoc = SecondSA->getLocStart();
SecondRange = SecondSA->getSourceRange();
}
ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
<< (FirstStr == nullptr);
ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
<< (SecondStr == nullptr);
Diagnosed = true;
break;
}
if (FirstStr && SecondStr &&
FirstStr->getString() != SecondStr->getString()) {
ODRDiagError(FirstStr->getLocStart(), FirstStr->getSourceRange(),
StaticAssertMessage);
ODRDiagNote(SecondStr->getLocStart(), SecondStr->getSourceRange(),
StaticAssertMessage);
Diagnosed = true;
break;
}
break;
}
}
if (Diagnosed == true)
continue;

View File

@ -21,7 +21,7 @@
// RUN: echo "}" >> %t/Inputs/module.map
// Run test
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++1z
#if !defined(FIRST) && !defined(SECOND)
#include "first.h"
@ -57,6 +57,64 @@ S2 s2;
#endif
} // namespace AccessSpecifiers
namespace StaticAssert {
#if defined(FIRST)
struct S1 {
static_assert(1 == 1, "First");
};
#elif defined(SECOND)
struct S1 {
static_assert(1 == 1, "Second");
};
#else
S1 s1;
// expected-error@second.h:* {{'StaticAssert::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with message}}
// expected-note@first.h:* {{but in 'FirstModule' found static assert with different message}}
#endif
#if defined(FIRST)
struct S2 {
static_assert(2 == 2, "Message");
};
#elif defined(SECOND)
struct S2 {
static_assert(2 == 2);
};
#else
S2 s2;
// expected-error@second.h:* {{'StaticAssert::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with no message}}
// expected-note@first.h:* {{but in 'FirstModule' found static assert with message}}
#endif
#if defined(FIRST)
struct S3 {
static_assert(3 == 3, "Message");
};
#elif defined(SECOND)
struct S3 {
static_assert(3 != 4, "Message");
};
#else
S3 s3;
// expected-error@second.h:* {{'StaticAssert::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with condition}}
// expected-note@first.h:* {{but in 'FirstModule' found static assert with different condition}}
#endif
#if defined(FIRST)
struct S4 {
static_assert(4 == 4, "Message");
};
#elif defined(SECOND)
struct S4 {
public:
};
#else
S4 s4;
// expected-error@second.h:* {{'StaticAssert::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
// expected-note@first.h:* {{but in 'FirstModule' found static assert}}
#endif
}
// Naive parsing of AST can lead to cycles in processing. Ensure
// self-references don't trigger an endless cycles of AST node processing.
namespace SelfReference {
@ -90,12 +148,18 @@ struct S {
public:
private:
protected:
static_assert(1 == 1, "Message");
static_assert(2 == 2);
};
#elif defined(SECOND)
struct S {
public:
private:
protected:
static_assert(1 == 1, "Message");
static_assert(2 == 2);
};
#else
S s;
@ -107,6 +171,9 @@ struct T {
private:
protected:
static_assert(1 == 1, "Message");
static_assert(2 == 2);
private:
};
#elif defined(SECOND)
@ -115,6 +182,9 @@ struct T {
private:
protected:
static_assert(1 == 1, "Message");
static_assert(2 == 2);
public:
};
#else