Patchwork ObjC/ObjC++: bug fixes for @catch

login
register
mail settings
Submitter Nicola Pero
Date Nov. 28, 2010, 2:06 p.m.
Message ID <1290953215.85423186@192.168.2.229>
Download mbox | patch
Permalink /patch/73330/
State New
Headers show

Comments

Nicola Pero - Nov. 28, 2010, 2:06 p.m.
>> * I moved the new testcases into objc.dg/exceptions and obj-c++.dg/ 
>> exceptions subdirectories
>>
>> * I changed the top-level dg.exp file to run the testcases in the  
>> subdirectories
>> as well as the testcases in the top-level (I don't want to duplicate  
>> the .exp files
>> in each subdirectory unless there is a reason to do so)
>
> there is a good reason to do so --
> 
> make check-objc RUNTESTFLAGS="exceptions.exp=* "
> 
> (and the existing equivalents) are very useful when tracking down  
> categories of problems..

Excellent point.  Let's do it that way (I also want to parallelize check-objc
and my understanding is that DejaGNU won't parallelize checks, so the way
to go is to run multiple DejaGNUs in parallel on different .exp files, ie
like check-gcc does.  So having a number of subdirectories, which are not 
too small and not too large, each with its own .exp, is good at it should 
allow us to parallelize check-objc effectively). :-)

So, a revised patch in attach.

Ok to commit ?

Thanks

In gcc/objc/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc-act.c (objc_eh_runtime_type): Avoid ICE if error_mark_node
        is passed as argument.
        (objc_begin_catch_clause): Added code to deal with an
        error_mark_node or NULL_TREE argument.  Improved checks for
        invalid arguments.  Added code to traverse typedefs.

In gcc/testsuite/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc.dg/exceptions/exceptions.exp: New.
        * objc.dg/exceptions/exceptions-1.m: New.
        * objc.dg/exceptions/exceptions-2.m: New.
        * objc.dg/exceptions/exceptions-3.m: New.
        * objc.dg/exceptions/exceptions-4.m: New.
        * objc.dg/exceptions/exceptions-5.m: New.
        * obj-c++.dg/exceptions/exceptions.exp: New.
        * obj-c++.dg/exceptions/exceptions-1.mm: New.
        * obj-c++.dg/exceptions/exceptions-2.mm: New.
        * obj-c++.dg/exceptions/exceptions-3.mm: New.
        * obj-c++.dg/exceptions/exceptions-4.mm: New.
        * obj-c++.dg/exceptions/exceptions-5.mm: New.

In gcc/cp/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * parser.c (cp_parser_objc_try_catch_finally_statement): Parse
        @catch(...)  and pass NULL_TREE to objc_begin_catch_clause() in
        that case.  Improved error recovery.  Reorganized code to be
        almost identical to c_parser_objc_try_catch_finally_statement.

In gcc/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * c-parser.c (c_parser_objc_try_catch_statement): Renamed to
        c_parser_objc_try_catch_finally_statement for consistency with the
        C++ parser.  Parse @catch(...) and pass NULL_TREE to
        objc_begin_catch_clause() in that case.  Improved error recovery.
        Reorganized code to be almost identical to
        cp_parser_objc_try_catch_finally_statement.

Patch

Index: gcc/objc/objc-act.c
===================================================================
--- gcc/objc/objc-act.c	(revision 167216)
+++ gcc/objc/objc-act.c	(working copy)
@@ -5024,7 +5024,14 @@  static GTY(()) tree objc_eh_personality_decl;
 tree
 objc_eh_runtime_type (tree type)
 {
-  return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names);
+  /* Use 'ErrorMarkNode' as class name when error_mark_node is found
+     to prevent an ICE.  Note that we know that the compiler will
+     terminate with an error and this 'ErrorMarkNode' class name will
+     never be actually used.  */
+  if (type == error_mark_node)
+    return add_objc_string (get_identifier ("ErrorMarkNode"), class_names);
+  else
+    return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names);
 }
 
 tree
