Patchwork ObjC/ObjC++ - support for dotsyntax without a declared property

login
register
mail settings
Submitter Nicola Pero
Date Nov. 3, 2010, 12:03 a.m.
Message ID <1288742590.152924472@192.168.2.230>
Download mbox | patch
Permalink /patch/69937/
State New
Headers show

Comments

Nicola Pero - Nov. 3, 2010, 12:03 a.m.
This patch fixes the lack of support for the Objective-C 2.0 "dotsyntax" when a property
is not explicitly declared.  In Objective-C 2.0, you can use object.component even if
an explicit @property named 'component' had not been explicitly declared; it's enough
for the getter (or setter) to exist and they will automatically be used.  Our implementation 
didn't allow for this.

This patch fixes this problem; all existing testcases pass and four new testcases for
dotsyntax are included (and now pass). :-)

Ok to commit to trunk ?

Thanks

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

        * objc-act.c (maybe_make_artificial_property_decl): New.
        (objc_maybe_build_component_ref): Call
        maybe_make_artificial_property_decl if a property can not be
        found.  Do not call objc_finish_message_expr if
        PROPERTY_HAS_NO_GETTER.
        * objc-act.h Updated comments.
        (PROPERTY_HAS_NO_GETTER): New.
        (PROPERTY_HAS_NO_SETTER): New.
        * objc-tree.def: Updated comment.

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

        * objc.dg/property/dotsyntax-1.m: New.
        * objc.dg/property/dotsyntax-2.m: New.
        * obj-c++.dg/property/dotsyntax-1.mm: New.
        * obj-c++.dg/property/dotsyntax-2.mm: New.
Mike Stump - Nov. 3, 2010, 2:04 a.m.
On Nov 2, 2010, at 5:03 PM, "Nicola Pero" <nicola.pero@meta-innovation.com> wrote:
> This patch fixes the lack of support for the Objective-C 2.0 "dotsyntax" when a property
> is not explicitly declared.

> Ok to commit to trunk ?

Ok.

Patch

Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 166218)
+++ objc/objc-act.c	(working copy)
@@ -1053,16 +1053,102 @@  lookup_property (tree interface_type, tree propert
   return inter;
 }
 
