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: Ziemowit Laski
Subject: Re: [objc-improvements] Message passing breaks when receiver type doesn't match promoted sender type (Was: How is it looking?)
Date: Mon, 18 Aug 2003 12:58:56 -0700


On Sunday, Aug 17, 2003, at 07:39 US/Pacific, Alexander Malmberg wrote:

Ziemowit Laski wrote:
Ah, good catch!  Thanks. :-)

The reason I did what I did is that, when constructing ObjC message
sends, esp. to receivers of type 'id' or 'Class', there may be multiple
method declarations floating around such that their selectors are the
same but their arguments/return types differ. :-(  This is a very real
phenomenon in our (i.e., Apple) frameworks.

I don't recall any instance where this has caused problems in normal
calls in GNUstep; name collisions are fairly rare. There have been
issues with forwarding in user code related to this, though.

Well, unfortunately, Apple/NeXT ObjC code tends to be more... uh,
polymorphic in that regard. :-(


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?

Of course, I do see the nature of your problem -- my patch has
essentially disabled implicit argument conversions when calling ObjC
methods.  Without it, though, you often get idiotic warnings or
unneeded/erroneous conversions.

Not all warnings or conversions or unneeded, though. Thinking more about
this, this seems like a rather deep issue. In particular, the return
value is just as important as the arguments, so any solution needs to
deal with that as well.

Yes, the return type should be treated analogously to argument types. As you can see, the current code discards 'void' return types in favor of 'id'. In retrospect, I see that this is a band-aid to the incorrect selector problem, and hopefully we can eliminate
that.

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.


(For class objects, replace id with Class.)

Current/old behavior in gcc seems to be:

a. Use it.
b. Pick one randomly. Warn, but only if both prototypes are in the same
@interface/@protocol.
c. If receiver is "id", pick one randomly. Otherwise, assume id return
and ... for arguments. Warn.
d. Use it. Warn if receiver isn't "id".
e. Warn. Assume id return and ... for arguments.


With (1), the warnings remain the same, and argument/return type
becomes:

a-d. ... for arguments. Return type picked from a "random" prototype.
e. As before. ... for arguments, id for return.

Aside from many not so technical issues I have with this, the return
type handling is a big flaw. The elegance of this solution is, I think,
that method prototypes wouldn't matter, and thus the issue of picking a
prototype is avoided. However, this doesn't work for return types, so
the solution fails to avoid and fails to address the core issue.


I also grabbed a copy of POC (3.1.32) and tested. Since there doesn't
seem to be any way of typing an object in POC, ie. all objects are of
type "id", a. and b. don't apply. For the others, I got:

c. Warn. Pick one randomly.
d. Use it.
e. Warn. The warning incorrectly claims that arguments are assumed to be
of type id; they're actually assumed to be ..., and the return type id.

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. :-) :-)

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 biggest departure (but a correct one, IMHO) from current practice is (d). 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.

Note that multiple selector scenarios should only occur for receivers of
type:
  a.  'id'/'Class'
b. 'Object<Foo, Bar, ...> *' or 'id<Foo, Bar, ...>', where protocols 'Foo' and 'Bar' (and, possibly, 'Object' itself) use the same selector with
      different types.

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

====> 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?

1. If a prototype is used, it's used completely. Thus, if the return
type is taken from a prototype, that argument types are taken from the
same prototype.

Agreed.

2. If a prototype isn't used, the return type is assumed to be id and
the arguments ....

Agreed.

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. I thought that the GNUStep problem you ran into was that there was a unique prototype that the compiler should have used but didn't, and that the prototype accepted an 'int' where a float was passed in, meaning that the compiler used to perform the float->int conversion but now no longer does?

Also, I think that once we decide that we do _not_ have a unique, valid prototype to work with (whatever our criteria for reaching such a decision), we should stick to our guns -- i.e., '(id *)(id, SEL, ...)' -- and not perform any last-minute second-guessing about the existence of a method whose parameters magically match
our arguments. :-)

In the source, I suggest moving method_prototype handling out of
build_objc_method_call(). Instead, finish_message_expr() should always
give build_objc_method_call() a valid prototype (looked up or
constructed).

Yes, I think that's a good idea.

I'll start hacking this stuff up shortly, barring any Apple compiler emergencies. :-)

--Zem
--------------------------------------------------------------
Ziemowit Laski                 1 Infinite Loop, MS 301-2K
Mac OS X Compiler Group        Cupertino, CA USA  95014-2083
Apple Computer, Inc.           +1.408.974.6229  Fax .5477





reply via email to

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