diff mbox

ObjC/ObjC++: Objective-C 2.0 class extensions

Message ID 1291942416.737832393@192.168.2.227
State New
Headers show

Commit Message

Nicola Pero Dec. 10, 2010, 12:53 a.m. UTC
This patch implements class extensions for Objective-C and Objective-C++.

I know it's phase 3, but it's such a simple change and it's low risk.
I keep hitting testcases in other ObjC compilers that use class extensions and
started to wonder why can't we just implement them and get rid of the problem
(considering how easy it is to implement), so I just did it.  Anyway, I won't
get offended if it's not deemed appropriate for this phase. ;-)

This patch:

 * does the small changes to the parsers to recognize a class extension (a category
with an empty category name);

 * produces an error if a class extension is used with -fobjc-std=objc1;

 * when a class extension is found, simply "reopens" that main @interface and adds any
protocols found in the class extension to it (since we "reopen" that main @interface,
all properties and methods found in the class extension will automatically be added
to the @interface as if they were actually found in the main @interface, which is
what we want);

 * it also sets an objc_in_class_extension flag to 'true' in that case (for a couple
of special differences with the main @interface that I'll detail);

 * when an @interface is closed, if objc_in_class_extension, then it skips creating the instance
variable templates as it's already been done when the original, main @interface was closed;

 * when a readwrite @property is found, if we are in a class extension, then it allows it
to override an existing readonly (but identical) @property already existing in the interface;

 * it includes 12 new testcases.

that's it. :-)

Ok to commit ?

Thanks

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

        * c-parser.c (c_parser_objc_class_definition): Recognize
        Objective-C 2.0 class extensions.

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

        * parser.c (cp_parser_objc_superclass_or_category): Recognize
        Objective-C 2.0 class extensions.  Added iface_p and
        is_class_extension arguments.
        (cp_parser_objc_class_interface): Updated call to
        cp_parser_objc_superclass_or_category.
        (cp_parser_objc_class_implementation): Same change.

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

        * objc-act.c (objc_in_class_extension): New.
        (objc_start_category_interface): If -fobjc-std=objc1
        was specified, produce an error if a class extension is used.
        (objc_finish_interface): Reset objc_in_class_extension to false.
        (objc_add_property_declaration): Allow a class extension to extend
        readonly properties in the main @interface to be readwrite.
        (start_class): Added code to deal with class extensions.  In that
        case, return the existing interface after adding any additional
        protocols to it and setting objc_in_class_extension to true.
        (continue_class): If in a class extension, do not generate the
        instance variable template.

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

        * objc.dg/class-extension-1.m: New.
        * objc.dg/class-extension-2.m: New.
        * objc.dg/class-extension-3.m: New.
        * objc.dg/property/at-property-26.m: New.
        * objc.dg/property/at-property-27.m: New.
        * objc.dg/property/at-property-28.m: New.
        * obj-c++.dg/class-extension-1.mm: New.
        * obj-c++.dg/class-extension-2.mm: New.
        * obj-c++.dg/class-extension-3.mm: New.
        * obj-c++.dg/property/at-property-26.mm: New.
        * obj-c++.dg/property/at-property-27.mm: New.
        * obj-c++.dg/property/at-property-28.mm: New.

Comments

Mike Stump Dec. 10, 2010, 4:27 a.m. UTC | #1
On Dec 9, 2010, at 4:53 PM, Nicola Pero wrote:
> This patch implements class extensions for Objective-C and Objective-C++.

> Ok to commit ?

Ok.
diff mbox

Patch

Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 167660)
+++ objc/objc-act.c	(working copy)
@@ -402,6 +402,12 @@  static bool objc_method_optional_flag = false;
 
 static int objc_collecting_ivars = 0;
 
+/* Flag that is set to 'true' while we are processing a class
+   extension.  Since a class extension just "reopens" the main
+   @interface, this can be used to determine if we are in the main
+   @interface, or in a class extension.  */
+static bool objc_in_class_extension = false;
+
 #define BUFSIZE		1024
 
 static char *errbuf;	/* Buffer for error diagnostics */
@@ -748,6 +754,11 @@  objc_start_category_interface (tree klass, tree ca
 		    "category attributes are not available in this version"
 		    " of the compiler, (ignored)");
     }
+  if (categ == NULL_TREE)
+    {
+      if (flag_objc1_only)
+	error_at (input_location, "class extensions are not available in Objective-C 1.0");
+    }
   objc_interface_context
     = start_class (CATEGORY_INTERFACE_TYPE, klass, categ, protos, NULL_TREE);
   objc_ivar_chain