@@ -5355,7 +5362,9 @@  objc_begin_try_stmt (location_t try_locus, tree bo
 
 /* Called just after parsing "@catch (parm)".  Open a binding level,
    enter DECL into the binding level, and initialize it.  Leave the
-   binding level open while the body of the compound statement is parsed.  */
+   binding level open while the body of the compound statement is
+   parsed.  If DECL is NULL_TREE, then we are compiling "@catch(...)"
+   which we compile as "@catch(id tmp_variable)".  */
 
 void
 objc_begin_catch_clause (tree decl)
@@ -5365,46 +5374,99 @@  objc_begin_catch_clause (tree decl)
   /* Begin a new scope that the entire catch clause will live in.  */
   compound = c_begin_compound_stmt (true);
 
-  /* The parser passed in a PARM_DECL, but what we really want is a VAR_DECL.  */
-  decl = build_decl (input_location,
-		     VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
-  lang_hooks.decls.pushdecl (decl);
+  /* Create the appropriate declaration for the argument.  */
+ if (decl == error_mark_node)
+   type = error_mark_node;
+ else
+   {
+     if (decl == NULL_TREE)
+       {
+	 /* If @catch(...) was specified, create a temporary variable of
+	    type 'id' and use it.  */
+	 decl = objc_create_temporary_var (objc_object_type, "__objc_generic_catch_var");
+	 DECL_SOURCE_LOCATION (decl) = input_location;
+       }
+     else
+       {
+	 /* The parser passed in a PARM_DECL, but what we really want is a VAR_DECL.  */
+	 decl = build_decl (input_location,
+			    VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
+       }
+     lang_hooks.decls.pushdecl (decl);
 
-  /* Since a decl is required here by syntax, don't warn if its unused.  */
-  /* ??? As opposed to __attribute__((unused))?  Anyway, this appears to
-     be what the previous objc implementation did.  */
-  TREE_USED (decl) = 1;
-  DECL_READ_P (decl) = 1;
+     /* Mark the declaration as used so you never any warnings whether
+	you use the exception argument or not.  TODO: Implement a
+	-Wunused-exception-parameter flag, which would cause warnings
+	if exception parameter is not used.  */
+     TREE_USED (decl) = 1;
+     DECL_READ_P (decl) = 1;
 
-  /* Verify that the type of the catch is valid.  It must be a pointer
-     to an Objective-C class, or "id" (which is catch-all).  */
-  type = TREE_TYPE (decl);
+     type = TREE_TYPE (decl);
+   }
 
-  if (POINTER_TYPE_P (type) && objc_is_object_id (TREE_TYPE (type)))
-    type = NULL;
-  else if (!POINTER_TYPE_P (type) || !TYPED_OBJECT (TREE_TYPE (type)))
+ /* Verify that the type of the catch is valid.  It must be a pointer
+    to an Objective-C class, or "id" (which is catch-all).  */
+ if (type == error_mark_node)
+   {
+     ;/* Just keep going.  */
+   }
+ else if (!objc_type_valid_for_messaging (type, false))
     {
       error ("@catch parameter is not a known Objective-C class type");
       type = error_mark_node;
     }
-  else if (cur_try_context->catch_list)
+  else if (TYPE_HAS_OBJC_INFO (TREE_TYPE (type))
+	   && TYPE_OBJC_PROTOCOL_LIST (TREE_TYPE (type)))
     {
-      /* Examine previous @catch clauses and see if we've already
-	 caught the type in question.  */
-      tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
-      for (; !tsi_end_p (i); tsi_next (&i))
+      error ("@catch parameter can not be protocol-qualified");
+      type = error_mark_node;      
+    }
+  else if (objc_is_object_id (TREE_TYPE (type)))
+    type = NULL;
+  else
+    {
+      /* If 'type' was built using typedefs, we need to get rid of
+	 them and get a simple pointer to the class.  */
+      bool is_typedef = false;
+      tree x = TYPE_MAIN_VARIANT (type);
+      
+      /* Skip from the pointer to the pointee.  */
+      if (TREE_CODE (x) == POINTER_TYPE)
+	x = TREE_TYPE (x);
+      
+      /* Traverse typedef aliases */
+      while (TREE_CODE (x) == RECORD_TYPE && OBJC_TYPE_NAME (x)
+	     && TREE_CODE (OBJC_TYPE_NAME (x)) == TYPE_DECL
+	     && DECL_ORIGINAL_TYPE (OBJC_TYPE_NAME (x)))
 	{
-	  tree stmt = tsi_stmt (i);
-	  t = CATCH_TYPES (stmt);
-	  if (t == error_mark_node)
-	    continue;
-	  if (!t || DERIVED_FROM_P (TREE_TYPE (t), TREE_TYPE (type)))
+	  is_typedef = true;
+	  x = DECL_ORIGINAL_TYPE (OBJC_TYPE_NAME (x));
+	}
+
+      /* If it was a typedef, build a pointer to the final, original
+	 class.  */
+      if (is_typedef)
+	type = build_pointer_type (x);
+
+      if (cur_try_context->catch_list)
+	{
+	  /* Examine previous @catch clauses and see if we've already
+	     caught the type in question.  */
+	  tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
+	  for (; !tsi_end_p (i); tsi_next (&i))
 	    {
-	      warning (0, "exception of type %<%T%> will be caught",
-		       TREE_TYPE (type));
-	      warning_at  (EXPR_LOCATION (stmt), 0, "   by earlier handler for %<%T%>",
-			   TREE_TYPE (t ? t : objc_object_type));
-	      break;
+	      tree stmt = tsi_stmt (i);
+	      t = CATCH_TYPES (stmt);
+	      if (t == error_mark_node)
+		continue;
+	      if (!t || DERIVED_FROM_P (TREE_TYPE (t), TREE_TYPE (type)))
+		{
+		  warning (0, "exception of type %<%T%> will be caught",
+			   TREE_TYPE (type));
+		  warning_at  (EXPR_LOCATION (stmt), 0, "   by earlier handler for %<%T%>",
+			       TREE_TYPE (t ? t : objc_object_type));
+		  break;
+		}
 	    }
 	}
     }
Index: gcc/objc/ChangeLog
===================================================================
--- gcc/objc/ChangeLog	(revision 167216)
+++ gcc/objc/ChangeLog	(working copy)
@@ -1,3 +1,11 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (objc_eh_runtime_type): Avoid ICE if error_mark_node
+	is passed as argument.
+	(objc_begin_catch_clause): Added code to deal with an
+	error_mark_node or NULL_TREE argument.  Improved checks for
+	invalid arguments.  Added code to traverse typedefs.
+
 2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	Implemented optional properties.
Index: gcc/ChangeLog
===================================================================
--- gcc/ChangeLog	(revision 167216)
+++ gcc/ChangeLog	(working copy)
@@ -1,3 +1,12 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-parser.c (c_parser_objc_try_catch_statement): Renamed to
+	c_parser_objc_try_catch_finally_statement for consistency with the
+	C++ parser.  Parse @catch(...) and pass NULL_TREE to
+	objc_begin_catch_clause() in that case.  Improved error recovery.
+	Reorganized code to be almost identical to
+	cp_parser_objc_try_catch_finally_statement.
+	
 2010-11-27  Jan Hubicka  <jh@suse.cz>
 
 	* dwarf2out.c (dwarf2out_begin_function): Set cold_text_section
Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 167216)
+++ gcc/testsuite/ChangeLog	(working copy)
@@ -1,3 +1,18 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/exceptions/exceptions.exp: New.
+	* objc.dg/exceptions/exceptions-1.m: New.
+	* objc.dg/exceptions/exceptions-2.m: New.
+	* objc.dg/exceptions/exceptions-3.m: New.
+	* objc.dg/exceptions/exceptions-4.m: New.
+	* objc.dg/exceptions/exceptions-5.m: New.
+	* obj-c++.dg/exceptions/exceptions.exp: New.
+	* obj-c++.dg/exceptions/exceptions-1.mm: New.
+	* obj-c++.dg/exceptions/exceptions-2.mm: New.
+	* obj-c++.dg/exceptions/exceptions-3.mm: New.
+	* obj-c++.dg/exceptions/exceptions-4.mm: New.
+	* obj-c++.dg/exceptions/exceptions-5.mm: New.
+
 2010-11-27  Tobias Burnus  <burnus@net-b.de>
 
 	PR fortran/46638
Index: gcc/testsuite/objc.dg/exceptions/exceptions-1.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-1.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-1.m	(revision 0)
@@ -0,0 +1,42 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  At the moment, @catch (...) is identical to @catch (id
+   exception).  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-2.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-2.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-2.m	(revision 0)
@@ -0,0 +1,52 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+/* { dg-options "-fobjc-exceptions -fgnu-runtime" } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+/* { dg-additional-sources "../../objc-obj-c++-shared/Object1.m" } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  Check that code using it runs correctly.  */
+
+#include "../../objc-obj-c++-shared/Object1.h"
+#include <stdlib.h>
+
+@interface MyObject : Object
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
+
+int main (void)
+{
+  if (test ([MyObject new]) != 5)
+    abort ();
+
+  if (test ([Object new]) != 6)
+    abort ();
+
+  return 0;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-3.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-3.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-3.m	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that the compiler is checking the argument of @catch(), and
+   produce errors when invalid types are used.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int x)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *x)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id x)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol> x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *x)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *x)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject x)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "conversion to non-scalar type requested" "" { target *-*-* } 72 } */
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *x) /* { dg-error "storage class specified for" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions.exp
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions.exp	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions.exp	(revision 0)
@@ -0,0 +1,44 @@ 
+# Copyright (C) 2010 Free Software Foundation, Inc.
+# 
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+
+load_lib objc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_CFLAGS
+if ![info exists DEFAULT_CFLAGS] then {
+    set DEFAULT_CFLAGS ""
+}
+
+# Initialize `dg'.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [glob -nocomplain $srcdir/$subdir/*.m]]
+
+# Main loop.
+dg-runtest $tests "-fgnu-runtime" $DEFAULT_CFLAGS
+
+# darwin targets can also run code with the NeXT runtime.
+if [istarget "*-*-darwin*" ] {
+  dg-runtest $tests "-fnext-runtime" $DEFAULT_CFLAGS
+}
+
+# All done.
+dg-finish
Index: gcc/testsuite/objc.dg/exceptions/exceptions-4.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-4.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-4.m	(revision 0)
@@ -0,0 +1,64 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test warnings when parsing syntax errors in @catch().  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@interface MyObject2
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject2
+@end
+
+@protocol MyProtocol;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch
+    { /* { dg-error "expected ... before ... token" } */
+      dummy++;
+    }
+  @catch ()  /* { dg-error "expected declaration specifiers or ..... before ..." } */
+    {
+      dummy++;
+    }
+  @catch (i) /* { dg-error "expected declaration specifiers or ..... before .i." } */
+    {
+      dummy++;
+    }
+  @catch (id <MyProtocol x) /* { dg-error "expected ... before .x." } */
+    {                       /* { dg-error "@catch parameter can not be protocol-qualified" "" { target *-*-* } 46 } */
+      dummy++;
+    }
+  @catch MyObject *x       /* { dg-error "expected ... before .MyObject." } */
+    {
+      dummy++;
+    }
+  @catch MyObject2 *x)      /* { dg-error "expected ... before .MyObject2." } */
+   {
+     dummy++;
+   }
+
+  @try { @throw object; }
+  @catch (MyObject *x)
+  @catch (MyObject2 *y)    /* { dg-error "expected ... before .catch." } */
+
+  return dummy;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-5.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-5.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-5.m	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that you can use an unnamed argument with @catch.  This test is the same
+   as exceptions-3.m, but with no name for @catch arguments.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol>) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "conversion to non-scalar type requested" "" { target *-*-* } 72 } */
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *) /* { dg-error "storage class specified for" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm	(revision 0)
@@ -0,0 +1,42 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  At the moment, @catch (...) is identical to @catch (id
+   exception).  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm	(revision 0)
@@ -0,0 +1,54 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions -fgnu-runtime" } */
+
+/* FIXME: This does not test running the code, because Objective-C exceptions at the moment
+   do not execute correctly in Objective-C++.  See PR objc++/23616.  Once that is fixed,
+   this test should be changed to use 'dg-run' instead of just 'dg-compile'.  */
+/* { dg-compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  Check that code using it runs correctly.  */
+
+#include "../../objc-obj-c++-shared/Object1.h"
+#include <stdlib.h>
+
+@interface MyObject : Object
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
+
+int main (void)
+{
+  if (test ([MyObject new]) != 5)
+    abort ();
+
+  if (test ([Object new]) != 6)
+    abort ();
+
+  return 0;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that the compiler is checking the argument of @catch(), and
+   produce errors when invalid types are used.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int x)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *x)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id x)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol> x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *x)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *x)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject x)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "no matching function" "" { target *-*-* } 72 } */
+      dummy++;            /* { dg-warning "MyObject" "" { target *-*-* } 13 } */
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *x) /* { dg-error "storage class" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm	(revision 0)
@@ -0,0 +1,64 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test warnings when parsing syntax errors in @catch().  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@interface MyObject2
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject2
+@end
+
+@protocol MyProtocol;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch
+    {          /* { dg-error "expected" } */
+      dummy++; /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 35 } */
+    }
+  @catch ()  /* { dg-error "expected identifier before" } */
+    {        /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 38 } */
+      dummy++;
+    }
+  @catch (i) /* { dg-error ".i. has not been declared" } */
+    {        /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 42 } */
+      dummy++;
+    }
+  @catch (id <MyProtocol x) /* { dg-error "expected ... before .x." } */
+    {                       /* { dg-error "@catch parameter can not be protocol-qualified" "" { target *-*-* } 46 } */
+      dummy++;
+    }
+  @catch MyObject *x       /* { dg-error "expected ... before .MyObject." } */
+    {
+      dummy++;
+    }
+  @catch MyObject2 *x)     /* { dg-error "expected ... before .MyObject2." } */
+   {
+     dummy++;
+   }
+
+  @try { @throw object; }
+  @catch (MyObject *x)
+  @catch (MyObject2 *y)    /* { dg-error "expected ... before .catch." } */
+
+  return dummy;            /* { dg-error "expected ... before .return." } */
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that you can use an unnamed argument with @catch.  This test is the same
+   as exceptions-3.mm, but with no name for @catch arguments.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol>) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "no matching function" "" { target *-*-* } 72 } */
+      dummy++;            /* { dg-warning "MyObject" "" { target *-*-* } 13 } */
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *) /* { dg-error "storage class" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions.exp
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions.exp	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions.exp	(revision 0)
@@ -0,0 +1,43 @@ 
+# Copyright (C) 2010 Free Software Foundation, Inc.
+# 
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Load support procs.
+load_lib obj-c++-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_OBJCXXFLAGS
+if ![info exists DEFAULT_OBJCXXFLAGS] then {
+    set DEFAULT_OBJCXXFLAGS " -ansi -pedantic-errors -Wno-long-long"
+}
+
+# Initialize `dg'.
+dg-init
+
+# Gather a list of all tests.
+set tests [lsort [glob -nocomplain $srcdir/$subdir/*.mm]]
+
+# Main loop.
+dg-runtest $tests "-fgnu-runtime" $DEFAULT_OBJCXXFLAGS
+
+# darwin targets can also run code with the NeXT runtime.
+if [istarget "*-*-darwin*" ] {
+  dg-runtest $tests "-fnext-runtime" $DEFAULT_OBJCXXFLAGS
+}
+
+# All done.
+dg-finish
Index: gcc/cp/ChangeLog
===================================================================
--- gcc/cp/ChangeLog	(revision 167216)
+++ gcc/cp/ChangeLog	(working copy)
@@ -1,3 +1,10 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* parser.c (cp_parser_objc_try_catch_finally_statement): Parse
+	@catch(...)  and pass NULL_TREE to objc_begin_catch_clause() in
+	that case.  Improved error recovery.  Reorganized code to be
+	almost identical to c_parser_objc_try_catch_finally_statement.
+
 2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	PR objc++/46222
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 167216)
+++ gcc/cp/parser.c	(working copy)
@@ -22598,15 +22598,25 @@  cp_parser_objc_declaration (cp_parser* parser, tre
      objc-catch-clause objc-catch-clause-seq [opt]
 
    objc-catch-clause:
-     @catch ( exception-declaration ) compound-statement
+     @catch ( objc-exception-declaration ) compound-statement
 
-   objc-finally-clause
+   objc-finally-clause:
      @finally compound-statement
 
-   Returns NULL_TREE.  */
+   objc-exception-declaration:
+     parameter-declaration
+     '...'
 
