summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--clang/include/clang/Sema/Scope.h6
-rw-r--r--clang/include/clang/Sema/Sema.h6
-rw-r--r--clang/lib/Parse/ParseStmt.cpp7
-rw-r--r--clang/lib/Sema/SemaStmt.cpp24
-rw-r--r--clang/lib/Sema/TreeTransform.h2
-rw-r--r--clang/test/Sema/__try.c78
7 files changed, 116 insertions, 10 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 299844ecb94..226c5ec6ef7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5490,6 +5490,9 @@ def err_mixing_cxx_try_seh_try : Error<
"cannot use C++ 'try' in the same function as SEH '__try'">;
def note_conflicting_try_here : Note<
"conflicting %0 here">;
+def warn_jump_out_of_seh_finally : Warning<
+ "jump out of __finally block has undefined behavior">,
+ InGroup<DiagGroup<"jump-seh-finally">>;
def warn_non_virtual_dtor : Warning<
"%0 has virtual functions but non-virtual destructor">,
InGroup<NonVirtualDtor>, DefaultIgnore;
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index cc6d9cca5b1..238469a2d5d 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -416,6 +416,12 @@ public:
/// \brief Determine whether this scope is a SEH '__except' block.
bool isSEHExceptScope() const { return getFlags() & Scope::SEHExceptScope; }
+ /// \brief Returns if rhs has a higher scope depth than this.
+ ///
+ /// The caller is responsible for calling this only if one of the two scopes
+ /// is an ancestor of the other.
+ bool Contains(const Scope& rhs) const { return Depth < rhs.Depth; }
+
/// containedInPrototypeScope - Return true if this or a parent scope
/// is a FunctionPrototypeScope.
bool containedInPrototypeScope() const;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 460111f522d..9879ed96110 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -303,6 +303,9 @@ public:
/// The stack always has at least one element in it.
SmallVector<MSVtorDispAttr::Mode, 2> VtorDispModeStack;
+ /// Stack of active SEH __finally scopes. Can be empty.
+ SmallVector<Scope*, 2> CurrentSEHFinally;
+
/// \brief Source location for newly created implicit MSInheritanceAttrs
SourceLocation ImplicitMSInheritanceAttrLoc;
@@ -3293,7 +3296,8 @@ public:
StmtResult ActOnSEHExceptBlock(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block);
- StmtResult ActOnSEHFinallyBlock(SourceLocation Loc, Stmt *Block);
+ void ActOnStartSEHFinallyBlock();
+ StmtResult ActOnFinishSEHFinallyBlock(SourceLocation Loc, Stmt *Block);
StmtResult ActOnSEHLeaveStmt(SourceLocation Loc, Scope *CurScope);
void DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index e77f07ab030..9028e4ac2dd 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -507,7 +507,7 @@ StmtResult Parser::ParseSEHExceptBlock(SourceLocation ExceptLoc) {
/// seh-finally-block:
/// '__finally' compound-statement
///
-StmtResult Parser::ParseSEHFinallyBlock(SourceLocation FinallyBlock) {
+StmtResult Parser::ParseSEHFinallyBlock(SourceLocation FinallyLoc) {
PoisonIdentifierRAIIObject raii(Ident__abnormal_termination, false),
raii2(Ident___abnormal_termination, false),
raii3(Ident_AbnormalTermination, false);
@@ -515,11 +515,14 @@ StmtResult Parser::ParseSEHFinallyBlock(SourceLocation FinallyBlock) {
if (Tok.isNot(tok::l_brace))
return StmtError(Diag(Tok, diag::err_expected) << tok::l_brace);
+ ParseScope FinallyScope(this, 0);
+ Actions.ActOnStartSEHFinallyBlock();
+
StmtResult Block(ParseCompoundStatement());
if(Block.isInvalid())
return Block;
- return Actions.ActOnSEHFinallyBlock(FinallyBlock,Block.get());
+ return Actions.ActOnFinishSEHFinallyBlock(FinallyLoc, Block.get());
}
/// Handle __leave
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index aaf29db702c..11ec4f5321e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2428,6 +2428,14 @@ Sema::ActOnIndirectGotoStmt(SourceLocation GotoLoc, SourceLocation StarLoc,
return new (Context) IndirectGotoStmt(GotoLoc, StarLoc, E);
}
+static void CheckJumpOutOfSEHFinally(Sema &S, SourceLocation Loc,
+ const Scope &DestScope) {
+ if (!S.CurrentSEHFinally.empty() &&
+ DestScope.Contains(*S.CurrentSEHFinally.back())) {
+ S.Diag(Loc, diag::warn_jump_out_of_seh_finally);
+ }
+}
+
StmtResult
Sema::ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope) {
Scope *S = CurScope->getContinueParent();
@@ -2435,6 +2443,7 @@ Sema::ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope) {
// C99 6.8.6.2p1: A break shall appear only in or as a loop body.
return StmtError(Diag(ContinueLoc, diag::err_continue_not_in_loop));
}
+ CheckJumpOutOfSEHFinally(*this, ContinueLoc, *S);
return new (Context) ContinueStmt(ContinueLoc);
}
@@ -2449,6 +2458,7 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
if (S->isOpenMPLoopScope())
return StmtError(Diag(BreakLoc, diag::err_omp_loop_cannot_use_stmt)
<< "break");
+ CheckJumpOutOfSEHFinally(*this, BreakLoc, *S);
return new (Context) BreakStmt(BreakLoc);
}
@@ -2908,6 +2918,8 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp,
CurScope->setNoNRVO();
}
+ CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
+
return R;
}
@@ -3406,11 +3418,14 @@ Sema::ActOnSEHExceptBlock(SourceLocation Loc,
return SEHExceptStmt::Create(Context,Loc,FilterExpr,Block);
}
-StmtResult
-Sema::ActOnSEHFinallyBlock(SourceLocation Loc,
- Stmt *Block) {
+void Sema::ActOnStartSEHFinallyBlock() {
+ CurrentSEHFinally.push_back(CurScope);
+}
+
+StmtResult Sema::ActOnFinishSEHFinallyBlock(SourceLocation Loc, Stmt *Block) {
assert(Block);
- return SEHFinallyStmt::Create(Context,Loc,Block);
+ CurrentSEHFinally.pop_back();
+ return SEHFinallyStmt::Create(Context, Loc, Block);
}
StmtResult
@@ -3420,6 +3435,7 @@ Sema::ActOnSEHLeaveStmt(SourceLocation Loc, Scope *CurScope) {
SEHTryParent = SEHTryParent->getParent();
if (!SEHTryParent)
return StmtError(Diag(Loc, diag::err_ms___leave_not_in___try));
+ CheckJumpOutOfSEHFinally(*this, Loc, *SEHTryParent);
return new (Context) SEHLeaveStmt(Loc);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e1d0d18dde4..351dacd256e 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1702,7 +1702,7 @@ public:
}
StmtResult RebuildSEHFinallyStmt(SourceLocation Loc, Stmt *Block) {
- return getSema().ActOnSEHFinallyBlock(Loc, Block);
+ return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
}
/// \brief Build a new predefined expression.
diff --git a/clang/test/Sema/__try.c b/clang/test/Sema/__try.c
index 0e5de2018db..925dbcc55ef 100644
--- a/clang/test/Sema/__try.c
+++ b/clang/test/Sema/__try.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fborland-extensions -DBORLAND -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fborland-extensions -DBORLAND -fsyntax-only -verify -fblocks %s
+// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify -fblocks %s
#define JOIN2(x,y) x ## y
#define JOIN(x,y) JOIN2(x,y)
@@ -207,3 +207,77 @@ void test_seh_leave_stmt() {
}
__leave; // expected-error{{'__leave' statement not in __try block}}
}
+
+void test_jump_out_of___finally() {
+ while(1) {
+ __try {
+ } __finally {
+ continue; // expected-warning{{jump out of __finally block has undefined behavior}}
+ }
+ }
+ __try {
+ } __finally {
+ while (1) {
+ continue;
+ }
+ }
+
+ // Check that a deep __finally containing a block with a shallow continue
+ // doesn't trigger the warning.
+ while(1) {{{{
+ __try {
+ } __finally {
+ ^{
+ while(1)
+ continue;
+ }();
+ }
+ }}}}
+
+ while(1) {
+ __try {
+ } __finally {
+ break; // expected-warning{{jump out of __finally block has undefined behavior}}
+ }
+ }
+ switch(1) {
+ case 1:
+ __try {
+ } __finally {
+ break; // expected-warning{{jump out of __finally block has undefined behavior}}
+ }
+ }
+ __try {
+ } __finally {
+ while (1) {
+ break;
+ }
+ }
+
+ __try {
+ __try {
+ } __finally {
+ __leave; // expected-warning{{jump out of __finally block has undefined behavior}}
+ }
+ } __finally {
+ }
+ __try {
+ } __finally {
+ __try {
+ __leave;
+ } __finally {
+ }
+ }
+
+ __try {
+ } __finally {
+ return; // expected-warning{{jump out of __finally block has undefined behavior}}
+ }
+
+ __try {
+ } __finally {
+ ^{
+ return;
+ }();
+ }
+}
OpenPOWER on IntegriCloud