OSDN Git Service

In gcc/objc/:
authornicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 11 Nov 2010 18:58:43 +0000 (18:58 +0000)
committernicola <nicola@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 11 Nov 2010 18:58:43 +0000 (18:58 +0000)
2010-11-11  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc-act.c (objc_add_property_declaration): Check that the type
        of a property and of an inherited property match.
        (objc_maybe_build_component_ref): Tidied up indentation and
        comments.
        (objc_common_type): Added new type of check (-5).  If an unknown
        class is involved in a comparison, try to look up its interface.
        (objc_add_synthesize_declaration_for_property): Check that the
        property to synthesize and the instance variable to use have the
        same type.

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

        * objc.dg/property/at-property-20.m: New.
        * objc.dg/property/synthesize-8.m: New.
        * obj-c++.dg/property/at-property-20.m: New.
        * obj-c++.dg/property/synthesize-8.mm: New.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@166612 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/objc/ChangeLog
gcc/objc/objc-act.c
gcc/testsuite/ChangeLog
gcc/testsuite/obj-c++.dg/property/at-property-20.mm [new file with mode: 0644]
gcc/testsuite/obj-c++.dg/property/synthesize-8.mm [new file with mode: 0644]
gcc/testsuite/objc.dg/property/at-property-20.m [new file with mode: 0644]
gcc/testsuite/objc.dg/property/synthesize-8.m [new file with mode: 0644]

index d60e2fb..b34b619 100644 (file)
@@ -1,3 +1,14 @@
+2010-11-11  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       * objc-act.c (objc_add_property_declaration): Check that the type
+       of a property and of an inherited property match.
+       (objc_maybe_build_component_ref): Tidied up indentation and
+       comments.
+       (objc_common_type): Added new type of check (-5).
+       (objc_add_synthesize_declaration_for_property): Check that the
+       property to synthesize and the instance variable to use have the
+       same type.
+       
 2010-11-10  Joseph Myers  <joseph@codesourcery.com>
 
        * objc-act.c (objc_init): Use %' in diagnostic.
index 6f499c3..715623f 100644 (file)
@@ -1184,22 +1184,41 @@ objc_add_property_declaration (location_t location, tree decl,
          return;
        }
 
-      if (property_readonly)
-       {
-         /* If the property is readonly, it is Ok if the property
-            type is a specialization of the previously declared one.
-            Eg, the superclass returns 'NSArray' while the subclass
-            returns 'NSMutableArray'.  */
-         
-         /* TODO: Check that the types are the same, or more specialized.  */
-         ;
-       }
-      else
-       {
-         /* Else, the types must match exactly.  */
-
-         /* TODO: Check that property types are identical.  */
-         ;
+      /* We now check that the new and old property declarations have
+        the same types (or compatible one).  In the Objective-C
+        tradition of loose type checking, we do type-checking but
+        only generate warnings (not errors) if they do not match.
+        For non-readonly properties, the types must match exactly;
+        for readonly properties, it is allowed to use a "more
+        specialized" type in the new property declaration.  Eg, the
+        superclass has a getter returning (NSArray *) and the
+        subclass a getter returning (NSMutableArray *).  The object's
+        getter returns an (NSMutableArray *); but if you cast the
+        object to the superclass, which is allowed, you'd still
+        expect the getter to return an (NSArray *), which works since
+        an (NSMutableArray *) is an (NSArray *) too.  So, the set of
+        objects belonging to the type of the new @property should be
+        a subset of the set of objects belonging to the type of the
+        old @property.  This is what "specialization" means.  And the
+        reason it only applies to readonly properties is that for a
+        readwrite property the setter would have the opposite
+        requirement - ie that the superclass type is more specialized
+        then the subclass one; hence the only way to satisfy both
+        constraints is that the types match.  */
+
+      /* If the types are not the same in the C sense, we warn ...  */
+      if (!comptypes (TREE_TYPE (x), TREE_TYPE (decl))
+         /* ... unless the property is readonly, in which case we
+            allow a new, more specialized, declaration.  */
+         && (!property_readonly 
+             || !objc_compare_types (TREE_TYPE (x),
+                                     TREE_TYPE (decl), -5, NULL_TREE)))
+       {
+         warning_at (location, 0,
+                     "type of property %qD conflicts with previous declaration", decl);
+         if (original_location != UNKNOWN_LOCATION)
+           inform (original_location, "originally specified here");
+         return;
        }
     }
 
