From: Richard Trieu Date: Tue, 2 May 2017 23:58:52 +0000 (+0000) Subject: [ODRHash] Add support for array and decayed types, and parameter names and types. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=386800d3a0134e44aceba44ad1cf4528e2e8ccb5;p=clang [ODRHash] Add support for array and decayed types, and parameter names and types. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@301989 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSerializationKinds.td b/include/clang/Basic/DiagnosticSerializationKinds.td index 4af4c18ced..35e2f67e24 100644 --- a/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/include/clang/Basic/DiagnosticSerializationKinds.td @@ -146,7 +146,10 @@ def err_module_odr_violation_mismatch_decl_diff : Error< "method %4 is %select{not static|static}5|" "method %4 is %select{not volatile|volatile}5|" "method %4 is %select{not const|const}5|" - "method %4 is %select{not inline|inline}5}3">; + "method %4 is %select{not inline|inline}5|" + "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}3">; def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " "%select{" @@ -166,7 +169,10 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " "method %2 is %select{not static|static}3|" "method %2 is %select{not volatile|volatile}3|" "method %2 is %select{not const|const}3|" - "method %2 is %select{not inline|inline}3}1">; + "method %2 is %select{not inline|inline}3|" + "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}1">; def warn_module_uses_date_time : Warning< "%select{precompiled header|module}0 uses __DATE__ or __TIME__">, diff --git a/lib/AST/ODRHash.cpp b/lib/AST/ODRHash.cpp index d72eebbe8e..83168d0924 100644 --- a/lib/AST/ODRHash.cpp +++ b/lib/AST/ODRHash.cpp @@ -169,6 +169,11 @@ public: Inherited::VisitValueDecl(D); } + void VisitParmVarDecl(const ParmVarDecl *D) { + // TODO: Handle default arguments. + Inherited::VisitParmVarDecl(D); + } + void VisitAccessSpecDecl(const AccessSpecDecl *D) { ID.AddInteger(D->getAccess()); Inherited::VisitAccessSpecDecl(D); @@ -202,6 +207,12 @@ public: Hash.AddBoolean(D->isPure()); Hash.AddBoolean(D->isDeletedAsWritten()); + ID.AddInteger(D->param_size()); + + for (auto *Param : D->parameters()) { + Hash.AddSubDecl(Param); + } + Inherited::VisitFunctionDecl(D); } @@ -256,6 +267,11 @@ void ODRHash::AddSubDecl(const Decl *D) { void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { assert(Record && Record->hasDefinition() && "Expected non-null record to be a definition."); + + if (isa(Record)) { + return; + } + AddDecl(Record); // Filter out sub-Decls which will not be processed in order to get an @@ -315,6 +331,14 @@ public: } } + void AddQualType(QualType T) { + Hash.AddQualType(T); + } + + void VisitQualifiers(Qualifiers Quals) { + ID.AddInteger(Quals.getAsOpaqueValue()); + } + void Visit(const Type *T) { ID.AddInteger(T->getTypeClass()); Inherited::Visit(T); @@ -322,11 +346,69 @@ public: void VisitType(const Type *T) {} + void VisitAdjustedType(const AdjustedType *T) { + AddQualType(T->getOriginalType()); + AddQualType(T->getAdjustedType()); + VisitType(T); + } + + void VisitDecayedType(const DecayedType *T) { + AddQualType(T->getDecayedType()); + AddQualType(T->getPointeeType()); + VisitAdjustedType(T); + } + + void VisitArrayType(const ArrayType *T) { + AddQualType(T->getElementType()); + ID.AddInteger(T->getSizeModifier()); + VisitQualifiers(T->getIndexTypeQualifiers()); + VisitType(T); + } + void VisitConstantArrayType(const ConstantArrayType *T) { + T->getSize().Profile(ID); + VisitArrayType(T); + } + + void VisitDependentSizedArrayType(const DependentSizedArrayType *T) { + AddStmt(T->getSizeExpr()); + VisitArrayType(T); + } + + void VisitIncompleteArrayType(const IncompleteArrayType *T) { + VisitArrayType(T); + } + + void VisitVariableArrayType(const VariableArrayType *T) { + AddStmt(T->getSizeExpr()); + VisitArrayType(T); + } + void VisitBuiltinType(const BuiltinType *T) { ID.AddInteger(T->getKind()); VisitType(T); } + void VisitFunctionType(const FunctionType *T) { + AddQualType(T->getReturnType()); + T->getExtInfo().Profile(ID); + Hash.AddBoolean(T->isConst()); + Hash.AddBoolean(T->isVolatile()); + Hash.AddBoolean(T->isRestrict()); + VisitType(T); + } + + void VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + VisitFunctionType(T); + } + + void VisitFunctionProtoType(const FunctionProtoType *T) { + ID.AddInteger(T->getNumParams()); + for (auto ParamType : T->getParamTypes()) + AddQualType(ParamType); + + VisitFunctionType(T); + } + void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); Hash.AddQualType(T->getDecl()->getUnderlyingType()); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 5312ad118d..01129a9415 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -9316,6 +9316,9 @@ void ASTReader::diagnoseOdrViolations() { MethodVolatile, MethodConst, MethodInline, + MethodNumberParameters, + MethodParameterType, + MethodParameterName, }; // These lambdas have the common portions of the ODR diagnostics. This @@ -9346,6 +9349,12 @@ void ASTReader::diagnoseOdrViolations() { return Hash.CalculateHash(); }; + auto ComputeQualTypeODRHash = [&Hash](QualType Ty) { + Hash.clear(); + Hash.AddQualType(Ty); + return Hash.CalculateHash(); + }; + switch (FirstDiffType) { case Other: case EndOfClass: @@ -9640,6 +9649,76 @@ void ASTReader::diagnoseOdrViolations() { break; } + const unsigned FirstNumParameters = FirstMethod->param_size(); + const unsigned SecondNumParameters = SecondMethod->param_size(); + if (FirstNumParameters != SecondNumParameters) { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodNumberParameters) + << FirstName << FirstNumParameters; + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodNumberParameters) + << SecondName << SecondNumParameters; + Diagnosed = true; + break; + } + + // Need this status boolean to know when break out of the switch. + bool ParameterMismatch = false; + for (unsigned I = 0; I < FirstNumParameters; ++I) { + const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I); + const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I); + + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + ComputeQualTypeODRHash(FirstParamType) != + ComputeQualTypeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs()) { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) + << FirstName << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterType) + << FirstName << (I + 1) << FirstParamType << false; + } + + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs()) { + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterType) + << SecondName << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterType) + << SecondName << (I + 1) << SecondParamType << false; + } + ParameterMismatch = true; + break; + } + + DeclarationName FirstParamName = FirstParam->getDeclName(); + DeclarationName SecondParamName = SecondParam->getDeclName(); + if (FirstParamName != SecondParamName) { + ODRDiagError(FirstMethod->getLocation(), + FirstMethod->getSourceRange(), MethodParameterName) + << FirstName << (I + 1) << FirstParamName; + ODRDiagNote(SecondMethod->getLocation(), + SecondMethod->getSourceRange(), MethodParameterName) + << SecondName << (I + 1) << SecondParamName; + ParameterMismatch = true; + break; + } + } + + if (ParameterMismatch) { + Diagnosed = true; + break; + } + break; } } diff --git a/test/Modules/odr_hash.cpp b/test/Modules/odr_hash.cpp index e57c36f386..294e925627 100644 --- a/test/Modules/odr_hash.cpp +++ b/test/Modules/odr_hash.cpp @@ -275,6 +275,33 @@ S11 s11; // expected-note@first.h:* {{but in 'FirstModule' found field 'x' with a different initializer}} #endif +#if defined(FIRST) +struct S12 { + unsigned x[5]; +}; +#elif defined(SECOND) +struct S12 { + unsigned x[7]; +}; +#else +S12 s12; +// expected-error@first.h:* {{'Field::S12::x' from module 'FirstModule' is not present in definition of 'Field::S12' in module 'SecondModule'}} +// expected-note@second.h:* {{declaration of 'x' does not match}} +#endif + +#if defined(FIRST) +struct S13 { + unsigned x[7]; +}; +#elif defined(SECOND) +struct S13 { + double x[7]; +}; +#else +S13 s13; +// expected-error@first.h:* {{'Field::S13::x' from module 'FirstModule' is not present in definition of 'Field::S13' in module 'SecondModule'}} +// expected-note@second.h:* {{declaration of 'x' does not match}} +#endif } // namespace Field namespace Method { @@ -403,6 +430,91 @@ S8 s8; // expected-note@first.h:* {{but in 'FirstModule' found method 'A' is const}} #endif +#if defined(FIRST) +struct S9 { + void A(int x) {} + void A(int x, int y) {} +}; +#elif defined(SECOND) +struct S9 { + void A(int x, int y) {} + void A(int x) {} +}; +#else +S9 s9; +// expected-error@second.h:* {{'Method::S9' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' that has 2 parameters}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'A' that has 1 parameter}} +#endif + +#if defined(FIRST) +struct S10 { + void A(int x) {} + void A(float x) {} +}; +#elif defined(SECOND) +struct S10 { + void A(float x) {} + void A(int x) {} +}; +#else +S10 s10; +// expected-error@second.h:* {{'Method::S10' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter of type 'float'}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter of type 'int'}} +#endif + +#if defined(FIRST) +struct S11 { + void A(int x) {} +}; +#elif defined(SECOND) +struct S11 { + void A(int y) {} +}; +#else +S11 s11; +// expected-error@second.h:* {{'Method::S11' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter named 'y'}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter named 'x'}} +#endif + +#if defined(FIRST) +struct S12 { + void A(int x) {} +}; +#elif defined(SECOND) +struct S12 { + void A(int x = 1) {} +}; +#else +S12 s12; +// TODO: This should produce an error. +#endif + +#if defined(FIRST) +struct S13 { + void A(int x = 1 + 0) {} +}; +#elif defined(SECOND) +struct S13 { + void A(int x = 1) {} +}; +#else +S13 s13; +// TODO: This should produce an error. +#endif + +#if defined(FIRST) +struct S14 { + void A(int x[2]) {} +}; +#elif defined(SECOND) +struct S14 { + void A(int x[3]) {} +}; +#else +S14 s14; +// expected-error@second.h:* {{'Method::S14' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter of type 'int *' decayed from 'int [3]'}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter of type 'int *' decayed from 'int [2]'}} +#endif } // namespace Method // Naive parsing of AST can lead to cycles in processing. Ensure @@ -526,37 +638,43 @@ S3 s3; // 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 { -#define CREATE_ALL_DECL_STRUCT(NAME, ACCESS) \ - typedef int INT; \ - struct NAME { \ - public: \ - private: \ - protected: \ - static_assert(1 == 1, "Message"); \ - static_assert(2 == 2); \ - \ - int x; \ - double y; \ - \ - INT z; \ - \ - unsigned a : 1; \ - unsigned b : 2 * 2 + 5 / 2; \ - \ - mutable int c = sizeof(x + y); \ - \ - void method() {} \ - static void static_method() {} \ - virtual void virtual_method() {} \ - virtual void pure_virtual_method() = 0; \ - inline void inline_method() {} \ - void volatile_method() volatile {} \ - void const_method() const {} \ - \ - typedef int typedef_int; \ - using using_int = int; \ - \ - ACCESS: \ +#define CREATE_ALL_DECL_STRUCT(NAME, ACCESS) \ + typedef int INT; \ + struct NAME { \ + public: \ + private: \ + protected: \ + static_assert(1 == 1, "Message"); \ + static_assert(2 == 2); \ + \ + int x; \ + double y; \ + \ + INT z; \ + \ + unsigned a : 1; \ + unsigned b : 2 * 2 + 5 / 2; \ + \ + mutable int c = sizeof(x + y); \ + \ + void method() {} \ + static void static_method() {} \ + virtual void virtual_method() {} \ + virtual void pure_virtual_method() = 0; \ + inline void inline_method() {} \ + void volatile_method() volatile {} \ + void const_method() const {} \ + \ + typedef int typedef_int; \ + using using_int = int; \ + \ + void method_one_arg(int x) {} \ + void method_one_arg_default_argument(int x = 5 + 5) {} \ + void method_decayed_type(int x[5]) {} \ + \ + int constant_arr[5]; \ + \ + ACCESS: \ }; #if defined(FIRST) @@ -933,6 +1051,34 @@ Alpha::Alpha() {} #endif } +namespace ParameterTest { +#if defined(FIRST) +class X {}; +template +class S { + public: + typedef G Type; + static inline G *Foo(const G *a, int * = nullptr); +}; + +template +G* S::Foo(const G* aaaa, int*) {} +#elif defined(SECOND) +template +class S { + public: + typedef G Type; + static inline G *Foo(const G *a, int * = nullptr); +}; + +template +G* S::Foo(const G* asdf, int*) {} +#else +S s; +#endif +} + + // Keep macros contained to one file. #ifdef FIRST #undef FIRST