diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index 7c890ce7e2e3..1ebff02f1cac 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,3 +1,20 @@ +2010-11-27 Nicola Pero + + Implemented optional properties. + * objc-act.h (PROPERTY_OPTIONAL): New. + * objc-act.c (objc_add_property_declaration): Set + PROPERTY_OPTIONAL if appropriate. + (finish_class): When generating definitions of setter and getter + methods associated with a property for a protocol, mark them as + optional if the property is optional. + (maybe_make_artificial_property_decl): Added 'getter_name' + argument. Set PROPERTY_OPTIONAL. + (objc_maybe_build_component_ref): Updated calls to + maybe_make_artificial_property_decl. Added code for optional, + readonly properties. + (objc_build_class_component_ref): Updated call to + maybe_make_artificial_property_decl. + 2010-11-27 Nicola Pero * objc-act.c (objc_build_struct): Fixed loops that save and diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index 232708f6dbbc..70056d394306 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -1181,7 +1181,7 @@ objc_add_property_declaration (location_t location, tree decl, /* An existing property was found; check that it has the same types, or it is compatible. */ location_t original_location = DECL_SOURCE_LOCATION (x); - + if (PROPERTY_NONATOMIC (x) != parsed_property_nonatomic) { warning_at (location, 0, @@ -1293,6 +1293,13 @@ objc_add_property_declaration (location_t location, tree decl, PROPERTY_IVAR_NAME (property_decl) = NULL_TREE; PROPERTY_DYNAMIC (property_decl) = 0; + /* Remember the fact that the property was found in the @optional + section in a @protocol, or not. */ + if (objc_method_optional_flag) + PROPERTY_OPTIONAL (property_decl) = 1; + else + PROPERTY_OPTIONAL (property_decl) = 0; + /* Note that PROPERTY_GETTER_NAME is always set for all PROPERTY_DECLs, and PROPERTY_SETTER_NAME is always set for all PROPERTY_DECLs where PROPERTY_READONLY == 0. Any time we deal @@ -1310,18 +1317,22 @@ objc_add_property_declaration (location_t location, tree decl, in the implementation, and failing that, the 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. */ + "setName:"). It is also possible to specify a different + 'getter_name' (this is used for @optional readonly properties). 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 implementation, - tree protocol_list, tree component, bool is_class) + tree protocol_list, tree component, bool is_class, + tree getter_name) { - tree getter_name = component; tree setter_name = get_identifier (objc_build_property_setter_name (component)); tree getter = NULL_TREE; tree setter = NULL_TREE; + if (getter_name == NULL_TREE) + getter_name = component; + /* First, check the @interface and all superclasses. */ if (interface) { @@ -1401,6 +1412,7 @@ maybe_make_artificial_property_decl (tree interface, tree implementation, PROPERTY_ASSIGN_SEMANTICS (property_decl) = 0; PROPERTY_IVAR_NAME (property_decl) = NULL_TREE; PROPERTY_DYNAMIC (property_decl) = 0; + PROPERTY_OPTIONAL (property_decl) = 0; if (!getter) PROPERTY_HAS_NO_GETTER (property_decl) = 1; @@ -1481,7 +1493,7 @@ objc_maybe_build_component_ref (tree object, tree property_ident) properties. */ if (!IS_CLASS (rtype)) x = lookup_property_in_protocol_list (rprotos, property_ident); - + if (x == NULL_TREE) { /* Ok, no property. Maybe it was an @@ -1493,7 +1505,25 @@ objc_maybe_build_component_ref (tree object, tree property_ident) NULL_TREE, rprotos, property_ident, - IS_CLASS (rtype)); + IS_CLASS (rtype), + NULL_TREE); + } + else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x)) + { + /* This is a special, complicated case. If the + property is optional, and is read-only, then the + property is always used for reading, but an + eventual existing non-property setter can be used + for writing. We create an artificial property + decl copying the getter from the optional + property, and looking up the setter in the + interface. */ + x = maybe_make_artificial_property_decl (NULL_TREE, + NULL_TREE, + rprotos, + property_ident, + false, + PROPERTY_GETTER_NAME (x)); } } } @@ -1538,7 +1568,22 @@ objc_maybe_build_component_ref (tree object, tree property_ident) x = maybe_make_artificial_property_decl (interface_type, implementation, NULL_TREE, property_ident, - (TREE_CODE (objc_method_context) == CLASS_METHOD_DECL)); + (TREE_CODE (objc_method_context) == CLASS_METHOD_DECL), + NULL_TREE); + } + else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x)) + { + tree implementation = NULL_TREE; + + if (t == self_decl) + implementation = objc_implementation_context; + + x = maybe_make_artificial_property_decl (interface_type, + implementation, + NULL_TREE, + property_ident, + false, + PROPERTY_GETTER_NAME (x)); } } } @@ -1603,8 +1648,25 @@ objc_maybe_build_component_ref (tree object, tree property_ident) implementation, protocol_list, property_ident, - IS_CLASS (rtype)); + IS_CLASS (rtype), + NULL_TREE); } + else if (PROPERTY_OPTIONAL (x) && PROPERTY_READONLY (x)) + { + tree implementation = NULL_TREE; + + if (objc_implementation_context + && CLASS_NAME (objc_implementation_context) + == OBJC_TYPE_NAME (interface_type)) + implementation = objc_implementation_context; + + x = maybe_make_artificial_property_decl (interface_type, + implementation, + protocol_list, + property_ident, + false, + PROPERTY_GETTER_NAME (x)); + } } } } @@ -1703,7 +1765,7 @@ objc_build_class_component_ref (tree class_name, tree property_ident) x = maybe_make_artificial_property_decl (rtype, NULL_TREE, NULL_TREE, property_ident, - true); + true, NULL_TREE); if (x) { @@ -10534,7 +10596,10 @@ finish_class (tree klass) getter_decl = build_method_decl (INSTANCE_METHOD_DECL, rettype, PROPERTY_GETTER_NAME (x), NULL_TREE, false); - objc_add_method (objc_interface_context, getter_decl, false, false); + if (PROPERTY_OPTIONAL (x)) + objc_add_method (objc_interface_context, getter_decl, false, true); + else + objc_add_method (objc_interface_context, getter_decl, false, false); METHOD_PROPERTY_CONTEXT (getter_decl) = x; } @@ -10574,7 +10639,10 @@ finish_class (tree klass) ret_type, selector, build_tree_list (NULL_TREE, NULL_TREE), false); - objc_add_method (objc_interface_context, setter_decl, false, false); + if (PROPERTY_OPTIONAL (x)) + objc_add_method (objc_interface_context, setter_decl, false, true); + else + objc_add_method (objc_interface_context, setter_decl, false, false); METHOD_PROPERTY_CONTEXT (setter_decl) = x; } } diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h index f612e745d046..6403161da7be 100644 --- a/gcc/objc/objc-act.h +++ b/gcc/objc/objc-act.h @@ -113,6 +113,11 @@ typedef enum objc_property_assign_semantics { setter, it is set to 1. */ #define PROPERTY_HAS_NO_SETTER(DECL) DECL_LANG_FLAG_4 (DECL) +/* PROPERTY_OPTIONAL can be 0 or 1. Normally it is 0, but if this is + a property declared as @optional in a @protocol, then it is set to + 1. */ +#define PROPERTY_OPTIONAL(DECL) DECL_LANG_FLAG_5 (DECL) + /* PROPERTY_REF. A PROPERTY_REF represents an 'object.property' expression. It is normally used for property access, but when the Objective-C 2.0 "dot-syntax" (object.component) is used diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d0b568a735e0..7b6f1306e9b3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2010-11-27 Nicola Pero + + * objc.dg/property/at-property-24.m: New. + * objc.dg/property/at-property-25.m: New. + * obj-c++.dg/property/at-property-24.mm: New. + * obj-c++.dg/property/at-property-25.mm: New. + 2010-11-27 Nicola Pero * objc.dg/protocol-qualifier-1.m: New. diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-24.mm b/gcc/testsuite/obj-c++.dg/property/at-property-24.mm new file mode 100644 index 000000000000..b4a7699f6e40 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/at-property-24.mm @@ -0,0 +1,118 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @optional @properties. */ + +#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 + +/* Use a different getters/setters, so that the only way to compile + object.countX is to find the actual @property. */ +@protocol count +@required +/* @required + @synthesize. */ +@property (getter=number1, setter=setNumber1:) int count1; +/* @required + manual setters/getters. */ +@property (getter=number2, setter=setNumber2:) int count2; + +@optional +/* @optional + @synthesize. */ +@property (getter=number3, setter=setNumber3:) int count3; +/* @optional + manual setters/getters. */ +@property (getter=number4, setter=setNumber4:) int count4; + +@optional +/* @optional + readonly, with a setter added in the class itself. */ +@property (readonly, getter=number5) int count5; +@end + +@interface MySubClass : MyRootClass +{ + int count1; + int count2; + int count3; + int count4; + int count5; +} +- (void) setCount5: (int)value; +@end + +@implementation MySubClass +@synthesize count1; +- (int) number2 +{ + return count2; +} +- (void) setNumber2: (int)value +{ + count2 = value; +} +@synthesize count3; +- (int) number4 +{ + return count4; +} +- (void) setNumber4: (int)value +{ + count4 = value; +} +- (int) number5 +{ + return count5; +} +- (void) setCount5: (int)value +{ + count5 = value; +} +@end + +int main (void) +{ + MySubClass *object = [[MySubClass alloc] init]; + + /* First, test that @required and @optional properties work as + expected if implemented either via @synthesize or manually. */ + object.count1 = 44; + if (object.count1 != 44) + abort (); + + object.count2 = 88; + if (object.count2 != 88) + abort (); + + object.count3 = 77; + if (object.count3 != 77) + abort (); + + object.count4 = 11; + if (object.count4 != 11) + abort (); + + /* Now, test the complication: @optional @property which is + readonly, but which has a setter manually implemented. + Apparently it is possible to use the dotsyntax and the @optional + @property getter is used when reading, while the manual setter is + used when writing. */ + object.count5 = 99; + if (object.count5 != 99) + abort (); + + return 0; +} diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-25.mm b/gcc/testsuite/obj-c++.dg/property/at-property-25.mm new file mode 100644 index 000000000000..422a29e552c8 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/property/at-property-25.mm @@ -0,0 +1,87 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test warnings and non-warnings with @optional @properties. */ + +#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 count +@optional +@property int count1; +@property (readonly) int count2; +@end + + +/* A class that implements all the properties. */ +@interface MySubClass1 : MyRootClass +{ + int count1; + int count2; +} +@end + +@implementation MySubClass1 +@synthesize count1; +@synthesize count2; +@end + + +/* A class that implements nothing; no warnings as the properties are + all optional. */ +@interface MySubClass2 : MyRootClass +@end + +@implementation MySubClass2 +@end + + +@protocol count2 +@required +@property int count1; +@property (readonly) int count2; +@end + +/* A class that implements all the properties. */ +@interface MySubClass3 : MyRootClass +{ + int count1; + int count2; +} +@end + +@implementation MySubClass3 +@synthesize count1; +@synthesize count2; +@end + + +/* A class that implements nothing; warnings as the properties are + all required. */ +@interface MySubClass4 : MyRootClass +@end + +@implementation MySubClass4 +@end + +/* { dg-warning "incomplete implementation of class" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..setCount1:. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..count1. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..count2. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "class .MySubClass4. does not fully implement the .count2. protocol" "" { target *-*-* } 81 } */ diff --git a/gcc/testsuite/objc.dg/property/at-property-24.m b/gcc/testsuite/objc.dg/property/at-property-24.m new file mode 100644 index 000000000000..b4a7699f6e40 --- /dev/null +++ b/gcc/testsuite/objc.dg/property/at-property-24.m @@ -0,0 +1,118 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do run } */ +/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */ + +/* Test @optional @properties. */ + +#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 + +/* Use a different getters/setters, so that the only way to compile + object.countX is to find the actual @property. */ +@protocol count +@required +/* @required + @synthesize. */ +@property (getter=number1, setter=setNumber1:) int count1; +/* @required + manual setters/getters. */ +@property (getter=number2, setter=setNumber2:) int count2; + +@optional +/* @optional + @synthesize. */ +@property (getter=number3, setter=setNumber3:) int count3; +/* @optional + manual setters/getters. */ +@property (getter=number4, setter=setNumber4:) int count4; + +@optional +/* @optional + readonly, with a setter added in the class itself. */ +@property (readonly, getter=number5) int count5; +@end + +@interface MySubClass : MyRootClass +{ + int count1; + int count2; + int count3; + int count4; + int count5; +} +- (void) setCount5: (int)value; +@end + +@implementation MySubClass +@synthesize count1; +- (int) number2 +{ + return count2; +} +- (void) setNumber2: (int)value +{ + count2 = value; +} +@synthesize count3; +- (int) number4 +{ + return count4; +} +- (void) setNumber4: (int)value +{ + count4 = value; +} +- (int) number5 +{ + return count5; +} +- (void) setCount5: (int)value +{ + count5 = value; +} +@end + +int main (void) +{ + MySubClass *object = [[MySubClass alloc] init]; + + /* First, test that @required and @optional properties work as + expected if implemented either via @synthesize or manually. */ + object.count1 = 44; + if (object.count1 != 44) + abort (); + + object.count2 = 88; + if (object.count2 != 88) + abort (); + + object.count3 = 77; + if (object.count3 != 77) + abort (); + + object.count4 = 11; + if (object.count4 != 11) + abort (); + + /* Now, test the complication: @optional @property which is + readonly, but which has a setter manually implemented. + Apparently it is possible to use the dotsyntax and the @optional + @property getter is used when reading, while the manual setter is + used when writing. */ + object.count5 = 99; + if (object.count5 != 99) + abort (); + + return 0; +} diff --git a/gcc/testsuite/objc.dg/property/at-property-25.m b/gcc/testsuite/objc.dg/property/at-property-25.m new file mode 100644 index 000000000000..422a29e552c8 --- /dev/null +++ b/gcc/testsuite/objc.dg/property/at-property-25.m @@ -0,0 +1,87 @@ +/* Contributed by Nicola Pero , November 2010. */ +/* { dg-do compile } */ + +/* Test warnings and non-warnings with @optional @properties. */ + +#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 count +@optional +@property int count1; +@property (readonly) int count2; +@end + + +/* A class that implements all the properties. */ +@interface MySubClass1 : MyRootClass +{ + int count1; + int count2; +} +@end + +@implementation MySubClass1 +@synthesize count1; +@synthesize count2; +@end + + +/* A class that implements nothing; no warnings as the properties are + all optional. */ +@interface MySubClass2 : MyRootClass +@end + +@implementation MySubClass2 +@end + + +@protocol count2 +@required +@property int count1; +@property (readonly) int count2; +@end + +/* A class that implements all the properties. */ +@interface MySubClass3 : MyRootClass +{ + int count1; + int count2; +} +@end + +@implementation MySubClass3 +@synthesize count1; +@synthesize count2; +@end + + +/* A class that implements nothing; warnings as the properties are + all required. */ +@interface MySubClass4 : MyRootClass +@end + +@implementation MySubClass4 +@end + +/* { dg-warning "incomplete implementation of class" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..setCount1:. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..count1. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "method definition for ..count2. not found" "" { target *-*-* } 81 } */ +/* { dg-warning "class .MySubClass4. does not fully implement the .count2. protocol" "" { target *-*-* } 81 } */