diff --git a/gcc/objc/ChangeLog b/gcc/objc/ChangeLog index b717a946d05..43ef65d6ec9 100644 --- a/gcc/objc/ChangeLog +++ b/gcc/objc/ChangeLog @@ -1,4 +1,15 @@ -2010-12-30 Nicola Pero +2010-12-30 Nicola Pero + + * objc-act.c (objc_add_method): When emitting an error because a + method with the same name but conflicting types is found in the + same class or category interface, print a note with the location + of the original method. Also, improved the error message to + clearly state that the conflict is due to conflicting types, and + produce it for protocols as well. Emit an error if two identical + methods are declared in a protocol, but one is @required and the + other one is @optional. When + +2010-12-30 Nicola Pero * objc-act.c (start_class): Warn when a class attribute is ignored. diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c index a0a7a095eb4..ec7fea59dd3 100644 --- a/gcc/objc/objc-act.c +++ b/gcc/objc/objc-act.c @@ -8898,22 +8898,69 @@ add_method_to_hash_list (hash *hash_list, tree method) static tree objc_add_method (tree klass, tree method, int is_class, bool is_optional) { - tree mth; + tree existing_method = NULL_TREE; - /* @optional methods are added to protocol's OPTIONAL list. Note - that this disables checking that the methods are implemented by - classes implementing the protocol, since these checks only use - the CLASS_CLS_METHODS and CLASS_NST_METHODS. */ - if (is_optional) + /* The first thing we do is look up the method in the list of + methods already defined in the interface (or implementation). */ + if (is_class) + existing_method = lookup_method (CLASS_CLS_METHODS (klass), method); + else + existing_method = lookup_method (CLASS_NST_METHODS (klass), method); + + /* In the case of protocols, we have a second list of methods to + consider, the list of optional ones. */ + if (TREE_CODE (klass) == PROTOCOL_INTERFACE_TYPE) { - gcc_assert (TREE_CODE (klass) == PROTOCOL_INTERFACE_TYPE); - if (!(mth = lookup_method (is_class - ? PROTOCOL_OPTIONAL_CLS_METHODS (klass) - : PROTOCOL_OPTIONAL_NST_METHODS (klass), - method))) + /* @required methods are added to the protocol's normal list. + @optional methods are added to the protocol's OPTIONAL lists. + Note that adding the methods to the optional lists disables + checking that the methods are implemented by classes + implementing the protocol, since these checks only use the + CLASS_CLS_METHODS and CLASS_NST_METHODS. */ + + /* First of all, if the method to add is @optional, and we found + it already existing as @required, emit an error. */ + if (is_optional && existing_method) + { + error ("method %<%c%E%> declared %<@optional%> and %<@required%> at the same time", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + inform (DECL_SOURCE_LOCATION (existing_method), + "previous declaration of %<%c%E%> as %<@required%>", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + } + + /* Now check the list of @optional methods if we didn't find the + method in the @required list. */ + if (!existing_method) + { + if (is_class) + existing_method = lookup_method (PROTOCOL_OPTIONAL_CLS_METHODS (klass), method); + else + existing_method = lookup_method (PROTOCOL_OPTIONAL_NST_METHODS (klass), method); + + if (!is_optional && existing_method) + { + error ("method %<%c%E%> declared %<@optional%> and %<@required%> at the same time", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + inform (DECL_SOURCE_LOCATION (existing_method), + "previous declaration of %<%c%E%> as %<@optional%>", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + } + } + } + + /* If the method didn't exist already, add it. */ + if (!existing_method) + { + if (is_optional) { if (is_class) { + /* Put the method on the list in reverse order. */ TREE_CHAIN (method) = PROTOCOL_OPTIONAL_CLS_METHODS (klass); PROTOCOL_OPTIONAL_CLS_METHODS (klass) = method; } @@ -8923,36 +8970,50 @@ objc_add_method (tree klass, tree method, int is_class, bool is_optional) PROTOCOL_OPTIONAL_NST_METHODS (klass) = method; } } - } - else if (!(mth = lookup_method (is_class - ? CLASS_CLS_METHODS (klass) - : CLASS_NST_METHODS (klass), method))) - { - /* put method on list in reverse order */ - if (is_class) - { - DECL_CHAIN (method) = CLASS_CLS_METHODS (klass); - CLASS_CLS_METHODS (klass) = method; - } else { - DECL_CHAIN (method) = CLASS_NST_METHODS (klass); - CLASS_NST_METHODS (klass) = method; + if (is_class) + { + DECL_CHAIN (method) = CLASS_CLS_METHODS (klass); + CLASS_CLS_METHODS (klass) = method; + } + else + { + DECL_CHAIN (method) = CLASS_NST_METHODS (klass); + CLASS_NST_METHODS (klass) = method; + } } } else { - /* When processing an @interface for a class or category, give hard - errors on methods with identical selectors but differing argument - and/or return types. We do not do this for @implementations, because - C/C++ will do it for us (i.e., there will be duplicate function - definition errors). */ + /* The method was already defined. Check that the types match + for an @interface for a class or category, or for a + @protocol. Give hard errors on methods with identical + selectors but differing argument and/or return types. We do + not do this for @implementations, because C/C++ will do it + for us (i.e., there will be duplicate function definition + errors). */ if ((TREE_CODE (klass) == CLASS_INTERFACE_TYPE - || TREE_CODE (klass) == CATEGORY_INTERFACE_TYPE) - && !comp_proto_with_proto (method, mth, 1)) - error ("duplicate declaration of method %<%c%E%>", - is_class ? '+' : '-', - METHOD_SEL_NAME (mth)); + || TREE_CODE (klass) == CATEGORY_INTERFACE_TYPE + /* Starting with GCC 4.6, we emit the same error for + protocols too. The situation is identical to + @interfaces as there is no possible meaningful reason + for defining the same method with different signatures + in the very same @protocol. If that was allowed, + whenever the protocol is used (both at compile and run + time) there wouldn't be any meaningful way to decide + which of the two method signatures should be used. */ + || TREE_CODE (klass) == PROTOCOL_INTERFACE_TYPE) + && !comp_proto_with_proto (method, existing_method, 1)) + { + error ("duplicate declaration of method %<%c%E%> with conflicting types", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + inform (DECL_SOURCE_LOCATION (existing_method), + "previous declaration of %<%c%E%>", + (is_class ? '+' : '-'), + METHOD_SEL_NAME (existing_method)); + } } if (is_class) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index a14d4160c67..681c27ef679 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2010-12-30 Nicola Pero + + * objc.dg/class-extension-3.m: Updated. + * objc.dg/method-1.m: Updated. + * objc.dg/method-conflict-1.m: New. + * objc.dg/method-conflict-2.m: New. + * obj-c++.dg/class-extension-3.mm: Updated. + * obj-c++.dg/method-8.mm: Updated. + * obj-c++.dg/method-conflict-1.mm: New. + * obj-c++.dg/method-conflict-2.mm: New. + 2010-12-30 Joseph Myers PR c/46889 diff --git a/gcc/testsuite/obj-c++.dg/class-extension-3.mm b/gcc/testsuite/obj-c++.dg/class-extension-3.mm index 8feb5c9b6f2..1d9d11b684e 100644 --- a/gcc/testsuite/obj-c++.dg/class-extension-3.mm +++ b/gcc/testsuite/obj-c++.dg/class-extension-3.mm @@ -10,7 +10,7 @@ Class isa; int count; } -- (int) test; +- (int) test; /* { dg-warning "previous declaration" } */ @property int count; /* { dg-warning "originally specified here" } */ @end diff --git a/gcc/testsuite/obj-c++.dg/method-8.mm b/gcc/testsuite/obj-c++.dg/method-8.mm index 310437a0023..11ec1751bb9 100644 --- a/gcc/testsuite/obj-c++.dg/method-8.mm +++ b/gcc/testsuite/obj-c++.dg/method-8.mm @@ -2,12 +2,12 @@ /* { dg-do compile } */ @interface class1 -- (int) meth1; +- (int) meth1; /* { dg-error "previous declaration" } */ - (void) meth1; /* { dg-error "duplicate declaration of method .\\-meth1." } */ @end @interface class2 -+ (void) meth1; ++ (void) meth1; /* { dg-error "previous declaration" } */ + (int) meth1; /* { dg-error "duplicate declaration of method .\\+meth1." } */ @end diff --git a/gcc/testsuite/obj-c++.dg/method-conflict-1.mm b/gcc/testsuite/obj-c++.dg/method-conflict-1.mm new file mode 100644 index 00000000000..9073125ee61 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/method-conflict-1.mm @@ -0,0 +1,26 @@ +/* Contributed by Nicola Pero , December 2010. */ +/* { dg-do compile } */ + +#include + +/* Test that you can not declare two methods, in the same protocol, + with the same name but conflicting method signatures. */ + +@protocol MyProtocol ++ (int) method1: (int)x; /* { dg-error "previous declaration" } */ ++ (float) method1: (int)x; /* { dg-error "duplicate declaration of method .\\+method1." } */ + +- (int) method2: (int)x; /* { dg-error "previous declaration" } */ +- (int) method2: (float)x; /* { dg-error "duplicate declaration of method .\\-method2." } */ + +@optional ++ (int *) method3: (int)x; /* { dg-error "previous declaration" } */ ++ (int *) method3: (int **)x; /* { dg-error "duplicate declaration of method .\\+method3." } */ + +- (id) method4: (id)x; /* { dg-error "previous declaration" } */ +- (void) method4: (id)x; /* { dg-error "duplicate declaration of method .\\-method4." } */ +@end + +/* We don't test conflicting types between @required and @optional + methods, as that is tested in method-conflict-2. */ + diff --git a/gcc/testsuite/obj-c++.dg/method-conflict-2.mm b/gcc/testsuite/obj-c++.dg/method-conflict-2.mm new file mode 100644 index 00000000000..ad6023d1af7 --- /dev/null +++ b/gcc/testsuite/obj-c++.dg/method-conflict-2.mm @@ -0,0 +1,34 @@ +/* Contributed by Nicola Pero , December 2010. */ +/* { dg-do compile } */ + +#include + +/* Test that you can not declare two methods, in the same protocol, + with the same name and method signature, but one as @required and + once as @optional. */ + +/* First, @required conflicting with @optional. */ +@protocol MyProtocol + +@optional ++ (void) method1: (id)x; /* { dg-error "previous declaration" } */ +- (id) method2: (long)x; /* { dg-error "previous declaration" } */ + +@required ++ (void) method1: (id)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ +- (id) method2: (long)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ + +@end + +/* Second, @optional conflicting with @required. */ +@protocol MyProtocol2 + +@required ++ (void) method3: (Class)x; /* { dg-error "previous declaration" } */ +- (id *) method4: (long)x; /* { dg-error "previous declaration" } */ + +@optional ++ (void) method3: (Class)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ +- (id *) method4: (long)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ + +@end diff --git a/gcc/testsuite/objc.dg/class-extension-3.m b/gcc/testsuite/objc.dg/class-extension-3.m index 9564bf022cb..69e57053969 100644 --- a/gcc/testsuite/objc.dg/class-extension-3.m +++ b/gcc/testsuite/objc.dg/class-extension-3.m @@ -10,7 +10,7 @@ Class isa; int count; } -- (int) test; +- (int) test; /* { dg-message "previous declaration" } */ @property int count; /* { dg-message "originally specified here" } */ @end diff --git a/gcc/testsuite/objc.dg/method-1.m b/gcc/testsuite/objc.dg/method-1.m index ce2aab12129..194c64fac4e 100644 --- a/gcc/testsuite/objc.dg/method-1.m +++ b/gcc/testsuite/objc.dg/method-1.m @@ -2,12 +2,12 @@ /* { dg-do compile } */ @interface class1 -- (int) meth1; +- (int) meth1; /* { dg-message "previous declaration" } */ - (void) meth1; /* { dg-error "duplicate declaration of method .\\-meth1." } */ @end @interface class2 -+ (void) meth1; ++ (void) meth1; /* { dg-message "previous declaration" } */ + (int) meth1; /* { dg-error "duplicate declaration of method .\\+meth1." } */ @end diff --git a/gcc/testsuite/objc.dg/method-conflict-1.m b/gcc/testsuite/objc.dg/method-conflict-1.m new file mode 100644 index 00000000000..2cc96e4fd08 --- /dev/null +++ b/gcc/testsuite/objc.dg/method-conflict-1.m @@ -0,0 +1,26 @@ +/* Contributed by Nicola Pero , December 2010. */ +/* { dg-do compile } */ + +#include + +/* Test that you can not declare two methods, in the same protocol, + with the same name but conflicting method signatures. */ + +@protocol MyProtocol ++ (int) method1: (int)x; /* { dg-message "previous declaration" } */ ++ (float) method1: (int)x; /* { dg-error "duplicate declaration of method .\\+method1." } */ + +- (int) method2: (int)x; /* { dg-message "previous declaration" } */ +- (int) method2: (float)x; /* { dg-error "duplicate declaration of method .\\-method2." } */ + +@optional ++ (int *) method3: (int)x; /* { dg-message "previous declaration" } */ ++ (int *) method3: (int **)x; /* { dg-error "duplicate declaration of method .\\+method3." } */ + +- (id) method4: (id)x; /* { dg-message "previous declaration" } */ +- (void) method4: (id)x; /* { dg-error "duplicate declaration of method .\\-method4." } */ +@end + +/* We don't test conflicting types between @required and @optional + methods, as that is tested in method-conflict-2. */ + diff --git a/gcc/testsuite/objc.dg/method-conflict-2.m b/gcc/testsuite/objc.dg/method-conflict-2.m new file mode 100644 index 00000000000..0b0612d771e --- /dev/null +++ b/gcc/testsuite/objc.dg/method-conflict-2.m @@ -0,0 +1,34 @@ +/* Contributed by Nicola Pero , December 2010. */ +/* { dg-do compile } */ + +#include + +/* Test that you can not declare two methods, in the same protocol, + with the same name and method signature, but one as @required and + once as @optional. */ + +/* First, @required conflicting with @optional. */ +@protocol MyProtocol + +@optional ++ (void) method1: (id)x; /* { dg-message "previous declaration" } */ +- (id) method2: (long)x; /* { dg-message "previous declaration" } */ + +@required ++ (void) method1: (id)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ +- (id) method2: (long)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ + +@end + +/* Second, @optional conflicting with @required. */ +@protocol MyProtocol2 + +@required ++ (void) method3: (Class)x; /* { dg-message "previous declaration" } */ +- (id *) method4: (long)x; /* { dg-message "previous declaration" } */ + +@optional ++ (void) method3: (Class)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ +- (id *) method4: (long)x; /* { dg-error "declared .@optional. and .@required. at the same time" } */ + +@end