@@ -778,6 +789,7 @@  objc_finish_interface (void)
   finish_class (objc_interface_context);
   objc_interface_context = NULL_TREE;
   objc_method_optional_flag = false;
+  objc_in_class_extension = false;
 }
 
 void
@@ -952,6 +964,7 @@  objc_add_property_declaration (location_t location
      is readwrite).  */
   bool property_readonly = false;
   objc_property_assign_semantics property_assign_semantics = OBJC_PROPERTY_ASSIGN;
+  bool property_extension_in_class_extension = false;
 
   if (flag_objc1_only)
     error_at (input_location, "%<@property%> is not available in Objective-C 1.0");
@@ -1125,60 +1138,80 @@  objc_add_property_declaration (location_t location
 
   /* Check for duplicate property declarations.  We first check the
      immediate context for a property with the same name.  Any such
-     declarations are an error.  */
+     declarations are an error, unless this is a class extension and
+     we are extending a property from readonly to readwrite.  */
   for (x = CLASS_PROPERTY_DECL (objc_interface_context); x; x = TREE_CHAIN (x))
     {
       if (PROPERTY_NAME (x) == DECL_NAME (decl))
 	{
-	  location_t original_location = DECL_SOURCE_LOCATION (x);
-	  
-	  error_at (location, "redeclaration of property %qD", decl);
-
-	  if (original_location != UNKNOWN_LOCATION)
-	    inform (original_location, "originally specified here");
-	  return;
-      }
+	  if (objc_in_class_extension
+	      && property_readonly == 0
+	      && PROPERTY_READONLY (x) == 1)
+	    {
+	      /* This is a class extension, and we are extending an
+		 existing readonly property to a readwrite one.
+		 That's fine.  :-) */
+	      property_extension_in_class_extension = true;
+	      break;
+	    }
+	  else
+	    {
+	      location_t original_location = DECL_SOURCE_LOCATION (x);
+	      
+	      error_at (location, "redeclaration of property %qD", decl);
+	      
+	      if (original_location != UNKNOWN_LOCATION)
+		inform (original_location, "originally specified here");
+	      return;
+	    }
+	}
     }
 
-  /* We now need to check for existing property declarations (in the
-     superclass, other categories or protocols) and check that the new
-     declaration is not in conflict with existing ones.  */
+  /* If x is not NULL_TREE, we must be in a class extension and we're
+     extending a readonly property.  In that case, no point in
+     searching for another declaration.  */
+  if (x == NULL_TREE)
+    {
+      /* We now need to check for existing property declarations (in
+	 the superclass, other categories or protocols) and check that
+	 the new declaration is not in conflict with existing
+	 ones.  */
 
-  /* Search for a previous, existing declaration of a property with
-     the same name in superclasses, protocols etc.  If one is found,
-     it will be in the 'x' variable.  */
-  x = NULL_TREE;
+      /* Search for a previous, existing declaration of a property
+	 with the same name in superclasses, protocols etc.  If one is
+	 found, it will be in the 'x' variable.  */
 
-  /* Note that, for simplicity, the following may search again the
-     local context.  That's Ok as nothing will be found (else we'd
-     have thrown an error above); it's only a little inefficient, but
-     the code is simpler.  */
-  switch (TREE_CODE (objc_interface_context))
-    {
-    case CLASS_INTERFACE_TYPE:
-      /* Look up the property in the current @interface (which will
-	 find nothing), then its protocols and categories and
-	 superclasses.  */
-      x = lookup_property (objc_interface_context, DECL_NAME (decl));
-      break;
-    case CATEGORY_INTERFACE_TYPE:
-      /* Look up the property in the main @interface, then protocols
-	 and categories (one of them is ours, and will find nothing)
-	 and superclasses.  */
-      x = lookup_property (lookup_interface (CLASS_NAME (objc_interface_context)),
-			   DECL_NAME (decl));
-      break;
-    case PROTOCOL_INTERFACE_TYPE:
-      /* Looks up the property in any protocols attached to the
-	 current protocol.  */
-      if (PROTOCOL_LIST (objc_interface_context))
+      /* Note that, for simplicity, the following may search again the
+	 local context.  That's Ok as nothing will be found (else we'd
+	 have thrown an error above); it's only a little inefficient,
+	 but the code is simpler.  */
+      switch (TREE_CODE (objc_interface_context))
 	{
-	  x = lookup_property_in_protocol_list (PROTOCOL_LIST (objc_interface_context),
-						DECL_NAME (decl));
+	case CLASS_INTERFACE_TYPE:
+	  /* Look up the property in the current @interface (which
+	     will find nothing), then its protocols and categories and
+	     superclasses.  */
+	  x = lookup_property (objc_interface_context, DECL_NAME (decl));
+	  break;
+	case CATEGORY_INTERFACE_TYPE:
+	  /* Look up the property in the main @interface, then
+	     protocols and categories (one of them is ours, and will
+	     find nothing) and superclasses.  */
+	  x = lookup_property (lookup_interface (CLASS_NAME (objc_interface_context)),
+			       DECL_NAME (decl));
+	  break;
+	case PROTOCOL_INTERFACE_TYPE:
+	  /* Looks up the property in any protocols attached to the
+	     current protocol.  */
+	  if (PROTOCOL_LIST (objc_interface_context))
+	    {
+	      x = lookup_property_in_protocol_list (PROTOCOL_LIST (objc_interface_context),
+						    DECL_NAME (decl));
+	    }
+	  break;
+	default:
+	  gcc_unreachable ();
 	}
-      break;
-    default:
-      gcc_unreachable ();
     }
 
   if (x != NULL_TREE)
@@ -1278,6 +1311,17 @@  objc_add_property_declaration (location_t location
 	    inform (original_location, "originally specified here");
 	  return;
 	}
+
+      /* If we are in a class extension and we're extending a readonly
+	 property in the main @interface, we'll just update the
+	 existing property with the readwrite flag and potentially the
+	 new setter name.  */
+      if (property_extension_in_class_extension)
+	{
+	  PROPERTY_READONLY (x) = 0;
+	  PROPERTY_SETTER_NAME (x) = parsed_property_setter_ident;
+	  return;
+	}
     }
 
   /* Create a PROPERTY_DECL node.  */
