diff mbox

ObjC/ObjC++ patch: implementation of @optional @properties

Message ID 1290817325.675814659@192.168.2.227
State New
Headers show

Commit Message

Nicola Pero Nov. 27, 2010, 12:22 a.m. UTC
This ObjC/ObjC++ patch adds the final missing @property bit that I was
planning to do for 4.6, which is support for @properties marked 
as @optional inside a @protocol.  4 testcases are included. :-)

Ok to commit ?

Thanks

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

        Implemented optional properties.
        * objc-act.h (PROPERTY_OPTIONAL): New.
        * objc-act.c (objc_add_property_declaration): Set
        PROPERTY_OPTIONAL if appropriate.
        (finish_class): When generating definitions of setter and getter
        methods associated with a property for a protocol, mark them as
        optional if the property is optional.
        (maybe_make_artificial_property_decl): Added 'getter_name'
        argument.  Set PROPERTY_OPTIONAL.
        (objc_maybe_build_component_ref): Updated calls to
        maybe_make_artificial_property_decl.  Added code for optional,
        readonly properties.
        (objc_build_class_component_ref): Updated call to
        maybe_make_artificial_property_decl.

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

        * objc.dg/property/at-property-24.m: New.
        * objc.dg/property/at-property-25.m: New.
        * obj-c++.dg/property/at-property-24.mm: New.
        * obj-c++.dg/property/at-property-25.mm: New.

Comments

Mike Stump Nov. 27, 2010, 4:12 a.m. UTC | #1
On Nov 26, 2010, at 4:22 PM, Nicola Pero wrote:
> This ObjC/ObjC++ patch adds the final missing @property bit that I was
> planning to do for 4.6, which is support for @properties marked 
> as @optional inside a @protocol.  4 testcases are included. :-)
> 
> Ok to commit ?

Ok.
diff mbox

