Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1501,9 +1501,6 @@ public: // Emit all deferred diagnostics. void emitDeferredDiags(); - // Emit any deferred diagnostics for FD and erase them from the map in which - // they're stored. - void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack); enum TUFragmentKind { /// The global module fragment, between 'module;' and a module-declaration. Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -1436,63 +1436,87 @@ } // Print notes showing how we can reach FD starting from an a priori -// known-callable function. -static void emitCallStackNotes(Sema &S, FunctionDecl *FD) { +// known-callable function. If \HasDiags is not null pointer, the flag +// about whether diagnostics emitted for a function is set. +static void emitCallStackNotes( + Sema &S, FunctionDecl *FD, + llvm::DenseMap, bool> *HasDiags = nullptr) { auto FnIt = S.DeviceKnownEmittedFns.find(FD); while (FnIt != S.DeviceKnownEmittedFns.end()) { DiagnosticBuilder Builder( S.Diags.Report(FnIt->second.Loc, diag::note_called_by)); Builder << FnIt->second.FD; Builder.setForceEmit(); + // Set the flag about whether deferred diagnostics directly or indirectly + // triggered by a function. + if (HasDiags) + (*HasDiags)[FnIt->second.FD] = true; FnIt = S.DeviceKnownEmittedFns.find(FnIt->second.FD); } } -// Emit any deferred diagnostics for FD and erase them from the map in which -// they're stored. -void Sema::emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) { - auto It = DeviceDeferredDiags.find(FD); - if (It == DeviceDeferredDiags.end()) - return; - bool HasWarningOrError = false; - bool FirstDiag = true; - for (PartialDiagnosticAt &PDAt : It->second) { - const SourceLocation &Loc = PDAt.first; - const PartialDiagnostic &PD = PDAt.second; - HasWarningOrError |= getDiagnostics().getDiagnosticLevel( - PD.getDiagID(), Loc) >= DiagnosticsEngine::Warning; - { - DiagnosticBuilder Builder(Diags.Report(Loc, PD.getDiagID())); - Builder.setForceEmit(); - PD.Emit(Builder); - } - - // Emit the note on the first diagnostic in case too many diagnostics cause - // the note not emitted. - if (FirstDiag && HasWarningOrError && ShowCallStack) { - emitCallStackNotes(*this, FD); - FirstDiag = false; - } - } - -} - namespace { + /// Helper class that emits deferred diagnostic messages if an entity directly /// or indirectly using the function that causes the deferred diagnostic /// messages is known to be emitted. +/// +/// During parsing of AST, certain diagnostic messages are recorded as deferred +/// diagnostics since it is unknown whether the functions containing such +/// diagnostics will be emitted. A list of potentially emitted functions and +/// variables that may potentially trigger emission of functions are also +/// recorded. DeferredDiagnosticsEmitter recursively visits used functions +/// by each function to emit deferred diagnostics. +/// +/// During the visit, certain OpenMP directives or initializer of variables +/// with certain OpenMP attributes will cause subsequent visiting of any +/// functions enter a state which is called OpenMP device context in this +/// implementation. The state is exited when the directive or initializer is +/// exited. This state can change the emission states of subsequent uses +/// of functions. +/// +/// Conceptually the functions or variables to be visited form a use graph +/// where the parent node uses the child node. At any point of the visit, +/// the tree nodes traversed from the tree root to the current node form a use +/// stack. The emission state of the current node depends on two factors: +/// 1. the emission state of the root node +/// 2. whether the current node is in OpenMP device context +/// If the function is decided to be emitted, its contained deferred diagnostics +/// are emitted, together with the information about the use stack. +/// +/// The flag about whether deferred diagnostics directly or indirectly triggered +/// by a function is added. A function is visited at least once to add the flag +/// about whether it triggers diagnostics. For subsequent visits, if the flag +/// for deferred diagnostics triggered by the function is false, the function +/// is skipped, since the subtree starting at this node does not trigger any +/// deferred diagnostics and does not trigger any OpenMP device context, +/// otherwise the flag cannot be false. +/// class DeferredDiagnosticsEmitter : public UsedDeclVisitor { public: typedef UsedDeclVisitor Inherited; llvm::SmallSet, 4> Visited; llvm::SmallVector, 4> UseStack; - bool ShouldEmit; + + // Whether deferred diagnostics are triggered directly or indirectly by + // a declaration. HasDiags[0] is for the case not in OpenMP device context. + // HasDiags[1] is for the case in OpenMP device context. We need two maps + // because diagnostics emission may be different depending on whether it + // is in OpenMP device context. + llvm::DenseMap, bool> HasDiagsMaps[2]; + + // Emission state of the root node of the current use graph. + bool ShouldEmitRootNode; + + // Current OpenMP device context level. It is initialized to 0 and each + // entering of device context increases it by 1 and each exit decreases + // it by 1. Non-zero value indicates it is currently in device context. unsigned InOMPDeviceContext; DeferredDiagnosticsEmitter(Sema &S) - : Inherited(S), ShouldEmit(false), InOMPDeviceContext(0) {} + : Inherited(S), ShouldEmitRootNode(false), InOMPDeviceContext(0) {} void VisitOMPTargetDirective(OMPTargetDirective *Node) { ++InOMPDeviceContext; @@ -1525,21 +1549,26 @@ } void checkFunc(SourceLocation Loc, FunctionDecl *FD) { + auto &HasDiags = HasDiagsMaps[InOMPDeviceContext]; + auto HasDiagsIt = HasDiags.find(FD); FunctionDecl *Caller = UseStack.empty() ? nullptr : UseStack.back(); - auto IsKnownEmitted = S.getEmissionStatus(FD, /*Final=*/true) == - Sema::FunctionEmissionStatus::Emitted; - if (!Caller) - ShouldEmit = IsKnownEmitted; - if ((!ShouldEmit && !S.getLangOpts().OpenMP && !Caller) || + if ((!ShouldEmitRootNode && !S.getLangOpts().OpenMP && !Caller) || S.shouldIgnoreInHostDeviceCheck(FD) || Visited.count(FD)) return; // Finalize analysis of OpenMP-specific constructs. if (Caller && S.LangOpts.OpenMP && UseStack.size() == 1) S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc); + // If the flag for deferred diagnostics for the function is available + // and is false, the visit of the function and its used functions can be + // skipped since they will not trigger any deferred diagnostics. + else if (HasDiagsIt != HasDiags.end() && !HasDiagsIt->second) + return; + if (HasDiagsIt == HasDiags.end()) + HasDiags[FD] = false; if (Caller) S.DeviceKnownEmittedFns[FD] = {Caller, Loc}; - if (ShouldEmit || InOMPDeviceContext) - S.emitDeferredDiags(FD, Caller); + if (ShouldEmitRootNode || InOMPDeviceContext) + emitDeferredDiags(FD, Caller); Visited.insert(FD); UseStack.push_back(FD); if (auto *S = FD->getBody()) { @@ -1550,11 +1579,43 @@ } void checkRecordedDecl(Decl *D) { - if (auto *FD = dyn_cast(D)) + if (auto *FD = dyn_cast(D)) { + ShouldEmitRootNode = S.getEmissionStatus(FD, /*Final=*/true) == + Sema::FunctionEmissionStatus::Emitted; checkFunc(SourceLocation(), FD); - else + } else checkVar(cast(D)); } + + // Emit any deferred diagnostics for FD + void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) { + auto &HasDiags = HasDiagsMaps[InOMPDeviceContext]; + auto It = S.DeviceDeferredDiags.find(FD); + if (It == S.DeviceDeferredDiags.end()) + return; + bool HasWarningOrError = false; + bool FirstDiag = true; + for (PartialDiagnosticAt &PDAt : It->second) { + const SourceLocation &Loc = PDAt.first; + const PartialDiagnostic &PD = PDAt.second; + HasWarningOrError |= + S.getDiagnostics().getDiagnosticLevel(PD.getDiagID(), Loc) >= + DiagnosticsEngine::Warning; + { + DiagnosticBuilder Builder(S.Diags.Report(Loc, PD.getDiagID())); + Builder.setForceEmit(); + PD.Emit(Builder); + } + + HasDiags[FD] = true; + // Emit the note on the first diagnostic in case too many diagnostics + // cause the note not emitted. + if (FirstDiag && HasWarningOrError && ShowCallStack) { + emitCallStackNotes(S, FD, &HasDiags); + FirstDiag = false; + } + } + } }; } // namespace