diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index 93c9f3e544a..6ba9514af10 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,21 @@ +2010-11-08 Nicola Pero + + * 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. + Search for an inherited @property declaration if none is found in + the local interface. If the required instance variable does not + exist, return instead of trying to continue to prevent a compiler + crash later. Check that the instance variable is not already + being used by another @synthesize. + (objc_add_dynamic_declaration_for_property): Iterate over + IMPL_PROPERTY_DECL, not CLASS_PROPERTY_DECL, when checking for an + existing @synthesize or @dynamic declaration. + (objc_synthesize_getter): Search for the getter declaration in + protocols and superclasses as well. + (objc_synthesize_setter): Search for the setter declaration in + protocols and superclasses as well. + 2010-11-08 Nicola Pero * objc-act.c (lookup_property): When checking categories, also diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index db41dcab0ac..ff694147ce2 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -9363,7 +9363,7 @@ lookup_ivar (tree interface, tree instance_variable_name) /* This routine synthesizes a 'getter' method. This is only called for @synthesize properties. */ static void -objc_synthesize_getter (tree klass, tree class_method, tree property) +objc_synthesize_getter (tree klass, tree class_methods ATTRIBUTE_UNUSED, tree property) { location_t location = DECL_SOURCE_LOCATION (property); tree fn, decl; @@ -9375,9 +9375,9 @@ objc_synthesize_getter (tree klass, tree class_method, tree property) PROPERTY_GETTER_NAME (property))) return; - /* Find declaration of the property getter in the interface. There - must be one. TODO: Search superclasses as well. */ - decl = lookup_method (CLASS_NST_METHODS (class_method), PROPERTY_GETTER_NAME (property)); + /* Find declaration of the property getter in the interface (or + superclass, or protocol). There must be one. */ + decl = lookup_method_static (klass, PROPERTY_GETTER_NAME (property), 0); /* If one not declared in the interface, this condition has already been reported as user error (because property was not declared in @@ -9542,7 +9542,7 @@ objc_synthesize_getter (tree klass, tree class_method, tree property) /* This routine synthesizes a 'setter' method. */ static void -objc_synthesize_setter (tree klass ATTRIBUTE_UNUSED, tree class_method, tree property) +objc_synthesize_setter (tree klass, tree class_methods ATTRIBUTE_UNUSED, tree property) { location_t location = DECL_SOURCE_LOCATION (property); tree fn, decl; @@ -9554,9 +9554,9 @@ objc_synthesize_setter (tree klass ATTRIBUTE_UNUSED, tree class_method, tree pro PROPERTY_SETTER_NAME (property))) return; - /* Find declaration of the property setter in the interface. There - must be one. TODO: Search superclasses as well. */ - decl = lookup_method (CLASS_NST_METHODS (class_method), PROPERTY_SETTER_NAME (property)); + /* Find declaration of the property setter in the interface (or + superclass, or protocol). There must be one. */ + decl = lookup_method_static (klass, PROPERTY_SETTER_NAME (property), 0); /* If one not declared in the interface, this condition has already been reported as user error (because property was not declared in @@ -9736,10 +9736,11 @@ objc_add_synthesize_declaration_for_property (location_t location, tree interfac { /* Find the @property declaration. */ tree property; + tree x; /* Check that synthesize or dynamic has not already been used for the same property. */ - for (property = CLASS_PROPERTY_DECL (objc_implementation_context); property; property = TREE_CHAIN (property)) + for (property = IMPL_PROPERTY_DECL (objc_implementation_context); property; property = TREE_CHAIN (property)) if (PROPERTY_NAME (property) == property_name) { location_t original_location = DECL_SOURCE_LOCATION (property); @@ -9756,12 +9757,9 @@ objc_add_synthesize_declaration_for_property (location_t location, tree interfac return; } - /* Check that the property is declared in the interface. */ - /* TODO: This only check the immediate class; we need to check the - superclass (and categories ?) as well. */ - 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) { @@ -9783,18 +9781,37 @@ 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 an - instance variable from the same class, not one from the - superclass. */ + /* 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 + 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)); + { + 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. */ - /* TODO: Check that no other property is using the same instance + /* Check that no other property is using the same instance variable. */ + for (x = IMPL_PROPERTY_DECL (objc_implementation_context); x; x = TREE_CHAIN (x)) + if (PROPERTY_IVAR_NAME (x) == ivar_name) + { + location_t original_location = DECL_SOURCE_LOCATION (x); + + error_at (location, "property %qs is using the same instance variable as property %qs", + IDENTIFIER_POINTER (property_name), + IDENTIFIER_POINTER (PROPERTY_NAME (x))); + + if (original_location != UNKNOWN_LOCATION) + inform (original_location, "originally specified here"); + + /* We keep going on. This won't cause the compiler to fail; + the failure would most likely be at runtime. */ + } /* Note that a @synthesize (and only a @synthesize) always sets PROPERTY_IVAR_NAME to a non-NULL_TREE. You can recognize a @@ -9876,7 +9893,7 @@ objc_add_dynamic_declaration_for_property (location_t location, tree interface, /* Check that synthesize or dynamic has not already been used for the same property. */ - for (property = CLASS_PROPERTY_DECL (objc_implementation_context); property; property = TREE_CHAIN (property)) + for (property = IMPL_PROPERTY_DECL (objc_implementation_context); property; property = TREE_CHAIN (property)) if (PROPERTY_NAME (property) == property_name) { location_t original_location = DECL_SOURCE_LOCATION (property); @@ -9912,9 +9929,10 @@ objc_add_dynamic_declaration_for_property (location_t location, tree interface, 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: Clean this up; maybe the - @property PROPERTY_DECL should contain a reference to the - @dynamic PROPERTY_DECL ? */ + 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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 97b3f51ddd4..d4d274a04d4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2010-11-08 Nicola Pero + + * objc.dg/property/synthesize-3.m: New. + * objc.dg/property/synthesize-4.m: New. + * objc.dg/property/synthesize-5.m: New. + * objc.dg/property/synthesize-6.m: New. + * obj-c++.dg/property/synthesize-3.mm: New. + * obj-c++.dg/property/synthesize-4.mm: New. + * obj-c++.dg/property/synthesize-5.mm: New. + * obj-c++.dg/property/synthesize-6.mm: New. + 2010-11-08 Nicola Pero * objc.dg/property/at-property-5.m: Updated test. diff --git a/gcc/testsuite/obj-c++.dg/property/synthesize-3.mm b/gcc/testsuite/obj-c++.dg/property/synthesize-3.mm new file mode 100644 index 00000000000..8669905316a --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/synthesize-3.mm @@ -0,0 +1,66 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @synthesize for a @property which is not declared directly in + the @interface, but in a @protocol that the @interface conforms + to. */ + +#include +#include +#include + +@interface MyRootClass +{ + Class isa; +} ++ (id) initialize; ++ (id) alloc; +- (id) init; +@end + +@implementation MyRootClass ++ (id) initialize { return self; } ++ (id) alloc { return class_createInstance (self, 0); } +- (id) init { return self; } +@end + +@protocol MyProtocol +@property int v1; +@end + +@protocol MyProtocol2 +@property int v2; +@end + +@interface Test : MyRootClass +{ + int v1; + int _v2; +} +@end + +@implementation Test +@synthesize v1; +@synthesize v2 = _v2; +@end + +int main (void) +{ + Test *object = [[Test alloc] init]; + + /* Check that the synthesized methods exist and work. Do not invoke + them via property syntax - that is another test. Here we just + want to test the synthesis of the methods. */ + [object setV1: 400]; + + if ([object v1] != 400) + abort (); + + [object setV2: 31]; + + if ([object v2] != 31) + abort (); + + return 0; +} diff --git a/gcc/testsuite/obj-c++.dg/property/synthesize-4.mm b/gcc/testsuite/obj-c++.dg/property/synthesize-4.mm new file mode 100644 index 00000000000..602dc68b468 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/synthesize-4.mm @@ -0,0 +1,67 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @synthesize for a @property where the setter/getter are also + declared by the user. This is fine. */ + +#include +#include +#include + +@interface MyRootClass +{ + Class isa; +} ++ (id) initialize; ++ (id) alloc; +- (id) init; +@end + +@implementation MyRootClass ++ (id) initialize { return self; } ++ (id) alloc { return class_createInstance (self, 0); } +- (id) init { return self; } +@end + +@protocol MyProtocol +@property int v1; +@end + +@protocol MyProtocol2 +@property int v2; +@end + +@interface Test : MyRootClass +{ + int v1; + int _v2; +} +- (int)v1; +- (void)setV1: (int)aNumber; +- (int)v2; +@end + +@implementation Test +@synthesize v1; +@synthesize v2 = _v2; +@end + +int main (void) +{ + Test *object = [[Test alloc] init]; + + /* We use dot-syntax here as this is just a general test that + user-declared setters/getters don't cause confusion. */ + object.v1 = 400; + + if (object.v1 != 400) + abort (); + + object.v2 = 31; + + if (object.v2 != 31) + abort (); + + return 0; +} diff --git a/gcc/testsuite/obj-c++.dg/property/synthesize-5.mm b/gcc/testsuite/obj-c++.dg/property/synthesize-5.mm new file mode 100644 index 00000000000..0871b63ee21 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/synthesize-5.mm @@ -0,0 +1,18 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that @synthesize does not ICE if asked to use a non-existing + ivar. */ + +#include + +@interface Test +@property int v1; +@end + +@implementation Test +@synthesize v1; /* { dg-error "must be an existing ivar" } */ +@end +/* { dg-warning "incomplete implementation" "" { target *-*-* } 15 } */ +/* { dg-warning "method definition for .-setV1:. not found" "" { target *-*-* } 15 } */ +/* { dg-warning "method definition for .-v1. not found" "" { target *-*-* } 15 } */ diff --git a/gcc/testsuite/obj-c++.dg/property/synthesize-6.mm b/gcc/testsuite/obj-c++.dg/property/synthesize-6.mm new file mode 100644 index 00000000000..2d039279430 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/synthesize-6.mm @@ -0,0 +1,32 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that each @synthesize is using a different instance variable, + and that it must belong to the class (not to a superclass). */ + +#include + +@interface Test +{ + int v; + int w; +} +@property int v1; +@property int v2; +@end +#if 0 /* This is a problem in the testsuite; the compiler is fine, but the testsuite still barfs on the following. */ +@implementation Test +@synthesize v1 = v; /* dg-message "originally specified here" */ +@synthesize v2 = v; /* dg-error "property .v2. is using the same instance variable as property .v1." */ +@end +#endif +@interface Test2 : Test +@property int w1; +@end + +@implementation Test2 +@synthesize w1; /* { dg-error "ivar .w1. used by .@synthesize. declaration must be an existing ivar" } */ +@end +/* { dg-warning "incomplete implementation" "" { target *-*-* } 29 } */ +/* { dg-warning "method definition for .-setW1:. not found" "" { target *-*-* } 29 } */ +/* { dg-warning "method definition for .-w1. not found" "" { target *-*-* } 29 } */ diff --git a/gcc/testsuite/objc.dg/property/synthesize-3.m b/gcc/testsuite/objc.dg/property/synthesize-3.m new file mode 100644 index 00000000000..8669905316a --- /dev/null +++ b/gcc/testsuite/objc.dg/property/synthesize-3.m @@ -0,0 +1,66 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @synthesize for a @property which is not declared directly in + the @interface, but in a @protocol that the @interface conforms + to. */ + +#include +#include +#include + +@interface MyRootClass +{ + Class isa; +} ++ (id) initialize; ++ (id) alloc; +- (id) init; +@end + +@implementation MyRootClass ++ (id) initialize { return self; } ++ (id) alloc { return class_createInstance (self, 0); } +- (id) init { return self; } +@end + +@protocol MyProtocol +@property int v1; +@end + +@protocol MyProtocol2 +@property int v2; +@end + +@interface Test : MyRootClass +{ + int v1; + int _v2; +} +@end + +@implementation Test +@synthesize v1; +@synthesize v2 = _v2; +@end + +int main (void) +{ + Test *object = [[Test alloc] init]; + + /* Check that the synthesized methods exist and work. Do not invoke + them via property syntax - that is another test. Here we just + want to test the synthesis of the methods. */ + [object setV1: 400]; + + if ([object v1] != 400) + abort (); + + [object setV2: 31]; + + if ([object v2] != 31) + abort (); + + return 0; +} diff --git a/gcc/testsuite/objc.dg/property/synthesize-4.m b/gcc/testsuite/objc.dg/property/synthesize-4.m new file mode 100644 index 00000000000..602dc68b468 --- /dev/null +++ b/gcc/testsuite/objc.dg/property/synthesize-4.m @@ -0,0 +1,67 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @synthesize for a @property where the setter/getter are also + declared by the user. This is fine. */ + +#include +#include +#include + +@interface MyRootClass +{ + Class isa; +} ++ (id) initialize; ++ (id) alloc; +- (id) init; +@end + +@implementation MyRootClass ++ (id) initialize { return self; } ++ (id) alloc { return class_createInstance (self, 0); } +- (id) init { return self; } +@end + +@protocol MyProtocol +@property int v1; +@end + +@protocol MyProtocol2 +@property int v2; +@end + +@interface Test : MyRootClass +{ + int v1; + int _v2; +} +- (int)v1; +- (void)setV1: (int)aNumber; +- (int)v2; +@end + +@implementation Test +@synthesize v1; +@synthesize v2 = _v2; +@end + +int main (void) +{ + Test *object = [[Test alloc] init]; + + /* We use dot-syntax here as this is just a general test that + user-declared setters/getters don't cause confusion. */ + object.v1 = 400; + + if (object.v1 != 400) + abort (); + + object.v2 = 31; + + if (object.v2 != 31) + abort (); + + return 0; +} diff --git a/gcc/testsuite/objc.dg/property/synthesize-5.m b/gcc/testsuite/objc.dg/property/synthesize-5.m new file mode 100644 index 00000000000..0871b63ee21 --- /dev/null +++ b/gcc/testsuite/objc.dg/property/synthesize-5.m @@ -0,0 +1,18 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that @synthesize does not ICE if asked to use a non-existing + ivar. */ + +#include + +@interface Test +@property int v1; +@end + +@implementation Test +@synthesize v1; /* { dg-error "must be an existing ivar" } */ +@end +/* { dg-warning "incomplete implementation" "" { target *-*-* } 15 } */ +/* { dg-warning "method definition for .-setV1:. not found" "" { target *-*-* } 15 } */ +/* { dg-warning "method definition for .-v1. not found" "" { target *-*-* } 15 } */ diff --git a/gcc/testsuite/objc.dg/property/synthesize-6.m b/gcc/testsuite/objc.dg/property/synthesize-6.m new file mode 100644 index 00000000000..19f7c953491 --- /dev/null +++ b/gcc/testsuite/objc.dg/property/synthesize-6.m @@ -0,0 +1,32 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test that each @synthesize is using a different instance variable, + and that it must belong to the class (not to a superclass). */ + +#include + +@interface Test +{ + int v; + int w; +} +@property int v1; +@property int v2; +@end + +@implementation Test +@synthesize v1 = v; /* { dg-message "originally specified here" } */ +@synthesize v2 = v; /* { dg-error "property .v2. is using the same instance variable as property .v1." } */ +@end + +@interface Test2 : Test +@property int w1; +@end + +@implementation Test2 +@synthesize w1; /* { dg-error "ivar .w1. used by .@synthesize. declaration must be an existing ivar" } */ +@end +/* { dg-warning "incomplete implementation" "" { target *-*-* } 29 } */ +/* { dg-warning "method definition for .-setW1:. not found" "" { target *-*-* } 29 } */ +/* { dg-warning "method definition for .-w1. not found" "" { target *-*-* } 29 } */