]> granicus.if.org Git - clang/commitdiff
Support catching Objective C pointers in C++ under the non-fragile NeXT runtime.
authorJohn McCall <rjmccall@apple.com>
Sat, 24 Jul 2010 00:37:23 +0000 (00:37 +0000)
committerJohn McCall <rjmccall@apple.com>
Sat, 24 Jul 2010 00:37:23 +0000 (00:37 +0000)
Diagnose attempts to do this under the GNU or fragile NeXT runtimes.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@109298 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/CodeGen/CGException.cpp
lib/CodeGen/CGObjCGNU.cpp
lib/CodeGen/CGObjCMac.cpp
lib/CodeGen/CGObjCRuntime.h
lib/Sema/SemaDeclCXX.cpp
test/CodeGenObjCXX/exceptions.mm [new file with mode: 0644]
test/SemaObjCXX/exceptions-fragile.mm [new file with mode: 0644]

index 12933ca601ee2a3a4e1581095694f5b40c684026..f2a9af4a1b540e532c4245f6192b91d6a088e652 100644 (file)
@@ -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 - "
index d31cab5194985f465415e5631edac705b181e45a..99105e165f3a93ebe987072ef61d653e4339cba4 100644 (file)
@@ -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.
index e4fb7bf4214f4f6c947d7815c5874827c4925cde..a9519ed2bb085c2b9b4123f075be396f9e0ce4ef 100644 (file)
@@ -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());
index d917c7d8a87e186a65cf278a8c2af0ceca5f0ae1..eeb06d8fc83fb0e61c57e7d4c93a85a968b05548 100644 (file)
@@ -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<ObjCObjectPointerType>();
+  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<ObjCObjectPointerType>();
-        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()];
index 88b18b1a26847c15a383c597222f9a20e542df70..bf172f9b1e3b56bf0ba1ead49a782467d8dd5ee7 100644 (file)
@@ -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;
 
index 1927aad4eac2f38b101f1d757316c1ecec4d2d69..827eaff92b0303871a032ef33436b16f064c9c0b 100644 (file)
@@ -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<ReferenceType>())
+      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/test/CodeGenObjCXX/exceptions.mm b/test/CodeGenObjCXX/exceptions.mm
new file mode 100644 (file)
index 0000000..00de88c
--- /dev/null
@@ -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/test/SemaObjCXX/exceptions-fragile.mm b/test/SemaObjCXX/exceptions-fragile.mm
new file mode 100644 (file)
index 0000000..11dd8e9
--- /dev/null
@@ -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}}
+    }
+  }
+}