@@ -9417,21 +9461,24 @@  check_protocols (tree proto_list, const char *type
     }
 }
 
-/* Make sure that the class CLASS_NAME is defined
-   CODE says which kind of thing CLASS_NAME ought to be.
-   It can be CLASS_INTERFACE_TYPE, CLASS_IMPLEMENTATION_TYPE,
-   CATEGORY_INTERFACE_TYPE, or CATEGORY_IMPLEMENTATION_TYPE.  */
-
+/* Make sure that the class CLASS_NAME is defined CODE says which kind
+   of thing CLASS_NAME ought to be.  It can be CLASS_INTERFACE_TYPE,
+   CLASS_IMPLEMENTATION_TYPE, CATEGORY_INTERFACE_TYPE, or
+   CATEGORY_IMPLEMENTATION_TYPE.  For a CATEGORY_INTERFACE_TYPE,
+   SUPER_NAME is the name of the category.  For a class extension,
+   CODE is CATEGORY_INTERFACE_TYPE and SUPER_NAME is NULL_TREE.  */
 static tree
 start_class (enum tree_code code, tree class_name, tree super_name,
 	     tree protocol_list, tree attributes)
 {
-  tree klass, decl;
+  tree klass = NULL_TREE;
+  tree decl;
 
 #ifdef OBJCPLUS
-  if (current_namespace != global_namespace) {
-    error ("Objective-C declarations may only appear in global scope");
-  }
+  if (current_namespace != global_namespace)
+    {
+      error ("Objective-C declarations may only appear in global scope");
+    }
 #endif /* OBJCPLUS */
 
   if (objc_implementation_context)
@@ -9442,8 +9489,14 @@  start_class (enum tree_code code, tree class_name,
       objc_implementation_context = NULL_TREE;
     }
 
-  klass = make_node (code);
-  TYPE_LANG_SLOT_1 (klass) = make_tree_vec (CLASS_LANG_SLOT_ELTS);
+  /* If this is a class extension, we'll be "reopening" the existing
+     CLASS_INTERFACE_TYPE, so in that case there is no need to create
+     a new node.  */
+  if (code != CATEGORY_INTERFACE_TYPE || super_name != NULL_TREE)
+    {
+      klass = make_node (code);
+      TYPE_LANG_SLOT_1 (klass) = make_tree_vec (CLASS_LANG_SLOT_ELTS);
+    }
 
   /* Check for existence of the super class, if one was specified.  Note
      that we must have seen an @interface, not just a @class.  If we
@@ -9473,9 +9526,12 @@  start_class (enum tree_code code, tree class_name,
 	}
     }
 
-  CLASS_NAME (klass) = class_name;
-  CLASS_SUPER_NAME (klass) = super_name;
-  CLASS_CLS_METHODS (klass) = NULL_TREE;
+  if (code != CATEGORY_INTERFACE_TYPE || super_name != NULL_TREE)
+    {
+      CLASS_NAME (klass) = class_name;
+      CLASS_SUPER_NAME (klass) = super_name;
+      CLASS_CLS_METHODS (klass) = NULL_TREE;
+    }
 
   if (! objc_is_class_name (class_name)
       && (decl = lookup_name (class_name)))
@@ -9592,15 +9648,35 @@  start_class (enum tree_code code, tree class_name,
 	    if (TREE_DEPRECATED (class_category_is_assoc_with))
 	      warning (OPT_Wdeprecated_declarations, "class %qE is deprecated", 
 		       class_name);
-	    add_category (class_category_is_assoc_with, klass);
+
+	    if (super_name == NULL_TREE)
+	      {
+		/* This is a class extension.  Get the original
+		   interface, and continue working on it.  */
+		objc_in_class_extension = true;
+		klass = class_category_is_assoc_with;
+
+		if (protocol_list)
+		  {
+		    /* Append protocols to the original protocol
+		       list.  */
+		    CLASS_PROTOCOL_LIST (klass)
+		      = chainon (CLASS_PROTOCOL_LIST (klass),
+				 lookup_and_install_protocols (protocol_list));
+		  }
+	      }
+	    else
+	      {
+		add_category (class_category_is_assoc_with, klass);
+		
+		if (protocol_list)
+		  CLASS_PROTOCOL_LIST (klass)
+		    = lookup_and_install_protocols (protocol_list);
+	      }
 	  }
