Fix for PR objc/48539 ("Missing warning when messaging a forward-declared class")

Nicola Pero nicola.pero@meta-innovation.com
Thu Jun 2 18:29:00 GMT 2011


This patch fixes PR objc/48539 ("Missing warning when messaging a forward-declared class").

The problem occurs when using @class, and then messaging class or instance objects of the class.
It can happen both with class and instance methods.

--

An example with class methods is

 @class MyClass;

 [MyClass method];

In this example, the compiler has no information on 'MyClass' and on what methods it responds to.
So, there is no way to determine if "MyClass" responds to the method "+method", and what the method
prototype is (in this example it doesn't matter, but if there are arguments or return values, it could
matter a lot, including potential causing a crash at runtime if the wrong method prototype is used).
Clang emits a warning there, which seems very appropriate, and with this patch, GCC 4.7.0 emits a warning
there too. :-)

--

Then there is then the issue of what to do about instance methods, as in the following example --

 @class MyClass;

 MyClass *x;
 [x method];

This is almost identical to the case above, and this patch adds a similar warning. ;-)

Note that in this case, the current behaviour of the compiler is substandard; the compiler silently throws away
the "MyClass *" type, silently casts "x" to "id", and proceeds to accept for it to be used as a receiver
of any possible method of any class, without any warning (!!).  clang does the same by the way.

We clearly do want to emit a warning there, instead.  The fact that the programmer has explicitly declared "x" to be
of type "MyClass *" instead of "id" means she is expecting the compiler to use that information to do the
standard method lookup/check based on the class.  If the @interface is missing, it is most likely an error / slip
in the program, which is worthwhile for the compiler to warn about (in the same way as we warn above for class
methods!). ;-)

So this patch changes this behaviour and adds a warning here; if the programmer doesn't want the warning and
is happy with "x" being treated as an "id", she can simply add a cast to "id" to clarify her mind, and the
warning will go away.  Ie. if you do

 @class MyClass;

 MyClass *x;
 [(id)x method];

you don't get any warning as you explicitly disabled type-based checks by casting to "id".  But if you leave "x" to be
of type "MyClass *", then you're asking for type-based checks / method lookup, the compiler will try to do the method lookup,
and if the @interface of "MyClass" was not found, will emit a warning because it can't do the requested type-based checks /
method lookup. ;-)

I tried this patch with gnustep core and it did find a number of slips in the code, which is good.  No major
bugs, but all cases where someone had forgotten to #include the header with the @interface of a class and so
where the compiler couldn't do the proper checks, but nobody would notice because of the current silent
behaviour where the missing @interface causes the variable to magically and silently become of type "id".

In fact, looking at the examples is very convincing that we need this warning.  Without it, "@class NSArray;" basically
makes "NSArray *" a typedef for "id" when doing method invocations.  So, in your code you may have methods or functions
taking (NSArray *) arguments, and then you call methods on these arguments, expecting the compiler to check that the methods
are appropriate for an NSArray.  Instead, the compiler is silently treating "NSArray *" as identical to "id", and performing
no checks whatsoever, and not bothering to tell you anything about the fact! ;-)

--

Finally, there's the additional complication of deciding what to do when this is mixed with protocols, as in --

 @class MyClass;
 @protocol MyProtocol
 - (void) method;
 @end

 MyClass <MyProtocol> *x;
 [x method];

This is a weird/rare case, and I don't expect to see it much in practice, but it needs to be sorted out, and GCC already
has at least two existing testcases for it in the GCC testsuite.  At the moment, the compiler does the equivalent of
silently converting "MyClass <MyProtocol> *" to "id <MyProtocol>".  That's not great, but I pondered about this for
a long time, tried a few variations, and then decided to make no changes. :-)

The reason is that if the method being called is part of the protocol, then the compiler can find the method prototype
(and do the type-checking) without needing any more information on the actual class.  Complaining about the @interface not
being available seems pointless nit-picking, since it's not required to do the type-based method lookup. ;-)

