discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [objc-improvements] Message passing breaks when receiver type doesn


From: Alexander Malmberg
Subject: Re: [objc-improvements] Message passing breaks when receiver type doesn't match promoted sender type (Was: How is it looking?)
Date: Wed, 20 Aug 2003 03:47:34 +0200

Ziemowit Laski wrote:
> On Sunday, Aug 17, 2003, at 07:39 US/Pacific, Alexander Malmberg wrote:
> > Ziemowit Laski wrote:
[snip]
> >> I once timidly proposed
> >> making ObjC more strict so that all methods sharing a selector also
> >> agree on argument/return types, but was shot down by our resident ObjC
> >> deities. :-)
> >
> > That's understandable. :) OTOH, testing with POC shows that it warns if
> > there are two selectors with the same name but different types.
> 
> It warns whenever two such selectors exist, or only when a
> corresponding message
> is being sent to a receiver for which more than one of those selectors
> are applicable?

It warns when the second (well, first non-matching) prototype is seen,
eg. given:

foo.h:62: warning: selector 'c:' previously declared at foo.h:57

Remember that in POC, all selectors are always applicable since the only
object type is id (afaict, at least).

It would be nice if we could do something similar, but we need to be
pickier. In any case, I think we would want to warn whenever a protocol
or class is created with conflicting prototypes, but doing this robustly
would be a bit hairy. Consider eg.:

@interface Foo : Object
@end

@interface Bar : Foo
-(int) zot;
@end

@interface Foo (aCategory)
-(float) zot;
@end

[snip]
> > Well, stepping back from the implementation for a while and looking at
> > it from a higher level, it seems we have the following cases:
> >
> > a. "Type" information for the receiver gives exactly one method
> > prototype.
> > b. "Type" information for the receiver gives more than one method
> > prototype.
> > c. "Type" information for the receiver gives no method prototype, and
> > there is more than one method prototype visible.
> > d. "Type" information for the receiver gives no method prototype, and
> > there is exactly one method prototype visible.
> > e. The selector is completely unknown.
[snip]
> The only real objection I have to your proposal is the "pick one
> randomly"
> bit. :-)  Yes, I realize that the compiler does this currently, but it
> has absolutely no reason for anointing one selector uber alles. :-) :-)

Yes, for case b. My thinking is that if the mismatch is minor (as it
might be if a prototype from an abstract superclass or protocol is
repeated in the @interface for a class, and the original prototype
changes slightly), either one will work, but ... might not. OTOH, if the
mismatch is major, there will probably be conflicting implementations,
or uses that conflict with the single implementation that wins, so
things will break horribly regardless of which prototype we choose.

> So, I would propose the following casting strategy:
>    a.  Cast to the selector's argument/return types.
>    b-c. Cast to '(id *)(id, SEL, ...)', and warn about multiple
> applicable
>         selectors (and its return type assumed to be 'id' and arguments
> passed through '...')
>    d-e. Cast to '(id *)(id, SEL, ...)', and warn about no applicable
> selectors
>       (and its return type assumed to be 'id' and arguments passed
> through '...')

The warnings don't seem consistent. The prototypes in case c. and d. are
equally applicable, so either they're both applicable (the receiver
_might_ respond to them), or they're not (the receiver isn't known to
respond to them).

> The biggest departure (but a correct one, IMHO) from current practice
> is (d).

I can see a couple of advantages with it, but also a few disadvantages.
If you implement it, I'll work through GNUstep (core/ and some
apps/libraries) to see how it affects real code. :)

I suspect that more "traditional", type-everything-as-id users of
objective-c might be a lot more upset about this than us *step guys are,
but I'd need feedback from such users to know for sure. :)

> It would
> occur when you're attempting to send a message 'foo' to a 'MyObject *',
> and neither MyObject
> nor any of its superclasses implements 'foo', but some other class does.

More commonly, cases c-e. apply any time the receiver is of type id.
(Just making sure that this is clear.) Regardless of the behavior or
"correctness", I believe that a lot of users would be upset if we
started issuing a warning every time a message is sent to "id".
Consider:

+(id)alloc;

[[Foo alloc] initWithFoo: ...];

[snip]
> Whenever possible, users should be able to suppress said warnings by
> casting
> the receiver to a more specific type.

Yes. IMHO, this is the clean way of handling cases c-e, so I think it's
very important that this be possible. :) (But not necessarily the only
possibility.)

> ====> BTW, how should receivers of type 'Class' be handled?  As
> currently implemented in
> the objc-improvements-branch, if no class method selector is found, a
> warning will
> be given.  Some at Apple don't like this, on the premise that the
> runtime will dispatch
> such messages to instance methods of the root class as a last resort,
> causing them
> to succeed in many cases.  Thing is, when looking at a receiver of type
> 'Class', we have
> no way of knowing what the root class is in that case (since, unlike
> Java, ObjC supports
> multiple root classes).  What is your thinking on this?

Since class objects are really just objects, I don't like the idea of
making a distinction between "class methods" and "instance methods". I'd
much prefer to simply do this based on the type of the receiver, just
like we do for id and other object types. Unfortunately, I think the
lack of class typing, ie. declaring a type "class object for class Foo
or a subclass of Foo", makes this impossible atm.

Anyway, from this pov, Class is just like id: "all" prototypes apply,
always cases c-e. (depending on the number of prototypes). "All" could
be restricted to "all known prototypes for class methods or instance
methods in root classes"; this makes a difference in my proposal.

Obviously, if we have typing information (as we do, sortof, for constant
classes: [NSString new]), we just do a normal search for prototypes
among the class methods of the class and the superclasses, and the
instance methods of the root class (with type information, we'll know
which one).

If we could cast to a class type, this would work well and be consistent
with how we treat objects in your proposal. Since we don't have such
casts or types, I think my proposal is better; with it, message sends to
Class would still work as long as there's only one class method
prototype.

[snip]
> > In all cases where we use ... for arguments, it seems like it might be
> > better to hack things to avoid the type promotion (eg. by constructing
> > a
> > prototype from the types of the arguments before promotion; I think
> > this
> > would be possible). Otherwise, it'd be impossible to call methods with
> > float arguments in c. and e.
> 
> I'm not sure I'm following you here.  If you pass floats or doubles via
> '...',
> they will be preserved as such.

This is not true. Arguments passed to variadic functions, or functions
without prototypes, have a few default promotions applied to them.
Floats are converted to doubles, and chars/shorts to ints. You can see
this in:

""
#include <objc/Object.h>

@interface Foo : Object
@end

int main(int argc,char **argv)
{
  Foo *foo=[Foo new];
  [foo method: (float)4.2];
  return 0;
}

@implementation Foo
-(void) method: (float)f
{
  printf("f=%g\n",f);
}
@end
""

This will print a bogus number. Change (float)f to (double)f and it'll
work because the receiver type now matches the type as it is actually
sent, after promotion.

Anyway, it's late here. I'll probably comment more on the virtues of
class types (and maybe "instance of receiving type" types ;-) tomorrow.
:)

- Alexander Malmberg




reply via email to

[Prev in Thread] Current Thread [Next in Thread]