+   where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS.
+
+   Returns NULL_TREE.
+
+   PS: This function is identical to c_parser_objc_try_catch_finally_statement
+   for C.  Keep them in sync.  */   
+
 static tree
-cp_parser_objc_try_catch_finally_statement (cp_parser *parser) {
+cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
+{
   location_t location;
   tree stmt;
 
@@ -22620,22 +22630,60 @@  static tree
 
   while (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_CATCH))
     {
-      cp_parameter_declarator *parmdecl;
-      tree parm;
+      cp_parameter_declarator *parm;
+      tree parameter_declaration = error_mark_node;
+      bool seen_open_paren = false;
 
       cp_lexer_consume_token (parser->lexer);
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
-      parmdecl = cp_parser_parameter_declaration (parser, false, NULL);
-      parm = grokdeclarator (parmdecl->declarator,
-			     &parmdecl->decl_specifiers,
-			     PARM, /*initialized=*/0,
-			     /*attrlist=*/NULL);
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
-      objc_begin_catch_clause (parm);
+      if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+	seen_open_paren = true;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  /* We have "@catch (...)" (where the '...' are literally
+	     what is in the code).  Skip the '...'.
+	     parameter_declaration is set to NULL_TREE, and
+	     objc_being_catch_clauses() knows that that means
+	     '...'.  */
+	  cp_lexer_consume_token (parser->lexer);
+	  parameter_declaration = NULL_TREE;
+	}
+      else
+	{
+	  /* We have "@catch (NSException *exception)" or something
+	     like that.  Parse the parameter declaration.  */
+	  parm = cp_parser_parameter_declaration (parser, false, NULL);
+	  if (parm == NULL)
+	    parameter_declaration = error_mark_node;
+	  else
+	    parameter_declaration = grokdeclarator (parm->declarator,
+						    &parm->decl_specifiers,
+						    PARM, /*initialized=*/0,
+						    /*attrlist=*/NULL);
+	}
+      if (seen_open_paren)
+	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      else
+	{
+	  /* If there was no open parenthesis, we are recovering from
+	     an error, and we are trying to figure out what mistake
+	     the user has made.  */
+
+	  /* If there is an immediate closing parenthesis, the user
+	     probably forgot the opening one (ie, they typed "@catch
+	     NSException *e)".  Parse the closing parenthesis and keep
+	     going.  */
+	  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    cp_lexer_consume_token (parser->lexer);
+	  
+	  /* If these is no immediate closing parenthesis, the user
+	     probably doesn't know that parenthesis are required at
+	     all (ie, they typed "@catch NSException *e").  So, just
+	     forget about the closing parenthesis and keep going.  */
+	}
+      objc_begin_catch_clause (parameter_declaration);
       cp_parser_compound_statement (parser, NULL, false);
       objc_finish_catch_clause ();
     }
