Parsing/AST support for Structured Exception Handling

Patch authored by Sohail Somani.

Provide parsing and AST support for Windows structured exception handling.

llvm-svn: 130366
This commit is contained in:
John Wiegley 2011-04-28 01:08:34 +00:00
parent 1d684c253f
commit 1c0675e155
26 changed files with 949 additions and 25 deletions

View File

@ -1933,6 +1933,10 @@ DEF_TRAVERSE_STMT(UnresolvedMemberExpr, {
}
})
DEF_TRAVERSE_STMT(SEHTryStmt, {})
DEF_TRAVERSE_STMT(SEHExceptStmt, {})
DEF_TRAVERSE_STMT(SEHFinallyStmt,{})
DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { })
DEF_TRAVERSE_STMT(OpaqueValueExpr, { })
DEF_TRAVERSE_STMT(CUDAKernelCallExpr, { })

View File

@ -1435,6 +1435,122 @@ public:
}
};
class SEHExceptStmt : public Stmt {
SourceLocation Loc;
Stmt *Children[2];
enum { FILTER_EXPR, BLOCK };
SEHExceptStmt(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block);
public:
static SEHExceptStmt* Create(ASTContext &C,
SourceLocation ExceptLoc,
Expr *FilterExpr,
Stmt *Block);
SourceRange getSourceRange() const {
return SourceRange(getExceptLoc(), getEndLoc());
}
SourceLocation getExceptLoc() const { return Loc; }
SourceLocation getEndLoc() const { return getBlock()->getLocEnd(); }
Expr *getFilterExpr() const { return reinterpret_cast<Expr*>(Children[FILTER_EXPR]); }
CompoundStmt *getBlock() const { return llvm::cast<CompoundStmt>(Children[BLOCK]); }
child_range children() {
return child_range(Children,Children+2);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == SEHExceptStmtClass;
}
static bool classof(SEHExceptStmt *) { return true; }
};
class SEHFinallyStmt : public Stmt {
SourceLocation Loc;
Stmt *Block;
SEHFinallyStmt(SourceLocation Loc,
Stmt *Block);
public:
static SEHFinallyStmt* Create(ASTContext &C,
SourceLocation FinallyLoc,
Stmt *Block);
SourceRange getSourceRange() const {
return SourceRange(getFinallyLoc(), getEndLoc());
}
SourceLocation getFinallyLoc() const { return Loc; }
SourceLocation getEndLoc() const { return Block->getLocEnd(); }
CompoundStmt *getBlock() const { return llvm::cast<CompoundStmt>(Block); }
child_range children() {
return child_range(&Block,&Block+1);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == SEHFinallyStmtClass;
}
static bool classof(SEHFinallyStmt *) { return true; }
};
class SEHTryStmt : public Stmt {
bool IsCXXTry;
SourceLocation TryLoc;
Stmt *Children[2];
enum { TRY = 0, HANDLER = 1 };
SEHTryStmt(bool isCXXTry, // true if 'try' otherwise '__try'
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler);
public:
static SEHTryStmt* Create(ASTContext &C,
bool isCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler);
SourceRange getSourceRange() const {
return SourceRange(getTryLoc(), getEndLoc());
}
SourceLocation getTryLoc() const { return TryLoc; }
SourceLocation getEndLoc() const { return Children[HANDLER]->getLocEnd(); }
bool getIsCXXTry() const { return IsCXXTry; }
CompoundStmt* getTryBlock() const { return llvm::cast<CompoundStmt>(Children[TRY]); }
Stmt *getHandler() const { return Children[HANDLER]; }
/// Returns 0 if not defined
SEHExceptStmt *getExceptHandler() const;
SEHFinallyStmt *getFinallyHandler() const;
child_range children() {
return child_range(Children,Children+2);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == SEHTryStmtClass;
}
static bool classof(SEHTryStmt *) { return true; }
};
} // end namespace clang
#endif

View File

@ -521,5 +521,17 @@ def warn_pragma_expected_enable_disable : Warning<
def warn_pragma_unknown_extension : Warning<
"unknown OpenCL extension %0 - ignoring">;
def err_seh_expected_handler : Error<
"expected '__except' or '__finally' block">;
def err_seh___except_block : Error<
"%0 only allowed in __except block">;
def err_seh___except_filter : Error<
"%0 only allowed in __except filter expression">;
def err_seh___finally_block : Error<
"%0 only allowed in __finally block">;
} // end of Parse Issue category.
} // end of Parser diagnostics

View File

@ -3946,6 +3946,9 @@ def err_unknown_any_addrof : Error<
def err_unknown_any_var_function_type : Error<
"variable %0 with unknown type cannot be given a function type">;
def err_filter_expression_integral : Error<
"filter expression type should be an integral value not %0">;
} // end of sema category
} // end of sema component.

View File