+/* This is a subroutine of objc_maybe_build_component_ref.  Search the
+   list of methods in the interface (and, failing that, 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.  */
+static tree
+maybe_make_artificial_property_decl (tree interface, tree protocol_list, tree component, bool is_class)
+{
+  tree getter_name = component;
+  tree setter_name = get_identifier (objc_build_property_setter_name (component));
+  tree getter = NULL_TREE;
+  tree setter = NULL_TREE;
 
+  if (interface)
+    {
+      int flags = 0;
+
+      if (is_class)
+	flags = OBJC_LOOKUP_CLASS;
+      
+      getter = lookup_method_static (interface, getter_name, flags);
+      setter = lookup_method_static (interface, setter_name, flags);
+    }
+
+  /* Try the protocol_list if we didn't find anything in the interface.  */
+  if (!getter && !setter)
+    {
+      getter = lookup_method_in_protocol_list (protocol_list, getter_name, is_class);
+      setter = lookup_method_in_protocol_list (protocol_list, setter_name, is_class);
+    }
+
+  /* There needs to be at least a getter or setter for this to be a
+     valid 'object.component' syntax.  */
+  if (getter || setter)
+    {
+      /* Yes ... determine the type of the expression.  */
+      tree property_decl;
+      tree type;
+      
+      if (getter)
+	type = TREE_VALUE (TREE_TYPE (getter));
+      else
+	type = TREE_VALUE (TREE_TYPE (METHOD_SEL_ARGS (setter)));
+      
+      /* Create an artificial property declaration with the
+	 information we collected on the type and getter/setter
+	 names.  */
+      property_decl = make_node (PROPERTY_DECL);
+      
+      TREE_TYPE (property_decl) = type;
+      DECL_SOURCE_LOCATION (property_decl) = input_location;
+      TREE_DEPRECATED (property_decl) = 0;
+      DECL_ARTIFICIAL (property_decl) = 1;
+	      
+      /* Add property-specific information.  Note that one of
+	 PROPERTY_GETTER_NAME or PROPERTY_SETTER_NAME may refer to a
+	 non-existing method; this will generate an error when the
+	 expression is later compiled.  At this stage we don't know if
+	 the getter or setter will be used, so we can't generate an
+	 error.  */
+      PROPERTY_NAME (property_decl) = component;
+      PROPERTY_GETTER_NAME (property_decl) = getter_name;
+      PROPERTY_SETTER_NAME (property_decl) = setter_name;
+      PROPERTY_READONLY (property_decl) = 0;
+      PROPERTY_NONATOMIC (property_decl) = 0;
+      PROPERTY_ASSIGN_SEMANTICS (property_decl) = 0;
+      PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
+      PROPERTY_DYNAMIC (property_decl) = 0;
+
+      if (!getter)
+	PROPERTY_HAS_NO_GETTER (property_decl) = 1;
+
+      /* The following is currently unused, but it's nice to have
+	 there.  We may use it if we need in the future.  */
+      if (!setter)
+	PROPERTY_HAS_NO_SETTER (property_decl) = 1;
+
+      return property_decl;
+    }
+
+  return NULL_TREE;
+}
+
 /* This hook routine is invoked by the parser when an expression such
    as 'xxx.yyy' is parsed.  We get a chance to process these
    expressions in a way that is specified to Objective-C (to implement
-   properties, or non-fragile ivars).  If the expression is not an
-   Objective-C specified expression, we should return NULL_TREE; else
-   we return the expression.
+   the Objective-C 2.0 dot-syntax, properties, or non-fragile ivars).
+   If the expression is not an Objective-C specified expression, we
+   should return NULL_TREE; else we return the expression.
 
-   At the moment this only implements properties (not non-fragile
-   ivars yet), ie 'object.property'.  */
+   At the moment this only implements dot-syntax and properties (not
+   non-fragile ivars yet), ie 'object.property' or 'object.component'
+   where 'component' is not a declared property, but a valid getter or
+   setter for it could be found.  */
 tree
 objc_maybe_build_component_ref (tree object, tree property_ident)
 {
@@ -1089,6 +1175,17 @@  objc_maybe_build_component_ref (tree object, tree
 		      : NULL_TREE);
       if (rprotos)
 	x = lookup_property_in_protocol_list (rprotos, property_ident);
+
+      if (x == NULL_TREE)
+	{
+	  /* Ok, no property.  Maybe it was an object.component
+	     dot-syntax without a declared property.  Look for
+	     getter/setter methods and internally declare an artifical
+	     property based on them if found.  */
+	  x = maybe_make_artificial_property_decl (NULL_TREE, rprotos, 
+						   property_ident,
+						   false);
+	}
     }
   else
     {
@@ -1115,6 +1212,14 @@  objc_maybe_build_component_ref (tree object, tree
 
 	  if (x == NULL_TREE)
 	    x = lookup_property_in_protocol_list (protocol_list, property_ident);
+
+	  if (x == NULL_TREE)
+	    {
+	      /* Ok, no property.  Try the dot-syntax without a
+		 declared property.  */
+	      x = maybe_make_artificial_property_decl (interface_type, protocol_list, 
+						       property_ident, false);
+	    }
 	}
     }
 
@@ -1144,10 +1249,16 @@  objc_maybe_build_component_ref (tree object, tree
 
 	 TODO: This can be made more efficient; in particular we don't
 	 need to build the whole message call, we could just work on
-	 the selector.  */
-      objc_finish_message_expr (object,
-				PROPERTY_GETTER_NAME (x),
-				NULL_TREE);
+	 the selector.
+
+	 If the PROPERTY_HAS_NO_GETTER() (ie, it is an artificial
+	 property decl created to deal with a dotsyntax not really
+	 referring to an existing property) then do not try to build a
+	 call to the getter as there is no getter.  */
+      if (!PROPERTY_HAS_NO_GETTER (x))
+	objc_finish_message_expr (object,
+				  PROPERTY_GETTER_NAME (x),
+				  NULL_TREE);
       
       return expression;
     }