-
-	if (protocol_list)
-	  CLASS_PROTOCOL_LIST (klass)
-	    = lookup_and_install_protocols (protocol_list);
       }
       break;
-
+	
     case CATEGORY_IMPLEMENTATION_TYPE:
       /* Reset for multiple classes per file.  */
       method_slot = 0;
@@ -9673,6 +9749,8 @@  continue_class (tree klass)
       }
     case CLASS_INTERFACE_TYPE:
       {
+	if (objc_in_class_extension)
+	  return NULL_TREE;
 #ifdef OBJCPLUS
 	push_lang_context (lang_name_c);
 #endif /* OBJCPLUS */
Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog	(revision 167660)
+++ objc/ChangeLog	(working copy)
@@ -1,3 +1,17 @@ 
+2010-12-10  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (objc_in_class_extension): New.
+	(objc_start_category_interface): If -fobjc-std=objc1
+	was specified, produce an error if a class extension is used.
+	(objc_finish_interface): Reset objc_in_class_extension to false.
+	(objc_add_property_declaration): Allow a class extension to extend
+	readonly properties in the main @interface to be readwrite.
+	(start_class): Added code to deal with class extensions.  In that
+	case, return the existing interface after adding any additional
+	protocols to it and setting objc_in_class_extension to true.
+	(continue_class): If in a class extension, do not generate the
+	instance variable template.
+
 2010-12-08  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	* objc-act.c (objc_build_throw_stmt): Check that the argument of
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 167660)
+++ ChangeLog	(working copy)
@@ -1,3 +1,8 @@ 
+2010-12-10  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-parser.c (c_parser_objc_class_definition): Recognize
+	Objective-C 2.0 class extensions.
+
 2010-12-09  Joseph Myers  <joseph@codesourcery.com>
 
 	* config/mips/vxworks.h (DBX_REGISTER_NUMBER): Undefine.
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 167660)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,18 @@ 
+2010-12-10  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/class-extension-1.m: New.
+	* objc.dg/class-extension-2.m: New.
+	* objc.dg/class-extension-3.m: New.
+	* objc.dg/property/at-property-26.m: New.
+	* objc.dg/property/at-property-27.m: New.
+	* objc.dg/property/at-property-28.m: New.
+	* obj-c++.dg/class-extension-1.mm: New.
+	* obj-c++.dg/class-extension-2.mm: New.
+	* obj-c++.dg/class-extension-3.mm: New.
+	* obj-c++.dg/property/at-property-26.mm: New.
+	* obj-c++.dg/property/at-property-27.mm: New.
+	* obj-c++.dg/property/at-property-28.mm: New.	
+	
 2010-12-09  Richard Guenther  <rguenther@suse.de>
 
 	PR middle-end/46844
