[ODRHash] Hash VarDecl members.

These VarDecl's are static data members of classes.  Since the initializers are
also hashed, this also provides checking for default arguments to methods.

llvm-svn: 305543
This commit is contained in:
Richard Trieu 2017-06-16 02:44:29 +00:00
parent af0f33a853
commit 6e13ff33c8
4 changed files with 326 additions and 8 deletions

View File

@ -121,10 +121,12 @@ 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|static assert|field|method|type alias|typedef}3">;
"protected access specifier|static assert|field|method|type alias|typedef|"
"data member}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|static assert|field|method|type alias|typedef}1">;
"protected access specifier|static assert|field|method|type alias|typedef|"
"data member}1">;
def err_module_odr_violation_mismatch_decl_diff : Error<
"%q0 has different definitions in different modules; first difference is "
@ -150,8 +152,16 @@ def err_module_odr_violation_mismatch_decl_diff : Error<
"method %4 that has %5 parameter%s5|"
"method %4 with %ordinal5 parameter of type %6%select{| decayed from %8}7|"
"method %4 with %ordinal5 parameter named %6|"
"method %4 with %ordinal5 parameter with%select{out|}6 a default argument|"
"method %4 with %ordinal5 parameter with a default argument|"
"%select{typedef|type alias}4 name %5|"
"%select{typedef|type alias}4 %5 with underlying type %6}3">;
"%select{typedef|type alias}4 %5 with underlying type %6|"
"data member with name %4|"
"data member %4 with type %5|"
"data member %4 with%select{out|}5 an initializer|"
"data member %4 with an initializer|"
"data member %4 %select{is constexpr|is not constexpr}5|"
"}3">;
def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
"%select{"
@ -175,18 +185,26 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
"method %2 that has %3 parameter%s3|"
"method %2 with %ordinal3 parameter of type %4%select{| decayed from %6}5|"
"method %2 with %ordinal3 parameter named %4|"
"method %2 with %ordinal3 parameter with%select{out|}4 a default argument|"
"method %2 with %ordinal3 parameter with a different default argument|"
"%select{typedef|type alias}2 name %3|"
"%select{typedef|type alias}2 %3 with different underlying type %4}1">;
"%select{typedef|type alias}2 %3 with different underlying type %4|"
"data member with name %2|"
"data member %2 with different type %3|"
"data member %2 with%select{out|}3 an initializer|"
"data member %2 with a different initializer|"
"data member %2 %select{is constexpr|is not constexpr}3|"
"}1">;
def err_module_odr_violation_mismatch_decl_unknown : Error<
"%q0 %select{with definition in module '%2'|defined here}1 has different "
"definitions in different modules; first difference is this "
"%select{||||static assert|field|method|type alias|typedef|"
"%select{||||static assert|field|method|type alias|typedef|data member|"
"unexpected decl}3">;
def note_module_odr_violation_mismatch_decl_unknown : Note<
"but in '%0' found "
"%select{||||different static assert|different field|different method|"
"different type alias|different typedef|"
"different type alias|different typedef|different data member|"
"another unexpected decl}1">;
def warn_duplicate_module_file_extension : Warning<

View File

@ -252,6 +252,17 @@ public:
Inherited::VisitValueDecl(D);
}
void VisitVarDecl(const VarDecl *D) {
Hash.AddBoolean(D->isStaticLocal());
Hash.AddBoolean(D->isConstexpr());
const bool HasInit = D->hasInit();
Hash.AddBoolean(HasInit);
if (HasInit) {
AddStmt(D->getInit());
}
Inherited::VisitVarDecl(D);
}
void VisitParmVarDecl(const ParmVarDecl *D) {
// TODO: Handle default arguments.
Inherited::VisitParmVarDecl(D);
@ -336,6 +347,7 @@ bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) {
case Decl::StaticAssert:
case Decl::TypeAlias:
case Decl::Typedef:
case Decl::Var:
return true;
}
}

View File

