"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;
/// \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;
/// 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;
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);
/// 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);
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
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();
// 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);
}
if (S->isOpenMPLoopScope())
return StmtError(Diag(BreakLoc, diag::err_omp_loop_cannot_use_stmt)
<< "break");
+ CheckJumpOutOfSEHFinally(*this, BreakLoc, *S);
return new (Context) BreakStmt(BreakLoc);
}
CurScope->setNoNRVO();
}
+ CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
+
return R;
}
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
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);
}
}
StmtResult RebuildSEHFinallyStmt(SourceLocation Loc, Stmt *Block) {
- return getSema().ActOnSEHFinallyBlock(Loc, Block);
+ return SEHFinallyStmt::Create(getSema().getASTContext(), Loc, Block);
}
/// \brief Build a new predefined expression.
-// 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)
}
__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;
+ }();
+ }
+}