Index: testsuite/objc.dg/class-extension-1.m
===================================================================
--- testsuite/objc.dg/class-extension-1.m	(revision 0)
+++ testsuite/objc.dg/class-extension-1.m	(revision 0)
@@ -0,0 +1,30 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests the basic of class extensions.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+- (int) test;
+@end
+
+@interface MyObject ()
+- (int) test2;
+- (int) test3;
+@end
+
+@implementation MyObject
+- (int) test
+{
+  return 20;
+}
+- (int) test2
+{
+  return 20;
+}
+@end /* { dg-warning "incomplete implementation of class .MyObject." } */
+     /* { dg-warning "method definition for .-test3. not found" "" { target *-*-* } 29 } */
Index: testsuite/objc.dg/property/at-property-26.m
===================================================================
--- testsuite/objc.dg/property/at-property-26.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-26.m	(revision 0)
@@ -0,0 +1,85 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test @properties in class extensions.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@end
+
+@protocol count4
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@property (getter=number4, setter=setNumber4:) int count4;
+@end
+
+@interface MySubClass : MyRootClass
+{
+  int count1;
+  int count2;
+  int count3;
+  int count4;
+}
+@property (getter=number1, setter=setNumber1:) int count1;
+@end
+
+@interface MySubClass ()
+@property (getter=number2, setter=setNumber2:) int count2;
+@end
+
+@interface MySubClass ()  <count4>
+@property (getter=number3, setter=setNumber3:) int count3;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+@synthesize count2;
+- (int) number3
+{
+  return count3;
+}
+- (void) setNumber3: (int)value
+{
+  count3 = value;
+}
+@synthesize count4;
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  object.count1 = 20;
+  if (object.count1 != 20)
+    abort ();
+
+  object.count2 = 11;
+  if (object.count2 != 11)
+    abort ();
+
+  object.count3 = 19;
+  if (object.count3 != 19)
+    abort ();
+
+  object.count4 = 74;
+  if (object.count4 != 74)
+    abort ();
+
+  return 0;
+}
Index: testsuite/objc.dg/property/at-property-28.m
===================================================================
--- testsuite/objc.dg/property/at-property-28.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-28.m	(revision 0)
@@ -0,0 +1,29 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* Test errors when extending a property in a class extension.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property (readonly, retain)    id property1; /* { dg-message "originally specified here" } */
+@property (readonly)           int property2; /* { dg-message "originally specified here" } */
+@property (readonly, getter=y) int property3; /* { dg-message "originally specified here" } */
+@property (readonly)           int property4; /* Ok */
+@property (readonly)           int property5; /* { dg-message "originally specified here" } */
+@end
+
+@interface MyRootClass ()
+@property (readwrite, copy)       id property1; /* { dg-warning "assign semantics attributes of property .property1. conflict with previous declaration" } */
+@property (readwrite, nonatomic) int property2; /* { dg-warning ".nonatomic. attribute of property .property2. conflicts with previous declaration" } */
+@property (readwrite, getter=x)  int property3; /* { dg-warning ".getter. attribute of property .property3. conflicts with previous declaration" } */
+@property (readwrite)            int property4; /* Ok */
+@property (readwrite)          float property5; /* { dg-warning "type of property .property5. conflicts with previous declaration" } */
+@end
+
+
+
Index: testsuite/objc.dg/property/at-property-27.m
===================================================================
--- testsuite/objc.dg/property/at-property-27.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-27.m	(revision 0)
@@ -0,0 +1,66 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test overriding a readonly @property with a readwrite one in a class extension.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@end
+
+@protocol count2
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@property (readonly, getter=number2) int count2;
+@end
+
+@interface MySubClass : MyRootClass
+{
+  int count1;
+  int count2;
+}
+@property (readonly, getter=number1) int count1;
+@end
+
+@interface MySubClass ()
+@property (readwrite, getter=number1, setter=setNumber1:) int count1;
+@end
+
+@interface MySubClass ()  <count2>
+@property (readwrite, getter=number2, setter=setNumber2:) int count2;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+@synthesize count2;
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  object.count1 = 20;
+  if (object.count1 != 20)
+    abort ();
+
+  object.count2 = 11;
+  if (object.count2 != 11)
+    abort ();
+
+  return 0;
+}
Index: testsuite/objc.dg/class-extension-2.m
===================================================================
--- testsuite/objc.dg/class-extension-2.m	(revision 0)
+++ testsuite/objc.dg/class-extension-2.m	(revision 0)
@@ -0,0 +1,56 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests class extensions and protocols.  */
+
+#include <objc/objc.h>
+
+/* First, a simple test where a plain class has a protocol attached to
+   it in a class extension.  */
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@protocol MyProtocol
+- (void) test;
+@end
+
+@interface MyObject () <MyProtocol>
+@end
+
+@implementation MyObject
+@end /* { dg-warning "incomplete implementation of class .MyObject." } */
+     /* { dg-warning "method definition for .-test. not found" "" { target *-*-* } 24 } */
+     /* { dg-warning "class .MyObject. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 24 } */
+
+
+
+/* Second, a more interesting test where protocols are added from the
+   main class and from two different class extensions.  */
+@interface MyObject2 : MyObject <MyProtocol>
+@end
+
+@protocol MyProtocol2
+- (void) test2;
+@end
+
+@protocol MyProtocol3
+- (void) test3;
+@end
+
+@interface MyObject2 () <MyProtocol2>
+@end
+
+@interface MyObject2 () <MyProtocol3>
+@end
+
+@implementation MyObject2
+@end /* { dg-warning "incomplete implementation of class .MyObject2." } */
+     /* { dg-warning "method definition for .-test. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 50 } */
+     /* { dg-warning "method definition for .-test2. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol2. protocol" "" { target *-*-* } 50 } */
+     /* { dg-warning "method definition for .-test3. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol3. protocol" "" { target *-*-* } 50 } */
Index: testsuite/objc.dg/class-extension-3.m
===================================================================
--- testsuite/objc.dg/class-extension-3.m	(revision 0)
+++ testsuite/objc.dg/class-extension-3.m	(revision 0)
@@ -0,0 +1,26 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests warnings on class extensions.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+  int count;
+}
+- (int) test;
+@property int count; /* { dg-message "originally specified here" } */
+@end
+
+@interface MyObject ()
+- (void) test; /* { dg-error "duplicate declaration of method .-test." } */
+@end
+
+@interface MyObject ()
+@end
+
+@interface MyObject ()
+@property int count; /* { dg-error "redeclaration of property .count." } */
+@end
Index: testsuite/obj-c++.dg/class-extension-2.mm
===================================================================
--- testsuite/obj-c++.dg/class-extension-2.mm	(revision 0)
+++ testsuite/obj-c++.dg/class-extension-2.mm	(revision 0)
@@ -0,0 +1,56 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests class extensions and protocols.  */
+
+#include <objc/objc.h>
+
+/* First, a simple test where a plain class has a protocol attached to
+   it in a class extension.  */
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@protocol MyProtocol
+- (void) test;
+@end
+
+@interface MyObject () <MyProtocol>
+@end
+
+@implementation MyObject
+@end /* { dg-warning "incomplete implementation of class .MyObject." } */
+     /* { dg-warning "method definition for .-test. not found" "" { target *-*-* } 24 } */
+     /* { dg-warning "class .MyObject. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 24 } */
+
+
+
+/* Second, a more interesting test where protocols are added from the
+   main class and from two different class extensions.  */
+@interface MyObject2 : MyObject <MyProtocol>
+@end
+
+@protocol MyProtocol2
+- (void) test2;
+@end
+
+@protocol MyProtocol3
+- (void) test3;
+@end
+
+@interface MyObject2 () <MyProtocol2>
+@end
+
+@interface MyObject2 () <MyProtocol3>
+@end
+
+@implementation MyObject2
+@end /* { dg-warning "incomplete implementation of class .MyObject2." } */
+     /* { dg-warning "method definition for .-test. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 50 } */
+     /* { dg-warning "method definition for .-test2. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol2. protocol" "" { target *-*-* } 50 } */
+     /* { dg-warning "method definition for .-test3. not found" "" { target *-*-* } 50 } */
+     /* { dg-warning "class .MyObject2. does not fully implement the .MyProtocol3. protocol" "" { target *-*-* } 50 } */
Index: testsuite/obj-c++.dg/property/at-property-28.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-28.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-28.mm	(revision 0)
@@ -0,0 +1,29 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* Test errors when extending a property in a class extension.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property (readonly, retain)    id property1; /* { dg-warning "originally specified here" } */
+@property (readonly)           int property2; /* { dg-warning "originally specified here" } */
+@property (readonly, getter=y) int property3; /* { dg-warning "originally specified here" } */
+@property (readonly)           int property4; /* Ok */
+@property (readonly)           int property5; /* { dg-warning "originally specified here" } */
+@end
+
+@interface MyRootClass ()
+@property (readwrite, copy)       id property1; /* { dg-warning "assign semantics attributes of property .property1. conflict with previous declaration" } */
+@property (readwrite, nonatomic) int property2; /* { dg-warning ".nonatomic. attribute of property .property2. conflicts with previous declaration" } */
+@property (readwrite, getter=x)  int property3; /* { dg-warning ".getter. attribute of property .property3. conflicts with previous declaration" } */
+@property (readwrite)            int property4; /* Ok */
+@property (readwrite)          float property5; /* { dg-warning "type of property .property5. conflicts with previous declaration" } */
+@end
+
+
+
Index: testsuite/obj-c++.dg/property/at-property-27.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-27.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-27.mm	(revision 0)
@@ -0,0 +1,66 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test overriding a readonly @property with a readwrite one in a class extension.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@end
+
+@protocol count2
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@property (readonly, getter=number2) int count2;
+@end
+
+@interface MySubClass : MyRootClass
+{
+  int count1;
+  int count2;
+}
+@property (readonly, getter=number1) int count1;
+@end
+
+@interface MySubClass ()
+@property (readwrite, getter=number1, setter=setNumber1:) int count1;
+@end
+
+@interface MySubClass ()  <count2>
+@property (readwrite, getter=number2, setter=setNumber2:) int count2;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+@synthesize count2;
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  object.count1 = 20;
+  if (object.count1 != 20)
+    abort ();
+
+  object.count2 = 11;
+  if (object.count2 != 11)
+    abort ();
+
+  return 0;
+}
Index: testsuite/obj-c++.dg/property/at-property-26.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-26.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-26.mm	(revision 0)
@@ -0,0 +1,85 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test @properties in class extensions.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@end
+
+@protocol count4
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@property (getter=number4, setter=setNumber4:) int count4;
+@end
+
+@interface MySubClass : MyRootClass
+{
+  int count1;
+  int count2;
+  int count3;
+  int count4;
+}
+@property (getter=number1, setter=setNumber1:) int count1;
+@end
+
+@interface MySubClass ()
+@property (getter=number2, setter=setNumber2:) int count2;
+@end
+
+@interface MySubClass ()  <count4>
+@property (getter=number3, setter=setNumber3:) int count3;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+@synthesize count2;
+- (int) number3
+{
+  return count3;
+}
+- (void) setNumber3: (int)value
+{
+  count3 = value;
+}
+@synthesize count4;
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  object.count1 = 20;
+  if (object.count1 != 20)
+    abort ();
+
+  object.count2 = 11;
+  if (object.count2 != 11)
+    abort ();
+
+  object.count3 = 19;
+  if (object.count3 != 19)
+    abort ();
+
+  object.count4 = 74;
+  if (object.count4 != 74)
+    abort ();
+
+  return 0;
+}
Index: testsuite/obj-c++.dg/class-extension-3.mm
===================================================================
--- testsuite/obj-c++.dg/class-extension-3.mm	(revision 0)
+++ testsuite/obj-c++.dg/class-extension-3.mm	(revision 0)
@@ -0,0 +1,26 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests warnings on class extensions.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+  int count;
+}
+- (int) test;
+@property int count; /* { dg-warning "originally specified here" } */
+@end
+
+@interface MyObject ()
+- (void) test; /* { dg-error "duplicate declaration of method .-test." } */
+@end
+
+@interface MyObject ()
+@end
+
+@interface MyObject ()
+@property int count; /* { dg-error "redeclaration of property .count." } */
+@end
Index: testsuite/obj-c++.dg/class-extension-1.mm
===================================================================
--- testsuite/obj-c++.dg/class-extension-1.mm	(revision 0)
+++ testsuite/obj-c++.dg/class-extension-1.mm	(revision 0)
@@ -0,0 +1,30 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do compile } */
+
+/* This test tests the basic of class extensions.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+- (int) test;
+@end
+
+@interface MyObject ()
+- (int) test2;
+- (int) test3;
+@end
+
+@implementation MyObject
+- (int) test
+{
+  return 20;
+}
+- (int) test2
+{
+  return 20;
+}
+@end /* { dg-warning "incomplete implementation of class .MyObject." } */
+     /* { dg-warning "method definition for .-test3. not found" "" { target *-*-* } 29 } */
Index: cp/ChangeLog
===================================================================
--- cp/ChangeLog	(revision 167660)
+++ cp/ChangeLog	(working copy)
@@ -1,3 +1,12 @@ 
+2010-12-10  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* parser.c (cp_parser_objc_superclass_or_category): Recognize
+	Objective-C 2.0 class extensions.  Added iface_p and
+	is_class_extension arguments.
+	(cp_parser_objc_class_interface): Updated call to
+	cp_parser_objc_superclass_or_category.
+	(cp_parser_objc_class_implementation): Same change.
+	
 2010-12-08  Jason Merrill  <jason@redhat.com>
 
 	PR c++/46348