Patch

Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 167188)
+++ objc/objc-act.c	(working copy)
@@ -1181,7 +1181,7 @@  objc_add_property_declaration (location_t location
       /* An existing property was found; check that it has the same
 	 types, or it is compatible.  */
       location_t original_location = DECL_SOURCE_LOCATION (x);
-	  
+
       if (PROPERTY_NONATOMIC (x) != parsed_property_nonatomic)
 	{
 	  warning_at (location, 0,
@@ -1293,6 +1293,13 @@  objc_add_property_declaration (location_t location
   PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
   PROPERTY_DYNAMIC (property_decl) = 0;
 
+  /* Remember the fact that the property was found in the @optional
+     section in a @protocol, or not.  */
+  if (objc_method_optional_flag)
+    PROPERTY_OPTIONAL (property_decl) = 1;
+  else
+    PROPERTY_OPTIONAL (property_decl) = 0;
+
   /* Note that PROPERTY_GETTER_NAME is always set for all
      PROPERTY_DECLs, and PROPERTY_SETTER_NAME is always set for all
      PROPERTY_DECLs where PROPERTY_READONLY == 0.  Any time we deal
@@ -1310,18 +1317,22 @@  objc_add_property_declaration (location_t location
    in the implementation, and failing that, the protocol list)
    provided for a 'setter' or 'getter' for 'component' with default
    names (ie, if 'component' is "name", then search for "name" and
-   "setName:").  If any is found, then create an artificial property
-   that uses them.  Return NULL_TREE if 'getter' or 'setter' could not
-   be found.  */
+   "setName:").  It is also possible to specify a different
+   'getter_name' (this is used for @optional readonly properties).  If
+   any is found, then create an artificial property that uses them.
+   Return NULL_TREE if 'getter' or 'setter' could not be found.  */
 static tree
 maybe_make_artificial_property_decl (tree interface, tree implementation, 
-				     tree protocol_list, tree component, bool is_class)
+				     tree protocol_list, tree component, bool is_class,
+				     tree getter_name)
 {
-  tree getter_name = component;
   tree setter_name = get_identifier (objc_build_property_setter_name (component));
   tree getter = NULL_TREE;
   tree setter = NULL_TREE;
 
+  if (getter_name == NULL_TREE)
+    getter_name = component;
+
   /* First, check the @interface and all superclasses.  */
   if (interface)
     {
@@ -1401,6 +1412,7 @@  maybe_make_artificial_property_decl (tree interfac
       PROPERTY_ASSIGN_SEMANTICS (property_decl) = 0;
       PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
       PROPERTY_DYNAMIC (property_decl) = 0;
+      PROPERTY_OPTIONAL (property_decl) = 0;
 
       if (!getter)
 	PROPERTY_HAS_NO_GETTER (property_decl) = 1;
@@ -1481,7 +1493,7 @@  objc_maybe_build_component_ref (tree object, tree
 		 properties.  */
 	      if (!IS_CLASS (rtype))
 		x = lookup_property_in_protocol_list (rprotos, property_ident);
-	      
+
 	      if (x == NULL_TREE)
 		{
 		  /* Ok, no property.  Maybe it was an
@@ -1493,8 +1505,26 @@  objc_maybe_build_component_ref (tree object, tree
 							   NULL_TREE,
 							   rprotos, 
 							   property_ident,
-							   IS_CLASS (rtype));
+							   IS_CLASS (rtype),
+							   NULL_TREE);
 		}
+	      else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x))
+		{
+		  /* This is a special, complicated case.  If the
+		     property is optional, and is read-only, then the
+		     property is always used for reading, but an
+		     eventual existing non-property setter can be used
+		     for writing.  We create an artificial property
+		     decl copying the getter from the optional
+		     property, and looking up the setter in the
+		     interface.  */
+		  x = maybe_make_artificial_property_decl (NULL_TREE,
+							   NULL_TREE,
+							   rprotos,
+							   property_ident,
+							   false,
+							   PROPERTY_GETTER_NAME (x));		  
+		}
 	    }
 	}
       else if (objc_method_context)
@@ -1538,8 +1568,23 @@  objc_maybe_build_component_ref (tree object, tree
 		  x = maybe_make_artificial_property_decl 
 		    (interface_type, implementation, NULL_TREE,
 		     property_ident,
-		     (TREE_CODE (objc_method_context) == CLASS_METHOD_DECL));
+		     (TREE_CODE (objc_method_context) == CLASS_METHOD_DECL),
+		     NULL_TREE);
 		}
+	      else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x))
+		{
+		  tree implementation = NULL_TREE;
+		  
+		  if (t == self_decl)
+		    implementation = objc_implementation_context;
+		  
+		  x = maybe_make_artificial_property_decl (interface_type,
+							   implementation,
+							   NULL_TREE,
+							   property_ident,
+							   false,
+							   PROPERTY_GETTER_NAME (x));		  
+		}
 	    }
 	}
     }
@@ -1603,8 +1648,25 @@  objc_maybe_build_component_ref (tree object, tree
 							   implementation,
 							   protocol_list, 
 							   property_ident,
-							   IS_CLASS (rtype));
+							   IS_CLASS (rtype),
+							   NULL_TREE);
 		}
+	      else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x))
+		{
+		  tree implementation = NULL_TREE;
+
+		  if (objc_implementation_context
+		      && CLASS_NAME (objc_implementation_context) 
+		      == OBJC_TYPE_NAME (interface_type))
+		    implementation = objc_implementation_context;
+		  
+		  x = maybe_make_artificial_property_decl (interface_type,
+							   implementation,
+							   protocol_list,
+							   property_ident,
+							   false,
+							   PROPERTY_GETTER_NAME (x));		  
+		}	      
 	    }
 	}
     }