@ -255,6 +255,25 @@ private:
}
};
/// \brief an RAII object for [un]poisoning an identifier
/// within a certain scope. II is allowed to be null, in
/// which case, objects of this type have no effect.
class PoisonIdentifierRAIIObject {
IdentifierInfo *const II;
const bool OldValue;
public:
PoisonIdentifierRAIIObject(IdentifierInfo *II, bool NewValue)
: II(II), OldValue(II ? II->isPoisoned() : false) {
if(II)
II->setIsPoisoned(NewValue);
}
~PoisonIdentifierRAIIObject() {
if(II)
II->setIsPoisoned(OldValue);
}
};
/// \brief An iterator that walks over all of the known identifiers
/// in the lookup table.
///

View File

@ -142,4 +142,7 @@ def OpaqueValueExpr : DStmt<Expr>;
// Microsoft Extensions.
def CXXUuidofExpr : DStmt<Expr>;
def SEHTryStmt : Stmt;
def SEHExceptStmt : Stmt;
def SEHFinallyStmt : Stmt;

View File

@ -783,6 +783,38 @@ public:
/// updating the token kind accordingly.
IdentifierInfo *LookUpIdentifierInfo(Token &Identifier) const;
private:
llvm::DenseMap<IdentifierInfo*,unsigned> PoisonReasons;
public:
// SetPoisonReason - Call this function to indicate the reason for
// poisoning an identifier. If that identifier is accessed while
// poisoned, then this reason will be used instead of the default
// "poisoned" diagnostic.
void SetPoisonReason(IdentifierInfo *II, unsigned DiagID);
// HandlePoisonedIdentifier - Display reason for poisoned
// identifier.
void HandlePoisonedIdentifier(Token & Tok);
void MaybeHandlePoisonedIdentifier(Token & Identifier) {
if(IdentifierInfo * II = Identifier.getIdentifierInfo()) {
if(II->isPoisoned()) {
HandlePoisonedIdentifier(Identifier);
}
}
}
private:
/// Identifiers used for SEH handling in Borland. These are only
/// allowed in particular circumstances
IdentifierInfo *Ident__exception_code, *Ident___exception_code, *Ident_GetExceptionCode; // __except block
IdentifierInfo *Ident__exception_info, *Ident___exception_info, *Ident_GetExceptionInfo; // __except filter expression
IdentifierInfo *Ident__abnormal_termination, *Ident___abnormal_termination, *Ident_AbnormalTermination; // __finally
public:
void PoisonSEHIdentifiers(bool Poison = true); // Borland
/// HandleIdentifier - This callback is invoked when the lexer reads an
/// identifier and has filled in the tokens IdentifierInfo member. This
/// callback potentially macro expands it or turns it into a named token (like

View File