If the method being called is not part of the protocol, the compiler already emits a warning that the method could not be
found in the protocol.  I thought about adding a second warning about the @interface not being found, and for a while had
it in the patch, but in practice it seemed overkill and I removed it from the final version.

--

The warning message that I chose for GCC is --

  method-lookup-1.m:42:3: warning: @interface of class ‘NotKnown’ not found [enabled by default]

which I hope is clear and to the point (and the action expected from the programmer, which is adding or including
the @interface, is hopefully evident).  As a comparison, clang has the warning

  method-lookup-1.m:42:4: warning: receiver 'NotKnown' is a forward class and corresponding @interface may not exist

which I find confusing and hard to read (and react to) for various reasons.

--

I added testcases with enumerations of cases and combinations to make sure I was covering the various complications.  I
did add a fair amount of comments to the code as well. :-)

Ok to commit to trunk ?

Thanks

Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog      (revision 174551)
+++ objc/ChangeLog      (working copy)
@@ -1,3 +1,12 @@
+2011-06-02  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       PR objc/48539
+       * objc-act.c (objc_finish_message_expr): Warn if messaging a class
+       that was only declared using @class without an @interface.  Warn
+       if messaging an instance of a class that was only declared using
+       @class without an @interface, unless the receiver was also typed
+       with a protocol list.
+
 2011-06-01  Nicola Pero  <nicola.pero@meta-innovation.com>
 
        * objc-act.c (objc_decl_method_attributes): Implement nonnull
Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c     (revision 174551)
+++ objc/objc-act.c     (working copy)
@@ -5432,15 +5432,21 @@ objc_finish_message_expr (tree receiver, tree sel_
      from the implementation context).  */
   rtype = receiver;
   while (TREE_CODE (rtype) == COMPOUND_EXPR
-             || TREE_CODE (rtype) == MODIFY_EXPR
-             || CONVERT_EXPR_P (rtype)
-             || TREE_CODE (rtype) == COMPONENT_REF)
+        || TREE_CODE (rtype) == MODIFY_EXPR
+        || CONVERT_EXPR_P (rtype)
+        || TREE_CODE (rtype) == COMPONENT_REF)
     rtype = TREE_OPERAND (rtype, 0);
 
+  /* self is 1 if this is a message to self, 0 otherwise  */
   self = (rtype == self_decl);
+
+  /* super is 1 if this is a message to super, 0 otherwise.  */
   super = (rtype == UOBJC_SUPER_decl);
+
+  /* rtype is the type of the receiver.  */
   rtype = TREE_TYPE (receiver);
 
+  /* have_cast is 1 if the receiver is casted.  */
   have_cast = (TREE_CODE (receiver) == NOP_EXPR
               || (TREE_CODE (receiver) == COMPOUND_EXPR
                   && !IS_SUPER (rtype)));