-
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_FINALLY))
     {
       cp_lexer_consume_token (parser->lexer);
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 167216)
+++ gcc/c-parser.c	(working copy)
@@ -1155,7 +1155,7 @@  static void c_parser_objc_methodproto (c_parser *)
 static tree c_parser_objc_method_decl (c_parser *, bool, tree *);
 static tree c_parser_objc_type_name (c_parser *);
 static tree c_parser_objc_protocol_refs (c_parser *);
-static void c_parser_objc_try_catch_statement (c_parser *);
+static void c_parser_objc_try_catch_finally_statement (c_parser *);
 static void c_parser_objc_synchronized_statement (c_parser *);
 static tree c_parser_objc_selector (c_parser *);
 static tree c_parser_objc_selector_arg (c_parser *);
@@ -4371,7 +4371,7 @@  c_parser_statement_after_labels (c_parser *parser)
 	  break;
 	case RID_AT_TRY:
 	  gcc_assert (c_dialect_objc ());
-	  c_parser_objc_try_catch_statement (parser);
+	  c_parser_objc_try_catch_finally_statement (parser);
 	  break;
 	case RID_AT_SYNCHRONIZED:
 	  gcc_assert (c_dialect_objc ());
@@ -7468,53 +7468,97 @@  c_parser_objc_protocol_refs (c_parser *parser)
   return list;
 }
 
