Patchwork ObjC/ObjC++ - fixes for @dynamic

login
register
mail settings
Submitter Nicola Pero
Date Nov. 7, 2010, 9:06 p.m.
Message ID <1289163967.15233619@192.168.2.228>
Download mbox | patch
Permalink /patch/70373/
State New
Headers show

Comments

Nicola Pero - Nov. 7, 2010, 9:06 p.m.
This patch (to be applied on top of my other two recent ObjC/ObjC++ patches) fixes a number
of issues with @dynamic.  With this patch --

 * @dynamic works for properties coming from protocols (not just for properties directly declared
in the current interface);

 * @dynamic works if you have @dynamic in a class, and @synthesize in a subclass (for the same @property);

 * @dynamic works in the case where the class is declared to conform to a @protocol which declares 
a method that matches a setter/getter for a @dynamic @property in the class.  Apple's llvm-gcc has a testcase 
for that (no warning should be generated for the missing method) and it now works in the same way with our 
compiler as well. :-)

I had to rework how @dynamic is implemented to get these to work, but it wasn't too bad.

6 new testcases are included.

Ok to commit to trunk ?

Thanks

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

        * objc-act.c (objc_add_dynamic_declaration_for_property): Do not
        search for the @property declation only in the current context,
        but also in inherited properties.  Do not mark the original
        PROPERTY_DECL in the @interface or @protocol with
        PROPERTY_DYNAMIC.
        (check_methods): To check if a method is associated with a
        @dynamic property, search for the property in IMPL_PROPERTY_DECL.
        (check_accessible_methods): Same change.
        * objc-act.h: Updated comment.

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

        * objc.dg/property/dynamic-4.m: New.
        * objc.dg/property/dynamic-5.m: New.
        * objc.dg/property/dynamic-6.m: New.    
        * obj-c++.dg/property/dynamic-4.mm: New.
        * obj-c++.dg/property/dynamic-5.mm: New.
        * obj-c++.dg/property/dynamic-6.mm: New.
Mike Stump - Nov. 8, 2010, 9:09 p.m.
On Nov 7, 2010, at 1:06 PM, Nicola Pero wrote:
> This patch (to be applied on top of my other two recent ObjC/ObjC++ patches) fixes a number
> of issues with @dynamic.

> Ok to commit to trunk ?

Ok.

Patch

diff -rupN --exclude=.svn trunk8/gcc/objc/ChangeLog trunk1/gcc/objc/ChangeLog
--- trunk8/gcc/objc/ChangeLog	2010-11-07 12:56:45.000000000 +0000
+++ trunk1/gcc/objc/ChangeLog	2010-11-07 21:03:42.000000000 +0000
@@ -1,5 +1,17 @@ 
 2010-11-07  Nicola Pero  <nicola.pero@meta-innovation.com>
 
+	* objc-act.c (objc_add_dynamic_declaration_for_property): Do not
+	search for the @property declation only in the current context,
+	but also in inherited properties.  Do not mark the original
+	PROPERTY_DECL in the @interface or @protocol with
+	PROPERTY_DYNAMIC.
+	(check_methods): To check if a method is associated with a
+	@dynamic property, search for the property in IMPL_PROPERTY_DECL.
+	(check_accessible_methods): Same change.
+	* objc-act.h: Updated comment.
+
+2010-11-07  Nicola Pero  <nicola.pero@meta-innovation.com>
+
 	* objc-act.c (objc_add_synthesize_declaration_for_property):
 	Iterate over IMPL_PROPERTY_DECL, not CLASS_PROPERTY_DECL, when
 	checking for an existing @synthesize or @dynamic declaration.