@@ -1445,15 +1464,10 @@ objc_maybe_build_component_ref (tree object, tree property_ident)
          else if (t == self_decl)
            interface_type = lookup_interface (CLASS_NAME (implementation_template));
 
-         /* TODO: Protocols.  */
-
          if (interface_type)
            {
              if (TREE_CODE (objc_method_context) != CLASS_METHOD_DECL)
-               {
-                 x = lookup_property (interface_type, property_ident);
-                 /* TODO: Protocols.  */
-               }
+               x = lookup_property (interface_type, property_ident);
        
              if (x == NULL_TREE)
                {
@@ -1468,8 +1482,6 @@ objc_maybe_build_component_ref (tree object, tree property_ident)
                  if (t == self_decl)
                    implementation = objc_implementation_context;
                  
-                 /* TODO: Protocols.  */
-
                  x = maybe_make_artificial_property_decl 
                    (interface_type, implementation, NULL_TREE,
                     property_ident,
@@ -1544,8 +1556,6 @@ objc_maybe_build_component_ref (tree object, tree property_ident)
        }
     }
 
-  /* TODO: Fix compiling super.accessor.  */
-
   if (x)
     {
       tree expression;
@@ -2121,8 +2131,8 @@ objc_common_type (tree type1, tree type2)
    returning 'true', this routine may issue warnings related to, e.g.,
    protocol conformance.  When returning 'false', the routine must
    produce absolutely no warnings; the C or C++ front-end will do so
-   instead, if needed.  If either LTYP or RTYP is not an Objective-C type,
-   the routine must return 'false'.
+   instead, if needed.  If either LTYP or RTYP is not an Objective-C
+   type, the routine must return 'false'.
 
    The ARGNO parameter is encoded as follows:
      >= 1      Parameter number (CALLEE contains function being called);
@@ -2130,8 +2140,11 @@ objc_common_type (tree type1, tree type2)
      -1                Assignment;
      -2                Initialization;
      -3                Comparison (LTYP and RTYP may match in either direction);
-     -4                Silent comparison (for C++ overload resolution).
-  */
+     -4                Silent comparison (for C++ overload resolution);
+     -5                Silent "specialization" comparison for RTYP to be a "specialization" 
+                of LTYP (a specialization means that RTYP is LTYP plus some constraints, 
+                so that each object of type RTYP is also of type LTYP).  This is used
+                when comparing property types.  */
 
 bool
 objc_compare_types (tree ltyp, tree rtyp, int argno, tree callee)
@@ -2216,11 +2229,24 @@ objc_compare_types (tree ltyp, tree rtyp, int argno, tree callee)
   if (rcls && TREE_CODE (rcls) == IDENTIFIER_NODE)
     rcls = NULL_TREE;
 
-  /* If either type is an unqualified 'id', we're done.  */
-  if ((!lproto && objc_is_object_id (ltyp))
-      || (!rproto && objc_is_object_id (rtyp)))
-    return true;
-
+  /* If either type is an unqualified 'id', we're done.  This is because
+     an 'id' can be assigned to or from any type with no warnings.  */
+  if (argno != -5)
+    {
+      if ((!lproto && objc_is_object_id (ltyp))
+         || (!rproto && objc_is_object_id (rtyp)))
+       return true;
+    }
+  else
+    {
+      /* For property checks, though, an 'id' is considered the most
+        general type of object, hence if you try to specialize an
+        'NSArray *' (ltyp) property with an 'id' (rtyp) one, we need
+        to warn.  */
+      if (!lproto && objc_is_object_id (ltyp))
+       return true;
+    }
+  
   pointers_compatible = (TYPE_MAIN_VARIANT (ltyp) == TYPE_MAIN_VARIANT (rtyp));
 
   /* If the underlying types are the same, and at most one of them has
@@ -2236,13 +2262,22 @@ objc_compare_types (tree ltyp, tree rtyp, int argno, tree callee)
   else
     {
       if (!pointers_compatible)
-       pointers_compatible
-         = (objc_is_object_id (ltyp) || objc_is_object_id (rtyp));
+       {
+         /* Again, if any of the two is an 'id', we're satisfied,
+            unless we're comparing properties, in which case only an
+            'id' on the left-hand side (old property) is good
+            enough.  */
+         if (argno != -5)
+           pointers_compatible
+             = (objc_is_object_id (ltyp) || objc_is_object_id (rtyp));
+         else
+           pointers_compatible = objc_is_object_id (ltyp);         
+       }
 
       if (!pointers_compatible)
        pointers_compatible = DERIVED_FROM_P (ltyp, rtyp);
 
-      if (!pointers_compatible && argno <= -3)
+      if (!pointers_compatible && (argno == -3 || argno == -4))
        pointers_compatible = DERIVED_FROM_P (rtyp, ltyp);
     }
 