-/* Parse an objc-try-catch-statement.
+/* Parse an objc-try-catch-finally-statement.
 
-   objc-try-catch-statement:
+   objc-try-catch-finally-statement:
      @try compound-statement objc-catch-list[opt]
      @try compound-statement objc-catch-list[opt] @finally compound-statement
 
    objc-catch-list:
-     @catch ( parameter-declaration ) compound-statement
-     objc-catch-list @catch ( parameter-declaration ) compound-statement
-*/
+     @catch ( objc-catch-parameter-declaration ) compound-statement
+     objc-catch-list @catch ( objc-catch-parameter-declaration ) compound-statement
 
+   objc-catch-parameter-declaration:
+     parameter-declaration
+     '...'
+
+   where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS.
+
+   PS: This function is identical to cp_parser_objc_try_catch_finally_statement
+   for C++.  Keep them in sync.  */   
+
 static void
-c_parser_objc_try_catch_statement (c_parser *parser)
+c_parser_objc_try_catch_finally_statement (c_parser *parser)
 {
-  location_t loc;
+  location_t location;
   tree stmt;
+
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_TRY));
   c_parser_consume_token (parser);
-  loc = c_parser_peek_token (parser)->location;
+  location = c_parser_peek_token (parser)->location;
   stmt = c_parser_compound_statement (parser);