@ -32,6 +32,7 @@ namespace clang {
class PragmaUnusedHandler;
class ColonProtectionRAIIObject;
class InMessageExpressionRAIIObject;
class PoisonSEHIdentifiersRAIIObject;
class VersionTuple;
/// PrettyStackTraceParserEntry - If a crash happens while the parser is active,
@ -75,6 +76,7 @@ class Parser : public CodeCompletionHandler {
friend class PragmaUnusedHandler;
friend class ColonProtectionRAIIObject;
friend class InMessageExpressionRAIIObject;
friend class PoisonSEHIdentifiersRAIIObject;
friend class ParenBraceBracketBalancer;
Preprocessor &PP;
@ -102,6 +104,12 @@ class Parser : public CodeCompletionHandler {
unsigned NumCachedScopes;
Scope *ScopeCache[ScopeCacheSize];
/// Identifiers used for SEH handling in Borland. These are only
/// allowed in particular circumstances
IdentifierInfo *Ident__exception_code, *Ident___exception_code, *Ident_GetExceptionCode; // __except block
IdentifierInfo *Ident__exception_info, *Ident___exception_info, *Ident_GetExceptionInfo; // __except filter expression
IdentifierInfo *Ident__abnormal_termination, *Ident___abnormal_termination, *Ident_AbnormalTermination; // __finally
/// Ident_super - IdentifierInfo for "super", to support fast
/// comparison.
IdentifierInfo *Ident_super;
@ -1311,6 +1319,14 @@ private:
StmtResult ParseCXXTryBlockCommon(SourceLocation TryLoc);
StmtResult ParseCXXCatchBlock();
//===--------------------------------------------------------------------===//
// MS: SEH Statements and Blocks
StmtResult ParseSEHTryBlock(ParsedAttributes &Attr);
StmtResult ParseSEHTryBlockCommon(SourceLocation Loc);
StmtResult ParseSEHExceptBlock(SourceLocation Loc);
StmtResult ParseSEHFinallyBlock(SourceLocation Loc);
//===--------------------------------------------------------------------===//
// Objective-C Statements

View File

@ -2001,6 +2001,19 @@ public:
Decl *ExDecl, Stmt *HandlerBlock);
StmtResult ActOnCXXTryBlock(SourceLocation TryLoc, Stmt *TryBlock,
MultiStmtArg Handlers);
StmtResult ActOnSEHTryBlock(bool IsCXXTry, // try (true) or __try (false) ?
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler);
StmtResult ActOnSEHExceptBlock(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block);
StmtResult ActOnSEHFinallyBlock(SourceLocation Loc,
Stmt *Block);
void DiagnoseReturnInConstructorExceptionHandler(CXXTryStmt *TryBlock);
bool ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const;

View File

@ -710,3 +710,61 @@ const Expr* ReturnStmt::getRetValue() const {
Expr* ReturnStmt::getRetValue() {
return cast_or_null<Expr>(RetExpr);
}
SEHTryStmt::SEHTryStmt(bool IsCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler)
: Stmt(SEHTryStmtClass),
IsCXXTry(IsCXXTry),
TryLoc(TryLoc)
{
Children[TRY] = TryBlock;
Children[HANDLER] = Handler;
}
SEHTryStmt* SEHTryStmt::Create(ASTContext &C,
bool IsCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler) {
return new(C) SEHTryStmt(IsCXXTry,TryLoc,TryBlock,Handler);
}
SEHExceptStmt* SEHTryStmt::getExceptHandler() const {
return dyn_cast<SEHExceptStmt>(getHandler());
}
SEHFinallyStmt* SEHTryStmt::getFinallyHandler() const {
return dyn_cast<SEHFinallyStmt>(getHandler());
}
SEHExceptStmt::SEHExceptStmt(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block)
: Stmt(SEHExceptStmtClass),
Loc(Loc)
{
Children[FILTER_EXPR] = reinterpret_cast<Stmt*>(FilterExpr);
Children[BLOCK] = Block;
}
SEHExceptStmt* SEHExceptStmt::Create(ASTContext &C,
SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block) {
return new(C) SEHExceptStmt(Loc,FilterExpr,Block);
}
SEHFinallyStmt::SEHFinallyStmt(SourceLocation Loc,
Stmt *Block)
: Stmt(SEHFinallyStmtClass),
Loc(Loc),
Block(Block)
{}
SEHFinallyStmt* SEHFinallyStmt::Create(ASTContext &C,
SourceLocation Loc,
Stmt *Block) {
return new(C)SEHFinallyStmt(Loc,Block);
}

View File

@ -66,6 +66,8 @@ namespace {
void PrintRawIfStmt(IfStmt *If);
void PrintRawCXXCatchStmt(CXXCatchStmt *Catch);
void PrintCallArgs(CallExpr *E);
void PrintRawSEHExceptHandler(SEHExceptStmt *S);
void PrintRawSEHFinallyStmt(SEHFinallyStmt *S);
void PrintExpr(Expr *E) {
if (E)
@ -473,6 +475,46 @@ void StmtPrinter::VisitCXXTryStmt(CXXTryStmt *Node) {
OS << "\n";
}
void StmtPrinter::VisitSEHTryStmt(SEHTryStmt *Node) {
Indent() << (Node->getIsCXXTry() ? "try " : "__try ");
PrintRawCompoundStmt(Node->getTryBlock());
SEHExceptStmt *E = Node->getExceptHandler();
SEHFinallyStmt *F = Node->getFinallyHandler();
if(E)
PrintRawSEHExceptHandler(E);
else {
assert(F && "Must have a finally block...");
PrintRawSEHFinallyStmt(F);
}
OS << "\n";
}
void StmtPrinter::PrintRawSEHFinallyStmt(SEHFinallyStmt *Node) {
OS << "__finally ";
PrintRawCompoundStmt(Node->getBlock());
OS << "\n";
}
void StmtPrinter::PrintRawSEHExceptHandler(SEHExceptStmt *Node) {
OS << "__except (";
VisitExpr(Node->getFilterExpr());
OS << ")\n";
PrintRawCompoundStmt(Node->getBlock());
OS << "\n";
}
void StmtPrinter::VisitSEHExceptStmt(SEHExceptStmt *Node) {
Indent();
PrintRawSEHExceptHandler(Node);
OS << "\n";
}
void StmtPrinter::VisitSEHFinallyStmt(SEHFinallyStmt *Node) {
Indent();
PrintRawSEHFinallyStmt(Node);
OS << "\n";
}
//===----------------------------------------------------------------------===//
// Expr printing methods.
//===----------------------------------------------------------------------===//

View File

@ -181,6 +181,18 @@ void StmtProfiler::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitSEHTryStmt(SEHTryStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitSEHFinallyStmt(SEHFinallyStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitSEHExceptStmt(SEHExceptStmt *S) {
VisitStmt(S);
}
void StmtProfiler::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
VisitStmt(S);
}

View File

@ -72,6 +72,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
switch (S->getStmtClass()) {
case Stmt::NoStmtClass:
case Stmt::CXXCatchStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHFinallyStmtClass:
llvm_unreachable("invalid statement class to emit generically");
case Stmt::NullStmtClass:
case Stmt::CompoundStmtClass:
@ -155,6 +157,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
break;
case Stmt::CXXForRangeStmtClass:
EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S));
case Stmt::SEHTryStmtClass:
// FIXME Not yet implemented
break;
}
}

View File

@ -89,6 +89,7 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
// "Poison" __VA_ARGS__, which can only appear in the expansion of a macro.
// This gets unpoisoned where it is allowed.
(Ident__VA_ARGS__ = getIdentifierInfo("__VA_ARGS__"))->setIsPoisoned();
SetPoisonReason(Ident__VA_ARGS__,diag::ext_pp_bad_vaargs_use);
// Initialize the pragma handlers.
PragmaHandlers = new PragmaNamespace(llvm::StringRef());
@ -96,6 +97,23 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
// Initialize builtin macros like __LINE__ and friends.
RegisterBuiltinMacros();
if(Features.Borland) {
Ident__exception_info = getIdentifierInfo("_exception_info");
Ident___exception_info = getIdentifierInfo("__exception_info");
Ident_GetExceptionInfo = getIdentifierInfo("GetExceptionInformation");
Ident__exception_code = getIdentifierInfo("_exception_code");
Ident___exception_code = getIdentifierInfo("__exception_code");
Ident_GetExceptionCode = getIdentifierInfo("GetExceptionCode");
Ident__abnormal_termination = getIdentifierInfo("_abnormal_termination");
Ident___abnormal_termination = getIdentifierInfo("__abnormal_termination");
Ident_AbnormalTermination = getIdentifierInfo("AbnormalTermination");
} else {
Ident__exception_info = Ident__exception_code = Ident__abnormal_termination = 0;
Ident___exception_info = Ident___exception_code = Ident___abnormal_termination = 0;
Ident_GetExceptionInfo = Ident_GetExceptionCode = Ident_AbnormalTermination = 0;
}
}
Preprocessor::~Preprocessor() {
@ -399,6 +417,34 @@ IdentifierInfo *Preprocessor::LookUpIdentifierInfo(Token &Identifier) const {
return II;
}
void Preprocessor::SetPoisonReason(IdentifierInfo *II, unsigned DiagID) {
PoisonReasons[II] = DiagID;
}
void Preprocessor::PoisonSEHIdentifiers(bool Poison) {
assert(Ident__exception_code && Ident__exception_info);
assert(Ident___exception_code && Ident___exception_info);
Ident__exception_code->setIsPoisoned(Poison);
Ident___exception_code->setIsPoisoned(Poison);
Ident_GetExceptionCode->setIsPoisoned(Poison);
Ident__exception_info->setIsPoisoned(Poison);
Ident___exception_info->setIsPoisoned(Poison);
Ident_GetExceptionInfo->setIsPoisoned(Poison);
Ident__abnormal_termination->setIsPoisoned(Poison);
Ident___abnormal_termination->setIsPoisoned(Poison);
Ident_AbnormalTermination->setIsPoisoned(Poison);
}
void Preprocessor::HandlePoisonedIdentifier(Token & Identifier) {
assert(Identifier.getIdentifierInfo() &&
"Can't handle identifiers without identifier info!");
llvm::DenseMap<IdentifierInfo*,unsigned>::const_iterator it =
PoisonReasons.find(Identifier.getIdentifierInfo());
if(it == PoisonReasons.end())
Diag(Identifier, diag::err_pp_used_poisoned_id);
else
Diag(Identifier,it->second) << Identifier.getIdentifierInfo();
}
/// HandleIdentifier - This callback is invoked when the lexer reads an
/// identifier. This callback looks up the identifier in the map and/or
@ -417,10 +463,7 @@ void Preprocessor::HandleIdentifier(Token &Identifier) {
// If this identifier was poisoned, and if it was not produced from a macro
// expansion, emit an error.
if (II.isPoisoned() && CurPPLexer) {
if (&II != Ident__VA_ARGS__) // We warn about __VA_ARGS__ with poisoning.
Diag(Identifier, diag::err_pp_used_poisoned_id);
else
Diag(Identifier, diag::ext_pp_bad_vaargs_use);
HandlePoisonedIdentifier(Identifier);
}
// If this is a macro to be expanded, do it.

View File

@ -367,11 +367,7 @@ void TokenLexer::Lex(Token &Tok) {
// won't be handled by Preprocessor::HandleIdentifier because this is coming
// from a macro expansion.
if (II->isPoisoned() && TokenIsFromPaste) {
// We warn about __VA_ARGS__ with poisoning.
if (II->isStr("__VA_ARGS__"))
PP.Diag(Tok, diag::ext_pp_bad_vaargs_use);
else
PP.Diag(Tok, diag::err_pp_used_poisoned_id);
PP.HandlePoisonedIdentifier(Tok);
}
if (!DisableMacroExpansion && II->isHandleIdentifierCase())

View File

@ -1915,6 +1915,8 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) {
assert(Tok.is(tok::colon) && "Constructor initializer always starts with ':'");
// Poison the SEH identifiers so they are flagged as illegal in constructor initializers
PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
SourceLocation ColonLoc = ConsumeToken();
llvm::SmallVector<CXXCtorInitializer*, 4> MemInitializers;

View File

@ -40,6 +40,7 @@ using namespace clang;
/// jump-statement
/// [C++] declaration-statement
/// [C++] try-block
/// [MS] seh-try-block
/// [OBC] objc-throw-statement
/// [OBC] objc-try-catch-statement
/// [OBC] objc-synchronized-statement
@ -272,6 +273,9 @@ Retry:
case tok::kw_try: // C++ 15: try-block
return ParseCXXTryBlock(attrs);
case tok::kw___try:
return ParseSEHTryBlock(attrs);
}
// If we reached this code, the statement must end in a semicolon.
@ -321,7 +325,106 @@ StmtResult Parser::ParseExprStatement(ParsedAttributes &Attrs) {
// Otherwise, eat the semicolon.
ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
}
StmtResult Parser::ParseSEHTryBlock(ParsedAttributes & Attrs) {
assert(Tok.is(tok::kw___try) && "Expected '__try'");
SourceLocation Loc = ConsumeToken();
return ParseSEHTryBlockCommon(Loc);
}
/// ParseSEHTryBlockCommon
///
/// seh-try-block:
/// '__try' compound-statement seh-handler
///
/// seh-handler:
/// seh-except-block
/// seh-finally-block
///
StmtResult Parser::ParseSEHTryBlockCommon(SourceLocation TryLoc) {
if(Tok.isNot(tok::l_brace))
return StmtError(Diag(Tok,diag::err_expected_lbrace));
ParsedAttributesWithRange attrs(AttrFactory);
StmtResult TryBlock(ParseCompoundStatement(attrs));
if(TryBlock.isInvalid())
return move(TryBlock);
StmtResult Handler;
if(Tok.is(tok::kw___except)) {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHExceptBlock(Loc);
} else if (Tok.is(tok::kw___finally)) {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHFinallyBlock(Loc);
} else {
return StmtError(Diag(Tok,diag::err_seh_expected_handler));
}
if(Handler.isInvalid())
return move(Handler);
return Actions.ActOnSEHTryBlock(false /* IsCXXTry */,
TryLoc,
TryBlock.take(),
Handler.take());
}
/// ParseSEHExceptBlock - Handle __except
///
/// seh-except-block:
/// '__except' '(' seh-filter-expression ')' compound-statement
///
StmtResult Parser::ParseSEHExceptBlock(SourceLocation ExceptLoc) {
PoisonIdentifierRAIIObject raii(Ident__exception_code, false),
raii2(Ident___exception_code, false),
raii3(Ident_GetExceptionCode, false);
if(ExpectAndConsume(tok::l_paren,diag::err_expected_lparen))
return StmtError();
ParseScope ExpectScope(this, Scope::DeclScope | Scope::ControlScope);
Ident__exception_info->setIsPoisoned(false);
Ident___exception_info->setIsPoisoned(false);
Ident_GetExceptionInfo->setIsPoisoned(false);
ExprResult FilterExpr(ParseExpression());
Ident__exception_info->setIsPoisoned(true);
Ident___exception_info->setIsPoisoned(true);
Ident_GetExceptionInfo->setIsPoisoned(true);
if(FilterExpr.isInvalid())
return StmtError();
if(ExpectAndConsume(tok::r_paren,diag::err_expected_rparen))
return StmtError();
ParsedAttributesWithRange attrs(AttrFactory);
StmtResult Block(ParseCompoundStatement(attrs));
if(Block.isInvalid())
return move(Block);
return Actions.ActOnSEHExceptBlock(ExceptLoc, FilterExpr.take(), Block.take());
}
/// ParseSEHFinallyBlock - Handle __finally
///
/// seh-finally-block:
/// '__finally' compound-statement
///
StmtResult Parser::ParseSEHFinallyBlock(SourceLocation FinallyBlock) {
PoisonIdentifierRAIIObject raii(Ident__abnormal_termination, false),
raii2(Ident___abnormal_termination, false),
raii3(Ident_AbnormalTermination, false);
ParsedAttributesWithRange attrs(AttrFactory);
StmtResult Block(ParseCompoundStatement(attrs));
if(Block.isInvalid())
return move(Block);
return Actions.ActOnSEHFinallyBlock(FinallyBlock,Block.take());
}
/// ParseLabeledStatement - We have an identifier and a ':' after it.
@ -1786,6 +1889,10 @@ StmtResult Parser::ParseCXXTryBlock(ParsedAttributes &attrs) {
/// handler-seq:
/// handler handler-seq[opt]
///
/// [Borland] try-block:
/// 'try' compound-statement seh-except-block
/// 'try' compound-statment seh-finally-block
///
StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
if (Tok.isNot(tok::l_brace))
return StmtError(Diag(Tok, diag::err_expected_lbrace));
@ -1795,6 +1902,27 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
if (TryBlock.isInvalid())
return move(TryBlock);
// Borland allows SEH-handlers with 'try'
if(Tok.is(tok::kw___except) || Tok.is(tok::kw___finally)) {
// TODO: Factor into common return ParseSEHHandlerCommon(...)
StmtResult Handler;
if(Tok.is(tok::kw___except)) {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHExceptBlock(Loc);
}
else {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHFinallyBlock(Loc);
}
if(Handler.isInvalid())
return move(Handler);
return Actions.ActOnSEHTryBlock(true /* IsCXXTry */,
TryLoc,
TryBlock.take(),
Handler.take());
}
else {
StmtVector Handlers(Actions);
MaybeParseCXX0XAttributes(attrs);
ProhibitAttributes(attrs);
@ -1813,6 +1941,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) {
return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.take(), move_arg(Handlers));
}
}
/// ParseCXXCatchBlock - Parse a C++ catch block, called handler in the standard
///

View File

@ -438,6 +438,32 @@ void Parser::Initialize() {
Ident_deprecated = 0;
Ident_obsoleted = 0;
Ident_unavailable = 0;
Ident__exception_code = Ident__exception_info = Ident__abnormal_termination = 0;
Ident___exception_code = Ident___exception_info = Ident___abnormal_termination = 0;
Ident_GetExceptionCode = Ident_GetExceptionInfo = Ident_AbnormalTermination = 0;
if(getLang().Borland) {
Ident__exception_info = PP.getIdentifierInfo("_exception_info");
Ident___exception_info = PP.getIdentifierInfo("__exception_info");
Ident_GetExceptionInfo = PP.getIdentifierInfo("GetExceptionInformation");
Ident__exception_code = PP.getIdentifierInfo("_exception_code");
Ident___exception_code = PP.getIdentifierInfo("__exception_code");
Ident_GetExceptionCode = PP.getIdentifierInfo("GetExceptionCode");
Ident__abnormal_termination = PP.getIdentifierInfo("_abnormal_termination");
Ident___abnormal_termination = PP.getIdentifierInfo("__abnormal_termination");
Ident_AbnormalTermination = PP.getIdentifierInfo("AbnormalTermination");
PP.SetPoisonReason(Ident__exception_code,diag::err_seh___except_block);
PP.SetPoisonReason(Ident___exception_code,diag::err_seh___except_block);
PP.SetPoisonReason(Ident_GetExceptionCode,diag::err_seh___except_block);
PP.SetPoisonReason(Ident__exception_info,diag::err_seh___except_filter);
PP.SetPoisonReason(Ident___exception_info,diag::err_seh___except_filter);
PP.SetPoisonReason(Ident_GetExceptionInfo,diag::err_seh___except_filter);
PP.SetPoisonReason(Ident__abnormal_termination,diag::err_seh___finally_block);
PP.SetPoisonReason(Ident___abnormal_termination,diag::err_seh___finally_block);
PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block);
}
}
/// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
@ -766,6 +792,8 @@ Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
///
Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo) {
// Poison the SEH identifiers so they are flagged as illegal in function bodies
PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
// If this is C90 and the declspecs were completely missing, fudge in an

View File

@ -113,6 +113,30 @@ namespace clang {
}
};
class PoisonSEHIdentifiersRAIIObject {
PoisonIdentifierRAIIObject Ident_AbnormalTermination;
PoisonIdentifierRAIIObject Ident_GetExceptionCode;
PoisonIdentifierRAIIObject Ident_GetExceptionInfo;
PoisonIdentifierRAIIObject Ident__abnormal_termination;
PoisonIdentifierRAIIObject Ident__exception_code;
PoisonIdentifierRAIIObject Ident__exception_info;
PoisonIdentifierRAIIObject Ident___abnormal_termination;
PoisonIdentifierRAIIObject Ident___exception_code;
PoisonIdentifierRAIIObject Ident___exception_info;
public:
PoisonSEHIdentifiersRAIIObject(Parser &Self, bool NewValue)
: Ident_AbnormalTermination(Self.Ident_AbnormalTermination, NewValue),
Ident_GetExceptionCode(Self.Ident_GetExceptionCode, NewValue),
Ident_GetExceptionInfo(Self.Ident_GetExceptionInfo, NewValue),
Ident__abnormal_termination(Self.Ident__abnormal_termination, NewValue),
Ident__exception_code(Self.Ident__exception_code, NewValue),
Ident__exception_info(Self.Ident__exception_info, NewValue),
Ident___abnormal_termination(Self.Ident___abnormal_termination, NewValue),
Ident___exception_code(Self.Ident___exception_code, NewValue),
Ident___exception_info(Self.Ident___exception_info, NewValue) {
}
};
} // end namespace clang
#endif