@ -9253,6 +9253,7 @@ void ASTReader::diagnoseOdrViolations() {
CXXMethod,
TypeAlias,
TypeDef,
Var,
Other
} FirstDiffType = Other,
SecondDiffType = Other;
@ -9284,6 +9285,8 @@ void ASTReader::diagnoseOdrViolations() {
return TypeAlias;
case Decl::Typedef:
return TypeDef;
case Decl::Var:
return Var;
}
};
@ -9380,8 +9383,15 @@ void ASTReader::diagnoseOdrViolations() {
MethodNumberParameters,
MethodParameterType,
MethodParameterName,
MethodParameterSingleDefaultArgument,
MethodParameterDifferentDefaultArgument,
TypedefName,
TypedefType,
VarName,
VarType,
VarSingleInitializer,
VarDifferentInitializer,
VarConstexpr,
};
// These lambdas have the common portions of the ODR diagnostics. This
@ -9748,6 +9758,38 @@ void ASTReader::diagnoseOdrViolations() {
ParameterMismatch = true;
break;
}
const Expr *FirstInit = FirstParam->getInit();
const Expr *SecondInit = SecondParam->getInit();
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
ODRDiagError(FirstMethod->getLocation(),
FirstMethod->getSourceRange(),
MethodParameterSingleDefaultArgument)
<< FirstName << (I + 1) << (FirstInit == nullptr)
<< (FirstInit ? FirstInit->getSourceRange() : SourceRange());
ODRDiagNote(SecondMethod->getLocation(),
SecondMethod->getSourceRange(),
MethodParameterSingleDefaultArgument)
<< SecondName << (I + 1) << (SecondInit == nullptr)
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
ParameterMismatch = true;
break;
}
if (FirstInit && SecondInit &&
ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) {
ODRDiagError(FirstMethod->getLocation(),
FirstMethod->getSourceRange(),
MethodParameterDifferentDefaultArgument)
<< FirstName << (I + 1) << FirstInit->getSourceRange();
ODRDiagNote(SecondMethod->getLocation(),
SecondMethod->getSourceRange(),
MethodParameterDifferentDefaultArgument)
<< SecondName << (I + 1) << SecondInit->getSourceRange();
ParameterMismatch = true;
break;
}
}
if (ParameterMismatch) {
@ -9789,6 +9831,77 @@ void ASTReader::diagnoseOdrViolations() {
}
break;
}
case Var: {
VarDecl *FirstVD = cast<VarDecl>(FirstDecl);
VarDecl *SecondVD = cast<VarDecl>(SecondDecl);
auto FirstName = FirstVD->getDeclName();
auto SecondName = SecondVD->getDeclName();
if (FirstName != SecondName) {
ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(),
VarName)
<< FirstName;
ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(),
VarName)
<< SecondName;
Diagnosed = true;
break;
}
QualType FirstType = FirstVD->getType();
QualType SecondType = SecondVD->getType();
if (ComputeQualTypeODRHash(FirstType) !=
ComputeQualTypeODRHash(SecondType)) {
ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(),
VarType)
<< FirstName << FirstType;
ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(),
VarType)
<< SecondName << SecondType;
Diagnosed = true;
break;
}
const Expr *FirstInit = FirstVD->getInit();
const Expr *SecondInit = SecondVD->getInit();
if ((FirstInit == nullptr) != (SecondInit == nullptr)) {
ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(),
VarSingleInitializer)
<< FirstName << (FirstInit == nullptr)
<< (FirstInit ? FirstInit->getSourceRange(): SourceRange());
ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(),
VarSingleInitializer)
<< SecondName << (SecondInit == nullptr)
<< (SecondInit ? SecondInit->getSourceRange() : SourceRange());
Diagnosed = true;
break;
}
if (FirstInit && SecondInit &&
ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) {
ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(),
VarDifferentInitializer)
<< FirstName << FirstInit->getSourceRange();
ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(),
VarDifferentInitializer)
<< SecondName << SecondInit->getSourceRange();
Diagnosed = true;
break;
}
const bool FirstIsConstexpr = FirstVD->isConstexpr();
const bool SecondIsConstexpr = SecondVD->isConstexpr();
if (FirstIsConstexpr != SecondIsConstexpr) {
ODRDiagError(FirstVD->getLocation(), FirstVD->getSourceRange(),
VarConstexpr)
<< FirstName << FirstIsConstexpr;
ODRDiagNote(SecondVD->getLocation(), SecondVD->getSourceRange(),
VarConstexpr)
<< SecondName << SecondIsConstexpr;
Diagnosed = true;
break;
}
break;
}
}
if (Diagnosed == true)

View File