-  objc_begin_try_stmt (loc, stmt);
+  objc_begin_try_stmt (location, stmt);
+
   while (c_parser_next_token_is_keyword (parser, RID_AT_CATCH))
     {
       struct c_parm *parm;
+      tree parameter_declaration = error_mark_node;
+      bool seen_open_paren = false;
+
       c_parser_consume_token (parser);
       if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	break;
-      parm = c_parser_parameter_declaration (parser, NULL_TREE);
-      if (parm == NULL)
+	seen_open_paren = true;
+      if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
 	{
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	  break;
+	  /* We have "@catch (...)" (where the '...' are literally
+	     what is in the code).  Skip the '...'.
+	     parameter_declaration is set to NULL_TREE, and
+	     objc_being_catch_clauses() knows that that means
+	     '...'.  */
+	  c_parser_consume_token (parser);
+	  parameter_declaration = NULL_TREE;
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
-      objc_begin_catch_clause (grokparm (parm));
+      else
+	{
+	  /* We have "@catch (NSException *exception)" or something
+	     like that.  Parse the parameter declaration.  */
+	  parm = c_parser_parameter_declaration (parser, NULL_TREE);
+	  if (parm == NULL)
+	    parameter_declaration = error_mark_node;
+	  else
+	    parameter_declaration = grokparm (parm);
+	}
+      if (seen_open_paren)
+	c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      else
+	{
+	  /* If there was no open parenthesis, we are recovering from
+	     an error, and we are trying to figure out what mistake
+	     the user has made.  */
+
+	  /* If there is an immediate closing parenthesis, the user
+	     probably forgot the opening one (ie, they typed "@catch
+	     NSException *e)".  Parse the closing parenthesis and keep
+	     going.  */
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    c_parser_consume_token (parser);
+	  
+	  /* If these is no immediate closing parenthesis, the user
+	     probably doesn't know that parenthesis are required at
+	     all (ie, they typed "@catch NSException *e").  So, just
+	     forget about the closing parenthesis and keep going.  */
+	}
+      objc_begin_catch_clause (parameter_declaration);
       if (c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
 	c_parser_compound_statement_nostart (parser);
       objc_finish_catch_clause ();
     }
   if (c_parser_next_token_is_keyword (parser, RID_AT_FINALLY))
     {
-      location_t finloc;
-      tree finstmt;
       c_parser_consume_token (parser);
-      finloc = c_parser_peek_token (parser)->location;
-      finstmt = c_parser_compound_statement (parser);
-      objc_build_finally_clause (finloc, finstmt);
+      location = c_parser_peek_token (parser)->location;
+      stmt = c_parser_compound_statement (parser);
+      objc_build_finally_clause (location, stmt);
     }
   objc_finish_try_stmt ();
 }