From e0242d84df1df4428f9e05a115a516ba86933cc7 Mon Sep 17 00:00:00 2001 From: Justin Lebar <jlebar@google.com> Date: Mon, 15 Aug 2016 20:38:56 +0000 Subject: [PATCH] Add the notion of deferred diagnostics. Summary: This patch lets you create diagnostics that are emitted if and only if a particular FunctionDecl is codegen'ed. This is necessary for CUDA, where some constructs -- e.g. calls from host+device functions to host functions when compiling for device -- are allowed to appear in semantically-correct programs, but only if they're never codegen'ed. Reviewers: rnk Subscribers: cfe-commits, tra Differential Revision: https://reviews.llvm.org/D23241 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@278735 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 11 ++++++++++ include/clang/AST/Decl.h | 8 ++++++++ lib/AST/Decl.cpp | 14 +++++++++++++ lib/CodeGen/CodeGenModule.cpp | 37 ++++++++++++++++++++++++++++++++++ lib/CodeGen/CodeGenModule.h | 4 ++++ 5 files changed, 74 insertions(+) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index a55762622e5..45127acba44 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -324,6 +324,12 @@ class ASTContext : public RefCountedBase<ASTContext> { }; llvm::DenseMap<Module*, PerModuleInitializers*> 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<const FunctionDecl *, std::vector<PartialDiagnosticAt>> + DeferredDiags; + public: /// \brief A type synonym for the TemplateOrInstantiation mapping. typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *> @@ -597,6 +603,11 @@ public: 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; } diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 254f575782e..77bbdb261fc 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2271,6 +2271,14 @@ public: /// 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<PartialDiagnosticAt> takeDeferredDiags() const; + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4486cb89404..98711822a51 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3446,6 +3446,20 @@ unsigned FunctionDecl::getMemoryFunctionKind() const { return 0; } +void FunctionDecl::addDeferredDiag(PartialDiagnosticAt PD) { + getASTContext().getDeferredDiags()[this].push_back(std::move(PD)); +} + +std::vector<PartialDiagnosticAt> 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 //===----------------------------------------------------------------------===// diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index f20baab784b..2dcccf4c6c8 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -497,6 +497,16 @@ void CodeGenModule::Release() { 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 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast<FunctionDecl>(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); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 54354451b85..ed18156a082 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -490,6 +490,10 @@ private: /// MDNodes. llvm::DenseMap<QualType, llvm::Metadata *> MetadataIdMap; + /// Diags gathered from FunctionDecl::takeDeferredDiags(). Emitted at the + /// very end of codegen. + std::vector<std::pair<SourceLocation, PartialDiagnostic>> DeferredDiags; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, -- GitLab