diff -rupN --exclude=.svn trunk8/gcc/objc/objc-act.c trunk1/gcc/objc/objc-act.c
--- trunk8/gcc/objc/objc-act.c	2010-11-07 12:34:17.000000000 +0000
+++ trunk1/gcc/objc/objc-act.c	2010-11-07 19:48:36.000000000 +0000
@@ -8738,9 +8738,19 @@  check_methods (tree chain, tree implemen
     {
       /* If the method is associated with a dynamic property, then it
 	 is Ok not to have the method implementation, as it will be
-	 generated dynamically at runtime.  */
-      tree property = METHOD_PROPERTY_CONTEXT (chain);
-      if (property != NULL_TREE && PROPERTY_DYNAMIC (property))
+	 generated dynamically at runtime.  To decide if the method is
+	 associated with a @dynamic property, we search the list of
+	 @synthesize and @dynamic for this implementation, and look
+	 for any @dynamic property with the same setter or getter name
+	 as this method.  */
+      tree x;
+      for (x = IMPL_PROPERTY_DECL (implementation); x; x = TREE_CHAIN (x))
+	if (PROPERTY_DYNAMIC (x)
+	    && (PROPERTY_GETTER_NAME (x) == METHOD_SEL_NAME (chain)
+		|| PROPERTY_SETTER_NAME (x) == METHOD_SEL_NAME (chain)))
+	  break;
+      
+      if (x != NULL_TREE)
 	{
 	  chain = TREE_CHAIN (chain); /* next method...  */
 	  continue;
@@ -8751,6 +8761,7 @@  check_methods (tree chain, tree implemen
 	  /* If the method is a property setter/getter, we'll still
 	     allow it to be missing if it is implemented by
 	     'interface' or any of its superclasses.  */
+	  tree property = METHOD_PROPERTY_CONTEXT (chain);
 	  if (property)
 	    {
 	      /* Note that since this is a property getter/setter, it
@@ -8864,13 +8875,21 @@  check_methods_accessible (tree chain, tr
     {
       /* If the method is associated with a dynamic property, then it
 	 is Ok not to have the method implementation, as it will be
-	 generated dynamically at runtime.  */
-      tree property = METHOD_PROPERTY_CONTEXT (chain);
-      if (property != NULL_TREE  &&  PROPERTY_DYNAMIC (property))
+	 generated dynamically at runtime.  Search for any @dynamic
+	 property with the same setter or getter name as this
+	 method.  TODO: Use a hashtable lookup.  */
+      tree x;
+      for (x = IMPL_PROPERTY_DECL (base_context); x; x = TREE_CHAIN (x))
+	if (PROPERTY_DYNAMIC (x)
+	    && (PROPERTY_GETTER_NAME (x) == METHOD_SEL_NAME (chain)
+		|| PROPERTY_SETTER_NAME (x) == METHOD_SEL_NAME (chain)))
+	  break;
+      
+      if (x != NULL_TREE)
 	{
 	  chain = TREE_CHAIN (chain); /* next method...  */
 	  continue;
-	}
+	}	
 
       context = base_context;
       while (context)
@@ -9910,11 +9929,9 @@  objc_add_dynamic_declaration_for_propert
 	return;
       }
 
-  /* Check that the property is declared in the corresponding
-     interface.  */
-  for (property = CLASS_PROPERTY_DECL (interface); property; property = TREE_CHAIN (property))
-    if (PROPERTY_NAME (property) == property_name)
-      break;
+  /* Check that the property is declared in the interface.  It could
+     also be declared in a superclass or protocol.  */
+  property = lookup_property (interface, property_name);
 
   if (!property)
     {
@@ -9924,17 +9941,6 @@  objc_add_dynamic_declaration_for_propert
     }
   else
     {
-      /* Mark the original PROPERTY_DECL as dynamic.  The reason is
-	 that the setter and getter methods in the interface have a
-	 METHOD_PROPERTY_CONTEXT that points to the original
-	 PROPERTY_DECL; when we check that these methods have been
-	 implemented, we need to easily find that they are associated
-	 with a dynamic property.  TODO: Remove this hack; it will not
-	 work with properties in a protocol that may be implemented by
-	 different classes and be @dynamic in some, and non-@dynamic
-	 in other ones.  */
-      PROPERTY_DYNAMIC (property) = 1;
-
       /* We have to copy the property, because we want to chain it to
 	 the implementation context, and we want to store the source
 	 location of the @synthesize, not of the original
diff -rupN --exclude=.svn trunk8/gcc/objc/objc-act.h trunk1/gcc/objc/objc-act.h
--- trunk8/gcc/objc/objc-act.h	2010-11-07 10:40:36.000000000 +0000
+++ trunk1/gcc/objc/objc-act.h	2010-11-07 17:50:42.000000000 +0000
@@ -100,8 +100,7 @@  typedef enum objc_property_assign_semant
 #define PROPERTY_IVAR_NAME(DECL) ((DECL)->decl_common.initial)
 
 /* PROPERTY_DYNAMIC can be 0 or 1.  This is 1 if the PROPERTY_DECL
-   represents a @dynamic (or if it is a @property for which a @dynamic
-   declaration has been parsed); otherwise, it is set to 0.  */
+   represents a @dynamic; otherwise, it is set to 0.  */
 #define PROPERTY_DYNAMIC(DECL) DECL_LANG_FLAG_2 (DECL)
 
 /* PROPERTY_HAS_NO_GETTER can be 0 or 1.  Normally it is 0, but if
diff -rupN --exclude=.svn trunk8/gcc/testsuite/ChangeLog trunk1/gcc/testsuite/ChangeLog
--- trunk8/gcc/testsuite/ChangeLog	2010-11-07 20:58:43.000000000 +0000
+++ trunk1/gcc/testsuite/ChangeLog	2010-11-07 20:38:12.000000000 +0000
@@ -1,5 +1,14 @@ 
 2010-11-07  Nicola Pero  <nicola.pero@meta-innovation.com>
 
+	* objc.dg/property/dynamic-4.m: New.
+	* objc.dg/property/dynamic-5.m: New.
+	* objc.dg/property/dynamic-6.m: New.	
+	* obj-c++.dg/property/dynamic-4.mm: New.
+	* obj-c++.dg/property/dynamic-5.mm: New.
+	* obj-c++.dg/property/dynamic-6.mm: New.
+
+2010-11-07  Nicola Pero  <nicola.pero@meta-innovation.com>
+
 	* objc.dg/property/synthesize-3.m: New.
 	* objc.dg/property/synthesize-4.m: New.
 	* objc.dg/property/synthesize-5.m: New.
diff -rupN --exclude=.svn trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-4.mm trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-4.mm
--- trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-4.mm	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-4.mm	2010-11-07 18:45:14.000000000 +0000
@@ -0,0 +1,45 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@implementation MyRootClass
+@end
+
+/* Test @property/@dynamic with protocols.  */
+
+@protocol MyProtocol
+@property int a;
+@end
+
+
+/* This class is declared to conform to the protocol, but because of
+   @dynamic, no warnings are issued even if the getter/setter for the
+   @property are missing.  */
+@interface MyClass1 : MyRootClass <MyProtocol>
+@end
+
+@implementation MyClass1
+@dynamic a;
+@end
+
+
+/* This class is declared to conform to the protocol and warnings are
+   issued because the setter for the @property is missing.  */
+@interface MyClass2 : MyRootClass <MyProtocol>
+@end
+
+@implementation MyClass2
+- (int) a
+{
+  return 0;
+}
+@end /* { dg-warning "incomplete implementation" } */
+/* { dg-warning "method definition for .-setA:. not found" "" { target *-*-* } 43 } */
+/* { dg-warning "class .MyClass2. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 43 } */
diff -rupN --exclude=.svn trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-5.mm trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-5.mm
--- trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-5.mm	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-5.mm	2010-11-07 18:44:01.000000000 +0000
@@ -0,0 +1,53 @@ 
+/* 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 @dynamic in the real scenario where a class declares a
+   @property, uses @dynamic to avoid implementing it, then subclasses
+   implement it.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <stdlib.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property int a;
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@dynamic a;
+@end
+
+@interface Test : MyRootClass
+{
+  int v1;
+}
+@end
+
+@implementation Test
+@synthesize a = v1;
+@end
+
+int main (void)
+{
+  /* Note how 'object' is declared to be of class 'MyRootClass', but
+     actually is of the subclass which implements the property for
+     real.  */
+  MyRootClass *object = [[Test alloc] init];
+
+  object.a = 40;
+
+  if (object.a != 40)
+    abort ();
+
+  return 0;
+}
diff -rupN --exclude=.svn trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-6.mm trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-6.mm
--- trunk8/gcc/testsuite/obj-c++.dg/property/dynamic-6.mm	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/obj-c++.dg/property/dynamic-6.mm	2010-11-07 20:11:03.000000000 +0000
@@ -0,0 +1,26 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test case when an accessor from a @property matches a method
+   required by a protocol.  If the @property is @dynamic, then no
+   warning should be generated.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <stdlib.h>
+
+@protocol Count
+- (int) count;
+@end
+
+@interface MyRootClass <Count>
+{
+  Class isa;
+}
+@property int count;
+@end
+
+@implementation MyRootClass
+/* This @dynamic turns off any warnings for -count and -setCount:.  */
+@dynamic count;
+@end
diff -rupN --exclude=.svn trunk8/gcc/testsuite/objc.dg/property/dynamic-4.m trunk1/gcc/testsuite/objc.dg/property/dynamic-4.m
--- trunk8/gcc/testsuite/objc.dg/property/dynamic-4.m	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/objc.dg/property/dynamic-4.m	2010-11-07 18:45:02.000000000 +0000
@@ -0,0 +1,45 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@implementation MyRootClass
+@end
+
+/* Test @property/@dynamic with protocols.  */
+
+@protocol MyProtocol
+@property int a;
+@end
+
+
+/* This class is declared to conform to the protocol, but because of
+   @dynamic, no warnings are issued even if the getter/setter for the
+   @property are missing.  */
+@interface MyClass1 : MyRootClass <MyProtocol>
+@end
+
+@implementation MyClass1
+@dynamic a;
+@end
+
+
+/* This class is declared to conform to the protocol and warnings are
+   issued because the setter for the @property is missing.  */
+@interface MyClass2 : MyRootClass <MyProtocol>
+@end
+
+@implementation MyClass2
+- (int) a
+{
+  return 0;
+}
+@end /* { dg-warning "incomplete implementation" } */
+/* { dg-warning "method definition for .-setA:. not found" "" { target *-*-* } 43 } */
+/* { dg-warning "class .MyClass2. does not fully implement the .MyProtocol. protocol" "" { target *-*-* } 43 } */
diff -rupN --exclude=.svn trunk8/gcc/testsuite/objc.dg/property/dynamic-5.m trunk1/gcc/testsuite/objc.dg/property/dynamic-5.m
--- trunk8/gcc/testsuite/objc.dg/property/dynamic-5.m	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/objc.dg/property/dynamic-5.m	2010-11-07 18:42:01.000000000 +0000
@@ -0,0 +1,53 @@ 
+/* 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 @dynamic in the real scenario where a class declares a
+   @property, uses @dynamic to avoid implementing it, then subclasses
+   implement it.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <stdlib.h>
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@property int a;
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+@dynamic a;
+@end
+
+@interface Test : MyRootClass
+{
+  int v1;
+}
+@end
+
+@implementation Test
+@synthesize a = v1;
+@end
+
+int main (void)
+{
+  /* Note how 'object' is declared to be of class 'MyRootClass', but
+     actually is of the subclass which implements the property for
+     real.  */
+  MyRootClass *object = [[Test alloc] init];
+
+  object.a = 40;
+
+  if (object.a != 40)
+    abort ();
+
+  return 0;
+}
diff -rupN --exclude=.svn trunk8/gcc/testsuite/objc.dg/property/dynamic-6.m trunk1/gcc/testsuite/objc.dg/property/dynamic-6.m
--- trunk8/gcc/testsuite/objc.dg/property/dynamic-6.m	1970-01-01 01:00:00.000000000 +0100
+++ trunk1/gcc/testsuite/objc.dg/property/dynamic-6.m	2010-11-07 20:10:54.000000000 +0000
@@ -0,0 +1,26 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test case when an accessor from a @property matches a method
+   required by a protocol.  If the @property is @dynamic, then no
+   warning should be generated.  */
+
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <stdlib.h>
+
+@protocol Count
+- (int) count;
+@end
+
+@interface MyRootClass <Count>
+{
+  Class isa;
+}
+@property int count;
+@end
+
+@implementation MyRootClass
+/* This @dynamic turns off any warnings for -count and -setCount:.  */
+@dynamic count;
+@end