diff --git a/lld/COFF/DebugTypes.h b/lld/COFF/DebugTypes.h --- a/lld/COFF/DebugTypes.h +++ b/lld/COFF/DebugTypes.h @@ -106,18 +106,18 @@ /// it is unique. This prevents a record from being added to the input ghash /// table. bool shouldOmitFromPdb(uint32_t ghashIdx) { - return ghashIdx == endPrecompGHashIdx; + return ghashIdx == endPrecompIdx; } const TpiKind kind; bool ownedGHashes = true; uint32_t tpiSrcIdx = 0; -protected: - /// The ghash index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record - /// in this object, if one exists. This is the all ones value otherwise. It is - /// recorded here so that it can be omitted from the final ghash table. - uint32_t endPrecompGHashIdx = ~0U; + /// The index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record in + /// this object, if one exists. This is the all ones value otherwise. It is + /// recorded here for validation, and so that it can be omitted from the final + /// ghash table. + uint32_t endPrecompIdx = ~0U; public: ObjFile *file; diff --git a/lld/COFF/DebugTypes.cpp b/lld/COFF/DebugTypes.cpp --- a/lld/COFF/DebugTypes.cpp +++ b/lld/COFF/DebugTypes.cpp @@ -125,18 +125,23 @@ class PrecompSource : public TpiSource { public: PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) { - if (!f->pchSignature || !*f->pchSignature) - fatal(toString(f) + - " claims to be a PCH object, but does not have a valid signature"); - auto it = ctx.precompSourceMappings.emplace(*f->pchSignature, this); - if (!it.second) - fatal("a PCH object with the same signature has already been provided (" + - toString(it.first->second->file) + " and " + toString(file) + ")"); + // If the S_OBJNAME record contains the PCH signature, we'll register this + // source file right away. + registerMapping(); } + Error mergeDebugT(TypeMerger *m) override; + void loadGHashes() override; bool isDependency() const override { return true; } + +private: + void registerMapping(); + + // Whether this precomp OBJ was recorded in the precompSourceMappings map. + // Only happens if the file->pchSignature is valid. + bool registered = false; }; // This class represents the debug type stream of an OBJ file that depends on a @@ -310,10 +315,15 @@ // When dealing with PCH.OBJ, some indices were already merged. unsigned nbHeadIndices = indexMapStorage.size(); - if (auto err = mergeTypeAndIdRecords( - m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature)) + Optional pchInfo; + if (auto err = mergeTypeAndIdRecords(m->idTable, m->typeTable, + indexMapStorage, types, pchInfo)) fatal("codeview::mergeTypeAndIdRecords failed: " + toString(std::move(err))); + if (pchInfo) { + file->pchSignature = pchInfo->PCHSignature; + endPrecompIdx = pchInfo->EndPrecompIndex; + } // In an object, there is only one mapping for both types and items. tpiMap = indexMapStorage; @@ -494,16 +504,15 @@ pr.getPrecompFilePath(), make_error(pdb::pdb_error_code::no_matching_pch)); - if (pr.getSignature() != file->pchSignature) + // Don't rely on the PCH signature to validate the concordance between the PCH + // and the OBJ that uses it. However we do validate here that the + // LF_ENDPRECOMP record index lines up with the number of type records + // LF_PRECOMP is expecting. + if (precomp->endPrecompIdx != pr.getTypesCount()) return createFileError( toString(file), make_error(pdb::pdb_error_code::no_matching_pch)); - if (pr.getSignature() != *precomp->file->pchSignature) - return createFileError( - toString(precomp->file), - make_error(pdb::pdb_error_code::no_matching_pch)); - return precomp; } @@ -541,6 +550,30 @@ return TpiSource::mergeDebugT(m); } +Error PrecompSource::mergeDebugT(TypeMerger *m) { + // In some cases, the S_OBJNAME record doesn't contain the PCH signature. + // The signature comes later with the LF_ENDPRECOMP record, so we first need + // to merge in all the .PCH.OBJ file type records, before registering below. + if (Error e = TpiSource::mergeDebugT(m)) + return e; + + registerMapping(); + + return Error::success(); +} + +void PrecompSource::registerMapping() { + if (registered) + return; + if (file->pchSignature && *file->pchSignature) { + auto it = ctx.precompSourceMappings.emplace(*file->pchSignature, this); + if (!it.second) + fatal("a PCH object with the same signature has already been provided (" + + toString(it.first->second->file) + " and " + toString(file) + ")"); + registered = true; + } +} + //===----------------------------------------------------------------------===// // Parellel GHash type merging implementation. //===----------------------------------------------------------------------===// @@ -808,8 +841,14 @@ // Remember the index of the LF_ENDPRECOMP record so it can be excluded from // the PDB. There must be an entry in the list of ghashes so that the type // indexes of the following records in the /Yc PCH object line up. - if (ty.kind() == LF_ENDPRECOMP) - endPrecompGHashIdx = ghashIdx; + if (ty.kind() == LF_ENDPRECOMP) { + EndPrecompRecord endPrecomp; + cantFail(TypeDeserializer::deserializeAs( + const_cast(ty), endPrecomp)); + file->pchSignature = endPrecomp.getSignature(); + registerMapping(); + endPrecompIdx = ghashIdx; + } hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec)); isItemIndex.push_back(isIdRecord(ty.kind())); @@ -819,9 +858,13 @@ } void UsePrecompSource::loadGHashes() { - PrecompSource *pchSrc = findPrecompSource(file, precompDependency); - if (!pchSrc) + auto e = findPrecompMap(file, precompDependency); + if (!e) { + warn(toString(e.takeError())); return; + } + + PrecompSource *pchSrc = *e; // To compute ghashes of a /Yu object file, we need to build on the ghashes of // the /Yc PCH object. After we are done hashing, discard the ghashes from the diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -734,7 +734,8 @@ if (sym->kind() == SymbolKind::S_OBJNAME) { auto objName = cantFail(SymbolDeserializer::deserializeAs( sym.get())); - pchSignature = objName.Signature; + if (objName.Signature) + pchSignature = objName.Signature; } offset += sym->length(); } @@ -800,6 +801,10 @@ if (firstType->kind() == LF_PRECOMP) { PrecompRecord precomp = cantFail( TypeDeserializer::deserializeAs(firstType->data())); + // We're better off trusting the LF_PRECOMP signature. In some cases the + // S_OBJNAME record doesn't contain a valid PCH signature. + if (precomp.Signature) + pchSignature = precomp.Signature; debugTypesObj = makeUsePrecompSource(ctx, this, precomp); // Drop the LF_PRECOMP record from the input stream. debugTypes = debugTypes.drop_front(firstType->RecordData.size()); diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -45,6 +45,9 @@ (`D137723 `_) * Switched from SHA1 to BLAKE3 for PDB type hashing / ``-gcodeview-ghash`` (`D137101 `_) +* Improvements to the PCH.OBJ files handling. Now LLD behaves the same as MSVC + link.exe when merging PCH.OBJ files that don't have the same signature. + (`D136762 `_) MinGW Improvements ------------------ diff --git a/lld/test/COFF/precomp-link.test b/lld/test/COFF/precomp-link.test --- a/lld/test/COFF/precomp-link.test +++ b/lld/test/COFF/precomp-link.test @@ -1,11 +1,13 @@ -RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:noghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY +RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s RUN: lld-link %S/Inputs/precomp.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s -RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 | FileCheck %s -check-prefix FAILURE -RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 | FileCheck %s -check-prefix FAILURE +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:noghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 +RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-invalid.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 FIXME: The following RUN line should fail, regardless of whether debug info is enabled or not. Normally this would result in an error due to missing _PchSym_ @@ -14,14 +16,11 @@ RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf 2>&1 | FileCheck %s -check-prefix FAILURE-MISSING-PRECOMPOBJ -FAILURE: warning: Cannot use debug info for '{{.*}}precomp-invalid.obj' [LNK4099] -FAILURE-NEXT: failed to load reference '{{.*}}precomp.obj': No matching precompiled header could be located. - FAILURE-MISSING-PRECOMPOBJ: warning: Cannot use debug info for '{{.*}}precomp-a.obj' [LNK4099] FAILURE-MISSING-PRECOMPOBJ-NEXT: failed to load reference '{{.*}}precomp.obj': No matching precompiled header could be located. -Check that a PCH object file with a missing S_OBJNAME record results in an -error. Edit out this record from the yaml-ified object: +Check that a PCH object file with an empty S_OBJNAME record works fine. +Edit out this record from the yaml-ified object: - Kind: S_OBJNAME ObjNameSym: Signature: 545589255 @@ -29,22 +28,27 @@ RUN: obj2yaml %S/Inputs/precomp.obj | grep -v 'SectionData: *04000000' > %t.precomp.yaml RUN: sed '/S_OBJNAME/,/ObjectName:/d' < %t.precomp.yaml > precomp-no-objname.yaml -RUN: sed 's/Signature: *545589255/Signature: 0/' < %t.precomp.yaml > precomp-zero-sig.yaml +RUN: sed '16,19s/Signature: *545589255/Signature: 0/' < %t.precomp.yaml > precomp-zero-sig.yaml RUN: yaml2obj precomp-no-objname.yaml -o %t.precomp-no-objname.obj RUN: yaml2obj precomp-zero-sig.yaml -o %t.precomp-zero-sig.obj +RUN: env LLD_IN_TEST=1 lld-link %t.precomp-no-objname.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:noghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty +RUN: env LLD_IN_TEST=1 lld-link %t.precomp-no-objname.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty +RUN: env LLD_IN_TEST=1 lld-link %t.precomp-zero-sig.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:noghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty +RUN: env LLD_IN_TEST=1 lld-link %t.precomp-zero-sig.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty -RUN: env LLD_IN_TEST=1 not lld-link %t.precomp-no-objname.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE +WARNING-NOT: warning -RUN: env LLD_IN_TEST=1 not lld-link %t.precomp-zero-sig.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-NO-SIGNATURE +Check that a PCH with wrong signature, but with right LF_PRECOMP records count, works. -FAILURE-NO-SIGNATURE: error: {{.*}}.obj claims to be a PCH object, but does not have a valid signature +RUN: sed '16,19s/Signature: *545589255/Signature: 123456789/' < %t.precomp.yaml > precomp-wrong-sig.yaml +RUN: yaml2obj precomp-wrong-sig.yaml -o %T/precomp.obj +RUN: env LLD_IN_TEST=1 lld-link %T/precomp.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:noghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty +RUN: env LLD_IN_TEST=1 lld-link %T/precomp.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug:ghash /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix WARNING --allow-empty Check that two PCH objs with duplicate signatures are an error. RUN: cp %S/Inputs/precomp.obj %t.precomp-dup.obj - RUN: env LLD_IN_TEST=1 not lld-link %S/Inputs/precomp.obj %t.precomp-dup.obj %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj /nodefaultlib /entry:main /debug /pdb:%t.pdb /out:%t.exe 2>&1 | FileCheck %s -check-prefix FAILURE-DUP-SIGNATURE - FAILURE-DUP-SIGNATURE: error: a PCH object with the same signature has already been provided ({{.*precomp.obj and .*precomp-dup.obj.*}}) @@ -52,13 +56,6 @@ CHECK-NOT: LF_PRECOMP CHECK-NOT: LF_ENDPRECOMP - -Re-run with ghash. Eventually, perhaps this will be the default. - -RUN: lld-link %S/Inputs/precomp-a.obj %S/Inputs/precomp-b.obj %S/Inputs/precomp.obj /nodefaultlib /entry:main /debug /debug:ghash /pdb:%t.pdb /out:%t.exe /opt:ref /opt:icf /summary | FileCheck %s -check-prefix SUMMARY -RUN: llvm-pdbutil dump -types %t.pdb | FileCheck %s - - SUMMARY: Summary SUMMARY-NEXT: -------------------------------------------------------------------------------- SUMMARY-NEXT: 3 Input OBJ files (expanded from all cmd-line inputs) diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h --- a/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h +++ b/llvm/include/llvm/DebugInfo/CodeView/TypeStreamMerger.h @@ -23,6 +23,13 @@ class GlobalTypeTableBuilder; class MergingTypeTableBuilder; +/// Used to forward information about PCH.OBJ (precompiled) files, when +/// applicable. +struct PCHMergerInfo { + uint32_t PCHSignature{}; + uint32_t EndPrecompIndex = ~0U; +}; + /// Merge one set of type records into another. This method assumes /// that all records are type records, and there are no Id records present. /// @@ -84,20 +91,20 @@ MergingTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, - Optional &PCHSignature); + Optional &PCHInfo); Error mergeTypeAndIdRecords(GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, ArrayRef Hashes, - Optional &PCHSignature); + Optional &PCHInfo); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, SmallVectorImpl &SourceToDest, const CVTypeArray &Types, ArrayRef Hashes, - Optional &PCHSignature); + Optional &PCHInfo); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef Types, SmallVectorImpl &SourceToDest, diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp --- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -77,7 +77,8 @@ // Local hashing entry points Error mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, - const CVTypeArray &IdsAndTypes, Optional &S); + const CVTypeArray &IdsAndTypes, + Optional &PCHInfo); Error mergeIdRecords(MergingTypeTableBuilder &Dest, ArrayRef TypeSourceToDest, const CVTypeArray &Ids); @@ -89,14 +90,14 @@ GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, ArrayRef Hashes, - Optional &S); + Optional &PCHInfo); Error mergeIdRecords(GlobalTypeTableBuilder &Dest, ArrayRef TypeSourceToDest, const CVTypeArray &Ids, ArrayRef Hashes); Error mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, ArrayRef Hashes, - Optional &S); + Optional &PCHInfo); private: Error doit(const CVTypeArray &Types); @@ -196,7 +197,7 @@ /// its type indices. SmallVector RemapStorage; - Optional PCHSignature; + Optional PCHInfo; }; } // end anonymous namespace @@ -259,12 +260,12 @@ Error TypeStreamMerger::mergeTypesAndIds(MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, - Optional &S) { + Optional &PCHInfo) { DestIdStream = &DestIds; DestTypeStream = &DestTypes; UseGlobalHashes = false; auto Err = doit(IdsAndTypes); - S = PCHSignature; + PCHInfo = this->PCHInfo; return Err; } @@ -272,12 +273,12 @@ Error TypeStreamMerger::mergeTypeRecords(GlobalTypeTableBuilder &Dest, const CVTypeArray &Types, ArrayRef Hashes, - Optional &S) { + Optional &PCHInfo) { DestGlobalTypeStream = &Dest; UseGlobalHashes = true; GlobalHashes = Hashes; auto Err = doit(Types); - S = PCHSignature; + PCHInfo = this->PCHInfo; return Err; } @@ -297,13 +298,13 @@ GlobalTypeTableBuilder &DestTypes, const CVTypeArray &IdsAndTypes, ArrayRef Hashes, - Optional &S) { + Optional &PCHInfo) { DestGlobalIdStream = &DestIds; DestGlobalTypeStream = &DestTypes; UseGlobalHashes = true; GlobalHashes = Hashes; auto Err = doit(IdsAndTypes); - S = PCHSignature; + PCHInfo = this->PCHInfo; return Err; } @@ -446,27 +447,26 @@ Error llvm::codeview::mergeTypeAndIdRecords( MergingTypeTableBuilder &DestIds, MergingTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, - Optional &PCHSignature) { + Optional &PCHInfo) { TypeStreamMerger M(SourceToDest); - return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, PCHSignature); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, PCHInfo); } Error llvm::codeview::mergeTypeAndIdRecords( GlobalTypeTableBuilder &DestIds, GlobalTypeTableBuilder &DestTypes, SmallVectorImpl &SourceToDest, const CVTypeArray &IdsAndTypes, - ArrayRef Hashes, Optional &PCHSignature) { + ArrayRef Hashes, Optional &PCHInfo) { TypeStreamMerger M(SourceToDest); - return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes, - PCHSignature); + return M.mergeTypesAndIds(DestIds, DestTypes, IdsAndTypes, Hashes, PCHInfo); } Error llvm::codeview::mergeTypeRecords(GlobalTypeTableBuilder &Dest, SmallVectorImpl &SourceToDest, const CVTypeArray &Types, ArrayRef Hashes, - Optional &PCHSignature) { + Optional &PCHInfo) { TypeStreamMerger M(SourceToDest); - return M.mergeTypeRecords(Dest, Types, Hashes, PCHSignature); + return M.mergeTypeRecords(Dest, Types, Hashes, PCHInfo); } Error llvm::codeview::mergeIdRecords(GlobalTypeTableBuilder &Dest, @@ -487,9 +487,10 @@ if (auto EC = TypeDeserializer::deserializeAs(const_cast(Type), EP)) return joinErrors(std::move(EC), errorCorruptRecord()); - if (PCHSignature) + // Only one record of this kind can appear in a OBJ. + if (PCHInfo) return errorCorruptRecord(); - PCHSignature.emplace(EP.getSignature()); + PCHInfo.emplace(PCHMergerInfo{EP.getSignature(), CurIndex.toArrayIndex()}); return false; } return true; diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp --- a/llvm/tools/llvm-readobj/COFFDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFDumper.cpp @@ -1371,17 +1371,17 @@ Obj->getFileName()); } SmallVector SourceToDest; - Optional PCHSignature; + Optional PCHInfo; if (GHash) { std::vector Hashes = GloballyHashedType::hashTypes(Types); if (Error E = mergeTypeAndIdRecords(GlobalCVIDs, GlobalCVTypes, SourceToDest, - Types, Hashes, PCHSignature)) + Types, Hashes, PCHInfo)) return reportError(std::move(E), Obj->getFileName()); } else { if (Error E = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, - PCHSignature)) + PCHInfo)) return reportError(std::move(E), Obj->getFileName()); } }