View File

@ -2231,3 +2231,36 @@ Sema::ActOnCXXTryBlock(SourceLocation TryLoc, Stmt *TryBlock,
return Owned(CXXTryStmt::Create(Context, TryLoc, TryBlock,
Handlers, NumHandlers));
}
StmtResult
Sema::ActOnSEHTryBlock(bool IsCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler) {
assert(TryBlock && Handler);
getCurFunction()->setHasBranchProtectedScope();
return Owned(SEHTryStmt::Create(Context,IsCXXTry,TryLoc,TryBlock,Handler));
}
StmtResult
Sema::ActOnSEHExceptBlock(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block) {
assert(FilterExpr && Block);
if(!FilterExpr->getType()->isIntegerType()) {
return StmtError(Diag(FilterExpr->getExprLoc(), diag::err_filter_expression_integral) << FilterExpr->getType());
}
return Owned(SEHExceptStmt::Create(Context,Loc,FilterExpr,Block));
}
StmtResult
Sema::ActOnSEHFinallyBlock(SourceLocation Loc,
Stmt *Block) {
assert(Block);
return Owned(SEHFinallyStmt::Create(Context,Loc,Block));
}

View File

@ -490,6 +490,9 @@ public:
QualType Transform##CLASS##Type(TypeLocBuilder &TLB, CLASS##TypeLoc T);
#include "clang/AST/TypeLocNodes.def"
StmtResult
TransformSEHHandler(Stmt *Handler);
QualType
TransformTemplateSpecializationType(TypeLocBuilder &TLB,
TemplateSpecializationTypeLoc TL,
@ -1254,6 +1257,24 @@ public:
return getSema().FinishCXXForRangeStmt(ForRange, Body);
}
StmtResult RebuildSEHTryStmt(bool IsCXXTry,
SourceLocation TryLoc,
Stmt *TryBlock,
Stmt *Handler) {
return getSema().ActOnSEHTryBlock(IsCXXTry,TryLoc,TryBlock,Handler);
}
StmtResult RebuildSEHExceptStmt(SourceLocation Loc,
Expr *FilterExpr,
Stmt *Block) {
return getSema().ActOnSEHExceptBlock(Loc,FilterExpr,Block);
}
StmtResult RebuildSEHFinallyStmt(SourceLocation Loc,
Stmt *Block) {
return getSema().ActOnSEHFinallyBlock(Loc,Block);
}
/// \brief Build a new expression that references a declaration.
///
/// By default, performs semantic analysis to build the new expression.
@ -5471,6 +5492,57 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSEHTryStmt(SEHTryStmt *S) {
StmtResult TryBlock; // = getDerived().TransformCompoundStmt(S->getTryBlock());
if(TryBlock.isInvalid()) return StmtError();
StmtResult Handler = getDerived().TransformSEHHandler(S->getHandler());
if(!getDerived().AlwaysRebuild() &&
TryBlock.get() == S->getTryBlock() &&
Handler.get() == S->getHandler())
return SemaRef.Owned(S);
return getDerived().RebuildSEHTryStmt(S->getIsCXXTry(),
S->getTryLoc(),
TryBlock.take(),
Handler.take());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSEHFinallyStmt(SEHFinallyStmt *S) {
StmtResult Block; // = getDerived().TransformCompoundStatement(S->getBlock());
if(Block.isInvalid()) return StmtError();
return getDerived().RebuildSEHFinallyStmt(S->getFinallyLoc(),
Block.take());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSEHExceptStmt(SEHExceptStmt *S) {
ExprResult FilterExpr = getDerived().TransformExpr(S->getFilterExpr());
if(FilterExpr.isInvalid()) return StmtError();
StmtResult Block; // = getDerived().TransformCompoundStatement(S->getBlock());
if(Block.isInvalid()) return StmtError();
return getDerived().RebuildSEHExceptStmt(S->getExceptLoc(),
FilterExpr.take(),
Block.take());
}
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformSEHHandler(Stmt *Handler) {
if(isa<SEHFinallyStmt>(Handler))
return getDerived().TransformSEHFinallyStmt(cast<SEHFinallyStmt>(Handler));
else
return getDerived().TransformSEHExceptStmt(cast<SEHExceptStmt>(Handler));
}
//===----------------------------------------------------------------------===//
// Expression transformation
//===----------------------------------------------------------------------===//

View File

@ -442,6 +442,9 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CXXNoexceptExprClass:
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHFinallyStmtClass:
{
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
Builder->BuildSinks = true;

28
clang/test/AST/__try.c Normal file
View File

@ -0,0 +1,28 @@
// RUN: %ast_test -fborland-extensions %s
#define JOIN2(x,y) x ## y
#define JOIN(x,y) JOIN2(x,y)
#define TEST2(name) JOIN(name,__LINE__)
#define TEST TEST2(test)
typedef int DWORD;
DWORD FilterExpression();
void TEST() {
__try // expected-stmt-class-name{{SEHTryStmt}}
{ // expected-stmt-class-name{{CompoundStmt}}
}
__except ( FilterExpression() ) // expected-stmt-class-name{{SEHExceptStmt}} expected-stmt-class-name{{CallExpr}} \
// expected-expr-type{{DWORD}}
{ // expected-stmt-class-name{{CompoundStmt}}
}
}
void TEST() {
__try // expected-stmt-class-name{{SEHTryStmt}}
{ // expected-stmt-class-name{{CompoundStmt}}
}
__finally // expected-stmt-class-name{{SEHFinallyStmt}}
{ // expected-stmt-class-name{{CompoundStmt}}
}
}

171
clang/test/Sema/__try.c Normal file
View File

@ -0,0 +1,171 @@
// RUN: %clang_cc1 -fborland-extensions -fsyntax-only -verify %s
#define JOIN2(x,y) x ## y
#define JOIN(x,y) JOIN2(x,y)
#define TEST2(name) JOIN(name,__LINE__)
#define TEST TEST2(test)
typedef int DWORD;
#pragma sysheader begin
struct EXCEPTION_INFO{};
int __exception_code();
struct EXCEPTION_INFO* __exception_info();
void __abnormal_termination();
#define GetExceptionCode __exception_code
#define GetExceptionInformation __exception_info
#define AbnormalTermination __abnormal_termination
#pragma sysheader end
DWORD FilterExpression(int);
DWORD FilterExceptionInformation(struct EXCEPTION_INFO*);
const char * NotFilterExpression();
void TEST() {
__try {
__try {
__try {
}
__finally{
}
}
__finally{
}
}
__finally{
}
}
void TEST() {
__try {
}
} // expected-error{{expected '__except' or '__finally' block}}
void TEST() {
__except ( FilterExpression() ) { // expected-error{{}}
}
}
void TEST() {
__finally { } // expected-error{{}}
}
void TEST() {
__try{
int try_scope = 0;
} // TODO: expected expression is an extra error
__except( try_scope ? 1 : -1 ) // expected-error{{undeclared identifier 'try_scope'}} expected-error{{expected expression}}
{}
}
void TEST() {
__try {
}
// TODO: Why are there two errors?
__except( ) { // expected-error{{expected expression}} expected-error{{expected expression}}
}
}
void TEST() {
__try {
}
__except ( FilterExpression(GetExceptionCode()) ) {
}
__try {
}
__except( FilterExpression(__exception_code()) ) {
}
__try {
}
__except( FilterExceptionInformation(__exception_info()) ) {
}
__try {
}
__except(FilterExceptionInformation( GetExceptionInformation() ) ) {
}
}
void TEST() {
__try {
}
__except ( NotFilterExpression() ) { // expected-error{{filter expression type should be an integral value not 'const char *'}}
}
}
void TEST() {
int function_scope = 0;
__try {
int try_scope = 0;
}
__except ( FilterExpression(GetExceptionCode()) ) {
(void)function_scope;
(void)try_scope; // expected-error{{undeclared identifier}}
}
}
void TEST() {
int function_scope = 0;
__try {
int try_scope = 0;
}
__finally {
(void)function_scope;
(void)try_scope; // expected-error{{undeclared identifier}}
}
}
void TEST() {
int function_scope = 0;
__try {
}
__except( function_scope ? 1 : -1 ) {}
}
void TEST() {
__try {
(void)AbnormalTermination; // expected-error{{only allowed in __finally block}}
(void)__abnormal_termination; // expected-error{{only allowed in __finally block}}
}
__except( 1 ) {
(void)AbnormalTermination; // expected-error{{only allowed in __finally block}}
(void)__abnormal_termination; // expected-error{{only allowed in __finally block}}
}
__try {
}
__finally {
AbnormalTermination();
__abnormal_termination();
}
}
void TEST() {
(void)__exception_code; // expected-error{{only allowed in __except block}}
(void)__exception_info; // expected-error{{only allowed in __except filter expression}}
(void)__abnormal_termination; // expected-error{{only allowed in __finally block}}
(void)GetExceptionCode(); // expected-error{{only allowed in __except block}}
(void)GetExceptionInformation(); // expected-error{{only allowed in __except filter expression}}
(void)AbnormalTermination(); // expected-error{{only allowed in __finally block}}
}

View File

@ -0,0 +1,58 @@
// RUN: %clang_cc1 -fsyntax-only -verify -fborland-extensions -fcxx-exceptions %s
// This test is from http://docwiki.embarcadero.com/RADStudio/en/Try
int puts(const char *);
template<typename T>
int printf(const char *, T);
const char * strdup(const char *);
void free(const void *);
#define EXCEPTION_EXECUTE_HANDLER 1
class Exception
{
public:
Exception(const char* s = "Unknown"){what = strdup(s); }
Exception(const Exception& e ){what = strdup(e.what); }
~Exception() {free(what); }
const char* msg() const {return what; }
private:
const char* what;
};
int main()
{
float e, f, g;
try
{
try
{
f = 1.0;
g = 0.0;
try
{
puts("Another exception:");
e = f / g;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
puts("Caught a C-based exception.");
throw(Exception("Hardware error: Divide by 0"));
}
}
catch(const Exception& e)
{
printf("Caught C++ Exception: %s :\n", e.msg());
}
}
__finally
{
puts("C++ allows __finally too!");
}
return e;
}

View File

@ -96,6 +96,9 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent,
case Stmt::CXXCatchStmtClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXForRangeStmtClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHFinallyStmtClass:
K = CXCursor_UnexposedStmt;
break;