diff options
Diffstat (limited to 'clang/lib/Sema/Sema.cpp')
-rw-r--r-- | clang/lib/Sema/Sema.cpp | 372 |
1 files changed, 263 insertions, 109 deletions
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2cd158a8b43c1..2f2b52106f3d4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "UsedDeclVisitor.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/DeclCXX.h" @@ -22,6 +23,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/Stack.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/HeaderSearch.h" @@ -52,6 +54,21 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } +IdentifierInfo * +Sema::InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned int Index) { + std::string InventedName; + llvm::raw_string_ostream OS(InventedName); + + if (!ParamName) + OS << "auto:" << Index + 1; + else + OS << ParamName->getName() << ":auto"; + + OS.flush(); + return &Context.Idents.get(OS.str()); +} + PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context, const Preprocessor &PP) { PrintingPolicy Policy = Context.getPrintingPolicy(); @@ -127,10 +144,13 @@ public: } // end namespace sema } // end namespace clang +const unsigned Sema::MaxAlignmentExponent; +const unsigned Sema::MaximumAlignment; + Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter) : ExternalSource(nullptr), isMultiplexExternalSource(false), - FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), + CurFPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), CollectStats(false), CodeCompleter(CodeCompleter), CurContext(nullptr), @@ -139,8 +159,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, LangOpts.getMSPointerToMemberRepresentationMethod()), VtorDispStack(LangOpts.getVtorDispMode()), PackStack(0), DataSegStack(nullptr), BSSSegStack(nullptr), ConstSegStack(nullptr), - CodeSegStack(nullptr), CurInitSeg(nullptr), VisContext(nullptr), - PragmaAttributeCurrentTargetDecl(nullptr), + CodeSegStack(nullptr), FpPragmaStack(0xffffffff), CurInitSeg(nullptr), + VisContext(nullptr), PragmaAttributeCurrentTargetDecl(nullptr), IsBuildingRecoveryCallExpr(false), Cleanup{}, LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr), IdResolver(pp), StdExperimentalNamespaceCache(nullptr), StdInitializerList(nullptr), @@ -153,10 +173,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TUKind(TUKind), NumSFINAEErrors(0), FullyCheckedComparisonCategories( static_cast<unsigned>(ComparisonCategoryType::Last) + 1), - AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), - NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), - CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), - TyposCorrected(0), AnalysisWarnings(*this), + SatisfactionCache(Context), AccessCheckingSFINAE(false), + InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), + ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), + DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr), CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; @@ -379,6 +399,14 @@ Sema::~Sema() { if (isMultiplexExternalSource) delete ExternalSource; + // Delete cached satisfactions. + std::vector<ConstraintSatisfaction *> Satisfactions; + Satisfactions.reserve(Satisfactions.size()); + for (auto &Node : SatisfactionCache) + Satisfactions.push_back(&Node); + for (auto *Node : Satisfactions) + delete Node; + threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); // Destroys data sharing attributes stack for OpenMP @@ -928,9 +956,7 @@ void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) { PerformPendingInstantiations(); } - // Finalize analysis of OpenMP-specific constructs. - if (LangOpts.OpenMP) - finalizeOpenMPDelayedAnalysis(); + emitDeferredDiags(); assert(LateParsedInstantiations.empty() && "end of TU template instantiation should not create more " @@ -983,6 +1009,11 @@ void Sema::ActOnEndOfTranslationUnit() { LateParsedInstantiations.begin(), LateParsedInstantiations.end()); LateParsedInstantiations.clear(); + + if (LangOpts.PCHInstantiateTemplates) { + llvm::TimeTraceScope TimeScope("PerformPendingInstantiations"); + PerformPendingInstantiations(); + } } DiagnoseUnterminatedPragmaPack(); @@ -1261,7 +1292,8 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC)) { + if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) || + isa<RequiresExprBodyDecl>(DC)) { DC = DC->getParent(); } else if (isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call && @@ -1413,38 +1445,184 @@ Sema::Diag(SourceLocation Loc, const PartialDiagnostic& PD) { static void emitCallStackNotes(Sema &S, FunctionDecl *FD) { auto FnIt = S.DeviceKnownEmittedFns.find(FD); while (FnIt != S.DeviceKnownEmittedFns.end()) { + // Respect error limit. + if (S.Diags.hasFatalErrorOccurred()) + return; DiagnosticBuilder Builder( S.Diags.Report(FnIt->second.Loc, diag::note_called_by)); Builder << FnIt->second.FD; - Builder.setForceEmit(); - FnIt = S.DeviceKnownEmittedFns.find(FnIt->second.FD); } } -// Emit any deferred diagnostics for FD and erase them from the map in which -// they're stored. -static void emitDeferredDiags(Sema &S, FunctionDecl *FD, bool ShowCallStack) { - auto It = S.DeviceDeferredDiags.find(FD); - if (It == S.DeviceDeferredDiags.end()) - return; - bool HasWarningOrError = false; - 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); +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. +/// +class DeferredDiagnosticsEmitter + : public UsedDeclVisitor<DeferredDiagnosticsEmitter> { +public: + typedef UsedDeclVisitor<DeferredDiagnosticsEmitter> Inherited; + + // Whether the function is already in the current use-path. + llvm::SmallSet<CanonicalDeclPtr<Decl>, 4> InUsePath; + + // The current use-path. + llvm::SmallVector<CanonicalDeclPtr<FunctionDecl>, 4> UsePath; + + // Whether the visiting of the function has been done. Done[0] is for the + // case not in OpenMP device context. Done[1] is for the case in OpenMP + // device context. We need two sets because diagnostics emission may be + // different depending on whether it is in OpenMP device context. + llvm::SmallSet<CanonicalDeclPtr<Decl>, 4> DoneMap[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), ShouldEmitRootNode(false), InOMPDeviceContext(0) {} + + void VisitOMPTargetDirective(OMPTargetDirective *Node) { + ++InOMPDeviceContext; + Inherited::VisitOMPTargetDirective(Node); + --InOMPDeviceContext; + } + + void visitUsedDecl(SourceLocation Loc, Decl *D) { + if (isa<VarDecl>(D)) + return; + if (auto *FD = dyn_cast<FunctionDecl>(D)) + checkFunc(Loc, FD); + else + Inherited::visitUsedDecl(Loc, D); + } + + void checkVar(VarDecl *VD) { + assert(VD->isFileVarDecl() && + "Should only check file-scope variables"); + if (auto *Init = VD->getInit()) { + auto DevTy = OMPDeclareTargetDeclAttr::getDeviceType(VD); + bool IsDev = DevTy && (*DevTy == OMPDeclareTargetDeclAttr::DT_NoHost || + *DevTy == OMPDeclareTargetDeclAttr::DT_Any); + if (IsDev) + ++InOMPDeviceContext; + this->Visit(Init); + if (IsDev) + --InOMPDeviceContext; + } + } + + void checkFunc(SourceLocation Loc, FunctionDecl *FD) { + auto &Done = DoneMap[InOMPDeviceContext > 0 ? 1 : 0]; + FunctionDecl *Caller = UsePath.empty() ? nullptr : UsePath.back(); + if ((!ShouldEmitRootNode && !S.getLangOpts().OpenMP && !Caller) || + S.shouldIgnoreInHostDeviceCheck(FD) || InUsePath.count(FD)) + return; + // Finalize analysis of OpenMP-specific constructs. + if (Caller && S.LangOpts.OpenMP && UsePath.size() == 1) + S.finalizeOpenMPDelayedAnalysis(Caller, FD, Loc); + if (Caller) + S.DeviceKnownEmittedFns[FD] = {Caller, Loc}; + // Always emit deferred diagnostics for the direct users. This does not + // lead to explosion of diagnostics since each user is visited at most + // twice. + if (ShouldEmitRootNode || InOMPDeviceContext) + emitDeferredDiags(FD, Caller); + // Do not revisit a function if the function body has been completely + // visited before. + if (!Done.insert(FD).second) + return; + InUsePath.insert(FD); + UsePath.push_back(FD); + if (auto *S = FD->getBody()) { + this->Visit(S); + } + UsePath.pop_back(); + InUsePath.erase(FD); + } + + void checkRecordedDecl(Decl *D) { + if (auto *FD = dyn_cast<FunctionDecl>(D)) { + ShouldEmitRootNode = S.getEmissionStatus(FD, /*Final=*/true) == + Sema::FunctionEmissionStatus::Emitted; + checkFunc(SourceLocation(), FD); + } else + checkVar(cast<VarDecl>(D)); + } + + // Emit any deferred diagnostics for FD + void emitDeferredDiags(FunctionDecl *FD, bool ShowCallStack) { + auto It = S.DeviceDeferredDiags.find(FD); + if (It == S.DeviceDeferredDiags.end()) + return; + bool HasWarningOrError = false; + bool FirstDiag = true; + for (PartialDiagnosticAt &PDAt : It->second) { + // Respect error limit. + if (S.Diags.hasFatalErrorOccurred()) + return; + 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())); + 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(S, FD); + FirstDiag = false; + } + } } - S.DeviceDeferredDiags.erase(It); +}; +} // namespace + +void Sema::emitDeferredDiags() { + if (ExternalSource) + ExternalSource->ReadDeclsToCheckForDeferredDiags( + DeclsToCheckForDeferredDiags); + + if ((DeviceDeferredDiags.empty() && !LangOpts.OpenMP) || + DeclsToCheckForDeferredDiags.empty()) + return; - // FIXME: Should this be called after every warning/error emitted in the loop - // above, instead of just once per function? That would be consistent with - // how we handle immediate errors, but it also seems like a bit much. - if (HasWarningOrError && ShowCallStack) - emitCallStackNotes(S, FD); + DeferredDiagnosticsEmitter DDE(*this); + for (auto D : DeclsToCheckForDeferredDiags) + DDE.checkRecordedDecl(D); } // In CUDA, there are some constructs which may appear in semantically-valid @@ -1517,71 +1695,6 @@ Sema::DeviceDiagBuilder::~DeviceDiagBuilder() { } } -// Indicate that this function (and thus everything it transtively calls) will -// be codegen'ed, and emit any deferred diagnostics on this function and its -// (transitive) callees. -void Sema::markKnownEmitted( - Sema &S, FunctionDecl *OrigCaller, FunctionDecl *OrigCallee, - SourceLocation OrigLoc, - const llvm::function_ref<bool(Sema &, FunctionDecl *)> IsKnownEmitted) { - // Nothing to do if we already know that FD is emitted. - if (IsKnownEmitted(S, OrigCallee)) { - assert(!S.DeviceCallGraph.count(OrigCallee)); - return; - } - - // We've just discovered that OrigCallee is known-emitted. Walk our call - // graph to see what else we can now discover also must be emitted. - - struct CallInfo { - FunctionDecl *Caller; - FunctionDecl *Callee; - SourceLocation Loc; - }; - llvm::SmallVector<CallInfo, 4> Worklist = {{OrigCaller, OrigCallee, OrigLoc}}; - llvm::SmallSet<CanonicalDeclPtr<FunctionDecl>, 4> Seen; - Seen.insert(OrigCallee); - while (!Worklist.empty()) { - CallInfo C = Worklist.pop_back_val(); - assert(!IsKnownEmitted(S, C.Callee) && - "Worklist should not contain known-emitted functions."); - S.DeviceKnownEmittedFns[C.Callee] = {C.Caller, C.Loc}; - emitDeferredDiags(S, C.Callee, C.Caller); - - // If this is a template instantiation, explore its callgraph as well: - // Non-dependent calls are part of the template's callgraph, while dependent - // calls are part of to the instantiation's call graph. - if (auto *Templ = C.Callee->getPrimaryTemplate()) { - FunctionDecl *TemplFD = Templ->getAsFunction(); - if (!Seen.count(TemplFD) && !S.DeviceKnownEmittedFns.count(TemplFD)) { - Seen.insert(TemplFD); - Worklist.push_back( - {/* Caller = */ C.Caller, /* Callee = */ TemplFD, C.Loc}); - } - } - - // Add all functions called by Callee to our worklist. - auto CGIt = S.DeviceCallGraph.find(C.Callee); - if (CGIt == S.DeviceCallGraph.end()) - continue; - - for (std::pair<CanonicalDeclPtr<FunctionDecl>, SourceLocation> FDLoc : - CGIt->second) { - FunctionDecl *NewCallee = FDLoc.first; - SourceLocation CallLoc = FDLoc.second; - if (Seen.count(NewCallee) || IsKnownEmitted(S, NewCallee)) - continue; - Seen.insert(NewCallee); - Worklist.push_back( - {/* Caller = */ C.Callee, /* Callee = */ NewCallee, CallLoc}); - } - - // C.Callee is now known-emitted, so we no longer need to maintain its list - // of callees in DeviceCallGraph. - S.DeviceCallGraph.erase(CGIt); - } -} - Sema::DeviceDiagBuilder Sema::targetDiag(SourceLocation Loc, unsigned DiagID) { if (LangOpts.OpenMP) return LangOpts.OpenMPIsDevice ? diagIfOpenMPDeviceCode(Loc, DiagID) @@ -1589,10 +1702,59 @@ Sema::DeviceDiagBuilder Sema::targetDiag(SourceLocation Loc, unsigned DiagID) { if (getLangOpts().CUDA) return getLangOpts().CUDAIsDevice ? CUDADiagIfDeviceCode(Loc, DiagID) : CUDADiagIfHostCode(Loc, DiagID); + + if (getLangOpts().SYCLIsDevice) + return SYCLDiagIfDeviceCode(Loc, DiagID); + return DeviceDiagBuilder(DeviceDiagBuilder::K_Immediate, Loc, DiagID, getCurFunctionDecl(), *this); } +void Sema::checkDeviceDecl(const ValueDecl *D, SourceLocation Loc) { + if (isUnevaluatedContext()) + return; + + Decl *C = cast<Decl>(getCurLexicalContext()); + + // Memcpy operations for structs containing a member with unsupported type + // are ok, though. + if (const auto *MD = dyn_cast<CXXMethodDecl>(C)) { + if ((MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) && + MD->isTrivial()) + return; + + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(MD)) + if (Ctor->isCopyOrMoveConstructor() && Ctor->isTrivial()) + return; + } + + auto CheckType = [&](QualType Ty) { + if (Ty->isDependentType()) + return; + + if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type()) || + ((Ty->isFloat128Type() || + (Ty->isRealFloatingType() && Context.getTypeSize(Ty) == 128)) && + !Context.getTargetInfo().hasFloat128Type()) || + (Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 && + !Context.getTargetInfo().hasInt128Type())) { + targetDiag(Loc, diag::err_device_unsupported_type) + << D << static_cast<unsigned>(Context.getTypeSize(Ty)) << Ty + << Context.getTargetInfo().getTriple().str(); + targetDiag(D->getLocation(), diag::note_defined_here) << D; + } + }; + + QualType Ty = D->getType(); + CheckType(Ty); + + if (const auto *FPTy = dyn_cast<FunctionProtoType>(Ty)) { + for (const auto &ParamTy : FPTy->param_types()) + CheckType(ParamTy); + CheckType(FPTy->getReturnType()); + } +} + /// Looks through the macro-expansion chain for the given /// location, looking for a macro expansion with the given name. /// If one is found, returns true and sets the location to that @@ -1794,7 +1956,7 @@ void Sema::PopCompoundScope() { /// Determine whether any errors occurred within this function/method/ /// block. bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const { - return getCurFunction()->ErrorTrap.hasUnrecoverableErrorOccurred(); + return getCurFunction()->hasUnrecoverableErrorOccurred(); } void Sema::setFunctionHasBranchIntoScope() { @@ -2246,16 +2408,8 @@ std::string Sema::getOpenCLExtensionsFromTypeExtMap(FunctionType *FT) { template <typename T, typename MapT> std::string Sema::getOpenCLExtensionsFromExtMap(T *FDT, MapT &Map) { - std::string ExtensionNames = ""; auto Loc = Map.find(FDT); - - for (auto const& I : Loc->second) { - ExtensionNames += I; - ExtensionNames += " "; - } - ExtensionNames.pop_back(); - - return ExtensionNames; + return llvm::join(Loc->second, " "); } bool Sema::isOpenCLDisabledDecl(Decl *FD) { |