For those who (like me) didn’t really pay attention to the WWDC last month, Apple has just sent patches for ‘lightweight’ (type-erasing) generic for Objective-C to the Clang list (see forwarded message). These don’t require any runtime support, so they should Just Work™ with GNUstep, though we’ll want to add things to various headers to support them (these can be done with macros to support older compilers - I’ll put together a patch once it’s landed in clang and is easier to test).
Given that Objective-C++ works well now with ARC, I’m less interested in this than I would have been 10 years ago - I tend to use C++ collection classes for Objective-C objects in new code - but it’s still quite a nice feature. David > Hi all, > > Last month, Apple introduced a new lightweight generics system into > Objective-C. The attached patch series provides the complete implementation > of this feature, the related “kindof” types feature, supporting warnings, > test-cases, and so on. It should get us back to the point where we can parse > the headers for Apple’s latest SDKs. > > For those of you wondering what this whole lightweight generics thing is, I > suggest either watching the following WWDC video starting at @21:45: > > https://developer.apple.com/videos/wwdc/2015/?id=401 > <https://urldefense.proofpoint.com/v2/url?u=https-3A__developer.apple.com_videos_wwdc_2015_-3Fid-3D401&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=CnzuN65ENJ1H9py9XLiRvC_UQz6u3oG6GUNn7_wosSM&m=gksuXWwW39OdeK_NxENNyU46455v_XexMqDHmfEJNjA&s=ALq4TVlEqBaa3eyce5u1amIVjdR5youDJg0j05H4htQ&e=> > > or keep reading for an explanation. > > Lightweight generics allows Objective-C classes to be parameterized, e.g., > NSArray can be parameterized on the type of its elements: > > @interface NSArray<ObjectType> : NSObject > -(ObjectType)objectAtIndex:(NSInteger)index; > @end > > Objective-C classes, categories, extensions, and forward declarations can all > specify type parameters. Naturally, the type parameters across different > declarations associated with the same class must be consistent. > > For a given parameterized class, one can provide type arguments, e.g., to > state the type of the elements of an NSArray: > > NSArray<NSString *> *strings; > NSArray<NSNumber *> *numbers; > > Naturally, messaging an object of specialized type substitutes the type > arguments for the type parameters, e.g., > > [strings objectAtIndex: 0]; // produces an NSString * > [numbers objectAtIndex: 0]; // produces an NSNumber * > > One can, of course, leave the type arguments unspecified: > > NSArray *array; > > Each of the types NSArray<NSString *>, NSArray<NSNumber *>, and NSArray are > distinct types. There are implicit conversions between the “specialized” > types (NSArray<NSString *>, NSArray<NSNumber *>) and the “unspecialized” type > (NSArray *), but not between different specialized types. For example: > > array = strings; // okay, dropping type arguments > numbers = array; // okay, adding type arguments > strings = numbers; // not okay! NSArray<NSString *> and > NSArray<NSNumber *> are incompatible types > > The “lightweight” in lightweight generics comes from the implementation > model, which is based on type erasure (a la Java Generics). All of the type > argument information is completely erased by IR generation, so this feature > requires no runtime or metadata changes. Obviously, this—along with the > implicit conversion rules above—means that some "obvious” errors will be > missed by the type checker (and won’t be enforced by the runtime). For > example, the sequence “array = strings; numbers = array;” is obviously bogus: > the elements cannot be both NSStrings and NSNumbers. Sema won’t catch this, > just like Sema wouldn’t catch an assignment chain that converts an NSNumber > to an NSString by way of ‘id’. > > Type parameters have a few advanced features. First of all, they can have > upper bounds, such that any type argument must be a subtype of the upper > bound of the corresponding type parameter. For example: > > @interface MyMap<KeyType : id<NSCopying>, ObjectType> > -(KeyType)firstKeyForObject:(ObjectType)object; // yes, this is a weird > example; explanation below > @end > > It’s an error to form a MyMap<T, U> where T isn’t a subtype of id<NSCopying>. > When the type bound is omitted, it is “id”, i.e., all type > parameters/arguments are all Objective-C objects. Type bounds are also > important when using unspecialized types (e.g., MyMap *); we’ll come back to > that later. > > Type parameters can also be co- or contra-variant, similarly to C#’s generic > type parameters (see, e.g., > https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx) > <https://urldefense.proofpoint.com/v2/url?u=https-3A__msdn.microsoft.com_en-2Dus_library_dd799517-28v-3Dvs.110-29.aspx-29&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=CnzuN65ENJ1H9py9XLiRvC_UQz6u3oG6GUNn7_wosSM&m=gksuXWwW39OdeK_NxENNyU46455v_XexMqDHmfEJNjA&s=lLGp0NGVuw3Ee3R8nbuaDTRg6Jp-Pft4EJs7bdXavL4&e=>. > This is particularly useful for the immutable collection classes, which > we’ll use for exposition purposes. NSArray is actually declared as: > > @interface NSArray<__covariant ObjectType> : NSObject > -(ObjectType)objectAtIndex:(NSInteger)index; > @end > > Because ObjectType is covariant, NSArray<T> is implicitly convertible to > NSArray<U> when T is a subtype of U, which allows, e.g., > > NSArray<NSMutableString *> *mutStrings; > NSArray<NSString *> *strings; > strings = mutStrings; // okay, because ObjectType is covariant (and > NSArray is an immutable collection) > > Contravariant type parameters go the opposite way, e.g., NSArray<T> is > implicitly convertible to NSArray<U> when U is a subtype of T. > > Objective-C lightweight generics also involves a related feature called > “kindof” types, which strike a balance between “id” and specific Objective-C > pointer types. For example, given: > > __kindof NSString *kindofStr; > > we can implicitly convert to supertypes *and* subtypes, but not unrelated > types. For example: > > NSObject *object = kindofStr; // okay, implicitly converting to > supertype > NSMutableString *mutString = kindofStr; // okay, implicitly converting > to subtype > NSNumber *number = kindofStr; // not okay! NSString and NSNumber are > incompatible > > Naturally, the features compose: one can create an array of kindof types, > e.g., > > NSArray<__kindof NSValue *> *values; > > which can be *very* helpful when adopting lightweight generics. Kindof types > are also important when working with unspecialized types. For example, let’s > invent a class that only wants to work with subclasses of ‘View’: > > @interface SomeClass<T : View *> // T must be View or a subclass thereof > - (T)view; > @end > > As noted earlier, when we message SomeClass, we substitute in the type > arguments for the type parameters, e.g., > > SomeClass<Button *> *sc; > [sc view]; // produces a "Button *" > > But what happens if we don’t have type arguments, because we’re messaging an > object of type ’SomeClass *’? We produce a __kindoftype from the type bound, > which gives us good static type information without forcing the user to > introduce a large number of casts: > > SomeClass *sc; > [sc view]; // produces a "__kindof View *" > > > That’s most of it! I’ll be happy to answer any questions. Actual coherent > documentation is forthcoming, but we wanted to get this implementation out > there for people to play with. > > The patches are fairly large, since this is a nontrivial feature. I’ve broken > it up into logical pieces, which are, roughly: > > 1) Type parameter parsing, ASTs, etc. > 2) Type argument handling > 3) Substitution of type arguments for parameters when using specialized > types > 4) Reworking our handling of the ternary operator on Objective-C > pointer types > 5) Interaction between C++ templates and Objective-C lightweight > generics > 6/7) Warnings for lightweight generics > 8) Kindof types > 9) Co- and contra-variant type parameters > 10) Annoying workaround for old Clang versions that apparently still > need to build with > > Detailed patch review should go to cfe-commits, so we can keep the discussion > here more high-level. > > - Doug -- Sent from my STANTEC-ZEBRA
_______________________________________________ Gnustep-dev mailing list Gnustep-dev@gnu.org https://lists.gnu.org/mailman/listinfo/gnustep-dev