diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 4db0b1ef949b..04a832e552a4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1759,6 +1759,11 @@ protected: unsigned IsCopyDeductionCandidate : 1; private: + + /// Store the ODRHash after first calculation. + unsigned HasODRHash : 1; + unsigned ODRHash; + /// \brief End part of this FunctionDecl's source range. /// /// We could compute the full range in getSourceRange(). However, when we're @@ -1841,8 +1846,9 @@ protected: IsExplicitlyDefaulted(false), HasImplicitReturnZero(false), IsLateTemplateParsed(false), IsConstexpr(isConstexprSpecified), InstantiationIsPending(false), UsesSEHTry(false), HasSkippedBody(false), - WillHaveBody(false), IsCopyDeductionCandidate(false), - EndRangeLoc(NameInfo.getEndLoc()), DNLoc(NameInfo.getInfo()) {} + WillHaveBody(false), IsCopyDeductionCandidate(false), HasODRHash(false), + ODRHash(0), EndRangeLoc(NameInfo.getEndLoc()), + DNLoc(NameInfo.getInfo()) {} using redeclarable_base = Redeclarable; @@ -2439,6 +2445,10 @@ public: /// returns 0. unsigned getMemoryFunctionKind() const; + /// \brief Returns ODRHash of the function. This value is calculated and + /// stored on first call, then the stored value returned on the other calls. + unsigned getODRHash(); + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h index e4cc12d35891..ed648bb8afb1 100644 --- a/clang/include/clang/AST/ODRHash.h +++ b/clang/include/clang/AST/ODRHash.h @@ -53,6 +53,10 @@ public: // more information than the AddDecl class. void AddCXXRecordDecl(const CXXRecordDecl *Record); + // Use this for ODR checking functions between modules. This method compares + // more information than the AddDecl class. + void AddFunctionDecl(const FunctionDecl *Function); + // Process SubDecls of the main Decl. This method calls the DeclVisitor // while AddDecl does not. void AddSubDecl(const Decl *D); diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td index 3949bc2146f6..250b49f2cac4 100644 --- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td +++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td @@ -270,6 +270,29 @@ def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found " "friend function %2|" "}1">; +def err_module_odr_violation_function : Error< + "%q0 has different definitions in different modules; " + "%select{definition in module '%2'|defined here}1 " + "first difference is " + "%select{" + "return type is %4|" + "%ordinal4 parameter with name %5|" + "%ordinal4 parameter with type %5%select{| decayed from %7}6|" + "%ordinal4 parameter with%select{out|}5 a default argument|" + "%ordinal4 parameter with a default argument|" + "function body" + "}3">; + +def note_module_odr_violation_function : Note<"but in '%0' found " + "%select{" + "different return type %2|" + "%ordinal2 parameter with name %3|" + "%ordinal2 parameter with type %3%select{| decayed from %5}4|" + "%ordinal2 parameter with%select{out|}3 a default argument|" + "%ordinal2 parameter with a different default argument|" + "a different body" + "}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 " diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 7b71fee95de2..37920fc143ea 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1092,6 +1092,10 @@ private: llvm::SmallDenseMap, 2> PendingOdrMergeFailures; + /// \brief Function definitions in which we found an ODR violation. + llvm::SmallDenseMap, 2> + PendingFunctionOdrMergeFailures; + /// \brief DeclContexts in which we have diagnosed an ODR violation. llvm::SmallPtrSet DiagnosedOdrMergeFailures; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index ee15a4d2b4f8..629037b1755c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -26,6 +26,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExternalASTSource.h" +#include "clang/AST/ODRHash.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/Redeclarable.h" #include "clang/AST/Stmt.h" @@ -3604,6 +3605,25 @@ unsigned FunctionDecl::getMemoryFunctionKind() const { return 0; } +unsigned FunctionDecl::getODRHash() { + if (HasODRHash) + return ODRHash; + + if (FunctionDecl *Definition = getDefinition()) { + if (Definition != this) { + HasODRHash = true; + ODRHash = Definition->getODRHash(); + return ODRHash; + } + } + + class ODRHash Hash; + Hash.AddFunctionDecl(this); + HasODRHash = true; + ODRHash = Hash.CalculateHash(); + return ODRHash; +} + //===----------------------------------------------------------------------===// // FieldDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 0f07b6225d95..b6874e7ba420 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -466,6 +466,36 @@ void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { } } +void ODRHash::AddFunctionDecl(const FunctionDecl *Function) { + assert(Function && "Expecting non-null pointer."); + + // Skip hashing these kinds of function. + if (Function->isImplicit()) return; + if (Function->isDefaulted()) return; + if (Function->isDeleted()) return; + if (!Function->hasBody()) return; + if (!Function->getBody()) return; + + // Skip functions that are specializations or in specialization context. + const DeclContext *DC = Function; + while (DC) { + if (isa(DC)) return; + if (auto *F = dyn_cast(DC)) + if (F->isFunctionTemplateSpecialization()) return; + DC = DC->getParent(); + } + + AddDecl(Function); + + AddQualType(Function->getReturnType()); + + ID.AddInteger(Function->param_size()); + for (auto Param : Function->parameters()) + AddSubDecl(Param); + + AddStmt(Function->getBody()); +} + void ODRHash::AddDecl(const Decl *D) { assert(D && "Expecting non-null pointer."); D = D->getCanonicalDecl(); diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 111ac4fcdaa4..4ed822e04f6c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -9235,8 +9235,16 @@ void ASTReader::finishPendingActions() { const FunctionDecl *Defn = nullptr; if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) { FD->setLazyBody(PB->second); - } else - mergeDefinitionVisibility(const_cast(Defn), FD); + } else { + auto *NonConstDefn = const_cast(Defn); + mergeDefinitionVisibility(NonConstDefn, FD); + + if (!FD->isLateTemplateParsed() && + !NonConstDefn->isLateTemplateParsed() && + FD->getODRHash() != NonConstDefn->getODRHash()) { + PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn); + } + } continue; } @@ -9253,7 +9261,8 @@ void ASTReader::finishPendingActions() { } void ASTReader::diagnoseOdrViolations() { - if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty()) + if (PendingOdrMergeFailures.empty() && PendingOdrMergeChecks.empty() && + PendingFunctionOdrMergeFailures.empty()) return; // Trigger the import of the full definition of each class that had any @@ -9275,6 +9284,20 @@ void ASTReader::diagnoseOdrViolations() { } } + // Trigger the import of functions. + auto FunctionOdrMergeFailures = std::move(PendingFunctionOdrMergeFailures); + PendingFunctionOdrMergeFailures.clear(); + for (auto &Merge : FunctionOdrMergeFailures) { + Merge.first->buildLookup(); + Merge.first->decls_begin(); + Merge.first->getBody(); + for (auto &FD : Merge.second) { + FD->buildLookup(); + FD->decls_begin(); + FD->getBody(); + } + } + // For each declaration from a merged context, check that the canonical // definition of that context also contains a declaration of the same // entity. @@ -9357,13 +9380,35 @@ void ASTReader::diagnoseOdrViolations() { } } - if (OdrMergeFailures.empty()) + if (OdrMergeFailures.empty() && FunctionOdrMergeFailures.empty()) return; // Ensure we don't accidentally recursively enter deserialization while // we're producing our diagnostics. Deserializing RecursionGuard(this); + // Common code for hashing helpers. + ODRHash Hash; + auto ComputeQualTypeODRHash = [&Hash](QualType Ty) { + Hash.clear(); + Hash.AddQualType(Ty); + return Hash.CalculateHash(); + }; + + auto ComputeODRHash = [&Hash](const Stmt *S) { + assert(S); + Hash.clear(); + Hash.AddStmt(S); + return Hash.CalculateHash(); + }; + + auto ComputeSubDeclODRHash = [&Hash](const Decl *D) { + assert(D); + Hash.clear(); + Hash.AddSubDecl(D); + return Hash.CalculateHash(); + }; + // Issue any pending ODR-failure diagnostics. for (auto &Merge : OdrMergeFailures) { // If we've already pointed out a specific problem with this class, don't @@ -9411,13 +9456,6 @@ void ASTReader::diagnoseOdrViolations() { << SecondModule << Range << DiffType; }; - ODRHash Hash; - auto ComputeQualTypeODRHash = [&Hash](QualType Ty) { - Hash.clear(); - Hash.AddQualType(Ty); - return Hash.CalculateHash(); - }; - unsigned FirstNumBases = FirstDD->NumBases; unsigned FirstNumVBases = FirstDD->NumVBases; unsigned SecondNumBases = SecondDD->NumBases; @@ -9520,14 +9558,12 @@ void ASTReader::diagnoseOdrViolations() { if (FirstTemplate && SecondTemplate) { DeclHashes FirstTemplateHashes; DeclHashes SecondTemplateHashes; - ODRHash Hash; auto PopulateTemplateParameterHashs = - [&Hash](DeclHashes &Hashes, const ClassTemplateDecl *TD) { + [&ComputeSubDeclODRHash](DeclHashes &Hashes, + const ClassTemplateDecl *TD) { for (auto *D : TD->getTemplateParameters()->asArray()) { - Hash.clear(); - Hash.AddSubDecl(D); - Hashes.emplace_back(D, Hash.CalculateHash()); + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); } }; @@ -9696,18 +9732,15 @@ void ASTReader::diagnoseOdrViolations() { DeclHashes FirstHashes; DeclHashes SecondHashes; - ODRHash Hash; - auto PopulateHashes = [&Hash, FirstRecord](DeclHashes &Hashes, - CXXRecordDecl *Record) { + auto PopulateHashes = [&ComputeSubDeclODRHash, FirstRecord]( + DeclHashes &Hashes, CXXRecordDecl *Record) { for (auto *D : Record->decls()) { // Due to decl merging, the first CXXRecordDecl is the parent of // Decls in both records. if (!ODRHash::isWhitelistedDecl(D, FirstRecord)) continue; - Hash.clear(); - Hash.AddSubDecl(D); - Hashes.emplace_back(D, Hash.CalculateHash()); + Hashes.emplace_back(D, ComputeSubDeclODRHash(D)); } }; PopulateHashes(FirstHashes, FirstRecord); @@ -9901,19 +9934,6 @@ void ASTReader::diagnoseOdrViolations() { << SecondModule << Range << DiffType; }; - auto ComputeODRHash = [&Hash](const Stmt* S) { - assert(S); - Hash.clear(); - Hash.AddStmt(S); - return Hash.CalculateHash(); - }; - - auto ComputeQualTypeODRHash = [&Hash](QualType Ty) { - Hash.clear(); - Hash.AddQualType(Ty); - return Hash.CalculateHash(); - }; - switch (FirstDiffType) { case Other: case EndOfClass: @@ -10488,6 +10508,160 @@ void ASTReader::diagnoseOdrViolations() { << Merge.first; } } + + // Issue ODR failures diagnostics for functions. + for (auto &Merge : FunctionOdrMergeFailures) { + enum ODRFunctionDifference { + ReturnType, + ParameterName, + ParameterType, + ParameterSingleDefaultArgument, + ParameterDifferentDefaultArgument, + FunctionBody, + }; + + FunctionDecl *FirstFunction = Merge.first; + std::string FirstModule = getOwningModuleNameForDiagnostic(FirstFunction); + + bool Diagnosed = false; + for (auto &SecondFunction : Merge.second) { + + if (FirstFunction == SecondFunction) + continue; + + std::string SecondModule = + getOwningModuleNameForDiagnostic(SecondFunction); + + auto ODRDiagError = [FirstFunction, &FirstModule, + this](SourceLocation Loc, SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::err_module_odr_violation_function) + << FirstFunction << FirstModule.empty() << FirstModule << Range + << DiffType; + }; + auto ODRDiagNote = [&SecondModule, this](SourceLocation Loc, + SourceRange Range, + ODRFunctionDifference DiffType) { + return Diag(Loc, diag::note_module_odr_violation_function) + << SecondModule << Range << DiffType; + }; + + if (ComputeQualTypeODRHash(FirstFunction->getReturnType()) != + ComputeQualTypeODRHash(SecondFunction->getReturnType())) { + ODRDiagError(FirstFunction->getReturnTypeSourceRange().getBegin(), + FirstFunction->getReturnTypeSourceRange(), ReturnType) + << FirstFunction->getReturnType(); + ODRDiagNote(SecondFunction->getReturnTypeSourceRange().getBegin(), + SecondFunction->getReturnTypeSourceRange(), ReturnType) + << SecondFunction->getReturnType(); + Diagnosed = true; + break; + } + + assert(FirstFunction->param_size() == SecondFunction->param_size() && + "Merged functions with different number of parameters"); + + auto ParamSize = FirstFunction->param_size(); + bool ParameterMismatch = false; + for (unsigned I = 0; I < ParamSize; ++I) { + auto *FirstParam = FirstFunction->getParamDecl(I); + auto *SecondParam = SecondFunction->getParamDecl(I); + + assert(getContext().hasSameType(FirstParam->getType(), + SecondParam->getType()) && + "Merged function has different parameter types."); + + if (FirstParam->getDeclName() != SecondParam->getDeclName()) { + ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterName) + << I + 1 << FirstParam->getDeclName(); + ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterName) + << I + 1 << SecondParam->getDeclName(); + ParameterMismatch = true; + break; + }; + + QualType FirstParamType = FirstParam->getType(); + QualType SecondParamType = SecondParam->getType(); + if (FirstParamType != SecondParamType && + ComputeQualTypeODRHash(FirstParamType) != + ComputeQualTypeODRHash(SecondParamType)) { + if (const DecayedType *ParamDecayedType = + FirstParamType->getAs()) { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), ParameterType) + << (I + 1) << FirstParamType << true + << ParamDecayedType->getOriginalType(); + } else { + ODRDiagError(FirstParam->getLocation(), + FirstParam->getSourceRange(), ParameterType) + << (I + 1) << FirstParamType << false; + } + + if (const DecayedType *ParamDecayedType = + SecondParamType->getAs()) { + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), ParameterType) + << (I + 1) << SecondParamType << true + << ParamDecayedType->getOriginalType(); + } else { + ODRDiagNote(SecondParam->getLocation(), + SecondParam->getSourceRange(), ParameterType) + << (I + 1) << SecondParamType << false; + } + ParameterMismatch = true; + break; + } + + const Expr *FirstInit = FirstParam->getInit(); + const Expr *SecondInit = SecondParam->getInit(); + if ((FirstInit == nullptr) != (SecondInit == nullptr)) { + ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (FirstInit == nullptr) + << (FirstInit ? FirstInit->getSourceRange() : SourceRange()); + ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterSingleDefaultArgument) + << (I + 1) << (SecondInit == nullptr) + << (SecondInit ? SecondInit->getSourceRange() : SourceRange()); + ParameterMismatch = true; + break; + } + + if (FirstInit && SecondInit && + ComputeODRHash(FirstInit) != ComputeODRHash(SecondInit)) { + ODRDiagError(FirstParam->getLocation(), FirstParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << FirstInit->getSourceRange(); + ODRDiagNote(SecondParam->getLocation(), SecondParam->getSourceRange(), + ParameterDifferentDefaultArgument) + << (I + 1) << SecondInit->getSourceRange(); + ParameterMismatch = true; + break; + } + + assert(ComputeSubDeclODRHash(FirstParam) == + ComputeSubDeclODRHash(SecondParam) && + "Undiagnosed parameter difference."); + } + + if (ParameterMismatch) { + Diagnosed = true; + break; + } + + // If no error has been generated before now, assume the problem is in + // the body and generate a message. + ODRDiagError(FirstFunction->getLocation(), + FirstFunction->getSourceRange(), FunctionBody); + ODRDiagNote(SecondFunction->getLocation(), + SecondFunction->getSourceRange(), FunctionBody); + Diagnosed = true; + break; + } + assert(Diagnosed && "Unable to emit ODR diagnostic."); + } } void ASTReader::StartedDeserializing() { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index a3bf0d971267..efbaf92a849a 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -796,6 +796,9 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->setCachedLinkage(Linkage(Record.readInt())); FD->EndRangeLoc = ReadSourceLocation(); + FD->ODRHash = Record.readInt(); + FD->HasODRHash = true; + switch ((FunctionDecl::TemplatedKind)Record.readInt()) { case FunctionDecl::TK_NonTemplate: mergeRedeclarable(FD, Redecl); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 3dac3a48297a..bb72a3b383ea 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -538,6 +538,8 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { Record.push_back(D->getLinkageInternal()); Record.AddSourceLocation(D->getLocEnd()); + Record.push_back(D->getODRHash()); + Record.push_back(D->getTemplatedKind()); switch (D->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: @@ -2072,6 +2074,7 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // TemplateKind // This Array slurps the rest of the record. Fortunately we want to encode // (nearly) all the remaining (variable number of) fields in the same way. diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp index b672695dce15..054e41a9eaea 100644 --- a/clang/test/Modules/odr_hash.cpp +++ b/clang/test/Modules/odr_hash.cpp @@ -557,11 +557,11 @@ S10 s10; #if defined(FIRST) struct S11 { - void A(int x) {} + void A(int x); }; #elif defined(SECOND) struct S11 { - void A(int y) {} + void A(int y); }; #else S11 s11; @@ -571,11 +571,11 @@ S11 s11; #if defined(FIRST) struct S12 { - void A(int x) {} + void A(int x); }; #elif defined(SECOND) struct S12 { - void A(int x = 1) {} + void A(int x = 1); }; #else S12 s12; @@ -585,11 +585,11 @@ S12 s12; #if defined(FIRST) struct S13 { - void A(int x = 1 + 0) {} + void A(int x = 1 + 0); }; #elif defined(SECOND) struct S13 { - void A(int x = 1) {} + void A(int x = 1); }; #else S13 s13; @@ -599,11 +599,11 @@ S13 s13; #if defined(FIRST) struct S14 { - void A(int x[2]) {} + void A(int x[2]); }; #elif defined(SECOND) struct S14 { - void A(int x[3]) {} + void A(int x[3]); }; #else S14 s14; @@ -2751,14 +2751,14 @@ namespace DefaultArguments { template struct S { struct R { - void foo(T x = 0) {} + void foo(T x = 0); }; }; #elif defined(SECOND) template struct S { struct R { - void foo(T x = 1) {} + void foo(T x = 1); }; }; #else @@ -2771,13 +2771,13 @@ void run() { #if defined(FIRST) template struct Bravo { - void charlie(bool delta = false) {} + void charlie(bool delta = false); }; typedef Bravo echo; echo foxtrot; #elif defined(SECOND) template struct Bravo { - void charlie(bool delta = (false)) {} + void charlie(bool delta = (false)); }; typedef Bravo echo; echo foxtrot; @@ -2788,6 +2788,142 @@ Bravo golf; #endif } // namespace DefaultArguments +namespace FunctionDecl { +#if defined(FIRST) +struct S1 {}; +S1 s1a; +#elif defined(SECOND) +struct S1 {}; +#else +S1 s1; +#endif + +#if defined(FIRST) +struct S2 { + S2() = default; +}; +S2 s2a = S2(); +#elif defined(SECOND) +struct S2 { + S2() = default; +}; +#else +S2 s2; +#endif + +#if defined(FIRST) +struct S3 { + S3() = delete; +}; +S3* s3c; +#elif defined(SECOND) +struct S3 { + S3() = delete; +}; +#else +S3* s3; +#endif + +#if defined(FIRST) || defined(SECOND) +int F1(int x, float y = 2.7) { return 1; } +#else +int I1 = F1(1); +#endif + +#if defined(FIRST) +int F2() { return 1; } +#elif defined(SECOND) +double F2() { return 1; } +#else +int I2 = F2(); +// expected-error@-1 {{call to 'F2' is ambiguous}} +// expected-note@first.h:* {{candidate function}} +// expected-note@second.h:* {{candidate function}} +#endif + +#if defined(FIRST) +int F3(float) { return 1; } +#elif defined(SECOND) +int F3(double) { return 1; } +#else +int I3 = F3(1); +// expected-error@-1 {{call to 'F3' is ambiguous}} +// expected-note@first.h:* {{candidate function}} +// expected-note@second.h:* {{candidate function}} +#endif + +#if defined(FIRST) +int F4(int x) { return 1; } +#elif defined(SECOND) +int F4(int y) { return 1; } +#else +int I4 = F4(1); +// expected-error@second.h:* {{'FunctionDecl::F4' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with name 'y'}} +// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with name 'x'}} +#endif + +#if defined(FIRST) +int F5(int x) { return 1; } +#elif defined(SECOND) +int F5(int x = 1) { return 1; } +#else +int I5 = F6(1); +// expected-error@second.h:* {{'FunctionDecl::F5' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter without a default argument}} +// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with a default argument}} +#endif + +#if defined(FIRST) +int F6(int x = 2) { return 1; } +#elif defined(SECOND) +int F6(int x = 1) { return 1; } +#else +int I6 = F6(1); +// expected-error@second.h:* {{'FunctionDecl::F6' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with a default argument}} +// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with a different default argument}} +#endif + +using I = int; +#if defined(FIRST) +I F7() { return 0; } +#elif defined(SECOND) +int F7() { return 0; } +#else +int I7 = F7(); +// expected-error@second.h:* {{'FunctionDecl::F7' has different definitions in different modules; definition in module 'SecondModule' first difference is return type is 'int'}} +// expected-note@first.h:* {{but in 'FirstModule' found different return type 'FunctionDecl::I' (aka 'int')}} +#endif + +#if defined(FIRST) +int F8(int) { return 0; } +#elif defined(SECOND) +int F8(I) { return 0; } +#else +int I8 = F8(1); +// expected-error@second.h:* {{'FunctionDecl::F8' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with type 'FunctionDecl::I' (aka 'int')}} +// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with type 'int'}} +#endif + +#if defined(FIRST) +int F9(int[1]) { return 0; } +#elif defined(SECOND) +int F9(int[2]) { return 0; } +#else +int I9 = F9(nullptr); +// expected-error@second.h:* {{'FunctionDecl::F9' has different definitions in different modules; definition in module 'SecondModule' first difference is 1st parameter with type 'int *' decayed from 'int [2]'}} +// expected-note@first.h:* {{but in 'FirstModule' found 1st parameter with type 'int *' decayed from 'int [1]'}} +#endif + +#if defined(FIRST) +int F10() { return 1; } +#elif defined(SECOND) +int F10() { return 2; } +#else +int I10 = F10(); +#endif +// expected-error@second.h:* {{'FunctionDecl::F10' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}} +// expected-note@first.h:* {{but in 'FirstModule' found a different body}} +} // namespace FunctionDecl + // Keep macros contained to one file. #ifdef FIRST #undef FIRST