@@ -1703,7 +1765,7 @@  objc_build_class_component_ref (tree class_name, t
 
   x = maybe_make_artificial_property_decl (rtype, NULL_TREE, NULL_TREE,
 					   property_ident,
-					   true);
+					   true, NULL_TREE);
   
   if (x)
     {
@@ -10515,7 +10577,10 @@  finish_class (tree klass)
 		getter_decl = build_method_decl (INSTANCE_METHOD_DECL, 
 						 rettype, PROPERTY_GETTER_NAME (x), 
 						 NULL_TREE, false);
-		objc_add_method (objc_interface_context, getter_decl, false, false);
+		if (PROPERTY_OPTIONAL (x))
+		  objc_add_method (objc_interface_context, getter_decl, false, true);
+		else
+		  objc_add_method (objc_interface_context, getter_decl, false, false);
 		METHOD_PROPERTY_CONTEXT (getter_decl) = x;
 	      }
 
@@ -10555,7 +10620,10 @@  finish_class (tree klass)
 						     ret_type, selector,
 						     build_tree_list (NULL_TREE, NULL_TREE),
 						     false);
-		    objc_add_method (objc_interface_context, setter_decl, false, false);
+		    if (PROPERTY_OPTIONAL (x))
+		      objc_add_method (objc_interface_context, setter_decl, false, true);
+		    else
+		      objc_add_method (objc_interface_context, setter_decl, false, false);
 		    METHOD_PROPERTY_CONTEXT (setter_decl) = x;
 		  }	       
 	      }
Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog	(revision 167188)
+++ objc/ChangeLog	(working copy)
@@ -1,3 +1,20 @@ 
+2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	Implemented optional properties.
+	* objc-act.h (PROPERTY_OPTIONAL): New.
+	* objc-act.c (objc_add_property_declaration): Set
+	PROPERTY_OPTIONAL if appropriate.
+	(finish_class): When generating definitions of setter and getter
+	methods associated with a property for a protocol, mark them as
+	optional if the property is optional.
+	(maybe_make_artificial_property_decl): Added 'getter_name'
+	argument.  Set PROPERTY_OPTIONAL.
+	(objc_maybe_build_component_ref): Updated calls to
+	maybe_make_artificial_property_decl.  Added code for optional,
+	readonly properties.
+	(objc_build_class_component_ref): Updated call to
+	maybe_make_artificial_property_decl.
+	
 2010-11-25  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	* objc-act.c (objc_build_struct): Install TYPE_OBJC_INTERFACE
