diff options
Diffstat (limited to 'clang/lib/Sema')
| -rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.cpp | 105 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 17 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 43 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaOpenMP.cpp | 126 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 |
5 files changed, 204 insertions, 93 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 140b709..41a9832 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2734,6 +2734,70 @@ static void flushDiagnostics(Sema &S, const sema::FunctionScopeInfo *fscope) { S.Diag(D.Loc, D.PD); } +template <typename Iterator> +static void emitPossiblyUnreachableDiags(Sema &S, AnalysisDeclContext &AC, + std::pair<Iterator, Iterator> PUDs) { + + if (PUDs.first == PUDs.second) + return; + + for (auto I = PUDs.first; I != PUDs.second; ++I) { + for (const Stmt *S : I->Stmts) + AC.registerForcedBlockExpression(S); + } + + if (AC.getCFG()) { + CFGReverseBlockReachabilityAnalysis *Analysis = + AC.getCFGReachablityAnalysis(); + + for (auto I = PUDs.first; I != PUDs.second; ++I) { + const auto &D = *I; + if (llvm::all_of(D.Stmts, [&](const Stmt *St) { + const CFGBlock *Block = AC.getBlockForRegisteredExpression(St); + // FIXME: We should be able to assert that block is non-null, but + // the CFG analysis can skip potentially-evaluated expressions in + // edge cases; see test/Sema/vla-2.c. + if (Block && Analysis) + if (!Analysis->isReachable(&AC.getCFG()->getEntry(), Block)) + return false; + return true; + })) { + S.Diag(D.Loc, D.PD); + } + } + } else { + for (auto I = PUDs.first; I != PUDs.second; ++I) + S.Diag(I->Loc, I->PD); + } +} + +void sema::AnalysisBasedWarnings::registerVarDeclWarning( + VarDecl *VD, clang::sema::PossiblyUnreachableDiag PUD) { + VarDeclPossiblyUnreachableDiags.emplace(VD, PUD); +} + +void sema::AnalysisBasedWarnings::issueWarningsForRegisteredVarDecl( + VarDecl *VD) { + if (!llvm::is_contained(VarDeclPossiblyUnreachableDiags, VD)) + return; + + AnalysisDeclContext AC(/*Mgr=*/nullptr, VD); + + AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true; + AC.getCFGBuildOptions().AddEHEdges = false; + AC.getCFGBuildOptions().AddInitializers = true; + AC.getCFGBuildOptions().AddImplicitDtors = true; + AC.getCFGBuildOptions().AddTemporaryDtors = true; + AC.getCFGBuildOptions().AddCXXNewAllocator = false; + AC.getCFGBuildOptions().AddCXXDefaultInitExprInCtors = true; + + auto Range = VarDeclPossiblyUnreachableDiags.equal_range(VD); + auto SecondRange = + llvm::make_second_range(llvm::make_range(Range.first, Range.second)); + emitPossiblyUnreachableDiags( + S, AC, std::make_pair(SecondRange.begin(), SecondRange.end())); +} + // An AST Visitor that calls a callback function on each callable DEFINITION // that is NOT in a dependent context: class CallableVisitor : public DynamicRecursiveASTVisitor { @@ -2945,45 +3009,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( } // Emit delayed diagnostics. - if (!fscope->PossiblyUnreachableDiags.empty()) { - bool analyzed = false; - - // Register the expressions with the CFGBuilder. - for (const auto &D : fscope->PossiblyUnreachableDiags) { - for (const Stmt *S : D.Stmts) - AC.registerForcedBlockExpression(S); - } - - if (AC.getCFG()) { - analyzed = true; - for (const auto &D : fscope->PossiblyUnreachableDiags) { - bool AllReachable = true; - for (const Stmt *S : D.Stmts) { - const CFGBlock *block = AC.getBlockForRegisteredExpression(S); - CFGReverseBlockReachabilityAnalysis *cra = - AC.getCFGReachablityAnalysis(); - // FIXME: We should be able to assert that block is non-null, but - // the CFG analysis can skip potentially-evaluated expressions in - // edge cases; see test/Sema/vla-2.c. - if (block && cra) { - // Can this block be reached from the entrance? - if (!cra->isReachable(&AC.getCFG()->getEntry(), block)) { - AllReachable = false; - break; - } - } - // If we cannot map to a basic block, assume the statement is - // reachable. - } - - if (AllReachable) - S.Diag(D.Loc, D.PD); - } - } - - if (!analyzed) - flushDiagnostics(S, fscope); - } + auto &PUDs = fscope->PossiblyUnreachableDiags; + emitPossiblyUnreachableDiags(S, AC, std::make_pair(PUDs.begin(), PUDs.end())); // Warning: check missing 'return' if (P.enableCheckFallThrough) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 086dd8b..25b89d6 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -59,6 +59,7 @@ #include "clang/Sema/SemaWasm.h" #include "clang/Sema/Template.h" #include "llvm/ADT/STLForwardCompat.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -13117,6 +13118,13 @@ namespace { if (isa<ParmVarDecl>(OrigDecl)) return; + // Skip checking for file-scope constexpr variables - constant evaluation + // will produce appropriate errors without needing runtime diagnostics. + // Local constexpr should still emit runtime warnings. + if (auto *VD = dyn_cast<VarDecl>(OrigDecl); + VD && VD->isConstexpr() && VD->isFileVarDecl()) + return; + E = E->IgnoreParens(); // Skip checking T a = a where T is not a record or reference type. @@ -13744,6 +13752,11 @@ void Sema::DiagnoseUniqueObjectDuplication(const VarDecl *VD) { } void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { + auto ResetDeclForInitializer = llvm::make_scope_exit([this]() { + if (this->ExprEvalContexts.empty()) + this->ExprEvalContexts.back().DeclForInitializer = nullptr; + }); + // If there is no declaration, there was an error parsing it. Just ignore // the initializer. if (!RealDecl) { @@ -15069,6 +15082,10 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { if (!VD) return; + // Emit any deferred warnings for the variable's initializer, even if the + // variable is invalid + AnalysisWarnings.issueWarningsForRegisteredVarDecl(VD); + // Apply an implicit SectionAttr if '#pragma clang section bss|data|rodata' is active if (VD->hasGlobalStorage() && VD->isThisDeclarationADefinition() && !inTemplateInstantiation() && !VD->hasAttr<SectionAttr>()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 2159a0d..10f0ec3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -20565,31 +20565,36 @@ void Sema::MarkDeclarationsReferencedInExpr(Expr *E, } /// Emit a diagnostic when statements are reachable. -/// FIXME: check for reachability even in expressions for which we don't build a -/// CFG (eg, in the initializer of a global or in a constant expression). -/// For example, -/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef<const Stmt *> Stmts, const PartialDiagnostic &PD) { - if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { - if (!FunctionScopes.empty()) - FunctionScopes.back()->PossiblyUnreachableDiags.push_back( - sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); - return true; - } - + VarDecl *Decl = ExprEvalContexts.back().DeclForInitializer; // The initializer of a constexpr variable or of the first declaration of a // static data member is not syntactically a constant evaluated constant, // but nonetheless is always required to be a constant expression, so we // can skip diagnosing. - // FIXME: Using the mangling context here is a hack. - if (auto *VD = dyn_cast_or_null<VarDecl>( - ExprEvalContexts.back().ManglingContextDecl)) { - if (VD->isConstexpr() || - (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) - return false; - // FIXME: For any other kind of variable, we should build a CFG for its - // initializer and check whether the context in question is reachable. + if (Decl && + (Decl->isConstexpr() || (Decl->isStaticDataMember() && + Decl->isFirstDecl() && !Decl->isInline()))) + return false; + + if (Stmts.empty()) { + Diag(Loc, PD); + return true; + } + + if (getCurFunction()) { + FunctionScopes.back()->PossiblyUnreachableDiags.push_back( + sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; + } + + // For non-constexpr file-scope variables with reachability context (non-empty + // Stmts), build a CFG for the initializer and check whether the context in + // question is reachable. + if (Decl && Decl->isFileVarDecl()) { + AnalysisWarnings.registerVarDeclWarning( + Decl, sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; } Diag(Loc, PD); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 465dab2..2ab2fd1 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -17319,45 +17319,101 @@ OMPClause *SemaOpenMP::ActOnOpenMPDefaultClause( << getOpenMPClauseNameForDiag(OMPC_default); return nullptr; } - - switch (M) { - case OMP_DEFAULT_none: - DSAStack->setDefaultDSANone(MLoc); - break; - case OMP_DEFAULT_shared: - DSAStack->setDefaultDSAShared(MLoc); - break; - case OMP_DEFAULT_firstprivate: - DSAStack->setDefaultDSAFirstPrivate(MLoc); - break; - case OMP_DEFAULT_private: - DSAStack->setDefaultDSAPrivate(MLoc); - break; - default: - llvm_unreachable("DSA unexpected in OpenMP default clause"); - } - - switch (VCKind) { - case OMPC_DEFAULT_VC_aggregate: - DSAStack->setDefaultDSAVCAggregate(VCKindLoc); - break; - case OMPC_DEFAULT_VC_all: - DSAStack->setDefaultDSAVCAll(VCKindLoc); - break; - case OMPC_DEFAULT_VC_allocatable: - DSAStack->setDefaultDSAVCAllocatable(VCKindLoc); - break; - case OMPC_DEFAULT_VC_pointer: - DSAStack->setDefaultDSAVCPointer(VCKindLoc); - break; - case OMPC_DEFAULT_VC_scalar: - DSAStack->setDefaultDSAVCScalar(VCKindLoc); - break; - default: + if (VCKind == OMPC_DEFAULT_VC_unknown) { Diag(VCKindLoc, diag::err_omp_default_vc) << getOpenMPSimpleClauseTypeName(OMPC_default, unsigned(M)); + return nullptr; } + bool IsTargetDefault = + getLangOpts().OpenMP >= 60 && + isOpenMPTargetExecutionDirective(DSAStack->getCurrentDirective()); + + // OpenMP 6.0, page 224, lines 3-4 default Clause, Semantics + // If data-sharing-attribute is shared then the clause has no effect + // on a target construct; + if (IsTargetDefault && M == OMP_DEFAULT_shared) + return nullptr; + + auto SetDefaultClauseAttrs = [&](llvm::omp::DefaultKind M, + OpenMPDefaultClauseVariableCategory VCKind) { + OpenMPDefaultmapClauseModifier DefMapMod; + OpenMPDefaultmapClauseKind DefMapKind; + // default data-sharing-attribute + switch (M) { + case OMP_DEFAULT_none: + if (IsTargetDefault) + DefMapMod = OMPC_DEFAULTMAP_MODIFIER_none; + else + DSAStack->setDefaultDSANone(MLoc); + break; + case OMP_DEFAULT_firstprivate: + if (IsTargetDefault) + DefMapMod = OMPC_DEFAULTMAP_MODIFIER_firstprivate; + else + DSAStack->setDefaultDSAFirstPrivate(MLoc); + break; + case OMP_DEFAULT_private: + if (IsTargetDefault) + DefMapMod = OMPC_DEFAULTMAP_MODIFIER_private; + else + DSAStack->setDefaultDSAPrivate(MLoc); + break; + case OMP_DEFAULT_shared: + assert(!IsTargetDefault && "DSA shared invalid with target directive"); + DSAStack->setDefaultDSAShared(MLoc); + break; + default: + llvm_unreachable("unexpected DSA in OpenMP default clause"); + } + // default variable-category + switch (VCKind) { + case OMPC_DEFAULT_VC_aggregate: + if (IsTargetDefault) + DefMapKind = OMPC_DEFAULTMAP_aggregate; + else + DSAStack->setDefaultDSAVCAggregate(VCKindLoc); + break; + case OMPC_DEFAULT_VC_pointer: + if (IsTargetDefault) + DefMapKind = OMPC_DEFAULTMAP_pointer; + else + DSAStack->setDefaultDSAVCPointer(VCKindLoc); + break; + case OMPC_DEFAULT_VC_scalar: + if (IsTargetDefault) + DefMapKind = OMPC_DEFAULTMAP_scalar; + else + DSAStack->setDefaultDSAVCScalar(VCKindLoc); + break; + case OMPC_DEFAULT_VC_all: + if (IsTargetDefault) + DefMapKind = OMPC_DEFAULTMAP_all; + else + DSAStack->setDefaultDSAVCAll(VCKindLoc); + break; + default: + llvm_unreachable("unexpected variable category in OpenMP default clause"); + } + // OpenMP 6.0, page 224, lines 4-5 default Clause, Semantics + // otherwise, its effect on a target construct is equivalent to + // specifying the defaultmap clause with the same data-sharing-attribute + // and variable-category. + // + // If earlier than OpenMP 6.0, or not a target directive, the default DSA + // is/was set as before. + if (IsTargetDefault) { + if (DefMapKind == OMPC_DEFAULTMAP_all) { + DSAStack->setDefaultDMAAttr(DefMapMod, OMPC_DEFAULTMAP_aggregate, MLoc); + DSAStack->setDefaultDMAAttr(DefMapMod, OMPC_DEFAULTMAP_scalar, MLoc); + DSAStack->setDefaultDMAAttr(DefMapMod, OMPC_DEFAULTMAP_pointer, MLoc); + } else { + DSAStack->setDefaultDMAAttr(DefMapMod, DefMapKind, MLoc); + } + } + }; + + SetDefaultClauseAttrs(M, VCKind); return new (getASTContext()) OMPDefaultClause(M, MLoc, VCKind, VCKindLoc, StartLoc, LParenLoc, EndLoc); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4d58f00..a56017c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6198,6 +6198,10 @@ void Sema::InstantiateVariableInitializer( currentEvaluationContext().RebuildDefaultArgOrDefaultInit = parentEvaluationContext().RebuildDefaultArgOrDefaultInit; + // Set DeclForInitializer for this variable so DiagIfReachable can properly + // suppress runtime diagnostics for constexpr/static member variables + currentEvaluationContext().DeclForInitializer = Var; + if (OldVar->getInit()) { // Instantiate the initializer. ExprResult Init = @@ -6467,6 +6471,8 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, PassToConsumerRAII.Var = Var; Var->setTemplateSpecializationKind(OldVar->getTemplateSpecializationKind(), OldVar->getPointOfInstantiation()); + // Emit any deferred warnings for the variable's initializer + AnalysisWarnings.issueWarningsForRegisteredVarDecl(Var); } // This variable may have local implicit instantiations that need to be |
