[cfe-dev] objc++ enhancements for new c++ features

John McCall via cfe-dev cfe-dev at lists.llvm.org
Thu Mar 29 11:22:32 PDT 2018



> On Mar 29, 2018, at 12:46 PM, David Chisnall <David.Chisnall at cl.cam.ac.uk> wrote:
> 
> 
>> On 29 Mar 2018, at 17:14, John McCall <rjmccall at apple.com> wrote:
>> 
>> 
>> 
>>> On Mar 29, 2018, at 12:07 PM, David Chisnall <David.Chisnall at cl.cam.ac.uk> wrote:
>>> 
>>> 
>>>> On 28 Mar 2018, at 21:07, John McCall <rjmccall at apple.com> wrote:
>>>> 
>>>> And what would the rules be for type-checking such an application?  If we're talking about C++ interactions here, we can't very well pretend that the parameters and return value are all going to be `id`.  (I wouldn't want to add new dependencies on that behavior anyway, even in pure Objective-C.)
>>>> 
>>>> Also, you can't actually make a selector a template argument today.  You can use SEL as the type of a template parameter, but it's basically useless because @selector is not a constant expression.
>>>> 
>>>> Now, there's an idea we've been kicking around for a long time to support typed selectors; I guess they would look something like
>>>> SEL<NSString*()>
>>>> for the type of
>>>> - (NSString*) foo;
>>>> 
>>>> Note that that argument is just a function type, and that it's not the actual type of an IMP because both `self` and `_cmd` are implicit.  That's an interesting thing to discuss.  You could make an argument that `self` should be explicit, so that you could say "this is a selector that only works on object arguments of a certain type".  That would be more type-safe, but I would worry about it making it more awkward to use this for the sorts of dynamic features that people use untyped selectors for today.  Maybe it'd be fine as long as you could convert `SEL<void(NSCell*)>` to `SEL<void(id)>`.
>>>> 
>>>> Anyway, since it's an ordinary function type, it can carry attributes like NS_RETURNS_RETAINED and NS_CONSUMED because we already taught the type system about those for ARC.  (It can't express the behavior of -init methods unless `self` is an explicit parameter, though.)
>>>> 
>>>> I think the application syntax is pretty obvious: you would just call the selector as if it were a C function, with the first argument being the receiver, and the compiler would automatically call the right objc_msgSend variant for the type.
>>>> 
>>>> You'd need some way of constructing one of these, probably by referring to an existing method.  Maybe that could be @selector(-Foo.bar) or something.
>>>> 
>>>> Anyway, if you had that, you could then add special template support for them.
>>> 
>>> I really like this idea - we currently don’t have a good mechanism for doing a type-safe version of -performSelector: or an ARC-safe way of calling objc_msgSend.  With the GNUstep runtime, we also have the ability to cheaply do run-time type checking at the invocation.
>>> 
>>> I don’t think you’d need special syntax for declaring the selectors, because the type here is purely a compile-time concept, so the compiler just needs to check, for example, on assignment from @selector(foo) to a variable of type SEL<NSString*()> that there exists a method of that type.  For template (or auto parameter) type deduction based on a selector, you could simple error if the selector is ambiguous and isn’t explicitly cast to a type (clang already has warnings similar to this).
>> 
>> Ah, that's true, we could do special-case checking / inference when converting a @selector expression to a concrete SEL<T> type.
>> 
>> Do you have an opinions about whether the receiver type should be explicit?  If we made it explicit, I think we'd still give it the usual ObjC lax conversions, i.e. SEL<T(R1, V...)> can be converted to SEL<T(R2, V...)> if R2 is a subtype of R1 (receivers are contravariant) *or* if either R1 or R2 is id.
> 
> I’m in somewhat two minds about this.  On the one hand, I like the idea of making it explicit so that I can describe an operation that’s only expected to be valid on a subset of the class hierarchy (not expecting that to be enforced at run time, but at least as a hint that if I try to apply it to the wrong kind of class without an explicit cast I should get an error).  On the other hand, if we allow implicit casts to the id variant then the contravariant logic can be nontrivial.  I think if someone other than me is implementing it, I’d prefer the explicit version!

The covariance/contravariance logic is easy; we already have code to do that with block pointers.

> On a related note, I’ve been considering whether it’s possible to provide helpers to improve NSInvocation’s handling of Objective-C++ messages that take types with either nontrivial copy constructors or no copy constructor.
> 
> My motivation for this was writing some code using higher-order messaging to perform things in another thread, which worked fine until I passed it a std::shared_ptr, at which point NSInvocation did a simple memcpy of the argument frame and I was left with a dangling pointer.  I eventually rewrote this code to use lambdas, but it would be nice if an NSInvocation constructed from a message containing a std::shared_ptr argument actually did the reference count manipulations correctly when copying the argument from the stack / registers into the NSInvocation object.  Most methods have trivially copyable arguments / return values (except for Objective-C objects, which NSInvocation can already handle automatically), so the metadata would only have to be for the (hopefully, relatively small) subset of C++ types that are not trivially copyable and are passed as Objective-C method arguments.  Do you have any thoughts?

Honestly?  I think NSInvocation ought to be deprecated in favor of just using blocks or, if blocks are unacceptable, static code-generation techniques.  NSInvocation works well enough for a highly reduced set of function prototypes, but the idea that it's going to transparently support every possible thing that the compiler knows how to do in a call seems pretty fanciful to me.

John.




More information about the cfe-dev mailing list