@ -486,7 +486,8 @@ struct S12 {
};
#else
S12 s12;
// TODO: This should produce an error.
// expected-error@second.h:* {{'Method::S12' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter without a default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter with a default argument}}
#endif
#if defined(FIRST)
@ -499,7 +500,8 @@ struct S13 {
};
#else
S13 s13;
// TODO: This should produce an error.
// expected-error@second.h:* {{'Method::S13' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter with a default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter with a different default argument}}
#endif
#if defined(FIRST)
@ -1112,6 +1114,179 @@ using TemplateTypeParmType::S2;
#endif
}
namespace VarDecl {
#if defined(FIRST)
struct S1 {
static int x;
static int y;
};
#elif defined(SECOND)
struct S1 {
static int y;
static int x;
};
#else
S1 s1;
// expected-error@second.h:* {{'VarDecl::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member with name 'y'}}
// expected-note@first.h:* {{but in 'FirstModule' found data member with name 'x'}}
#endif
#if defined(FIRST)
struct S2 {
static int x;
};
#elif defined(SECOND)
using I = int;
struct S2 {
static I x;
};
#else
S2 s2;
// expected-error@second.h:* {{'VarDecl::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member 'x' with type 'VarDecl::I' (aka 'int')}}
// expected-note@first.h:* {{but in 'FirstModule' found data member 'x' with different type 'int'}}
#endif
#if defined(FIRST)
struct S3 {
static const int x = 1;
};
#elif defined(SECOND)
struct S3 {
static const int x;
};
#else
S3 s3;
// expected-error@second.h:* {{'VarDecl::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member 'x' with an initializer}}
// expected-note@first.h:* {{but in 'FirstModule' found data member 'x' without an initializer}}
#endif
#if defined(FIRST)
struct S4 {
static const int x = 1;
};
#elif defined(SECOND)
struct S4 {
static const int x = 2;
};
#else
S4 s4;
// expected-error@second.h:* {{'VarDecl::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member 'x' with an initializer}}
// expected-note@first.h:* {{but in 'FirstModule' found data member 'x' with a different initializer}}
#endif
#if defined(FIRST)
struct S5 {
static const int x = 1;
};
#elif defined(SECOND)
struct S5 {
static constexpr int x = 1;
};
#else
S5 s5;
// expected-error@second.h:* {{'VarDecl::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member 'x' is not constexpr}}
// expected-note@first.h:* {{but in 'FirstModule' found data member 'x' is constexpr}}
#endif
#if defined(FIRST)
struct S6 {
static const int x = 1;
};
#elif defined(SECOND)
struct S6 {
static const int y = 1;
};
#else
S6 s6;
// expected-error@first.h:* {{'VarDecl::S6::x' from module 'FirstModule' is not present in definition of 'VarDecl::S6' in module 'SecondModule'}}
// expected-note@second.h:* {{definition has no member 'x'}}
#endif
#if defined(FIRST)
struct S7 {
static const int x = 1;
};
#elif defined(SECOND)
struct S7 {
static const unsigned x = 1;
};
#else
S7 s7;
// expected-error@first.h:* {{'VarDecl::S7::x' from module 'FirstModule' is not present in definition of 'VarDecl::S7' in module 'SecondModule'}}
// expected-note@second.h:* {{declaration of 'x' does not match}}
#endif
#if defined(FIRST)
struct S8 {
public:
static const int x = 1;
};
#elif defined(SECOND)
struct S8 {
static const int x = 1;
public:
};
#else
S8 s8;
// expected-error@second.h:* {{'VarDecl::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found data member}}
// expected-note@first.h:* {{but in 'FirstModule' found public access specifier}}
#endif
#if defined(FIRST)
struct S9 {
static const int x = 1;
};
#elif defined(SECOND)
struct S9 {
static int x;
};
#else
S9 s9;
// expected-error@first.h:* {{'VarDecl::S9::x' from module 'FirstModule' is not present in definition of 'VarDecl::S9' in module 'SecondModule'}}
// expected-note@second.h:* {{declaration of 'x' does not match}}
#endif
#if defined(FIRST)
template <typename T>
struct S {
struct R {
void foo(T x = 0) {}
};
};
#elif defined(SECOND)
template <typename T>
struct S {
struct R {
void foo(T x = 1) {}
};
};
#else
void run() {
S<int>::R().foo();
}
// expected-error@second.h:* {{'VarDecl::S::R' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'foo' with 1st parameter with a default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found method 'foo' with 1st parameter with a different default argument}}
#endif
#if defined(FIRST)
template <typename alpha> struct Bravo {
void charlie(bool delta = false) {}
};
typedef Bravo<char> echo;
echo foxtrot;
#elif defined(SECOND)
template <typename alpha> struct Bravo {
void charlie(bool delta = (false)) {}
};
typedef Bravo<char> echo;
echo foxtrot;
#else
Bravo<char> golf;
// expected-error@second.h:* {{'VarDecl::Bravo' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'charlie' with 1st parameter with a default argument}}
// expected-note@first.h:* {{but in 'FirstModule' found method 'charlie' with 1st parameter with a different default argument}}
#endif
}
// Interesting cases that should not cause errors. struct S should not error
// while struct T should error at the access specifier mismatch at the end.
namespace AllDecls {