Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -324,6 +324,12 @@ }; llvm::DenseMap ModuleInitializers; + /// Diagnostics that are emitted if and only if the given function is + /// codegen'ed. Access these through FunctionDecl::addDeferredDiag() and + /// FunctionDecl::takeDeferredDiags(). + llvm::DenseMap> + DeferredDiags; + public: /// \brief A type synonym for the TemplateOrInstantiation mapping. typedef llvm::PointerUnion @@ -597,6 +603,11 @@ return DiagAllocator; } + decltype(DeferredDiags) &getDeferredDiags() { return DeferredDiags; } + const decltype(DeferredDiags) &getDeferredDiags() const { + return DeferredDiags; + } + const TargetInfo &getTargetInfo() const { return *Target; } const TargetInfo *getAuxTargetInfo() const { return AuxTarget; } Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -2271,6 +2271,14 @@ /// returns 0. unsigned getMemoryFunctionKind() const; + /// Add a diagnostic to be emitted if and when this function is codegen'ed. + void addDeferredDiag(PartialDiagnosticAt PD); + + /// Gets this object's list of deferred diagnostics, if there are any. + /// + /// Although this is logically const, it clears our list of deferred diags. + std::vector takeDeferredDiags() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -3446,6 +3446,20 @@ return 0; } +void FunctionDecl::addDeferredDiag(PartialDiagnosticAt PD) { + getASTContext().getDeferredDiags()[this].push_back(std::move(PD)); +} + +std::vector FunctionDecl::takeDeferredDiags() const { + auto &DD = getASTContext().getDeferredDiags(); + auto It = DD.find(this); + if (It == DD.end()) + return {}; + auto Ret = std::move(It->second); + DD.erase(It); + return Ret; +} + //===----------------------------------------------------------------------===// // FieldDecl Implementation //===----------------------------------------------------------------------===// Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -490,6 +490,10 @@ /// MDNodes. llvm::DenseMap MetadataIdMap; + /// Diags gathered from FunctionDecl::takeDeferredDiags(). Emitted at the + /// very end of codegen. + std::vector> DeferredDiags; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -497,6 +497,16 @@ EmitVersionIdentMetadata(); EmitTargetMetadata(); + + // Emit any deferred diagnostics gathered during codegen. We didn't emit them + // when we first discovered them because that would have halted codegen, + // preventing us from gathering other deferred diags. + for (const PartialDiagnosticAt &DiagAt : DeferredDiags) { + SourceLocation Loc = DiagAt.first; + const PartialDiagnostic &PD = DiagAt.second; + DiagnosticBuilder Builder(getDiags().Report(Loc, PD.getDiagID())); + PD.Emit(Builder); + } } void CodeGenModule::UpdateCompletedType(const TagDecl *TD) { @@ -2872,6 +2882,33 @@ llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); + // Emit this function's deferred diagnostics, if none of them are errors. If + // any of them are errors, don't codegen the function, but also don't emit any + // of the diagnostics just yet. Emitting an error during codegen stops + // further codegen, and we want to display as many deferred diags as possible. + // We'll emit the now twice-deferred diags at the very end of codegen. + // + // (If a function has both error and non-error diags, we don't emit the + // non-error diags here, because order can be significant, e.g. with notes + // that follow errors.) + auto Diags = D->takeDeferredDiags(); + bool HasError = llvm::any_of(Diags, [this](const PartialDiagnosticAt &PDAt) { + return getDiags().getDiagnosticLevel(PDAt.second.getDiagID(), PDAt.first) >= + DiagnosticsEngine::Error; + }); + if (HasError) { + DeferredDiags.insert(DeferredDiags.end(), + std::make_move_iterator(Diags.begin()), + std::make_move_iterator(Diags.end())); + return; + } + for (PartialDiagnosticAt &PDAt : Diags) { + const SourceLocation &Loc = PDAt.first; + const PartialDiagnostic &PD = PDAt.second; + DiagnosticBuilder Builder(getDiags().Report(Loc, PD.getDiagID())); + PD.Emit(Builder); + } + // Compute the function info and LLVM type. const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD); llvm::FunctionType *Ty = getTypes().GetFunctionType(FI);