@@ -1197,6 +1308,9 @@  objc_maybe_build_modify_expr (tree lhs, tree rhs)
 	{
 	  tree setter_argument = build_tree_list (NULL_TREE, rhs);
 	  tree setter;
+
+	  /* TODO: Check that the setter return type is 'void'.  */
+
 	  /* TODO: Decay argument in C.  */
 	  setter = objc_finish_message_expr (object_expr, 
 					     PROPERTY_SETTER_NAME (property_decl),
Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog	(revision 166218)
+++ objc/ChangeLog	(working copy)
@@ -1,3 +1,15 @@ 
+2010-11-02  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (maybe_make_artificial_property_decl): New.
+	(objc_maybe_build_component_ref): Call
+	maybe_make_artificial_property_decl if a property can not be
+	found.  Do not call objc_finish_message_expr if
+	PROPERTY_HAS_NO_GETTER.
+	* objc-act.h Updated comments.
+	(PROPERTY_HAS_NO_GETTER): New.
+	(PROPERTY_HAS_NO_SETTER): New.
+	* objc-tree.def: Updated comment.
+	
 2010-11-01  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	Implemented format and noreturn attributes for Objective-C methods.
Index: objc/objc-tree.def
===================================================================
--- objc/objc-tree.def	(revision 166218)
+++ objc/objc-tree.def	(working copy)
@@ -55,7 +55,11 @@  DEFTREECODE (CLASS_REFERENCE_EXPR, "class_referenc
     * else, it will remain as a PROPERTY_REF until we get to
       gimplification; at that point, we convert each PROPERTY_REF into
       a 'getter' call during ObjC/ObjC++ gimplify.
-*/
+
+  Please note that when the Objective-C 2.0 "dot-syntax" 'object.component' 
+  is encountered, where 'component' is not a property but there are valid
+  setter/getter methods for it, an artificial PROPERTY_DECL is generated
+  and used in the PROPERTY_REF.  */
 DEFTREECODE (PROPERTY_REF, "property_ref", tcc_expression, 2)
 
 /*
Index: objc/objc-act.h
===================================================================
--- objc/objc-act.h	(revision 166218)
+++ objc/objc-act.h	(working copy)
@@ -62,6 +62,11 @@  tree objc_eh_personality (void);
 
 /* TREE_TYPE is the type (int, float, etc) of the property.  */
 
+/* DECL_ARTIFICIAL is set to 1 if the PROPERTY_DECL is an artificial
+   property declaration created when the dot-syntax object.component
+   is used with no actual @property matching the component, but a
+   valid getter/setter.  */
+
 /* PROPERTY_NAME is the name of the property.  */
 #define PROPERTY_NAME(DECL) DECL_NAME(DECL)
 
@@ -99,9 +104,21 @@  typedef enum objc_property_assign_semantics {
    declaration has been parsed); 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
+   this is an artificial PROPERTY_DECL that we generate even without a
+   getter, it is set to 1.  */
+#define PROPERTY_HAS_NO_GETTER(DECL) DECL_LANG_FLAG_3 (DECL)
 
+/* PROPERTY_HAS_NO_SETTER can be 0 or 1.  Normally it is 0, but if
+   this is an artificial PROPERTY_DECL that we generate even without a
+   setter, it is set to 1.  */
+#define PROPERTY_HAS_NO_SETTER(DECL) DECL_LANG_FLAG_4 (DECL)
+
 /* PROPERTY_REF.  A PROPERTY_REF represents an 'object.property'
-   expression.  */
+   expression.  It is normally used for property access, but when
+   the Objective-C 2.0 "dot-syntax" (object.component) is used
+   with no matching property, a PROPERTY_REF is still created to
+   represent it, with an artificial PROPERTY_DECL.  */
 
 /* PROPERTY_REF_OBJECT is the object whose property we are
    accessing.  */
@@ -109,7 +126,9 @@  typedef enum objc_property_assign_semantics {
 
 /* PROPERTY_REF_PROPERTY_DECL is the PROPERTY_DECL for the property
    used in the expression.  From it, you can get the property type,
-   and the getter/setter names.  */
+   and the getter/setter names.  This PROPERTY_DECL could be artificial
+   if we are processing an 'object.component' syntax with no matching 
+   declared property.  */
 #define PROPERTY_REF_PROPERTY_DECL(NODE) TREE_OPERAND (PROPERTY_REF_CHECK (NODE), 1)
 
 
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 166218)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,10 @@ 
+2010-11-02  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/property/dotsyntax-1.m: New.
+	* objc.dg/property/dotsyntax-2.m: New.	
+	* obj-c++.dg/property/dotsyntax-1.mm: New.
+	* obj-c++.dg/property/dotsyntax-2.mm: New.	
+
 2010-11-02  H.J. Lu  <hongjiu.lu@intel.com>
 
 	* gcc.target/i386/avx-vzeroupper-15.c: New.
Index: testsuite/objc.dg/property/dotsyntax-1.m
===================================================================
--- testsuite/objc.dg/property/dotsyntax-1.m	(revision 0)
+++ testsuite/objc.dg/property/dotsyntax-1.m	(revision 0)
@@ -0,0 +1,62 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+
+/* Test the 'dot syntax' without a declarated property.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+  id b;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+- (int) count;
+- (void) setCount: (int)value;
+- (id) next;
+- (void) setNext: (id)value;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+- (int) count
+{
+  return a;
+}
+- (void) setCount: (int)value
+{
+  a = value;
+}
+- (id) next
+{
+  return b;
+}
+- (void) setNext: (id)value
+{
+  b = value;
+}
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  object.count = 40;
+  if (object.count != 40)
+    abort ();
+
+  object.next = object;
+  if (object.next != object)
+    abort ();
+
+  return 0;
+}
+
+
Index: testsuite/objc.dg/property/dotsyntax-2.m
===================================================================
--- testsuite/objc.dg/property/dotsyntax-2.m	(revision 0)
+++ testsuite/objc.dg/property/dotsyntax-2.m	(revision 0)
@@ -0,0 +1,71 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+
+/* Test the 'dot syntax' without a declarated property.  This tests the case where
+   only the setter (or only the getter) exists.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+  id b;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+- (int) a;
+- (void) setCount: (int)value;
+- (id) b;
+- (void) setNext: (id)value;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+- (int) a
+{
+  return a;
+}
+- (void) setCount: (int)value
+{
+  a = value;
+}
+- (id) b
+{
+  return b;
+}
+- (void) setNext: (id)value
+{
+  b = value;
+}
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  /* This should work because -setCount: exists (even if -count does
+     not).  */
+  object.count = 40;
+
+  /* This should work because -a exists (even if -setA: does not).  */
+  if (object.a != 40)
+    abort ();
+
+  /* This should work because -setNext: exists (even if -next does
+     not).  */
+  object.next = object;
+
+  /* This should work because -b exists (even if -setB: does not).  */
+  if (object.b != object)
+    abort ();
+
+  return 0;
+}
+
+
Index: testsuite/obj-c++.dg/property/dotsyntax-2.mm
===================================================================
--- testsuite/obj-c++.dg/property/dotsyntax-2.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/dotsyntax-2.mm	(revision 0)
@@ -0,0 +1,71 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+
+/* Test the 'dot syntax' without a declarated property.  This tests the case where
+   only the setter (or only the getter) exists.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+  id b;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+- (int) a;
+- (void) setCount: (int)value;
+- (id) b;
+- (void) setNext: (id)value;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+- (int) a
+{
+  return a;
+}
+- (void) setCount: (int)value
+{
+  a = value;
+}
+- (id) b
+{
+  return b;
+}
+- (void) setNext: (id)value
+{
+  b = value;
+}
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  /* This should work because -setCount: exists (even if -count does
+     not).  */
+  object.count = 40;
+
+  /* This should work because -a exists (even if -setA: does not).  */
+  if (object.a != 40)
+    abort ();
+
+  /* This should work because -setNext: exists (even if -next does
+     not).  */
+  object.next = object;
+
+  /* This should work because -b exists (even if -setB: does not).  */
+  if (object.b != object)
+    abort ();
+
+  return 0;
+}
+
+
Index: testsuite/obj-c++.dg/property/dotsyntax-1.mm
===================================================================
--- testsuite/obj-c++.dg/property/dotsyntax-1.mm	(revision 0)
+++ testsuite/obj-c++.dg/property/dotsyntax-1.mm	(revision 0)
@@ -0,0 +1,62 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+
+/* Test the 'dot syntax' without a declarated property.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+@interface MyRootClass
+{
+  Class isa;
+  int a;
+  id b;
+}
++ (id) initialize;
++ (id) alloc;
+- (id) init;
+- (int) count;
+- (void) setCount: (int)value;
+- (id) next;
+- (void) setNext: (id)value;
+@end
+
+@implementation MyRootClass
++ (id) initialize { return self; }
++ (id) alloc { return class_createInstance (self, 0); }
+- (id) init { return self; }
+- (int) count
+{
+  return a;
+}
+- (void) setCount: (int)value
+{
+  a = value;
+}
+- (id) next
+{
+  return b;
+}
+- (void) setNext: (id)value
+{
+  b = value;
+}
+@end
+
+int main (void)
+{
+  MyRootClass *object = [[MyRootClass alloc] init];
+
+  object.count = 40;
+  if (object.count != 40)
+    abort ();
+
+  object.next = object;
+  if (object.next != object)
+    abort ();
+
+  return 0;
+}
+
+