===================================================================
@@ -1184,23 +1184,42 @@ objc_add_property_declaration (location_t location
return;
}
- if (property_readonly)
+ /* 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)))
{
- /* 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. */
- ;
+ 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;
}
- else
- {
- /* Else, the types must match exactly. */
-
- /* TODO: Check that property types are identical. */
- ;
- }
}
/* Create a PROPERTY_DECL node. */
@@ -1445,15 +1464,10 @@ objc_maybe_build_component_ref (tree object, tree
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
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
}
}
- /* 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 argn
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 argn
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 argn
ObjC-specific. */
switch (argno)
{
+ case -5:
case -4:
return false;
@@ -9797,19 +9833,32 @@ objc_add_synthesize_declaration_for_property (loca
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. */
===================================================================
@@ -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 (dump_base_name): Don't declare here.
===================================================================
@@ -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-10 Quentin Neill <quentin.neill.gnu@gmail.com>
* g++.dg/other/i386-2.C: Add -mtbm.
===================================================================
@@ -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
===================================================================
@@ -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
===================================================================
@@ -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
===================================================================
@@ -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