From: Richard Trieu Date: Fri, 16 Jun 2017 02:44:29 +0000 (+0000) Subject: [ODRHash] Hash VarDecl members. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dd646d623e79ae9d8ec9850f18157bb2d247eb70;p=clang [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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@305543 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index 5d286ee83a..0a59a63323 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -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< diff --git a/lib/AST/ODRHash.cpp b/lib/AST/ODRHash.cpp index 3bf9896985..05bed658f3 100644 --- a/lib/AST/ODRHash.cpp +++ b/lib/AST/ODRHash.cpp @@ -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; } } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 5bef65fc89..eeb0132c16 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -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(FirstDecl); + VarDecl *SecondVD = cast(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) diff --git a/test/Modules/odr_hash.cpp b/test/Modules/odr_hash.cpp index 28b05a5356..c94940c73e 100644 --- a/test/Modules/odr_hash.cpp +++ b/test/Modules/odr_hash.cpp @@ -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 +struct S { + struct R { + void foo(T x = 0) {} + }; +}; +#elif defined(SECOND) +template +struct S { + struct R { + void foo(T x = 1) {} + }; +}; +#else +void run() { + S::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 struct Bravo { + void charlie(bool delta = false) {} +}; +typedef Bravo echo; +echo foxtrot; +#elif defined(SECOND) +template struct Bravo { + void charlie(bool delta = (false)) {} +}; +typedef Bravo echo; +echo foxtrot; +#else +Bravo 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 {