diff --git a/clang/include/clang/AST/StmtObjC.h b/clang/include/clang/AST/StmtObjC.h index 3fd8f1672deb..6cb2b1f18a3c 100644 --- a/clang/include/clang/AST/StmtObjC.h +++ b/clang/include/clang/AST/StmtObjC.h @@ -71,30 +71,23 @@ public: /// ObjCAtCatchStmt - This represents objective-c's @catch statement. class ObjCAtCatchStmt : public Stmt { private: - enum { BODY, NEXT_CATCH, END_EXPR }; ParmVarDecl *ExceptionDecl; - Stmt *SubExprs[END_EXPR]; + Stmt *Body; SourceLocation AtCatchLoc, RParenLoc; public: ObjCAtCatchStmt(SourceLocation atCatchLoc, SourceLocation rparenloc, ParmVarDecl *catchVarDecl, - Stmt *atCatchStmt, Stmt *atCatchList); + Stmt *atCatchStmt) + : Stmt(ObjCAtCatchStmtClass), ExceptionDecl(catchVarDecl), + Body(atCatchStmt), AtCatchLoc(atCatchLoc), RParenLoc(rparenloc) { } explicit ObjCAtCatchStmt(EmptyShell Empty) : Stmt(ObjCAtCatchStmtClass, Empty) { } - const Stmt *getCatchBody() const { return SubExprs[BODY]; } - Stmt *getCatchBody() { return SubExprs[BODY]; } - void setCatchBody(Stmt *S) { SubExprs[BODY] = S; } - - const ObjCAtCatchStmt *getNextCatchStmt() const { - return static_cast(SubExprs[NEXT_CATCH]); - } - ObjCAtCatchStmt *getNextCatchStmt() { - return static_cast(SubExprs[NEXT_CATCH]); - } - void setNextCatchStmt(Stmt *S) { SubExprs[NEXT_CATCH] = S; } + const Stmt *getCatchBody() const { return Body; } + Stmt *getCatchBody() { return Body; } + void setCatchBody(Stmt *S) { Body = S; } const ParmVarDecl *getCatchParamDecl() const { return ExceptionDecl; @@ -110,7 +103,7 @@ public: void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } virtual SourceRange getSourceRange() const { - return SourceRange(AtCatchLoc, SubExprs[BODY]->getLocEnd()); + return SourceRange(AtCatchLoc, Body->getLocEnd()); } bool hasEllipsis() const { return getCatchParamDecl() == 0; } @@ -160,49 +153,93 @@ public: /// @try ... @catch ... @finally statement. class ObjCAtTryStmt : public Stmt { private: - enum { TRY, CATCH, FINALLY, END_EXPR }; - Stmt* SubStmts[END_EXPR]; - + // The location of the SourceLocation AtTryLoc; -public: + + // The number of catch blocks in this statement. + unsigned NumCatchStmts : 16; + + // Whether this statement has a @finally statement. + bool HasFinally : 1; + + /// \brief Retrieve the statements that are stored after this @try statement. + /// + /// The order of the statements in memory follows the order in the source, + /// with the @try body first, followed by the @catch statements (if any) and, + /// finally, the @finally (if it exists). + Stmt **getStmts() { return reinterpret_cast (this + 1); } + const Stmt* const *getStmts() const { + return reinterpret_cast (this + 1); + } + ObjCAtTryStmt(SourceLocation atTryLoc, Stmt *atTryStmt, - Stmt *atCatchStmt, - Stmt *atFinallyStmt) - : Stmt(ObjCAtTryStmtClass) { - SubStmts[TRY] = atTryStmt; - SubStmts[CATCH] = atCatchStmt; - SubStmts[FINALLY] = atFinallyStmt; - AtTryLoc = atTryLoc; - } - explicit ObjCAtTryStmt(EmptyShell Empty) : - Stmt(ObjCAtTryStmtClass, Empty) { } + Stmt **CatchStmts, unsigned NumCatchStmts, + Stmt *atFinallyStmt); + + explicit ObjCAtTryStmt(EmptyShell Empty, unsigned NumCatchStmts, + bool HasFinally) + : Stmt(ObjCAtTryStmtClass, Empty), NumCatchStmts(NumCatchStmts), + HasFinally(HasFinally) { } +public: + static ObjCAtTryStmt *Create(ASTContext &Context, SourceLocation atTryLoc, + Stmt *atTryStmt, + Stmt **CatchStmts, unsigned NumCatchStmts, + Stmt *atFinallyStmt); + static ObjCAtTryStmt *CreateEmpty(ASTContext &Context, + unsigned NumCatchStmts, + bool HasFinally); + + /// \brief Retrieve the location of the @ in the @try. SourceLocation getAtTryLoc() const { return AtTryLoc; } void setAtTryLoc(SourceLocation Loc) { AtTryLoc = Loc; } - const Stmt *getTryBody() const { return SubStmts[TRY]; } - Stmt *getTryBody() { return SubStmts[TRY]; } - void setTryBody(Stmt *S) { SubStmts[TRY] = S; } + /// \brief Retrieve the @try body. + const Stmt *getTryBody() const { return getStmts()[0]; } + Stmt *getTryBody() { return getStmts()[0]; } + void setTryBody(Stmt *S) { getStmts()[0] = S; } - const ObjCAtCatchStmt *getCatchStmts() const { - return dyn_cast_or_null(SubStmts[CATCH]); + /// \brief Retrieve the number of @catch statements in this try-catch-finally + /// block. + unsigned getNumCatchStmts() const { return NumCatchStmts; } + + /// \brief Retrieve a @catch statement. + const ObjCAtCatchStmt *getCatchStmt(unsigned I) const { + assert(I < NumCatchStmts && "Out-of-bounds @catch index"); + return cast_or_null(getStmts()[I + 1]); } - ObjCAtCatchStmt *getCatchStmts() { - return dyn_cast_or_null(SubStmts[CATCH]); + + /// \brief Retrieve a @catch statement. + ObjCAtCatchStmt *getCatchStmt(unsigned I) { + assert(I < NumCatchStmts && "Out-of-bounds @catch index"); + return cast_or_null(getStmts()[I + 1]); } - void setCatchStmts(Stmt *S) { SubStmts[CATCH] = S; } - + + /// \brief Set a particular catch statement. + void setCatchStmt(unsigned I, ObjCAtCatchStmt *S) { + assert(I < NumCatchStmts && "Out-of-bounds @catch index"); + getStmts()[I + 1] = S; + } + + /// Retrieve the @finally statement, if any. const ObjCAtFinallyStmt *getFinallyStmt() const { - return dyn_cast_or_null(SubStmts[FINALLY]); + if (!HasFinally) + return 0; + + return cast_or_null(getStmts()[1 + NumCatchStmts]); } ObjCAtFinallyStmt *getFinallyStmt() { - return dyn_cast_or_null(SubStmts[FINALLY]); + if (!HasFinally) + return 0; + + return cast_or_null(getStmts()[1 + NumCatchStmts]); + } + void setFinallyStmt(Stmt *S) { + assert(HasFinally && "@try does not have a @finally slot!"); + getStmts()[1 + NumCatchStmts] = S; } - void setFinallyStmt(Stmt *S) { SubStmts[FINALLY] = S; } - virtual SourceRange getSourceRange() const { - return SourceRange(AtTryLoc, SubStmts[TRY]->getLocEnd()); - } + virtual SourceRange getSourceRange() const; static bool classof(const Stmt *T) { return T->getStmtClass() == ObjCAtTryStmtClass; diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index c4ffa8be4091..dd12fdfe3ecf 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -938,20 +938,46 @@ public: } // Objective-c statements + + /// \brief Parsed an Objective-C @catch statement. + /// + /// \param AtLoc The location of the '@' starting the '@catch'. + /// + /// \param RParen The location of the right parentheses ')' after the + /// exception variable. + /// + /// \param Parm The variable that will catch the exception. Will be NULL if + /// this is a @catch(...) block. + /// + /// \param Body The body of the @catch block. virtual OwningStmtResult ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, - DeclPtrTy Parm, StmtArg Body, - StmtArg CatchList) { + DeclPtrTy Parm, StmtArg Body) { return StmtEmpty(); } + /// \brief Parsed an Objective-C @finally statement. + /// + /// \param AtLoc The location of the '@' starting the '@finally'. + /// + /// \param Body The body of the @finally block. virtual OwningStmtResult ActOnObjCAtFinallyStmt(SourceLocation AtLoc, StmtArg Body) { return StmtEmpty(); } + /// \brief Parsed an Objective-C @try-@catch-@finally statement. + /// + /// \param AtLoc The location of the '@' starting '@try'. + /// + /// \param Try The body of the '@try' statement. + /// + /// \param CatchStmts The @catch statements. + /// + /// \param Finally The @finally statement. virtual OwningStmtResult ActOnObjCAtTryStmt(SourceLocation AtLoc, - StmtArg Try, StmtArg Catch, + StmtArg Try, + MultiStmtArg CatchStmts, StmtArg Finally) { return StmtEmpty(); } diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index ee4a74a6125d..67fd74c288c7 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -392,26 +392,53 @@ ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect, RParenLoc = RPL; } +ObjCAtTryStmt::ObjCAtTryStmt(SourceLocation atTryLoc, Stmt *atTryStmt, + Stmt **CatchStmts, unsigned NumCatchStmts, + Stmt *atFinallyStmt) + : Stmt(ObjCAtTryStmtClass), AtTryLoc(atTryLoc), + NumCatchStmts(NumCatchStmts), HasFinally(atFinallyStmt != 0) +{ + Stmt **Stmts = getStmts(); + Stmts[0] = atTryStmt; + for (unsigned I = 0; I != NumCatchStmts; ++I) + Stmts[I + 1] = CatchStmts[I]; + + if (HasFinally) + Stmts[NumCatchStmts + 1] = atFinallyStmt; +} -ObjCAtCatchStmt::ObjCAtCatchStmt(SourceLocation atCatchLoc, - SourceLocation rparenloc, - ParmVarDecl *catchVarDecl, Stmt *atCatchStmt, - Stmt *atCatchList) -: Stmt(ObjCAtCatchStmtClass) { - ExceptionDecl = catchVarDecl; - SubExprs[BODY] = atCatchStmt; - SubExprs[NEXT_CATCH] = NULL; - // FIXME: O(N^2) in number of catch blocks. - if (atCatchList) { - ObjCAtCatchStmt *AtCatchList = static_cast(atCatchList); +ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context, + SourceLocation atTryLoc, + Stmt *atTryStmt, + Stmt **CatchStmts, + unsigned NumCatchStmts, + Stmt *atFinallyStmt) { + unsigned Size = sizeof(ObjCAtTryStmt) + + (1 + NumCatchStmts + (atFinallyStmt != 0)) * sizeof(Stmt *); + void *Mem = Context.Allocate(Size, llvm::alignof()); + return new (Mem) ObjCAtTryStmt(atTryLoc, atTryStmt, CatchStmts, NumCatchStmts, + atFinallyStmt); +} - while (ObjCAtCatchStmt* NextCatch = AtCatchList->getNextCatchStmt()) - AtCatchList = NextCatch; +ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context, + unsigned NumCatchStmts, + bool HasFinally) { + unsigned Size = sizeof(ObjCAtTryStmt) + + (1 + NumCatchStmts + HasFinally) * sizeof(Stmt *); + void *Mem = Context.Allocate(Size, llvm::alignof()); + return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally); +} - AtCatchList->SubExprs[NEXT_CATCH] = this; - } - AtCatchLoc = atCatchLoc; - RParenLoc = rparenloc; +SourceRange ObjCAtTryStmt::getSourceRange() const { + SourceLocation EndLoc; + if (HasFinally) + EndLoc = getFinallyStmt()->getLocEnd(); + else if (NumCatchStmts) + EndLoc = getCatchStmt(NumCatchStmts - 1)->getLocEnd(); + else + EndLoc = getTryBody()->getLocEnd(); + + return SourceRange(AtTryLoc, EndLoc); } CXXTryStmt *CXXTryStmt::Create(ASTContext &C, SourceLocation tryLoc, @@ -630,19 +657,18 @@ Stmt::child_iterator AsmStmt::child_end() { } // ObjCAtCatchStmt -Stmt::child_iterator ObjCAtCatchStmt::child_begin() { return &SubExprs[0]; } -Stmt::child_iterator ObjCAtCatchStmt::child_end() { - return &SubExprs[0]+END_EXPR; -} +Stmt::child_iterator ObjCAtCatchStmt::child_begin() { return &Body; } +Stmt::child_iterator ObjCAtCatchStmt::child_end() { return &Body + 1; } // ObjCAtFinallyStmt Stmt::child_iterator ObjCAtFinallyStmt::child_begin() { return &AtFinallyStmt; } Stmt::child_iterator ObjCAtFinallyStmt::child_end() { return &AtFinallyStmt+1; } // ObjCAtTryStmt -Stmt::child_iterator ObjCAtTryStmt::child_begin() { return &SubStmts[0]; } -Stmt::child_iterator ObjCAtTryStmt::child_end() { - return &SubStmts[0]+END_EXPR; +Stmt::child_iterator ObjCAtTryStmt::child_begin() { return getStmts(); } + +Stmt::child_iterator ObjCAtTryStmt::child_end() { + return getStmts() + 1 + NumCatchStmts + HasFinally; } // ObjCAtThrowStmt diff --git a/clang/lib/AST/StmtDumper.cpp b/clang/lib/AST/StmtDumper.cpp index 05adc0292e38..8481055b9d07 100644 --- a/clang/lib/AST/StmtDumper.cpp +++ b/clang/lib/AST/StmtDumper.cpp @@ -143,6 +143,7 @@ namespace { void DumpCXXTemporary(CXXTemporary *Temporary); // ObjC + void VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node); void VisitObjCEncodeExpr(ObjCEncodeExpr *Node); void VisitObjCMessageExpr(ObjCMessageExpr* Node); void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); @@ -524,6 +525,16 @@ void StmtDumper::VisitObjCMessageExpr(ObjCMessageExpr* Node) { } } +void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) { + DumpStmt(Node); + if (ParmVarDecl *CatchParam = Node->getCatchParamDecl()) { + OS << " catch parm = "; + DumpDeclarator(CatchParam); + } else { + OS << " catch all"; + } +} + void StmtDumper::VisitObjCEncodeExpr(ObjCEncodeExpr *Node) { DumpExpr(Node); OS << " "; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index a895b63a79cb..3996528287dc 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -388,11 +388,8 @@ void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { OS << "\n"; } - for (ObjCAtCatchStmt *catchStmt = - static_cast(Node->getCatchStmts()); - catchStmt; - catchStmt = - static_cast(catchStmt->getNextCatchStmt())) { + for (unsigned I = 0, N = Node->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *catchStmt = Node->getCatchStmt(I); Indent() << "@catch("; if (catchStmt->getCatchParamDecl()) { if (Decl *DS = catchStmt->getCatchParamDecl()) diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 36162f7e5e52..a248c5a1be0c 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -1781,11 +1781,12 @@ void CGObjCGNU::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, bool HasCatchAll = false; // Only @try blocks are allowed @catch blocks, but both can have @finally if (isTry) { - if (const ObjCAtCatchStmt* CatchStmt = - cast(S).getCatchStmts()) { + if (cast(S).getNumCatchStmts()) { + const ObjCAtTryStmt &AtTry = cast(S); CGF.setInvokeDest(CatchInCatch); - for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) { + for (unsigned I = 0, N = AtTry.getNumCatchStmts(); I != N; ++I) { + const ObjCAtCatchStmt *CatchStmt = AtTry.getCatchStmt(I); const ParmVarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody())); diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 421136ba47e3..e000bd22aaa1 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2646,8 +2646,9 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, CGF.Builder.CreateStore(llvm::ConstantInt::getFalse(VMContext), CallTryExitPtr); CGF.EmitBranchThroughCleanup(FinallyRethrow); - } else if (const ObjCAtCatchStmt* CatchStmt = - cast(S).getCatchStmts()) { + } else if (cast(S).getNumCatchStmts()) { + const ObjCAtTryStmt* AtTryStmt = cast(&S); + // Enter a new exception try block (in case a @catch block throws // an exception). CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData); @@ -2666,7 +2667,8 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, // matched and avoid generating code for falling off the end if // so. bool AllMatched = false; - for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) { + for (unsigned I = 0, N = AtTryStmt->getNumCatchStmts(); I != N; ++I) { + const ObjCAtCatchStmt *CatchStmt = AtTryStmt->getCatchStmt(I); llvm::BasicBlock *NextCatchBlock = CGF.createBasicBlock("catch"); const ParmVarDecl *CatchParam = CatchStmt->getCatchParamDecl(); @@ -5579,42 +5581,41 @@ CGObjCNonFragileABIMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, llvm::SmallVector, 8> Handlers; bool HasCatchAll = false; if (isTry) { - if (const ObjCAtCatchStmt* CatchStmt = - cast(S).getCatchStmts()) { - for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) { - const ParmVarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); - Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody())); + const ObjCAtTryStmt &AtTry = cast(S); + for (unsigned I = 0, N = AtTry.getNumCatchStmts(); I != N; ++I) { + const ObjCAtCatchStmt *CatchStmt = AtTry.getCatchStmt(I); + const ParmVarDecl *CatchDecl = CatchStmt->getCatchParamDecl(); + Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody())); - // catch(...) always matches. - if (!CatchDecl) { - // Use i8* null here to signal this is a catch all, not a cleanup. - llvm::Value *Null = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); - SelectorArgs.push_back(Null); - HasCatchAll = true; - break; - } + // catch(...) always matches. + if (!CatchDecl) { + // Use i8* null here to signal this is a catch all, not a cleanup. + llvm::Value *Null = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); + SelectorArgs.push_back(Null); + HasCatchAll = true; + break; + } - if (CatchDecl->getType()->isObjCIdType() || - CatchDecl->getType()->isObjCQualifiedIdType()) { - llvm::Value *IDEHType = - CGM.getModule().getGlobalVariable("OBJC_EHTYPE_id"); - if (!IDEHType) - IDEHType = - new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, - false, - llvm::GlobalValue::ExternalLinkage, - 0, "OBJC_EHTYPE_id"); - SelectorArgs.push_back(IDEHType); - } else { - // All other types should be Objective-C interface pointer types. - const ObjCObjectPointerType *PT = - CatchDecl->getType()->getAs(); - assert(PT && "Invalid @catch type."); - const ObjCInterfaceType *IT = PT->getInterfaceType(); - assert(IT && "Invalid @catch type."); - llvm::Value *EHType = GetInterfaceEHType(IT->getDecl(), false); - SelectorArgs.push_back(EHType); - } + if (CatchDecl->getType()->isObjCIdType() || + CatchDecl->getType()->isObjCQualifiedIdType()) { + llvm::Value *IDEHType = + CGM.getModule().getGlobalVariable("OBJC_EHTYPE_id"); + if (!IDEHType) + IDEHType = + new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.EHTypeTy, + false, + llvm::GlobalValue::ExternalLinkage, + 0, "OBJC_EHTYPE_id"); + SelectorArgs.push_back(IDEHType); + } else { + // All other types should be Objective-C interface pointer types. + const ObjCObjectPointerType *PT = + CatchDecl->getType()->getAs(); + assert(PT && "Invalid @catch type."); + const ObjCInterfaceType *IT = PT->getInterfaceType(); + assert(IT && "Invalid @catch type."); + llvm::Value *EHType = GetInterfaceEHType(IT->getDecl(), false); + SelectorArgs.push_back(EHType); } } } diff --git a/clang/lib/Frontend/PCHReaderStmt.cpp b/clang/lib/Frontend/PCHReaderStmt.cpp index d253654f7c50..cf26b6e23726 100644 --- a/clang/lib/Frontend/PCHReaderStmt.cpp +++ b/clang/lib/Frontend/PCHReaderStmt.cpp @@ -838,12 +838,11 @@ unsigned PCHStmtReader::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { unsigned PCHStmtReader::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) { VisitStmt(S); - S->setCatchBody(cast_or_null(StmtStack[StmtStack.size() - 2])); - S->setNextCatchStmt(cast_or_null(StmtStack[StmtStack.size() - 1])); + S->setCatchBody(cast_or_null(StmtStack.back())); S->setCatchParamDecl(cast_or_null(Reader.GetDecl(Record[Idx++]))); S->setAtCatchLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); S->setRParenLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); - return 2; + return 1; } unsigned PCHStmtReader::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { @@ -855,11 +854,21 @@ unsigned PCHStmtReader::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { unsigned PCHStmtReader::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { VisitStmt(S); - S->setTryBody(cast_or_null(StmtStack[StmtStack.size() - 3])); - S->setCatchStmts(cast_or_null(StmtStack[StmtStack.size() - 2])); - S->setFinallyStmt(cast_or_null(StmtStack[StmtStack.size() - 1])); + assert(Record[Idx] == S->getNumCatchStmts()); + ++Idx; + bool HasFinally = Record[Idx++]; + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + unsigned Offset = StmtStack.size() - N - HasFinally + I; + S->setCatchStmt(I, cast_or_null(StmtStack[Offset])); + } + + unsigned TryOffset + = StmtStack.size() - S->getNumCatchStmts() - HasFinally - 1; + S->setTryBody(cast_or_null(StmtStack[TryOffset])); + if (HasFinally) + S->setFinallyStmt(cast_or_null(StmtStack[StmtStack.size() - 1])); S->setAtTryLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); - return 3; + return 1 + S->getNumCatchStmts() + HasFinally; } unsigned PCHStmtReader::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S) { @@ -1231,7 +1240,9 @@ Stmt *PCHReader::ReadStmt(llvm::BitstreamCursor &Cursor) { S = new (Context) ObjCAtFinallyStmt(Empty); break; case pch::STMT_OBJC_AT_TRY: - S = new (Context) ObjCAtTryStmt(Empty); + S = ObjCAtTryStmt::CreateEmpty(*Context, + Record[PCHStmtReader::NumStmtFields], + Record[PCHStmtReader::NumStmtFields + 1]); break; case pch::STMT_OBJC_AT_SYNCHRONIZED: S = new (Context) ObjCAtSynchronizedStmt(Empty); diff --git a/clang/lib/Frontend/PCHWriterStmt.cpp b/clang/lib/Frontend/PCHWriterStmt.cpp index 9a9539bb5473..a0ea5c9553c1 100644 --- a/clang/lib/Frontend/PCHWriterStmt.cpp +++ b/clang/lib/Frontend/PCHWriterStmt.cpp @@ -764,7 +764,6 @@ void PCHStmtWriter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { void PCHStmtWriter::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) { Writer.WriteSubStmt(S->getCatchBody()); - Writer.WriteSubStmt(S->getNextCatchStmt()); Writer.AddDeclRef(S->getCatchParamDecl(), Record); Writer.AddSourceLocation(S->getAtCatchLoc(), Record); Writer.AddSourceLocation(S->getRParenLoc(), Record); @@ -778,9 +777,13 @@ void PCHStmtWriter::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { } void PCHStmtWriter::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { + Record.push_back(S->getNumCatchStmts()); + Record.push_back(S->getFinallyStmt() != 0); Writer.WriteSubStmt(S->getTryBody()); - Writer.WriteSubStmt(S->getCatchStmts()); - Writer.WriteSubStmt(S->getFinallyStmt()); + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) + Writer.WriteSubStmt(S->getCatchStmt(I)); + if (S->getFinallyStmt()) + Writer.WriteSubStmt(S->getFinallyStmt()); Writer.AddSourceLocation(S->getAtTryLoc(), Record); Code = pch::STMT_OBJC_AT_TRY; } diff --git a/clang/lib/Frontend/RewriteObjC.cpp b/clang/lib/Frontend/RewriteObjC.cpp index 66b8d39c70bf..fc674354c445 100644 --- a/clang/lib/Frontend/RewriteObjC.cpp +++ b/clang/lib/Frontend/RewriteObjC.cpp @@ -1861,8 +1861,7 @@ Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { assert((*startBuf == '}') && "bogus @try block"); SourceLocation lastCurlyLoc = startLoc; - ObjCAtCatchStmt *catchList = S->getCatchStmts(); - if (catchList) { + if (S->getNumCatchStmts()) { startLoc = startLoc.getFileLocWithOffset(1); buf = " /* @catch begin */ else {\n"; buf += " id _caught = objc_exception_extract(&_stack);\n"; @@ -1880,26 +1879,27 @@ Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { } bool sawIdTypedCatch = false; Stmt *lastCatchBody = 0; - while (catchList) { - ParmVarDecl *catchDecl = catchList->getCatchParamDecl(); + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + ParmVarDecl *catchDecl = Catch->getCatchParamDecl(); - if (catchList == S->getCatchStmts()) + if (I == 0) buf = "if ("; // we are generating code for the first catch clause else buf = "else if ("; - startLoc = catchList->getLocStart(); + startLoc = Catch->getLocStart(); startBuf = SM->getCharacterData(startLoc); assert((*startBuf == '@') && "bogus @catch location"); const char *lParenLoc = strchr(startBuf, '('); - if (catchList->hasEllipsis()) { + if (Catch->hasEllipsis()) { // Now rewrite the body... - lastCatchBody = catchList->getCatchBody(); + lastCatchBody = Catch->getCatchBody(); SourceLocation bodyLoc = lastCatchBody->getLocStart(); const char *bodyBuf = SM->getCharacterData(bodyLoc); - assert(*SM->getCharacterData(catchList->getRParenLoc()) == ')' && + assert(*SM->getCharacterData(Catch->getRParenLoc()) == ')' && "bogus @catch paren location"); assert((*bodyBuf == '{') && "bogus @catch body location"); @@ -1923,8 +1923,8 @@ Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { } } // Now rewrite the body... - lastCatchBody = catchList->getCatchBody(); - SourceLocation rParenLoc = catchList->getRParenLoc(); + lastCatchBody = Catch->getCatchBody(); + SourceLocation rParenLoc = Catch->getRParenLoc(); SourceLocation bodyLoc = lastCatchBody->getLocStart(); const char *bodyBuf = SM->getCharacterData(bodyLoc); const char *rParenBuf = SM->getCharacterData(rParenLoc); @@ -1937,8 +1937,6 @@ Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { } else { assert(false && "@catch rewrite bug"); } - // make sure all the catch bodies get rewritten! - catchList = catchList->getNextCatchStmt(); } // Complete the catch list... if (lastCatchBody) { diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 7337445b64d1..4846dad7b09b 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -1509,7 +1509,7 @@ Parser::OwningStmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { Diag(Tok, diag::err_expected_lbrace); return StmtError(); } - OwningStmtResult CatchStmts(Actions); + StmtVector CatchStmts(Actions); OwningStmtResult FinallyStmt(Actions); ParseScope TryScope(this, Scope::DeclScope); OwningStmtResult TryBody(ParseCompoundStatementBody()); @@ -1564,9 +1564,14 @@ Parser::OwningStmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { Diag(Tok, diag::err_expected_lbrace); if (CatchBody.isInvalid()) CatchBody = Actions.ActOnNullStmt(Tok.getLocation()); - CatchStmts = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc, - RParenLoc, FirstPart, move(CatchBody), - move(CatchStmts)); + + OwningStmtResult Catch = Actions.ActOnObjCAtCatchStmt(AtCatchFinallyLoc, + RParenLoc, + FirstPart, + move(CatchBody)); + if (!Catch.isInvalid()) + CatchStmts.push_back(Catch.release()); + } else { Diag(AtCatchFinallyLoc, diag::err_expected_lparen_after) << "@catch clause"; @@ -1595,7 +1600,9 @@ Parser::OwningStmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { Diag(atLoc, diag::err_missing_catch_finally); return StmtError(); } - return Actions.ActOnObjCAtTryStmt(atLoc, move(TryBody), move(CatchStmts), + + return Actions.ActOnObjCAtTryStmt(atLoc, move(TryBody), + move_arg(CatchStmts), move(FinallyStmt)); } diff --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp index 1c761b95039c..069429490385 100644 --- a/clang/lib/Sema/JumpDiagnostics.cpp +++ b/clang/lib/Sema/JumpDiagnostics.cpp @@ -155,8 +155,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { BuildScopeInformation(TryPart, Scopes.size()-1); // Jump from the catch to the finally or try is not valid. - for (ObjCAtCatchStmt *AC = AT->getCatchStmts(); AC; - AC = AC->getNextCatchStmt()) { + for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *AC = AT->getCatchStmt(I); Scopes.push_back(GotoScope(ParentScope, diag::note_protected_by_objc_catch, AC->getAtCatchLoc())); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 1065fc4fa81d..e9cd6ca7d64e 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1675,15 +1675,15 @@ public: virtual OwningStmtResult ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, - DeclPtrTy Parm, StmtArg Body, - StmtArg CatchList); + DeclPtrTy Parm, StmtArg Body); virtual OwningStmtResult ActOnObjCAtFinallyStmt(SourceLocation AtLoc, StmtArg Body); virtual OwningStmtResult ActOnObjCAtTryStmt(SourceLocation AtLoc, StmtArg Try, - StmtArg Catch, StmtArg Finally); + MultiStmtArg Catch, + StmtArg Finally); virtual OwningStmtResult BuildObjCAtThrowStmt(SourceLocation AtLoc, ExprArg Throw); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index ee7cc6924207..a5292efea172 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1526,8 +1526,7 @@ Sema::OwningStmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, Action::OwningStmtResult Sema::ActOnObjCAtCatchStmt(SourceLocation AtLoc, SourceLocation RParen, DeclPtrTy Parm, - StmtArg Body, StmtArg catchList) { - Stmt *CatchList = catchList.takeAs(); + StmtArg Body) { ParmVarDecl *PVD = cast_or_null(Parm.getAs()); // PVD == 0 implies @catch(...). @@ -1544,9 +1543,8 @@ Sema::ActOnObjCAtCatchStmt(SourceLocation AtLoc, diag::err_illegal_qualifiers_on_catch_parm)); } - ObjCAtCatchStmt *CS = new (Context) ObjCAtCatchStmt(AtLoc, RParen, - PVD, Body.takeAs(), CatchList); - return Owned(CatchList ? CatchList : CS); + return Owned(new (Context) ObjCAtCatchStmt(AtLoc, RParen, PVD, + Body.takeAs())); } Action::OwningStmtResult @@ -1556,12 +1554,14 @@ Sema::ActOnObjCAtFinallyStmt(SourceLocation AtLoc, StmtArg Body) { } Action::OwningStmtResult -Sema::ActOnObjCAtTryStmt(SourceLocation AtLoc, - StmtArg Try, StmtArg Catch, StmtArg Finally) { +Sema::ActOnObjCAtTryStmt(SourceLocation AtLoc, StmtArg Try, + MultiStmtArg CatchStmts, StmtArg Finally) { FunctionNeedsScopeChecking() = true; - return Owned(new (Context) ObjCAtTryStmt(AtLoc, Try.takeAs(), - Catch.takeAs(), - Finally.takeAs())); + unsigned NumCatchStmts = CatchStmts.size(); + return Owned(ObjCAtTryStmt::Create(Context, AtLoc, Try.takeAs(), + (Stmt **)CatchStmts.release(), + NumCatchStmts, + Finally.takeAs())); } Sema::OwningStmtResult Sema::BuildObjCAtThrowStmt(SourceLocation AtLoc, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a41cbc38a292..8b2aa4e30882 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -896,9 +896,9 @@ public: /// Subclasses may override this routine to provide different behavior. OwningStmtResult RebuildObjCAtTryStmt(SourceLocation AtLoc, StmtArg TryBody, - StmtArg Catch, + MultiStmtArg CatchStmts, StmtArg Finally) { - return getSema().ActOnObjCAtTryStmt(AtLoc, move(TryBody), move(Catch), + return getSema().ActOnObjCAtTryStmt(AtLoc, move(TryBody), move(CatchStmts), move(Finally)); } @@ -3688,12 +3688,16 @@ TreeTransform::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { if (TryBody.isInvalid()) return SemaRef.StmtError(); - // Transform the @catch statement (if present). - OwningStmtResult Catch(SemaRef); - if (S->getCatchStmts()) { - Catch = getDerived().TransformStmt(S->getCatchStmts()); + // Transform the @catch statements (if present). + bool AnyCatchChanged = false; + ASTOwningVector<&ActionBase::DeleteStmt> CatchStmts(SemaRef); + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + OwningStmtResult Catch = getDerived().TransformStmt(S->getCatchStmt(I)); if (Catch.isInvalid()) return SemaRef.StmtError(); + if (Catch.get() != S->getCatchStmt(I)) + AnyCatchChanged = true; + CatchStmts.push_back(Catch.release()); } // Transform the @finally statement (if present). @@ -3707,13 +3711,13 @@ TreeTransform::TransformObjCAtTryStmt(ObjCAtTryStmt *S) { // If nothing changed, just retain this statement. if (!getDerived().AlwaysRebuild() && TryBody.get() == S->getTryBody() && - Catch.get() == S->getCatchStmts() && + !AnyCatchChanged && Finally.get() == S->getFinallyStmt()) return SemaRef.Owned(S->Retain()); // Build a new statement. return getDerived().RebuildObjCAtTryStmt(S->getAtTryLoc(), move(TryBody), - move(Catch), move(Finally)); + move_arg(CatchStmts), move(Finally)); } template diff --git a/clang/test/PCH/objc_stmts.h b/clang/test/PCH/objc_stmts.h new file mode 100644 index 000000000000..5f705dfcdbb7 --- /dev/null +++ b/clang/test/PCH/objc_stmts.h @@ -0,0 +1,22 @@ +/* For use with the methods.m test */ + +@interface A +@end + +@interface B +@end + +@interface TestPCH +- (void)instMethod; +@end + +@implementation TestPCH +- (void)instMethod { + @try { + } @catch(A *a) { + } @catch(B *b) { + } @catch(...) { + } @finally { + } +} +@end diff --git a/clang/test/PCH/objc_stmts.m b/clang/test/PCH/objc_stmts.m new file mode 100644 index 000000000000..ed7466a53d9e --- /dev/null +++ b/clang/test/PCH/objc_stmts.m @@ -0,0 +1,12 @@ +// Test this without pch. +// RUN: %clang_cc1 -include %S/objc_stmts.h -emit-llvm -o - %s +// RUN: %clang_cc1 -include %S/objc_stmts.h -ast-dump -o - %s 2>&1 | FileCheck %s + +// Test with pch. +// RUN: %clang_cc1 -x objective-c -emit-pch -o %t %S/objc_stmts.h +// RUN: %clang_cc1 -include-pch %t -emit-llvm -o - %s +// RUN: %clang_cc1 -include-pch %t -ast-dump -o - %s 2>&1 | FileCheck %s + +// CHECK: catch parm = "A *a" +// CHECK: catch parm = "B *b" +// CHECK: catch all