Index: objc/objc-act.h
===================================================================
--- objc/objc-act.h	(revision 167188)
+++ objc/objc-act.h	(working copy)
@@ -113,6 +113,11 @@  typedef enum objc_property_assign_semantics {
    setter, it is set to 1.  */
 #define PROPERTY_HAS_NO_SETTER(DECL) DECL_LANG_FLAG_4 (DECL)
 
+/* PROPERTY_OPTIONAL can be 0 or 1.  Normally it is 0, but if this is
+   a property declared as @optional in a @protocol, then it is set to
+   1.  */
+#define PROPERTY_OPTIONAL(DECL) DECL_LANG_FLAG_5 (DECL)
+
 /* PROPERTY_REF.  A PROPERTY_REF represents an 'object.property'
    expression.  It is normally used for property access, but when
    the Objective-C 2.0 "dot-syntax" (object.component) is used
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 167188)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,10 @@ 
+2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/property/at-property-24.m: New.
+	* objc.dg/property/at-property-25.m: New.	
+	* obj-c++.dg/property/at-property-24.mm: New.
+	* obj-c++.dg/property/at-property-25.mm: New.
+
 2010-11-26  Rainer Orth  <ro@CeBiTec.Uni-Bielefeld.DE>
 
 	* lib/gnat.exp: Load gcc.exp.
Index: testsuite/objc.dg/property/at-property-24.m
===================================================================
--- testsuite/objc.dg/property/at-property-24.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-24.m	(revision 0)
@@ -0,0 +1,118 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test @optional @properties.  */
+
+#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
+
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@protocol count
+@required
+/* @required + @synthesize.  */
+@property (getter=number1, setter=setNumber1:) int count1;
+/* @required + manual setters/getters.  */
+@property (getter=number2, setter=setNumber2:) int count2;
+
+@optional
+/* @optional + @synthesize.  */
+@property (getter=number3, setter=setNumber3:) int count3;
+/* @optional + manual setters/getters.  */
+@property (getter=number4, setter=setNumber4:) int count4;
+
+@optional
+/* @optional + readonly, with a setter added in the class itself.  */
+@property (readonly, getter=number5) int count5;
+@end
+
+@interface MySubClass : MyRootClass <count>
+{
+  int count1;
+  int count2;
+  int count3;
+  int count4;
+  int count5;
+}
+- (void) setCount5: (int)value;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+- (int) number2
+{
+  return count2;
+}
+- (void) setNumber2: (int)value
+{
+  count2 = value;
+}
+@synthesize count3;
+- (int) number4
+{
+  return count4;
+}
+- (void) setNumber4: (int)value
+{
+  count4 = value;
+}
+- (int) number5
+{
+  return count5;
+}
+- (void) setCount5: (int)value
+{
+  count5 = value;
+}
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  /* First, test that @required and @optional properties work as
+     expected if implemented either via @synthesize or manually.  */
+  object.count1 = 44;
+  if (object.count1 != 44)
+    abort ();
+
+  object.count2 = 88;
+  if (object.count2 != 88)
+    abort ();
+
+  object.count3 = 77;
+  if (object.count3 != 77)
+    abort ();
+
+  object.count4 = 11;
+  if (object.count4 != 11)
+    abort ();
+
+  /* Now, test the complication: @optional @property which is
+     readonly, but which has a setter manually implemented.
+     Apparently it is possible to use the dotsyntax and the @optional
+     @property getter is used when reading, while the manual setter is
+     used when writing.  */
+  object.count5 = 99;
+  if (object.count5 != 99)
+    abort ();
+
+  return 0;
+}
Index: testsuite/objc.dg/property/at-property-25.m
===================================================================
--- testsuite/objc.dg/property/at-property-25.m	(revision 0)
+++ testsuite/objc.dg/property/at-property-25.m	(revision 0)
@@ -0,0 +1,87 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test warnings and non-warnings with @optional @properties.  */
+
+#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 count
+@optional
+@property int count1;
+@property (readonly) int count2;
+@end
+
+
+/* A class that implements all the properties.  */
+@interface MySubClass1 : MyRootClass <count>
+{
+  int count1;
+  int count2;
+}
+@end
+
+@implementation MySubClass1
+@synthesize count1;
+@synthesize count2;
+@end
+
+
+/* A class that implements nothing; no warnings as the properties are
+   all optional.  */
+@interface MySubClass2 : MyRootClass <count>
+@end
+
+@implementation MySubClass2
+@end
+
+
+@protocol count2
+@required
+@property int count1;
+@property (readonly) int count2;
+@end
+
+/* A class that implements all the properties.  */
+@interface MySubClass3 : MyRootClass <count2>
+{
+  int count1;
+  int count2;
+}
+@end
+
+@implementation MySubClass3
+@synthesize count1;
+@synthesize count2;
+@end
+
+
+/* A class that implements nothing; warnings as the properties are
+   all required.  */
+@interface MySubClass4 : MyRootClass <count2>
+@end
+
+@implementation MySubClass4
+@end
+
+/* { dg-warning "incomplete implementation of class" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..setCount1:. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..count1. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..count2. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "class .MySubClass4. does not fully implement the .count2. protocol" "" { target *-*-* } 81 } */
Index: testsuite/obj-c++.dg/property/at-property-25.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-25.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-25.mm	(revision 0)
@@ -0,0 +1,87 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test warnings and non-warnings with @optional @properties.  */
+
+#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 count
+@optional
+@property int count1;
+@property (readonly) int count2;
+@end
+
+
+/* A class that implements all the properties.  */
+@interface MySubClass1 : MyRootClass <count>
+{
+  int count1;
+  int count2;
+}
+@end
+
+@implementation MySubClass1
+@synthesize count1;
+@synthesize count2;
+@end
+
+
+/* A class that implements nothing; no warnings as the properties are
+   all optional.  */
+@interface MySubClass2 : MyRootClass <count>
+@end
+
+@implementation MySubClass2
+@end
+
+
+@protocol count2
+@required
+@property int count1;
+@property (readonly) int count2;
+@end
+
+/* A class that implements all the properties.  */
+@interface MySubClass3 : MyRootClass <count2>
+{
+  int count1;
+  int count2;
+}
+@end
+
+@implementation MySubClass3
+@synthesize count1;
+@synthesize count2;
+@end
+
+
+/* A class that implements nothing; warnings as the properties are
+   all required.  */
+@interface MySubClass4 : MyRootClass <count2>
+@end
+
+@implementation MySubClass4
+@end
+
+/* { dg-warning "incomplete implementation of class" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..setCount1:. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..count1. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "method definition for ..count2. not found" "" { target *-*-* } 81 } */
+/* { dg-warning "class .MySubClass4. does not fully implement the .count2. protocol" "" { target *-*-* } 81 } */
Index: testsuite/obj-c++.dg/property/at-property-24.mm
===================================================================
--- testsuite/obj-c++.dg/property/at-property-24.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/at-property-24.mm	(revision 0)
@@ -0,0 +1,118 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+/* Test @optional @properties.  */
+
+#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
+
+/* Use a different getters/setters, so that the only way to compile
+   object.countX is to find the actual @property.  */
+@protocol count
+@required
+/* @required + @synthesize.  */
+@property (getter=number1, setter=setNumber1:) int count1;
+/* @required + manual setters/getters.  */
+@property (getter=number2, setter=setNumber2:) int count2;
+
+@optional
+/* @optional + @synthesize.  */
+@property (getter=number3, setter=setNumber3:) int count3;
+/* @optional + manual setters/getters.  */
+@property (getter=number4, setter=setNumber4:) int count4;
+
+@optional
+/* @optional + readonly, with a setter added in the class itself.  */
+@property (readonly, getter=number5) int count5;
+@end
+
+@interface MySubClass : MyRootClass <count>
+{
+  int count1;
+  int count2;
+  int count3;
+  int count4;
+  int count5;
+}
+- (void) setCount5: (int)value;
+@end
+
+@implementation MySubClass
+@synthesize count1;
+- (int) number2
+{
+  return count2;
+}
+- (void) setNumber2: (int)value
+{
+  count2 = value;
+}
+@synthesize count3;
+- (int) number4
+{
+  return count4;
+}
+- (void) setNumber4: (int)value
+{
+  count4 = value;
+}
+- (int) number5
+{
+  return count5;
+}
+- (void) setCount5: (int)value
+{
+  count5 = value;
+}
+@end
+
+int main (void)
+{
+  MySubClass *object = [[MySubClass alloc] init];
+
+  /* First, test that @required and @optional properties work as
+     expected if implemented either via @synthesize or manually.  */
+  object.count1 = 44;
+  if (object.count1 != 44)
+    abort ();
+
+  object.count2 = 88;
+  if (object.count2 != 88)
+    abort ();
+
+  object.count3 = 77;
+  if (object.count3 != 77)
+    abort ();
+
+  object.count4 = 11;
+  if (object.count4 != 11)
+    abort ();
+
+  /* Now, test the complication: @optional @property which is
+     readonly, but which has a setter manually implemented.
+     Apparently it is possible to use the dotsyntax and the @optional
+     @property getter is used when reading, while the manual setter is
+     used when writing.  */
+  object.count5 = 99;
+  if (object.count5 != 99)
+    abort ();
+
+  return 0;
+}