Index: cp/parser.c
===================================================================
--- cp/parser.c	(revision 167660)
+++ cp/parser.c	(working copy)
@@ -22435,12 +22435,15 @@  cp_parser_objc_protocol_declaration (cp_parser* pa
 /* Parse an Objective-C superclass or category.  */
 
 static void
-cp_parser_objc_superclass_or_category (cp_parser *parser, tree *super,
-							  tree *categ)
+cp_parser_objc_superclass_or_category (cp_parser *parser, 
+				       bool iface_p,
+				       tree *super,
+				       tree *categ, bool *is_class_extension)
 {
   cp_token *next = cp_lexer_peek_token (parser->lexer);
 
   *super = *categ = NULL_TREE;
+  *is_class_extension = false;
   if (next->type == CPP_COLON)
     {
       cp_lexer_consume_token (parser->lexer);  /* Eat ':'.  */
@@ -22449,7 +22452,17 @@  static void
   else if (next->type == CPP_OPEN_PAREN)
     {
       cp_lexer_consume_token (parser->lexer);  /* Eat '('.  */
-      *categ = cp_parser_identifier (parser);
+
+      /* If there is no category name, and this is an @interface, we
+	 have a class extension.  */
+      if (iface_p && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  *categ = NULL_TREE;
+	  *is_class_extension = true;
+	}
+      else
+	*categ = cp_parser_identifier (parser);
+
       cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
     }
 }
@@ -22460,6 +22473,7 @@  static void
 cp_parser_objc_class_interface (cp_parser* parser, tree attributes)
 {
   tree name, super, categ, protos;
+  bool is_class_extension;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@interface'.  */
   name = cp_parser_identifier (parser);
@@ -22472,11 +22486,12 @@  cp_parser_objc_class_interface (cp_parser* parser,
       */
       return;
     }
-  cp_parser_objc_superclass_or_category (parser, &super, &categ);
+  cp_parser_objc_superclass_or_category (parser, true, &super, &categ,
+					 &is_class_extension);
   protos = cp_parser_objc_protocol_refs_opt (parser);
 
   /* We have either a class or a category on our hands.  */
-  if (categ)
+  if (categ || is_class_extension)
     objc_start_category_interface (name, categ, protos, attributes);
   else
     {
@@ -22495,6 +22510,7 @@  static void
 cp_parser_objc_class_implementation (cp_parser* parser)
 {
   tree name, super, categ;
+  bool is_class_extension;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@implementation'.  */
   name = cp_parser_identifier (parser);
@@ -22508,7 +22524,8 @@  cp_parser_objc_class_implementation (cp_parser* pa
       */
       return;
     }
-  cp_parser_objc_superclass_or_category (parser, &super, &categ);
+  cp_parser_objc_superclass_or_category (parser, false, &super, &categ,
+					 &is_class_extension);
 
   /* We have either a class or a category on our hands.  */
   if (categ)
Index: c-parser.c
===================================================================
--- c-parser.c	(revision 167660)
+++ c-parser.c	(working copy)
@@ -6743,6 +6743,8 @@  c_parser_expr_list (c_parser *parser, bool convert
        objc-class-instance-variables[opt]
      @interface identifier ( identifier ) objc-protocol-refs[opt]
        objc-methodprotolist @end
+     @interface identifier ( ) objc-protocol-refs[opt]
+       objc-methodprotolist @end
      @implementation identifier ( identifier )
 
    objc-superclass:
@@ -6777,17 +6779,29 @@  c_parser_objc_class_definition (c_parser *parser,
   c_parser_consume_token (parser);
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
+      /* We have a category or class extension.  */
       tree id2;
       tree proto = NULL_TREE;
       c_parser_consume_token (parser);
       if (c_parser_next_token_is_not (parser, CPP_NAME))
 	{
-	  c_parser_error (parser, "expected identifier");
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	  return;
+	  if (iface_p && c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    {
+	      /* We have a class extension.  */
+	      id2 = NULL_TREE;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier or %<)%>");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      return;
+	    }
 	}
-      id2 = c_parser_peek_token (parser)->value;
-      c_parser_consume_token (parser);
+      else
+	{
+	  id2 = c_parser_peek_token (parser)->value;
+	  c_parser_consume_token (parser);
+	}
       c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
       if (!iface_p)
 	{