@@ -5450,7 +5456,10 @@ objc_finish_message_expr (tree receiver, tree sel_
     should_call_super_dealloc = 0;
 
   /* If the receiver is a class object, retrieve the corresponding
-     @interface, if one exists. */
+     @interface, if one exists.  class_tree is the class name
+     identifier, or NULL_TREE if this is not a class method or the
+     class name could not be determined (as in the case "Class c; [c
+     method];").  */
   class_tree = receiver_is_class_object (receiver, self, super);
 
   /* Now determine the receiver type (if an explicit cast has not been
@@ -5458,7 +5467,27 @@ objc_finish_message_expr (tree receiver, tree sel_
   if (!have_cast)
     {
       if (class_tree)
-       rtype = lookup_interface (class_tree);
+       {
+         /* We are here when we have no cast, and we have a class
+            name.  So, this is a plain method to a class object, as
+            in [NSObject alloc].  Find the interface corresponding to
+            the class name.  */
+         rtype = lookup_interface (class_tree);
+
+         if (rtype == NULL_TREE)
+           {
+             /* If 'rtype' is NULL_TREE at this point it means that
+                we have seen no @interface corresponding to that
+                class name, only a @class declaration.  So, we have a
+                class name (class_tree) but no actual details of the
+                class methods.  We won't be able to check that the
+                class responds to the method, and we will have to
+                guess the method prototype.  Emit a warning, then
+                keep going (this will use any method with a matching
+                name, as if the receiver was of type 'Class').  */
+             warning (0, "@interface of class %qE not found", class_tree);
+           }
+       }
       /* Handle `self' and `super'.  */
       else if (super)
        {
@@ -5474,28 +5503,41 @@ objc_finish_message_expr (tree receiver, tree sel_
        rtype = lookup_interface (CLASS_NAME (implementation_template));
     }
 
-  /* If receiver is of type `id' or `Class' (or if the @interface for a
-     class is not visible), we shall be satisfied with the existence of
-     any instance or class method. */
   if (objc_is_id (rtype))
     {
+      /* The receiver is of type 'id' or 'Class' (with or without some
+        protocols attached to it).  */
+
+      /* We set class_tree to the identifier for 'Class' if this is a
+        class method, and to NULL_TREE if not.  */
       class_tree = (IS_CLASS (rtype) ? objc_class_name : NULL_TREE);
+      
+      /* 'rprotos' is the list of protocols that the receiver
+        supports.  */
       rprotos = (TYPE_HAS_OBJC_INFO (TREE_TYPE (rtype))
                 ? TYPE_OBJC_PROTOCOL_LIST (TREE_TYPE (rtype))
                 : NULL_TREE);
+
+      /* We have no information on the type, and we set it to
+        NULL_TREE.  */
       rtype = NULL_TREE;
 
+      /* If there are any protocols, check that the method we are
+        calling appears in the protocol list.  If there are no
+        protocols, this is a message to 'id' or 'Class' and we accept
+        any method that exists.  */
       if (rprotos)
        {
-         /* If messaging 'id <Protos>' or 'Class <Proto>', first search
-            in protocols themselves for the method prototype.  */
+         /* If messaging 'id <Protos>' or 'Class <Proto>', first
+            search in protocols themselves for the method
+            prototype.  */
          method_prototype
            = lookup_method_in_protocol_list (rprotos, sel_name,
                                              class_tree != NULL_TREE);
 
-         /* If messaging 'Class <Proto>' but did not find a class method
-            prototype, search for an instance method instead, and warn
-            about having done so.  */
+         /* If messaging 'Class <Proto>' but did not find a class
+            method prototype, search for an instance method instead,
+            and warn about having done so.  */
          if (!method_prototype && !rtype && class_tree != NULL_TREE)
            {
              method_prototype
@@ -5509,6 +5551,8 @@ objc_finish_message_expr (tree receiver, tree sel_
     }
   else if (rtype)
     {
+      /* We have a receiver type which is more specific than 'id' or
+        'Class'.  */
       tree orig_rtype = rtype;
 
       if (TREE_CODE (rtype) == POINTER_TYPE)
@@ -5523,25 +5567,70 @@ objc_finish_message_expr (tree receiver, tree sel_
          rprotos = TYPE_OBJC_PROTOCOL_LIST (rtype);
          rtype = TYPE_OBJC_INTERFACE (rtype);
        }
-      /* If we could not find an @interface declaration, we must have
-        only seen a @class declaration; so, we cannot say anything
-        more intelligent about which methods the receiver will
-        understand. */
       if (!rtype || TREE_CODE (rtype) == IDENTIFIER_NODE)
        {
+         /* If we could not find an @interface declaration, we must
+            have only seen a @class declaration; so, we cannot say
+            anything more intelligent about which methods the
+            receiver will understand.  Note that this only happens
+            for instance methods; for class methods to a class where
+            we have only seen a @class declaration,
+            lookup_interface() above would have set rtype to
+            NULL_TREE.  */
+         if (rprotos)
+           {
+             /* We could not find an @interface declaration, yet, if
+                there are protocols attached to the type, we can
+                still look up the method in the protocols.  Ie, we
+                are in the following case:
+            
+                @class MyClass;
+                MyClass<MyProtocol> *x;
+                [x method];
+                
+                If 'MyProtocol' has the method 'method', we can check
+                and retrieve the method prototype.  */
+             method_prototype
+               = lookup_method_in_protocol_list (rprotos, sel_name, 0);
+
+             /* At this point, if we have found the method_prototype,
+                we are quite happy.  The details of the class are
+                irrelevant.  If we haven't found it, a warning will
+                have been produced that the method could not be found
+                in the protocol, and we won't produce further
+                warnings (please note that this means that "@class
+                MyClass; MyClass <MyProtocol> *x;" is exactly
+                equivalent to "id <MyProtocol> x", which isn't too
+                satisfactory but it's not easy to see how to do
+                better).  */
+           }
+         else
+           {
+             if (rtype)
+               {
+                 /* We could not find an @interface declaration, and
+                    there are no protocols attached to the receiver,
+                    so we can't complete the check that the receiver
+                    responds to the method, and we can't retrieve the
+                    method prototype.  But, because the receiver has
+                    a well-specified class, the programmer did want
+                    this check to be performed.  Emit a warning, then
+                    keep going as if it was an 'id'.  To remove the
+                    warning, either include an @interface for the
+                    class, or cast the receiver to 'id'.  Note that
+                    rtype is an IDENTIFIER_NODE at this point.  */
+                 warning (0, "@interface of class %qE not found", rtype);
+               }
+           }
+
          rtype = NULL_TREE;
-         /* We could not find an @interface declaration, yet Message maybe in a
-            @class's protocol. */
-         if (!method_prototype && rprotos)
-           method_prototype
-             = lookup_method_in_protocol_list (rprotos, sel_name, 0);
        }
       else if (TREE_CODE (rtype) == CLASS_INTERFACE_TYPE
          || TREE_CODE (rtype) == CLASS_IMPLEMENTATION_TYPE)
        {
-         /* We have a valid ObjC class name.  Look up the method name
-            in the published @interface for the class (and its
-            superclasses). */
+         /* We have a valid ObjC class name with an associated
+            @interface.  Look up the method name in the published
+            @interface for the class (and its superclasses).  */
          method_prototype
            = lookup_method_static (rtype, sel_name, class_tree != NULL_TREE);
 
@@ -5566,6 +5655,7 @@ objc_finish_message_expr (tree receiver, tree sel_
        }
       else
        {
+         /* We have a type, but it's not an Objective-C type (!).  */
          warning (0, "invalid receiver type %qs",
                   identifier_to_locale (gen_type_name (orig_rtype)));
          /* After issuing the "invalid receiver" warning, perform method
@@ -5573,11 +5663,13 @@ objc_finish_message_expr (tree receiver, tree sel_
          rtype = rprotos = NULL_TREE;
        }
     }
+  /* Note that rtype could also be NULL_TREE.  This happens if we are
+     messaging a class by name, but the class was only
+     forward-declared using @class.  */
 
-
-  /* For 'id' or 'Class' receivers, search in the global hash table
-     as a last resort.  For all receivers, warn if protocol searches
-     have failed.  */
+  /* For 'id' or 'Class' receivers, search in the global hash table as
+     a last resort.  For all receivers, warn if protocol searches have
+     failed.  */
   if (!method_prototype)
     {
       if (rprotos)
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog (revision 174551)
+++ testsuite/ChangeLog (working copy)
@@ -1,3 +1,13 @@
+2011-06-02  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       PR objc/48539
+       * objc.dg/method-5.m: Updated.  
+       * objc.dg/method-19.m: Updated.
+       * objc.dg/method-lookup-1.m: New.       
+       * obj-c++.dg/method-6.mm: Updated.
+       * obj-c++.dg/method-7.mm: Updated.
+       * obj-c++.dg/method-lookup-1.mm: New.   
+
 2011-06-01  Kaz Kojima  <kkojima@gcc.gnu.org>
 
        PR target/49238
Index: testsuite/objc.dg/method-19.m
===================================================================
--- testsuite/objc.dg/method-19.m       (revision 174551)
+++ testsuite/objc.dg/method-19.m       (working copy)
@@ -8,8 +8,9 @@
 @class NotKnown;
 
 void foo(NotKnown *n) {
-  [NotKnown new];
-  [n nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
+  [NotKnown new];         /* { dg-warning ".interface of class .NotKnown. not found" } */
+  [n nonexistent_method]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+                          /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
 }
 
 /* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
Index: testsuite/objc.dg/method-lookup-1.m
===================================================================
--- testsuite/objc.dg/method-lookup-1.m (revision 0)
+++ testsuite/objc.dg/method-lookup-1.m (revision 0)
@@ -0,0 +1,94 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, May 2011.  */
+/* { dg-do compile } */
+
+@class NotKnown;
+
+@protocol MyProtocol
++ (id) classMethod;
+- (id) instanceMethod;
+@end
+
+@protocol MyProtocol2
++ (id) classMethod2;
+- (id) instanceMethod2;
+@end
+
+void test (Class x, Class <MyProtocol> y, id w, id <MyProtocol> z, NotKnown *a, NotKnown <MyProtocol> *b)
+{
+  /* "Class x" means that "x" responds to any class methods, and may
+     also respond to instance methods because instance methods of the
+     root class are class methods.  */
+  [x classMethod]; /* No warning here.  */
+
+  [x instanceMethod]; /* No warning here.  */
+
+
+  /* "Class <MyProtocol> y" means that "y" responds to any class
+     methods specified in the protocol MyProtocol, but not to other
+     class or instance methods.  If a class method is not found, an
+     instance method from the protocol may be used instead but that is
+     suspicious and gets a warning.  */
+  [y classMethod]; /* No warning here.  */
+
+  [y instanceMethod]; /* { dg-warning "found .\\-instanceMethod. instead of .\\+instanceMethod. in protocol" } */
+
+  [y classMethod2]; /* { dg-warning ".\\+classMethod2. not found in protocol" } */
+
+  [y instanceMethod2]; /* { dg-warning ".\\+instanceMethod2. not found in protocol" } */
+
+
+  /* If a class is specified by name, the @interface must be available
+     to check what it responds to.  */
+  [NotKnown classMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+
+  /* "id w" means that "w" responds to anything, both class and
+     instance methods.  */
+  [w instanceMethod]; /* No warning here.  */
+
+  [w instanceMethod2]; /* No warning here.  */
+
+  [w classMethod]; /* No warning here.  */
+
+  [w classMethod2]; /* No warning here.  */
+
+
+  /* "id <MyProtocol> z" means that "z" responds to any instance
+     methods in the protocol, but not class methods.  To select class
+     methods, you use "Class <MyProtocol> z".  */
+  [z instanceMethod]; /* No warning here.  */
+
+  [z instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+  [z classMethod];     /* { dg-warning ".\\-classMethod. not found in protocol" } */
+
+  [z classMethod2];    /* { dg-warning ".\\-classMethod2. not found in protocol" } */
+
+
+  /* "NotKnown *a" means that "a" is an instance of NotKnown.  Since
+     the programmer explicitly specified the class name, it must be
+     because they expect the compiler to do type-checking; the
+     @interface must be available to do this check, otherwise the
+     compiler does not know what "a" responds to.  */
+  [a instanceMethod];  /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+  /* But, if you cast it to "id", then you're disabling type-checking
+     and the warnings should go away.  */
+  [(id)a instanceMethod]; /* No warning here.  */
+
+
+  /* "NotKnown <MyProtocol> *b" means that "a" is an instance of
+     NotKnown, and also implements protocol <MyProtocol>.  If you send
+     a message that is part of the protocol, then the compiler can do
+     type-checking and all is fine.  */
+  [b instanceMethod];
+
+  /* But if you send a message that is not part of the protocol, then
+     you'll get a warning that the method can not be found in the
+     protocol.  */
+  [b instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */ 
+
+  /* But, if you cast it to "id", then you're disabling type-checking
+     and the warnings should go away.  */
+  [(id)b instanceMethod2]; /* No warning here.  */
+}
Index: testsuite/objc.dg/method-5.m
===================================================================
--- testsuite/objc.dg/method-5.m        (revision 174551)
+++ testsuite/objc.dg/method-5.m        (working copy)
@@ -11,8 +11,10 @@ void foo(UnderSpecified *u, NotAClass *n) {
   [n nonexistent_method];    /* { dg-warning "invalid receiver type" } */
        /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 11 } */
   [NotAClass nonexistent_method]; /* { dg-error ".NotAClass. is not an Objective\\-C class name or alias" } */
-  [u nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
-  [UnderSpecified nonexistent_method]; /* { dg-warning "no .\\+nonexistent_method. method found" } */
+  [u nonexistent_method];    /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+                             /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 14 } */
+  [UnderSpecified nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+                                       /* { dg-warning "no .\\+nonexistent_method. method found" "" { target *-*-* } 16 } */
 }
 
 /* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
Index: testsuite/obj-c++.dg/method-lookup-1.mm
===================================================================
--- testsuite/obj-c++.dg/method-lookup-1.mm     (revision 0)
+++ testsuite/obj-c++.dg/method-lookup-1.mm     (revision 0)
@@ -0,0 +1,94 @@
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, May 2011.  */
+/* { dg-do compile } */
+
+@class NotKnown;
+
+@protocol MyProtocol
++ (id) classMethod;
+- (id) instanceMethod;
+@end
+
+@protocol MyProtocol2
++ (id) classMethod2;
+- (id) instanceMethod2;
+@end
+
+void test (Class x, Class <MyProtocol> y, id w, id <MyProtocol> z, NotKnown *a, NotKnown <MyProtocol> *b)
+{
+  /* "Class x" means that "x" responds to any class methods, and may
+     also respond to instance methods because instance methods of the
+     root class are class methods.  */
+  [x classMethod]; /* No warning here.  */
+
+  [x instanceMethod]; /* No warning here.  */
+
+
+  /* "Class <MyProtocol> y" means that "y" responds to any class
+     methods specified in the protocol MyProtocol, but not to other
+     class or instance methods.  If a class method is not found, an
+     instance method from the protocol may be used instead but that is
+     suspicious and gets a warning.  */
+  [y classMethod]; /* No warning here.  */
+
+  [y instanceMethod]; /* { dg-warning "found .\\-instanceMethod. instead of .\\+instanceMethod. in protocol" } */
+
+  [y classMethod2]; /* { dg-warning ".\\+classMethod2. not found in protocol" } */
+
+  [y instanceMethod2]; /* { dg-warning ".\\+instanceMethod2. not found in protocol" } */
+
+
+  /* If a class is specified by name, the @interface must be available
+     to check what it responds to.  */
+  [NotKnown classMethod]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+
+  /* "id w" means that "w" responds to anything, both class and
+     instance methods.  */
+  [w instanceMethod]; /* No warning here.  */
+
+  [w instanceMethod2]; /* No warning here.  */
+
+  [w classMethod]; /* No warning here.  */
+
+  [w classMethod2]; /* No warning here.  */
+
+
+  /* "id <MyProtocol> z" means that "z" responds to any instance
+     methods in the protocol, but not class methods.  To select class
+     methods, you use "Class <MyProtocol> z".  */
+  [z instanceMethod]; /* No warning here.  */
+
+  [z instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */
+
+  [z classMethod];     /* { dg-warning ".\\-classMethod. not found in protocol" } */
+
+  [z classMethod2];    /* { dg-warning ".\\-classMethod2. not found in protocol" } */
+
+
+  /* "NotKnown *a" means that "a" is an instance of NotKnown.  Since
+     the programmer explicitly specified the class name, it must be
+     because they expect the compiler to do type-checking; the
+     @interface must be available to do this check, otherwise the
+     compiler does not know what "a" responds to.  */
+  [a instanceMethod];  /* { dg-warning ".interface of class .NotKnown. not found" } */
+
+  /* But, if you cast it to "id", then you're disabling type-checking
+     and the warnings should go away.  */
+  [(id)a instanceMethod]; /* No warning here.  */
+
+
+  /* "NotKnown <MyProtocol> *b" means that "a" is an instance of
+     NotKnown, and also implements protocol <MyProtocol>.  If you send
+     a message that is part of the protocol, then the compiler can do
+     type-checking and all is fine.  */
+  [b instanceMethod];
+
+  /* But if you send a message that is not part of the protocol, then
+     you'll get a warning that the method can not be found in the
+     protocol.  */
+  [b instanceMethod2]; /* { dg-warning ".\\-instanceMethod2. not found in protocol" } */ 
+
+  /* But, if you cast it to "id", then you're disabling type-checking
+     and the warnings should go away.  */
+  [(id)b instanceMethod2]; /* No warning here.  */
+}
Index: testsuite/obj-c++.dg/method-6.mm
===================================================================
--- testsuite/obj-c++.dg/method-6.mm    (revision 174551)
+++ testsuite/obj-c++.dg/method-6.mm    (working copy)
@@ -1,6 +1,5 @@
-/* The following should NOT generate "may not respond to" warnings,
-   since a forward-declared @class (instance) should be treated like a
-   'Class') ('id').  */
+/* The following should NOT generate "may not respond to" warnings, since a forward-declared
+   @class (instance) should be treated like a 'Class') ('id').  */
 
 /* { dg-do compile } */
 
@@ -9,11 +8,11 @@
 @class NotKnown;
 
 void foo(NotKnown *n) {
-  [NotKnown new];
-  [n nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
+  [NotKnown new];         /* { dg-warning ".interface of class .NotKnown. not found" } */
+  [n nonexistent_method]; /* { dg-warning ".interface of class .NotKnown. not found" } */
+                          /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
 }
 
 /* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */
 /* { dg-warning "will be assumed to return .id. and accept" "" { target *-*-* } 0 } */
 /* { dg-warning ".\.\.\.. as arguments" "" { target *-*-* } 0 } */
-
Index: testsuite/obj-c++.dg/method-7.mm
===================================================================
--- testsuite/obj-c++.dg/method-7.mm    (revision 174551)
+++ testsuite/obj-c++.dg/method-7.mm    (working copy)
@@ -1,6 +1,5 @@
 /* Check if sending messages to "underspecified" objects is handled gracefully.  */
 /* Author: Ziemowit Laski <zlaski@apple.com>.  */
-
 /* { dg-do compile } */
 
 @class UnderSpecified;
@@ -10,10 +9,12 @@ typedef struct NotAClass {
 
 void foo(UnderSpecified *u, NotAClass *n) {
   [n nonexistent_method];    /* { dg-warning "invalid receiver type" } */
-       /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 12 } */
+       /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 11 } */
   [NotAClass nonexistent_method]; /* { dg-error ".NotAClass. is not an Objective\\-C class name or alias" } */
-  [u nonexistent_method]; /* { dg-warning "no .\\-nonexistent_method. method found" } */
-  [UnderSpecified nonexistent_method]; /* { dg-warning "no .\\+nonexistent_method. method found" } */
+  [u nonexistent_method];    /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+                             /* { dg-warning "no .\\-nonexistent_method. method found" "" { target *-*-* } 14 } */
+  [UnderSpecified nonexistent_method]; /* { dg-warning ".interface of class .UnderSpecified. not found" } */
+                                       /* { dg-warning "no .\\+nonexistent_method. method found" "" { target *-*-* } 16 } */
 }
 
 /* { dg-warning "Messages without a matching method signature" "" { target *-*-* } 0 } */




More information about the Gcc-patches mailing list