From ad312852d3e53a608dcedac520a4aa9adca20792 Mon Sep 17 00:00:00 2001 From: nicola Date: Thu, 11 Nov 2010 18:58:43 +0000 Subject: [PATCH] In gcc/objc/: 2010-11-11 Nicola Pero * 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 * 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 | 11 ++ gcc/objc/objc-act.c | 147 ++++++++++++++------- gcc/testsuite/ChangeLog | 7 + .../obj-c++.dg/property/at-property-20.mm | 82 ++++++++++++ gcc/testsuite/obj-c++.dg/property/synthesize-8.mm | 80 +++++++++++ gcc/testsuite/objc.dg/property/at-property-20.m | 81 ++++++++++++ gcc/testsuite/objc.dg/property/synthesize-8.m | 80 +++++++++++ 7 files changed, 439 insertions(+), 49 deletions(-) create mode 100644 gcc/testsuite/obj-c++.dg/property/at-property-20.mm create mode 100644 gcc/testsuite/obj-c++.dg/property/synthesize-8.mm create mode 100644 gcc/testsuite/objc.dg/property/at-property-20.m create mode 100644 gcc/testsuite/objc.dg/property/synthesize-8.m diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index d60e2fb..b34b619 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,14 @@ +2010-11-11 Nicola Pero + + * 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 * objc-act.c (objc_init): Use %' in diagnostic. diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index 6f499c3..715623f 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -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. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9aedbfe..3156601 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2010-11-11 Nicola Pero + + * 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 * 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 index 0000000..0fae36b --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/at-property-20.mm @@ -0,0 +1,82 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +#include + +/* 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 +@end + +@interface MySubClass4 : MySubClass1 +@end + +/* Now, the test. */ + +@interface MyClass : MyRootClass +{ } +@property (assign) id 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 *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 index 0000000..071f6f2 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/synthesize-8.mm @@ -0,0 +1,80 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that when using @synthesize the instance variable and the + property have exactly the same type. */ + +#include + +@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 *z; + ClassA *a; + ClassB *b; + ClassA *c; +} +@property (assign) int v; +@property (assign) float w; +@property (assign) id x; +@property (assign) Test *y; +@property (assign) id *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 *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 index 0000000..1bb49da --- /dev/null +++ b/gcc/testsuite/objc.dg/property/at-property-20.m @@ -0,0 +1,81 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +#include + +/* 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 +@end + +@interface MySubClass4 : MySubClass1 +@end + +/* Now, the test. */ + +@interface MyClass : MyRootClass +{ } +@property (assign) id 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 *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 index 0000000..4af3ecc --- /dev/null +++ b/gcc/testsuite/objc.dg/property/synthesize-8.m @@ -0,0 +1,80 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that when using @synthesize the instance variable and the + property have exactly the same type. */ + +#include + +@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 *z; + ClassA *a; + ClassB *b; + ClassA *c; +} +@property (assign) int v; +@property (assign) float w; +@property (assign) id x; +@property (assign) Test *y; +@property (assign) id *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 *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 -- 2.7.4