aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp105
-rw-r--r--clang/lib/Sema/SemaDecl.cpp17
-rw-r--r--clang/lib/Sema/SemaExpr.cpp43
-rw-r--r--clang/lib/Sema/SemaOpenMP.cpp126
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp6
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