@@ -2268,6 +2303,7 @@ objc_compare_types (tree ltyp, tree rtyp, int argno, tree callee)
         ObjC-specific.  */
       switch (argno)
        {
+       case -5:
        case -4:
          return false;
 
@@ -9797,19 +9833,32 @@ objc_add_synthesize_declaration_for_property (location_t location, tree interfac
   if (ivar_name == NULL_TREE)
     ivar_name = property_name;
 
-  /* Check that the instance variable exists.  You can only use a
-     non-private instance variable from the same class, not one from
-     the superclass (this makes sense as it allows us to check that an
+  /* Check that the instance variable exists.  You can only use an
+     instance variable from the same class, not one from the
+     superclass (this makes sense as it allows us to check that an
      instance variable is only used in one synthesized property).  */
-  if (!is_ivar (CLASS_IVARS (interface), ivar_name))
-    {
-      error_at (location, "ivar %qs used by %<@synthesize%> declaration must be an existing ivar", 
-               IDENTIFIER_POINTER (property_name));
-      return;
-    }
+  {
+    tree ivar = is_ivar (CLASS_IVARS (interface), ivar_name);
+    if (!ivar)
+      {
+       error_at (location, "ivar %qs used by %<@synthesize%> declaration must be an existing ivar", 
+                 IDENTIFIER_POINTER (property_name));
+       return;
+      }
 
-  /* TODO: Check that the types of the instance variable and of the
-     property match.  */
+    /* If the instance variable has a different C type, we warn.  */
+    if (!comptypes (TREE_TYPE (property), TREE_TYPE (ivar)))
+      {
+       location_t original_location = DECL_SOURCE_LOCATION (ivar);
+       
+       error_at (location, "property %qs is using instance variable %qs of incompatible type",
+                 IDENTIFIER_POINTER (property_name),
+                 IDENTIFIER_POINTER (ivar_name));
+       
+       if (original_location != UNKNOWN_LOCATION)
+         inform (original_location, "originally specified here");
+      }
+  }
 
   /* Check that no other property is using the same instance
      variable.  */
index 9aedbfe..3156601 100644 (file)
@@ -1,3 +1,10 @@
+2010-11-11  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       * objc.dg/property/at-property-20.m: New.
+       * objc.dg/property/synthesize-8.m: New. 
+       * obj-c++.dg/property/at-property-20.m: New.
+       * obj-c++.dg/property/synthesize-8.mm: New.
+       
 2010-11-11  Joseph Myers  <joseph@codesourcery.com>
 
        * gcc.dg/cpp/warn-normalized-3.c: Update expected note text.
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-20.mm b/gcc/testsuite/obj-c++.dg/property/at-property-20.mm
new file mode 100644 (file)
index 0000000..0fae36b
--- /dev/null
@@ -0,0 +1,82 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+/* Test that if you have a property declared in a class and a
+   sub-class, the types match (unless it's a readonly property, in
+   which case a "specialization" is enough).  */
+
+@protocol MyProtocolA
+- (void) doNothingA;
+@end
+
+@protocol MyProtocolB
+- (void) doNothingB;
+@end
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@interface MySubClass1 : MyRootClass
+@end
+
+@interface MySubClass2 : MyRootClass
+@end
+
+@interface MySubClass3 : MyRootClass <MyProtocolA>
+@end
+
+@interface MySubClass4 : MySubClass1
+@end
+
+/* Now, the test.  */
+
+@interface MyClass : MyRootClass
+{ }
+@property (assign) id <MyProtocolA> a;        /* { dg-warning "originally specified here" } */
+@property int b;                              /* { dg-warning "originally specified here" } */
+@property float c;                            /* { dg-warning "originally specified here" } */
+@property (assign) MyRootClass *d;            /* { dg-warning "originally specified here" } */
+@property (assign) MySubClass1 *e;            /* { dg-warning "originally specified here" } */
+/* FIXME: The compiler seems to generate messages correctly, but the testsuite still fails the test.  */
+/*@property (assign, readonly) MySubClass1 *f; */ /*  dg-warning "originally specified here"  */
+@property (assign) MySubClass3 *g;            /* { dg-warning "originally specified here" } */
+/*@property (assign, readonly) MySubClass3 *h; */ /*  dg-warning "originally specified here"  */
+@end
+
+/* The following are all OK because they are identical.  */
+@interface MyClass2 : MyClass
+{ }
+@property (assign) id a;
+@property int b;
+@property float c;
+@property (assign) MyRootClass *d;
+@property (assign) MySubClass1 *e;
+@property (assign, readonly) MySubClass1 *f;
+@property (assign) MySubClass3 *g;
+@property (assign, readonly) MySubClass3 *h;
+@end
+
+/* The following are not OK.  */
+@interface MyClass3 : MyClass
+{ }
+@property (assign) MySubClass1 *a;            /* { dg-warning "type of property .a. conflicts with previous declaration" } */
+@property float b;                            /* { dg-warning "type of property .b. conflicts with previous declaration" } */
+@property int c;                              /* { dg-warning "type of property .c. conflicts with previous declaration" } */
+@property (assign) id d;                      /* { dg-warning "type of property .d. conflicts with previous declaration" } */
+@property (assign) MyRootClass *e;            /* { dg-warning "type of property .e. conflicts with previous declaration" } */
+/*@property (assign, readonly) MyRootClass *f; */ /*  dg-warning "type of property .f. conflicts with previous declaration"  */
+@property (assign) MySubClass2 *g;            /* { dg-warning "type of property .g. conflicts with previous declaration" } */
+/*@property (assign, readonly) MySubClass2 *h; */ /*  dg-warning "type of property .h. conflicts with previous declaration"  */
+@end
+
+/* The following are OK.  */
+@interface MyClass4 : MyClass
+{ }
+@property (assign, readonly) MySubClass4 *f;
+@property (assign, readonly) MySubClass3 <MyProtocolB> *h;
+@end
diff --git a/gcc/testsuite/obj-c++.dg/property/synthesize-8.mm b/gcc/testsuite/obj-c++.dg/property/synthesize-8.mm
new file mode 100644 (file)
index 0000000..071f6f2
--- /dev/null
@@ -0,0 +1,80 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test that when using @synthesize the instance variable and the
+   property have exactly the same type.  */
+
+#include <objc/objc.h>
+
+@protocol MyProtocol
+- (void)aMethod;
+@end
+
+@interface ClassA
+@end
+
+@interface ClassB : ClassA
+@end
+
+
+/* This is all OK.  */
+@interface Test
+{
+  int v;
+  float w;
+  id x;
+  Test *y;
+  id <MyProtocol> *z;
+  ClassA *a;
+  ClassB *b;
+  ClassA <MyProtocol> *c;
+}
+@property (assign) int v;
+@property (assign) float w;
+@property (assign) id x;
+@property (assign) Test *y;
+@property (assign) id <MyProtocol> *z;
+@property (assign) ClassA *a;
+@property (assign) ClassB *b;
+@end
+
+@implementation Test
+@synthesize v;
+@synthesize w;
+@synthesize x;
+@synthesize y;
+@synthesize z;
+@synthesize a;
+@synthesize b;
+@end
+
+
+/* This is not OK.  */
+@interface Test2
+{
+  int v;                   /* { dg-warning "originally specified here" } */
+  float w;                 /* { dg-warning "originally specified here" } */
+  id x;                    /* { dg-warning "originally specified here" } */
+  Test *y;                 /* { dg-warning "originally specified here" } */
+  id <MyProtocol> *z;      /* { dg-warning "originally specified here" } */
+  ClassA *a;               /* { dg-warning "originally specified here" } */
+  ClassB *b;               /* { dg-warning "originally specified here" } */
+}
+@property (assign) float v;
+@property (assign) id w;
+@property (assign) int x;
+@property (assign) id y;
+@property (assign) Test *z;
+@property (assign) ClassB *a;
+@property (assign) ClassA *b;
+@end
+
+@implementation Test2
+@synthesize v; /* { dg-error "property .v. is using instance variable .v. of incompatible type" } */
+@synthesize w; /* { dg-error "property .w. is using instance variable .w. of incompatible type" } */
+@synthesize x; /* { dg-error "property .x. is using instance variable .x. of incompatible type" } */
+@synthesize y; /* { dg-error "property .y. is using instance variable .y. of incompatible type" } */
+@synthesize z; /* { dg-error "property .z. is using instance variable .z. of incompatible type" } */
+@synthesize a; /* { dg-error "property .a. is using instance variable .a. of incompatible type" } */
+@synthesize b; /* { dg-error "property .b. is using instance variable .b. of incompatible type" } */
+@end
diff --git a/gcc/testsuite/objc.dg/property/at-property-20.m b/gcc/testsuite/objc.dg/property/at-property-20.m
new file mode 100644 (file)
index 0000000..1bb49da
--- /dev/null
@@ -0,0 +1,81 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+/* Test that if you have a property declared in a class and a
+   sub-class, the types match (unless it's a readonly property, in
+   which case a "specialization" is enough).  */
+
+@protocol MyProtocolA
+- (void) doNothingA;
+@end
+
+@protocol MyProtocolB
+- (void) doNothingB;
+@end
+
+@interface MyRootClass
+{
+  Class isa;
+}
+@end
+
+@interface MySubClass1 : MyRootClass
+@end
+
+@interface MySubClass2 : MyRootClass
+@end
+
+@interface MySubClass3 : MyRootClass <MyProtocolA>
+@end
+
+@interface MySubClass4 : MySubClass1
+@end
+
+/* Now, the test.  */
+
+@interface MyClass : MyRootClass
+{ }
+@property (assign) id <MyProtocolA> a;        /* { dg-message "originally specified here" } */
+@property int b;                              /* { dg-message "originally specified here" } */
+@property float c;                            /* { dg-message "originally specified here" } */
+@property (assign) MyRootClass *d;            /* { dg-message "originally specified here" } */
+@property (assign) MySubClass1 *e;            /* { dg-message "originally specified here" } */
+@property (assign, readonly) MySubClass1 *f;  /* { dg-message "originally specified here" } */
+@property (assign) MySubClass3 *g;            /* { dg-message "originally specified here" } */
+@property (assign, readonly) MySubClass3 *h;  /* { dg-message "originally specified here" } */
+@end
+
+/* The following are all OK because they are identical.  */
+@interface MyClass2 : MyClass
+{ }
+@property (assign) id a;
+@property int b;
+@property float c;
+@property (assign) MyRootClass *d;
+@property (assign) MySubClass1 *e;
+@property (assign, readonly) MySubClass1 *f;
+@property (assign) MySubClass3 *g;
+@property (assign, readonly) MySubClass3 *h;
+@end
+
+/* The following are not OK.  */
+@interface MyClass3 : MyClass
+{ }
+@property (assign) MySubClass1 *a;            /* { dg-warning "type of property .a. conflicts with previous declaration" } */
+@property float b;                            /* { dg-warning "type of property .b. conflicts with previous declaration" } */
+@property int c;                              /* { dg-warning "type of property .c. conflicts with previous declaration" } */
+@property (assign) id d;                      /* { dg-warning "type of property .d. conflicts with previous declaration" } */
+@property (assign) MyRootClass *e;            /* { dg-warning "type of property .e. conflicts with previous declaration" } */
+@property (assign, readonly) MyRootClass *f;  /* { dg-warning "type of property .f. conflicts with previous declaration" } */
+@property (assign) MySubClass2 *g;            /* { dg-warning "type of property .g. conflicts with previous declaration" } */
+@property (assign, readonly) MySubClass2 *h;  /* { dg-warning "type of property .h. conflicts with previous declaration" } */
+@end
+
+/* The following are OK.  */
+@interface MyClass4 : MyClass
+{ }
+@property (assign, readonly) MySubClass4 *f;
+@property (assign, readonly) MySubClass3 <MyProtocolB> *h;
+@end
diff --git a/gcc/testsuite/objc.dg/property/synthesize-8.m b/gcc/testsuite/objc.dg/property/synthesize-8.m
new file mode 100644 (file)
index 0000000..4af3ecc
--- /dev/null
@@ -0,0 +1,80 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do compile } */
+
+/* Test that when using @synthesize the instance variable and the
+   property have exactly the same type.  */
+
+#include <objc/objc.h>
+
+@protocol MyProtocol
+- (void)aMethod;
+@end
+
+@interface ClassA
+@end
+
+@interface ClassB : ClassA
+@end
+
+
+/* This is all OK.  */
+@interface Test
+{
+  int v;
+  float w;
+  id x;
+  Test *y;
+  id <MyProtocol> *z;
+  ClassA *a;
+  ClassB *b;
+  ClassA <MyProtocol> *c;
+}
+@property (assign) int v;
+@property (assign) float w;
+@property (assign) id x;
+@property (assign) Test *y;
+@property (assign) id <MyProtocol> *z;
+@property (assign) ClassA *a;
+@property (assign) ClassB *b;
+@end
+
+@implementation Test
+@synthesize v;
+@synthesize w;
+@synthesize x;
+@synthesize y;
+@synthesize z;
+@synthesize a;
+@synthesize b;
+@end
+
+
+/* This is not OK.  */
+@interface Test2
+{
+  int v;                   /* { dg-message "originally specified here" } */
+  float w;                 /* { dg-message "originally specified here" } */
+  id x;                    /* { dg-message "originally specified here" } */
+  Test *y;                 /* { dg-message "originally specified here" } */
+  id <MyProtocol> *z;      /* { dg-message "originally specified here" } */
+  ClassA *a;               /* { dg-message "originally specified here" } */
+  ClassB *b;               /* { dg-message "originally specified here" } */
+}
+@property (assign) float v;
+@property (assign) id w;
+@property (assign) int x;
+@property (assign) id y;
+@property (assign) Test *z;
+@property (assign) ClassB *a;
+@property (assign) ClassA *b;
+@end
+
+@implementation Test2
+@synthesize v; /* { dg-error "property .v. is using instance variable .v. of incompatible type" } */
+@synthesize w; /* { dg-error "property .w. is using instance variable .w. of incompatible type" } */
+@synthesize x; /* { dg-error "property .x. is using instance variable .x. of incompatible type" } */
+@synthesize y; /* { dg-error "property .y. is using instance variable .y. of incompatible type" } */
+@synthesize z; /* { dg-error "property .z. is using instance variable .z. of incompatible type" } */
+@synthesize a; /* { dg-error "property .a. is using instance variable .a. of incompatible type" } */
+@synthesize b; /* { dg-error "property .b. is using instance variable .b. of incompatible type" } */
+@end