diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 12933ca601ee..f2a9af4a1b54 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2266,7 +2266,13 @@ def warn_register_objc_catch_parm : Warning< "'register' storage specifier on @catch parameter will be ignored">; def err_qualified_objc_catch_parm : Error< "@catch parameter declarator cannot be qualified">; - +def err_objc_pointer_cxx_catch_gnu : Error< + "can't catch Objective C exceptions in C++ in the GNU runtime">; +def err_objc_pointer_cxx_catch_fragile : Error< + "can't catch Objective C exceptions in C++ in the non-unified " + "exception model">; +def err_objc_object_catch : Error< + "can't catch an Objective C object by value">; def warn_setter_getter_impl_required : Warning< "property %0 requires method %1 to be defined - " diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index d31cab519498..99105e165f3a 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -16,6 +16,7 @@ #include "llvm/Intrinsics.h" #include "llvm/Support/CallSite.h" +#include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "CGException.h" #include "TargetInfo.h" @@ -617,7 +618,12 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388 QualType CaughtType = C->getCaughtType(); CaughtType = CaughtType.getNonReferenceType().getUnqualifiedType(); - llvm::Value *TypeInfo = CGM.GetAddrOfRTTIDescriptor(CaughtType, true); + + llvm::Value *TypeInfo = 0; + if (CaughtType->isObjCObjectPointerType()) + TypeInfo = CGM.getObjCRuntime().GetEHType(CaughtType); + else + TypeInfo = CGM.GetAddrOfRTTIDescriptor(CaughtType, true); CatchScope->setHandler(I, TypeInfo, Handler); } else { // No exception decl indicates '...', a catch-all. diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index e4fb7bf4214f..a9519ed2bb08 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -167,6 +167,7 @@ public: bool lval = false); virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method); + virtual llvm::Constant *GetEHType(QualType T); virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD); @@ -403,6 +404,11 @@ llvm::Value *CGObjCGNU::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl return Builder.CreateLoad(Sel); } +llvm::Constant *CGObjCGNU::GetEHType(QualType T) { + llvm_unreachable("asking for catch type for ObjC type in GNU runtime"); + return 0; +} + llvm::Constant *CGObjCGNU::MakeConstantString(const std::string &Str, const std::string &Name) { llvm::Constant *ConstStr = CGM.GetAddrOfConstantCString(Str, Name.c_str()); diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index d917c7d8a87e..eeb06d8fc83f 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -1184,6 +1184,8 @@ public: virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method); + virtual llvm::Constant *GetEHType(QualType T); + virtual void GenerateCategory(const ObjCCategoryImplDecl *CMD); virtual void GenerateClass(const ObjCImplementationDecl *ClassDecl); @@ -1354,7 +1356,7 @@ private: /// GetInterfaceEHType - Get the cached ehtype for the given Objective-C /// interface. The return value has type EHTypePtrTy. - llvm::Value *GetInterfaceEHType(const ObjCInterfaceDecl *ID, + llvm::Constant *GetInterfaceEHType(const ObjCInterfaceDecl *ID, bool ForDefinition); const char *getMetaclassSymbolPrefix() const { @@ -1429,6 +1431,8 @@ public: virtual llvm::Value *GenerateProtocolRef(CGBuilderTy &Builder, const ObjCProtocolDecl *PD); + virtual llvm::Constant *GetEHType(QualType T); + virtual llvm::Constant *GetPropertyGetFunction() { return ObjCTypes.getGetPropertyFn(); } @@ -1527,6 +1531,11 @@ llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl return EmitSelector(Builder, Method->getSelector()); } +llvm::Constant *CGObjCMac::GetEHType(QualType T) { + llvm_unreachable("asking for catch type for ObjC type in fragile runtime"); + return 0; +} + /// Generate a constant CFString object. /* struct __builtin_CFString { @@ -5777,6 +5786,31 @@ namespace { }; } +llvm::Constant * +CGObjCNonFragileABIMac::GetEHType(QualType T) { + // There's a particular fixed type info for 'id'. + if (T->isObjCIdType() || + T->isObjCQualifiedIdType()) { + llvm::Constant *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"); + return IDEHType; + } + + // All other types should be Objective-C interface pointer types. + const ObjCObjectPointerType *PT = + T->getAs(); + assert(PT && "Invalid @catch type."); + const ObjCInterfaceType *IT = PT->getInterfaceType(); + assert(IT && "Invalid @catch type."); + return GetInterfaceEHType(IT->getDecl(), false); +} + void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtTryStmt &S) { // Jump destination for falling out of catch bodies. @@ -5812,27 +5846,7 @@ void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF, break; } - // There's a particular fixed type info for 'id'. - 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"); - Handler.TypeInfo = 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."); - Handler.TypeInfo = GetInterfaceEHType(IT->getDecl(), false); - } + Handler.TypeInfo = GetEHType(CatchDecl->getType()); } EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size()); @@ -5931,7 +5945,7 @@ void CGObjCNonFragileABIMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, CGF.Builder.ClearInsertionPoint(); } -llvm::Value * +llvm::Constant * CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, bool ForDefinition) { llvm::GlobalVariable * &Entry = EHTypeReferences[ID->getIdentifier()]; diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h index 88b18b1a2684..bf172f9b1e3b 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -103,6 +103,12 @@ public: virtual llvm::Value *GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl *Method) = 0; + /// Get the type constant to catch for the given ObjC pointer type. + /// This is used externally to implement catching ObjC types in C++. + /// Runtimes which don't support this should add the appropriate + /// error to Sema. + virtual llvm::Constant *GetEHType(QualType T) = 0; + /// Generate a constant string object. virtual llvm::Constant *GenerateConstantString(const StringLiteral *) = 0; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 1927aad4eac2..827eaff92b03 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -5983,6 +5983,27 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, QualType ExDeclType, AbstractVariableType)) Invalid = true; + // Only the non-fragile NeXT runtime currently supports C++ catches + // of ObjC types, and no runtime supports catching ObjC types by value. + if (!Invalid && getLangOptions().ObjC1) { + QualType T = ExDeclType; + if (const ReferenceType *RT = T->getAs()) + T = RT->getPointeeType(); + + if (T->isObjCObjectType()) { + Diag(Loc, diag::err_objc_object_catch); + Invalid = true; + } else if (T->isObjCObjectPointerType()) { + if (!getLangOptions().NeXTRuntime) { + Diag(Loc, diag::err_objc_pointer_cxx_catch_gnu); + Invalid = true; + } else if (!getLangOptions().ObjCNonFragileABI) { + Diag(Loc, diag::err_objc_pointer_cxx_catch_fragile); + Invalid = true; + } + } + } + VarDecl *ExDecl = VarDecl::Create(Context, CurContext, Loc, Name, ExDeclType, TInfo, VarDecl::None, VarDecl::None); diff --git a/clang/test/CodeGenObjCXX/exceptions.mm b/clang/test/CodeGenObjCXX/exceptions.mm new file mode 100644 index 000000000000..00de88c15220 --- /dev/null +++ b/clang/test/CodeGenObjCXX/exceptions.mm @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fexceptions -o - %s | FileCheck %s + +@interface OCType @end +void opaque(); + +namespace test0 { + + // CHECK: define void @_ZN5test03fooEv + void foo() { + try { + // CHECK: invoke void @_Z6opaquev + opaque(); + } catch (OCType *T) { + // CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector({{.*}} @__objc_personality_v0 {{.*}} @"OBJC_EHTYPE_$_OCType" + } + } +} diff --git a/clang/test/SemaObjCXX/exceptions-fragile.mm b/clang/test/SemaObjCXX/exceptions-fragile.mm new file mode 100644 index 000000000000..11dd8e94c1de --- /dev/null +++ b/clang/test/SemaObjCXX/exceptions-fragile.mm @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +@interface NSException @end +void opaque(); + +namespace test0 { + void test() { + try { + } catch (NSException *e) { // expected-error {{can't catch Objective C exceptions in C++ in the non-unified exception model}} + } + } +}