r321395 - [ODRHash] Support ODR violation detection in functions.
Eric Fiselier via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 12 20:53:05 PST 2018
Seems to be working now.
Thank you!
On Thu, Jan 11, 2018 at 9:46 PM, Richard Trieu <rtrieu at google.com> wrote:
> Hi Vedant and Eric,
>
> Please retry after r322350. I suspect an interaction between templates
> and friend functions is causing this issue. This revision disables hashing
> for friend functions for now.
>
> Richard
>
> On Thu, Jan 11, 2018 at 3:34 PM, Eric Fiselier <eric at efcs.ca> wrote:
>
>> I'm hitting the same issue as well.
>>
>> Please let me know if there is anything I can do to get this fixed
>> quickly.
>>
>> /Eric
>>
>> On Wed, Jan 3, 2018 at 5:20 PM, Richard Trieu via cfe-commits <
>> cfe-commits at lists.llvm.org> wrote:
>>
>>> Vedant,
>>>
>>> I'm looking into it.
>>>
>>>
>>> On Wed, Jan 3, 2018 at 11:12 AM, Vedant Kumar <vsk at apple.com> wrote:
>>>
>>>> Oops, the build log was too big to attach. Resending with just the bot
>>>> link, then:
>>>> http://lab.llvm.org:8080/green/view/Experimental/job/clang-s
>>>> tage2-coverage-R/2193/consoleText
>>>>
>>>> vedant
>>>>
>>>> On Jan 3, 2018, at 11:09 AM, Vedant Kumar <vsk at apple.com> wrote:
>>>>
>>>> Hi Richard,
>>>>
>>>> This commit is causing an unexpected build failure in the stage2
>>>> modules-enabled coverage bot. I've attached the build log. Here's the error:
>>>>
>>>> [3685/3899] /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/bin/clang++ -DGTEST_HAS_RTTI=0 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/lld/COFF -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/include -Itools/lld/include -I/usr/include/libxml2 -Iinclude -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/include -fPIC -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -std=c++11 -fmodules -fmodules-cache-path=/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/clang-build/module.cache -fcxx-modules -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wstring-conversion -fcolor-diagnostics -fprofile-instr-generate='/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/clang-build/profiles/%6m.profraw' -fcoverage-mapping -O3 -DNDEBUG -fno-exceptions -fno-rtti -MMD -MT tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o -MF tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o.d -o tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o -c /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF/PDB.cpp
>>>> FAILED: tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o
>>>> /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/bin/clang++ -DGTEST_HAS_RTTI=0 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/lld/COFF -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/include -Itools/lld/include -I/usr/include/libxml2 -Iinclude -I/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/include -fPIC -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -std=c++11 -fmodules -fmodules-cache-path=/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/clang-build/module.cache -fcxx-modules -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wcovered-switch-default -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wstring-conversion -fcolor-diagnostics -fprofile-instr-generate='/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/clang-build/profiles/%6m.profraw' -fcoverage-mapping -O3 -DNDEBUG -fno-exceptions -fno-rtti -MMD -MT tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o -MF tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o.d -o tools/lld/COFF/CMakeFiles/lldCOFF.dir/PDB.cpp.o -c /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF/PDB.cpp
>>>> In module 'std' imported from /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF/Config.h:16:
>>>> /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/include/c++/v1/list:389:10: error: 'std::__1::operator==' has different definitions in different modules; definition in module 'std.list' first difference is function body
>>>> bool operator==(const __list_iterator& __x, const __list_iterator& __y)
>>>> ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>> /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/include/c++/v1/list:389:10: note: but in 'std.list' found a different body
>>>> bool operator==(const __list_iterator& __x, const __list_iterator& __y)
>>>> ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>> /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/include/c++/v1/list:394:11: error: 'std::__1::operator!=' has different definitions in different modules; definition in module 'std.list' first difference is function body
>>>> bool operator!=(const __list_iterator& __x, const __list_iterator& __y)
>>>> ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>> /Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/host-compiler/include/c++/v1/list:394:11: note: but in 'std.list' found a different body
>>>> bool operator!=(const __list_iterator& __x, const __list_iterator& __y)
>>>> ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>> 2 errors generated.
>>>>
>>>> I'm not sure how to act on this, because it looks like the error is
>>>> saying that a definition in 'std.list' is incompatible with itself.
>>>>
>>>> I've temporarily disabled building with modules enabled on the bot.
>>>> Could you take a look?
>>>>
>>>> thanks,
>>>> vedant
>>>>
>>>> http://lab.llvm.org:8080/green/view/Experimental/job/clang-s
>>>> tage2-coverage-R/2193
>>>>
>>>> <coverage-build-log.txt>
>>>>
>>>>
>>>>
>>>> On Dec 22, 2017, at 4:41 PM, Richard Trieu via cfe-commits <
>>>> cfe-commits at lists.llvm.org> wrote:
>>>>
>>>> Author: rtrieu
>>>> Date: Fri Dec 22 16:41:01 2017
>>>> New Revision: 321395
>>>>
>>>> URL: http://llvm.org/viewvc/llvm-project?rev=321395&view=rev
>>>> Log:
>>>> [ODRHash] Support ODR violation detection in functions.
>>>>
>>>> Extend the hashing to functions, which allows detection of function
>>>> definition
>>>> mismatches across modules. This is a re-commit of r320230.
>>>>
>>>> Modified:
>>>> cfe/trunk/include/clang/AST/Decl.h
>>>> cfe/trunk/include/clang/AST/ODRHash.h
>>>> cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>>>> cfe/trunk/include/clang/Serialization/ASTReader.h
>>>> cfe/trunk/lib/AST/Decl.cpp
>>>> cfe/trunk/lib/AST/ODRHash.cpp
>>>> cfe/trunk/lib/Serialization/ASTReader.cpp
>>>> cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
>>>> cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
>>>> cfe/trunk/test/Modules/odr_hash.cpp
>>>>
>>>> Modified: cfe/trunk/include/clang/AST/Decl.h
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> AST/Decl.h?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/AST/Decl.h (original)
>>>> +++ cfe/trunk/include/clang/AST/Decl.h Fri Dec 22 16:41:01 2017
>>>> @@ -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<FunctionDecl>;
>>>>
>>>> @@ -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) {
>>>>
>>>> Modified: cfe/trunk/include/clang/AST/ODRHash.h
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> AST/ODRHash.h?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/AST/ODRHash.h (original)
>>>> +++ cfe/trunk/include/clang/AST/ODRHash.h Fri Dec 22 16:41:01 2017
>>>> @@ -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);
>>>>
>>>> Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> Basic/DiagnosticSerializationKinds.td?rev=321395&r1=321394&r
>>>> 2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
>>>> (original)
>>>> +++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Fri
>>>> Dec 22 16:41:01 2017
>>>> @@ -270,6 +270,29 @@ def note_module_odr_violation_mismatch_d
>>>> "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 "
>>>>
>>>> Modified: cfe/trunk/include/clang/Serialization/ASTReader.h
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/
>>>> Serialization/ASTReader.h?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/include/clang/Serialization/ASTReader.h (original)
>>>> +++ cfe/trunk/include/clang/Serialization/ASTReader.h Fri Dec 22
>>>> 16:41:01 2017
>>>> @@ -1092,6 +1092,10 @@ private:
>>>> llvm::SmallDenseMap<CXXRecordDecl *, llvm::SmallVector<DataPointers,
>>>> 2>, 2>
>>>> PendingOdrMergeFailures;
>>>>
>>>> + /// \brief Function definitions in which we found an ODR violation.
>>>> + llvm::SmallDenseMap<FunctionDecl *, llvm::SmallVector<FunctionDecl
>>>> *, 2>, 2>
>>>> + PendingFunctionOdrMergeFailures;
>>>> +
>>>> /// \brief DeclContexts in which we have diagnosed an ODR violation.
>>>> llvm::SmallPtrSet<DeclContext*, 2> DiagnosedOdrMergeFailures;
>>>>
>>>>
>>>> Modified: cfe/trunk/lib/AST/Decl.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.c
>>>> pp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/AST/Decl.cpp (original)
>>>> +++ cfe/trunk/lib/AST/Decl.cpp Fri Dec 22 16:41:01 2017
>>>> @@ -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::getMemoryFunction
>>>> 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
>>>> //===-------------------------------------------------------
>>>> ---------------===//
>>>>
>>>> Modified: cfe/trunk/lib/AST/ODRHash.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHas
>>>> h.cpp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/AST/ODRHash.cpp (original)
>>>> +++ cfe/trunk/lib/AST/ODRHash.cpp Fri Dec 22 16:41:01 2017
>>>> @@ -466,6 +466,36 @@ void ODRHash::AddCXXRecordDecl(const CXX
>>>> }
>>>> }
>>>>
>>>> +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<ClassTemplateSpecializationDecl>(DC)) return;
>>>> + if (auto *F = dyn_cast<FunctionDecl>(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();
>>>>
>>>> Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serializat
>>>> ion/ASTReader.cpp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
>>>> +++ cfe/trunk/lib/Serialization/ASTReader.cpp Fri Dec 22 16:41:01 2017
>>>> @@ -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<FunctionDecl*>(Defn),
>>>> FD);
>>>> + } else {
>>>> + auto *NonConstDefn = const_cast<FunctionDecl*>(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(PendingFunctionOdrMe
>>>> rgeFailures);
>>>> + 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 = getOwningModuleNameForDiagnost
>>>> ic(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<DecayedType>()) {
>>>> + 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<DecayedType>()) {
>>>> + 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() {
>>>>
>>>> Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serializat
>>>> ion/ASTReaderDecl.cpp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
>>>> +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Fri Dec 22 16:41:01
>>>> 2017
>>>> @@ -796,6 +796,9 @@ void ASTDeclReader::VisitFunctionDecl(Fu
>>>> 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);
>>>>
>>>> Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serializat
>>>> ion/ASTWriterDecl.cpp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
>>>> +++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Fri Dec 22 16:41:01
>>>> 2017
>>>> @@ -538,6 +538,8 @@ void ASTDeclWriter::VisitFunctionDecl(Fu
>>>> 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.
>>>>
>>>> Modified: cfe/trunk/test/Modules/odr_hash.cpp
>>>> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/o
>>>> dr_hash.cpp?rev=321395&r1=321394&r2=321395&view=diff
>>>> ============================================================
>>>> ==================
>>>> --- cfe/trunk/test/Modules/odr_hash.cpp (original)
>>>> +++ cfe/trunk/test/Modules/odr_hash.cpp Fri Dec 22 16:41:01 2017
>>>> @@ -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 <typename T>
>>>> struct S {
>>>> struct R {
>>>> - void foo(T x = 0) {}
>>>> + void foo(T x = 0);
>>>> };
>>>> };
>>>> #elif defined(SECOND)
>>>> template <typename T>
>>>> 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 <typename alpha> struct Bravo {
>>>> - void charlie(bool delta = false) {}
>>>> + void charlie(bool delta = false);
>>>> };
>>>> typedef Bravo<char> echo;
>>>> echo foxtrot;
>>>> #elif defined(SECOND)
>>>> template <typename alpha> struct Bravo {
>>>> - void charlie(bool delta = (false)) {}
>>>> + void charlie(bool delta = (false));
>>>> };
>>>> typedef Bravo<char> echo;
>>>> echo foxtrot;
>>>> @@ -2788,6 +2788,142 @@ Bravo<char> 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 at -1 {{call to 'F2' is ambiguous}}
>>>> +// expected-note at first.h:* {{candidate function}}
>>>> +// expected-note at 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 at -1 {{call to 'F3' is ambiguous}}
>>>> +// expected-note at first.h:* {{candidate function}}
>>>> +// expected-note at 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 at second.h:* {{'FunctionDecl::F4' has different
>>>> definitions in different modules; definition in module 'SecondModule' first
>>>> difference is 1st parameter with name 'y'}}
>>>> +// expected-note at 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 at 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 at 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 at 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 at 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 at second.h:* {{'FunctionDecl::F7' has different
>>>> definitions in different modules; definition in module 'SecondModule' first
>>>> difference is return type is 'int'}}
>>>> +// expected-note at 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 at 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 at 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 at 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 at 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 at second.h:* {{'FunctionDecl::F10' has different
>>>> definitions in different modules; definition in module 'SecondModule' first
>>>> difference is function body}}
>>>> +// expected-note at first.h:* {{but in 'FirstModule' found a different
>>>> body}}
>>>> +} // namespace FunctionDecl
>>>> +
>>>> // Keep macros contained to one file.
>>>> #ifdef FIRST
>>>> #undef FIRST
>>>>
>>>>
>>>> _______________________________________________
>>>> cfe-commits mailing list
>>>> cfe-commits at lists.llvm.org
>>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>>
>>>>
>>>>
>>>>
>>>
>>> _______________________________________________
>>> cfe-commits mailing list
>>> cfe-commits at lists.llvm.org
>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180112/bc9b197c/attachment-0001.html>
More information about the cfe-commits
mailing list