mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-13 13:45:16 +00:00
[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:
parent
fccbda967a
commit
639d7b68d6
@ -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
|
||||
|
@ -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__">,
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user