<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Hi all,<div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">For those of you wondering what this whole lightweight generics thing is, I suggest either watching the following WWDC video starting at @21:45:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span><a href="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=" class="">https://developer.apple.com/videos/wwdc/2015/?id=401</a></div><div class=""><br class=""></div><div class="">or keep reading for an explanation.</div><div class=""><br class=""></div><div class="">Lightweight generics allows Objective-C classes to be parameterized, e.g., NSArray can be parameterized on the type of its elements:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">  </span>@interface NSArray<ObjectType> : NSObject</div><div class=""><span class="Apple-tab-span" style="white-space:pre">   </span>-(ObjectType)objectAtIndex:(NSInteger)index;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">      </span>@end</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">For a given parameterized class, one can provide type arguments, e.g., to state the type of the elements of an NSArray:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>NSArray<NSString *> *strings;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>NSArray<NSNumber *> *numbers;</div><div class=""><br class=""></div><div class=""><div class="">Naturally, messaging an object of specialized type substitutes the type arguments for the type parameters, e.g.,</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">       </span>[strings objectAtIndex: 0]; // produces an NSString *</div></div><div class=""><div class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>[numbers objectAtIndex: 0]; // produces an NSNumber *</div></div><div class=""><br class=""></div><div class="">One can, of course, leave the type arguments unspecified:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>NSArray *array;</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">  </span>array = strings; // okay, dropping type arguments</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>numbers = array; // okay, adding type arguments</div><div class=""><span class="Apple-tab-span" style="white-space:pre">   </span>strings = numbers; // not okay! NSArray<NSString *> and NSArray<NSNumber *> are incompatible types</div><div class=""><br class=""></div><div class="">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’.</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@interface MyMap<KeyType : id<NSCopying>, ObjectType></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>-(KeyType)firstKeyForObject:(ObjectType)object; // yes, this is a weird example; explanation below</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@end</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">Type parameters can also be co- or contra-variant, similarly to C#’s generic type parameters (see, e.g., <a href="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=" class="">https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx)</a>. This is particularly useful for the immutable collection classes, which we’ll use for exposition purposes. NSArray is actually declared as:</div><div class=""><br class=""></div><div class=""><div class=""><span class="Apple-tab-span" style="white-space: pre;">     </span>@interface NSArray<__covariant ObjectType> : NSObject</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">     </span>-(ObjectType)objectAtIndex:(NSInteger)index;</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">    </span>@end</div></div><div class=""><br class=""></div><div class="">Because ObjectType is covariant, NSArray<T> is implicitly convertible to NSArray<U> when T is a subtype of U, which allows, e.g.,</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">     </span>NSArray<NSMutableString *> *mutStrings;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">     </span>NSArray<NSString *> *strings;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>strings = mutStrings; // okay, because ObjectType is covariant (and NSArray is an immutable collection)</div><div class=""><br class=""></div><div class="">Contravariant type parameters go the opposite way, e.g., NSArray<T> is implicitly convertible to NSArray<U> when U is a subtype of T.</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">  </span>__kindof NSString *kindofStr;</div><div class=""><br class=""></div><div class="">we can implicitly convert to supertypes *and* subtypes, but not unrelated types. For example:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>NSObject *object = kindofStr; // okay, implicitly converting to supertype</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>NSMutableString *mutString = kindofStr; // okay, implicitly converting to subtype</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>NSNumber *number = kindofStr; // not okay! NSString and NSNumber are incompatible</div><div class=""><br class=""></div><div class="">Naturally, the features compose: one can create an array of kindof types, e.g.,</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">  </span>NSArray<__kindof NSValue *> *values;</div><div class=""><br class=""></div><div class="">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’:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>@interface SomeClass<T : View *> // T must be View or a subclass thereof</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>- (T)view;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@end</div><div class=""><br class=""></div><div class="">As noted earlier, when we message SomeClass, we substitute in the type arguments for the type parameters, e.g.,</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">       </span>SomeClass<Button *> *sc;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>[sc view]; // produces a "Button *"</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>SomeClass *sc;</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>[sc view]; // produces a "__kindof View *"</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">The patches are fairly large, since this is a nontrivial feature. I’ve broken it up into logical pieces, which are, roughly:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>1) Type parameter parsing, ASTs, etc.</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>2) Type argument handling</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>3) Substitution of type arguments for parameters when using specialized types</div><div class=""><span class="Apple-tab-span" style="white-space:pre">     </span>4) Reworking our handling of the ternary operator on Objective-C pointer types</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>5) Interaction between C++ templates and Objective-C lightweight generics</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>6/7) Warnings for lightweight generics</div><div class=""><span class="Apple-tab-span" style="white-space:pre">    </span>8) Kindof types</div><div class=""><span class="Apple-tab-span" style="white-space:pre">   </span>9) Co- and contra-variant type parameters</div><div class=""><span class="Apple-tab-span" style="white-space:pre"> </span>10) Annoying workaround for old Clang versions that apparently still need to build with</div><div class=""><br class=""></div><div class="">Detailed patch review should go to cfe-commits, so we can keep the discussion here more high-level.</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">      </span>- Doug</div><div class=""><br class=""></div><div class=""></div></body></html>