r241549 - Implement variance for Objective-C type parameters.

Douglas Gregor dgregor at apple.com
Mon Jul 6 20:58:55 PDT 2015


Author: dgregor
Date: Mon Jul  6 22:58:54 2015
New Revision: 241549

URL: http://llvm.org/viewvc/llvm-project?rev=241549&view=rev
Log:
Implement variance for Objective-C type parameters.

Introduce co- and contra-variance for Objective-C type parameters,
which allows us to express that (for example) an NSArray is covariant
in its type parameter. This means that NSArray<NSMutableString *> * is
a subtype of NSArray<NSString *> *, which is expected of the immutable
Foundation collections.

Type parameters can be annotated with __covariant or __contravariant
to make them co- or contra-variant, respectively. This feature can be
detected by __has_feature(objc_generics_variance). Implements
rdar://problem/20217490.

Modified:
    cfe/trunk/include/clang/AST/DeclObjC.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/TokenKinds.def
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/ASTDumper.cpp
    cfe/trunk/lib/AST/ASTImporter.cpp
    cfe/trunk/lib/AST/DeclObjC.cpp
    cfe/trunk/lib/AST/DeclPrinter.cpp
    cfe/trunk/lib/Lex/PPMacroExpansion.cpp
    cfe/trunk/lib/Parse/ParseObjc.cpp
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/Index/comment-objc-parameterized-classes.m
    cfe/trunk/test/PCH/objc_parameterized_classes.m
    cfe/trunk/test/SemaObjC/parameterized_classes.m
    cfe/trunk/test/SemaObjC/parameterized_classes_subst.m
    cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm

Modified: cfe/trunk/include/clang/AST/DeclObjC.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclObjC.h?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclObjC.h (original)
+++ cfe/trunk/include/clang/AST/DeclObjC.h Mon Jul  6 22:58:54 2015
@@ -505,6 +505,18 @@ public:
   friend class ASTDeclWriter;
 };
 
+/// Describes the variance of a given generic parameter.
+enum class ObjCTypeParamVariance : uint8_t {
+  /// The parameter is invariant: must match exactly.
+  Invariant,
+  /// The parameter is covariant, e.g., X<T> is a subtype of X<U> when
+  /// the type parameter is covariant and T is a subtype of U.
+  Covariant,
+  /// The parameter is contravariant, e.g., X<T> is a subtype of X<U>
+  /// when the type parameter is covariant and U is a subtype of T.
+  Contravariant,
+};
+
 /// Represents the declaration of an Objective-C type parameter.
 ///
 /// \code
@@ -521,21 +533,32 @@ class ObjCTypeParamDecl : public Typedef
   void anchor() override;
 
   /// Index of this type parameter in the type parameter list.
-  unsigned Index : 16;
+  unsigned Index : 14;
+
+  /// The variance of the type parameter.
+  unsigned Variance : 2;
 
-  // The location of the ':', which will be valid when the bound was
-  // explicitly specified.
+  /// The location of the variance, if any.
+  SourceLocation VarianceLoc;
+
+  /// The location of the ':', which will be valid when the bound was
+  /// explicitly specified.
   SourceLocation ColonLoc;
 
-  ObjCTypeParamDecl(ASTContext &ctx, DeclContext *dc, unsigned index,
+  ObjCTypeParamDecl(ASTContext &ctx, DeclContext *dc, 
+                    ObjCTypeParamVariance variance, SourceLocation varianceLoc,
+                    unsigned index,
                     SourceLocation nameLoc, IdentifierInfo *name,
                     SourceLocation colonLoc, TypeSourceInfo *boundInfo)
     : TypedefNameDecl(ObjCTypeParam, ctx, dc, nameLoc, nameLoc, name,
                       boundInfo),
-      Index(index), ColonLoc(colonLoc) { }
+      Index(index), Variance(static_cast<unsigned>(variance)),
+      VarianceLoc(varianceLoc), ColonLoc(colonLoc) { }
 
 public:
   static ObjCTypeParamDecl *Create(ASTContext &ctx, DeclContext *dc,
+                                   ObjCTypeParamVariance variance,
+                                   SourceLocation varianceLoc,
                                    unsigned index,
                                    SourceLocation nameLoc,
                                    IdentifierInfo *name,
@@ -545,6 +568,19 @@ public:
 
   SourceRange getSourceRange() const override LLVM_READONLY;
 
+  /// Determine the variance of this type parameter.
+  ObjCTypeParamVariance getVariance() const {
+    return static_cast<ObjCTypeParamVariance>(Variance);
+  }
+
+  /// Set the variance of this type parameter.
+  void setVariance(ObjCTypeParamVariance variance) {
+    Variance = static_cast<unsigned>(variance);
+  }
+
+  /// Retrieve the location of the variance keyword.
+  SourceLocation getVarianceLoc() const { return VarianceLoc; }
+
   /// Retrieve the index into its type parameter list.
   unsigned getIndex() const { return Index; }
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Jul  6 22:58:54 2015
@@ -7782,6 +7782,10 @@ def err_objc_type_param_bound_conflict :
   "type bound %0 for type parameter %1 conflicts with "
   "%select{implicit|previous}2 bound %3%select{for type parameter %5|}4">;
 
+def err_objc_type_param_variance_conflict : Error<
+  "%select{in|co|contra}0variant type parameter %1 conflicts with previous "
+  "%select{in|co|contra}2variant type parameter %3">;
+
 def note_objc_type_param_here : Note<"type parameter %0 declared here">;
 
 def err_objc_type_param_bound_missing : Error<

Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.def Mon Jul  6 22:58:54 2015
@@ -527,7 +527,9 @@ KEYWORD(__bridge_retained            , K
 KEYWORD(__bridge_retain              , KEYARC)
 
 // Objective-C keywords.
-KEYWORD(__kindof                 , KEYOBJC2)
+KEYWORD(__covariant                  , KEYOBJC2)
+KEYWORD(__contravariant              , KEYOBJC2)
+KEYWORD(__kindof                     , KEYOBJC2)
 
 // Alternate spelling for various tokens.  There are GCC extensions in all
 // languages, but should not be disabled in strict conformance mode.

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Jul  6 22:58:54 2015
@@ -7089,7 +7089,10 @@ public:
   };
   ObjCContainerKind getObjCContainerKind() const;
 
-  DeclResult actOnObjCTypeParam(Scope *S, unsigned index,
+  DeclResult actOnObjCTypeParam(Scope *S,
+                                ObjCTypeParamVariance variance,
+                                SourceLocation varianceLoc,
+                                unsigned index,
                                 IdentifierInfo *paramName,
                                 SourceLocation paramLoc,
                                 SourceLocation colonLoc,

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Mon Jul  6 22:58:54 2015
@@ -6943,20 +6943,62 @@ void getIntersectionOfProtocols(ASTConte
                        compareObjCProtocolsByName);
 }
 
+/// Determine whether the first type is a subtype of the second.
+static bool canAssignObjCObjectTypes(ASTContext &ctx, QualType lhs,
+                                     QualType rhs) {
+  // Common case: two object pointers.
+  const ObjCObjectPointerType *lhsOPT = lhs->getAs<ObjCObjectPointerType>();
+  const ObjCObjectPointerType *rhsOPT = rhs->getAs<ObjCObjectPointerType>();
+  if (lhsOPT && rhsOPT)
+    return ctx.canAssignObjCInterfaces(lhsOPT, rhsOPT);
+
+  // Two block pointers.
+  const BlockPointerType *lhsBlock = lhs->getAs<BlockPointerType>();
+  const BlockPointerType *rhsBlock = rhs->getAs<BlockPointerType>();
+  if (lhsBlock && rhsBlock)
+    return ctx.typesAreBlockPointerCompatible(lhs, rhs);
+
+  // If either is an unqualified 'id' and the other is a block, it's
+  // acceptable.
+  if ((lhsOPT && lhsOPT->isObjCIdType() && rhsBlock) ||
+      (rhsOPT && rhsOPT->isObjCIdType() && lhsBlock))
+    return true;
+
+  return false;
+}
+
 // Check that the given Objective-C type argument lists are equivalent.
-static bool sameObjCTypeArgs(const ASTContext &ctx, ArrayRef<QualType> lhsArgs,
+static bool sameObjCTypeArgs(ASTContext &ctx,
+                             const ObjCInterfaceDecl *iface,
+                             ArrayRef<QualType> lhsArgs,
                              ArrayRef<QualType> rhsArgs,
                              bool stripKindOf) {
   if (lhsArgs.size() != rhsArgs.size())
     return false;
 
+  ObjCTypeParamList *typeParams = iface->getTypeParamList();
   for (unsigned i = 0, n = lhsArgs.size(); i != n; ++i) {
-    if (!ctx.hasSameType(lhsArgs[i], rhsArgs[i])) {
+    if (ctx.hasSameType(lhsArgs[i], rhsArgs[i]))
+      continue;
+
+    switch (typeParams->begin()[i]->getVariance()) {
+    case ObjCTypeParamVariance::Invariant:
       if (!stripKindOf ||
           !ctx.hasSameType(lhsArgs[i].stripObjCKindOfType(ctx),
                            rhsArgs[i].stripObjCKindOfType(ctx))) {
         return false;
       }
+      break;
+
+    case ObjCTypeParamVariance::Covariant:
+      if (!canAssignObjCObjectTypes(ctx, lhsArgs[i], rhsArgs[i]))
+        return false;
+      break;
+
+    case ObjCTypeParamVariance::Contravariant:
+      if (!canAssignObjCObjectTypes(ctx, rhsArgs[i], lhsArgs[i]))
+        return false;
+      break;
     }
   }
 
@@ -6989,7 +7031,8 @@ QualType ASTContext::areCommonBaseCompat
       bool anyChanges = false;
       if (LHS->isSpecialized() && RHS->isSpecialized()) {
         // Both have type arguments, compare them.
-        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+        if (!sameObjCTypeArgs(*this, LHS->getInterface(),
+                              LHS->getTypeArgs(), RHS->getTypeArgs(),
                               /*stripKindOf=*/true))
           return QualType();
       } else if (LHS->isSpecialized() != RHS->isSpecialized()) {
@@ -7037,7 +7080,8 @@ QualType ASTContext::areCommonBaseCompat
       bool anyChanges = false;
       if (LHS->isSpecialized() && RHS->isSpecialized()) {
         // Both have type arguments, compare them.
-        if (!sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHS->getTypeArgs(),
+        if (!sameObjCTypeArgs(*this, LHS->getInterface(),
+                              LHS->getTypeArgs(), RHS->getTypeArgs(),
                               /*stripKindOf=*/true))
           return QualType();
       } else if (LHS->isSpecialized() != RHS->isSpecialized()) {
@@ -7127,7 +7171,8 @@ bool ASTContext::canAssignObjCInterfaces
 
     // If the RHS is specializd, compare type arguments.
     if (RHSSuper->isSpecialized() &&
-        !sameObjCTypeArgs(*this, LHS->getTypeArgs(), RHSSuper->getTypeArgs(),
+        !sameObjCTypeArgs(*this, LHS->getInterface(),
+                          LHS->getTypeArgs(), RHSSuper->getTypeArgs(),
                           /*stripKindOf=*/true)) {
       return false;
     }

Modified: cfe/trunk/lib/AST/ASTDumper.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTDumper.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTDumper.cpp (original)
+++ cfe/trunk/lib/AST/ASTDumper.cpp Mon Jul  6 22:58:54 2015
@@ -1475,6 +1475,19 @@ void ASTDumper::VisitObjCMethodDecl(cons
 
 void ASTDumper::VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D) {
   dumpName(D);
+  switch (D->getVariance()) {
+  case ObjCTypeParamVariance::Invariant:
+    break;
+
+  case ObjCTypeParamVariance::Covariant:
+    OS << " covariant";
+    break;
+
+  case ObjCTypeParamVariance::Contravariant:
+    OS << " contravariant";
+    break;
+  }
+
   if (D->hasExplicitBound())
     OS << " bounded";
   dumpType(D->getUnderlyingType());

Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Jul  6 22:58:54 2015
@@ -3452,6 +3452,8 @@ Decl *ASTNodeImporter::VisitObjCTypePara
 
   ObjCTypeParamDecl *Result = ObjCTypeParamDecl::Create(
                                 Importer.getToContext(), DC,
+                                D->getVariance(),
+                                Importer.Import(D->getVarianceLoc()),
                                 D->getIndex(),
                                 Importer.Import(D->getLocation()),
                                 Name.getAsIdentifierInfo(),

Modified: cfe/trunk/lib/AST/DeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclObjC.cpp (original)
+++ cfe/trunk/lib/AST/DeclObjC.cpp Mon Jul  6 22:58:54 2015
@@ -1198,28 +1198,36 @@ ObjCMethodDecl::findPropertyDecl(bool Ch
 void ObjCTypeParamDecl::anchor() { }
 
 ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc,
+                                             ObjCTypeParamVariance variance,
+                                             SourceLocation varianceLoc,
                                              unsigned index,
                                              SourceLocation nameLoc,
                                              IdentifierInfo *name,
                                              SourceLocation colonLoc,
                                              TypeSourceInfo *boundInfo) {
-  return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, index, nameLoc, name, colonLoc,
-                                         boundInfo);
+  return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
+                                         nameLoc, name, colonLoc, boundInfo);
 }
 
 ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx,
                                                          unsigned ID) {
-  return new (ctx, ID) ObjCTypeParamDecl(ctx, nullptr, 0, SourceLocation(),
+  return new (ctx, ID) ObjCTypeParamDecl(ctx, nullptr,
+                                         ObjCTypeParamVariance::Invariant,
+                                         SourceLocation(), 0, SourceLocation(),
                                          nullptr, SourceLocation(), nullptr);
 }
 
 SourceRange ObjCTypeParamDecl::getSourceRange() const {
+  SourceLocation startLoc = VarianceLoc;
+  if (startLoc.isInvalid())
+    startLoc = getLocation();
+
   if (hasExplicitBound()) {
-    return SourceRange(getLocation(),
+    return SourceRange(startLoc,
                        getTypeSourceInfo()->getTypeLoc().getEndLoc());
   }
 
-  return SourceRange(getLocation());
+  return SourceRange(startLoc);
 }
 
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/AST/DeclPrinter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclPrinter.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclPrinter.cpp (original)
+++ cfe/trunk/lib/AST/DeclPrinter.cpp Mon Jul  6 22:58:54 2015
@@ -974,6 +974,19 @@ void DeclPrinter::PrintObjCTypeParams(Ob
       Out << ", ";
     }
 
+    switch (Param->getVariance()) {
+    case ObjCTypeParamVariance::Invariant:
+      break;
+
+    case ObjCTypeParamVariance::Covariant:
+      Out << "__covariant ";
+      break;
+
+    case ObjCTypeParamVariance::Contravariant:
+      Out << "__contravariant ";
+      break;
+    }
+
     Out << Param->getDeclName().getAsString();
 
     if (Param->hasExplicitBound()) {

Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
+++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Mon Jul  6 22:58:54 2015
@@ -1108,6 +1108,7 @@ static bool HasFeature(const Preprocesso
       .Case("objc_bridge_id", true)
       .Case("objc_bridge_id_on_typedefs", true)
       .Case("objc_generics", LangOpts.ObjC2)
+      .Case("objc_generics_variance", LangOpts.ObjC2)
       // C11 features
       .Case("c_alignas", LangOpts.C11)
       .Case("c_alignof", LangOpts.C11)

Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
+++ cfe/trunk/lib/Parse/ParseObjc.cpp Mon Jul  6 22:58:54 2015
@@ -416,11 +416,15 @@ static void addContextSensitiveTypeNulla
 ///     '<' objc-type-parameter (',' objc-type-parameter)* '>'
 ///
 ///   objc-type-parameter:
-///     identifier objc-type-parameter-bound[opt]
+///     objc-type-parameter-variance? identifier objc-type-parameter-bound[opt]
 ///
 ///   objc-type-parameter-bound:
 ///     ':' type-name
 ///
+///   objc-type-parameter-variance:
+///     '__covariant'
+///     '__contravariant'
+///
 /// \param lAngleLoc The location of the starting '<'.
 ///
 /// \param protocolIdents Will capture the list of identifiers, if the
@@ -444,12 +448,15 @@ ObjCTypeParamList *Parser::parseObjCType
   auto makeProtocolIdentsIntoTypeParameters = [&]() {
     unsigned index = 0;
     for (const auto &pair : protocolIdents) {
-      DeclResult typeParam = Actions.actOnObjCTypeParam(getCurScope(),
-                                                        index++,
-                                                        pair.first,
-                                                        pair.second,
-                                                        SourceLocation(),
-                                                        ParsedType());
+      DeclResult typeParam = Actions.actOnObjCTypeParam(
+                               getCurScope(),
+                               ObjCTypeParamVariance::Invariant,
+                               SourceLocation(),
+                               index++,
+                               pair.first,
+                               pair.second,
+                               SourceLocation(),
+                               ParsedType());
       if (typeParam.isUsable())
         typeParams.push_back(typeParam.get());
     }
@@ -460,7 +467,26 @@ ObjCTypeParamList *Parser::parseObjCType
 
   bool invalid = false;
   lAngleLoc = ConsumeToken();
+
   do {
+    // Parse the variance, if any.
+    SourceLocation varianceLoc;
+    ObjCTypeParamVariance variance = ObjCTypeParamVariance::Invariant;
+    if (Tok.is(tok::kw___covariant) || Tok.is(tok::kw___contravariant)) {
+      variance = Tok.is(tok::kw___covariant)
+                   ? ObjCTypeParamVariance::Covariant
+                   : ObjCTypeParamVariance::Contravariant;
+      varianceLoc = ConsumeToken();
+
+      // Once we've seen a variance specific , we know this is not a
+      // list of protocol references.
+      if (mayBeProtocolList) {
+        // Up until now, we have been queuing up parameters because they
+        // might be protocol references. Turn them into parameters now.
+        makeProtocolIdentsIntoTypeParameters();
+      }
+    }
+
     // Parse the identifier.
     if (!Tok.is(tok::identifier)) {
       // Code completion.
@@ -508,6 +534,8 @@ ObjCTypeParamList *Parser::parseObjCType
 
     // Create the type parameter.
     DeclResult typeParam = Actions.actOnObjCTypeParam(getCurScope(),
+                                                      variance,
+                                                      varianceLoc,
                                                       typeParams.size(),
                                                       paramName,
                                                       paramLoc,

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Mon Jul  6 22:58:54 2015
@@ -590,7 +590,10 @@ ActOnSuperClassOfClassInterface(Scope *S
   }
 }
 
-DeclResult Sema::actOnObjCTypeParam(Scope *S, unsigned index,
+DeclResult Sema::actOnObjCTypeParam(Scope *S,
+                                    ObjCTypeParamVariance variance,
+                                    SourceLocation varianceLoc,
+                                    unsigned index,
                                     IdentifierInfo *paramName,
                                     SourceLocation paramLoc,
                                     SourceLocation colonLoc,
@@ -661,8 +664,9 @@ DeclResult Sema::actOnObjCTypeParam(Scop
   }
 
   // Create the type parameter.
-  return ObjCTypeParamDecl::Create(Context, CurContext, index, paramLoc, 
-                                   paramName, colonLoc, typeBoundInfo);
+  return ObjCTypeParamDecl::Create(Context, CurContext, variance, varianceLoc,
+                                   index, paramLoc, paramName, colonLoc,
+                                   typeBoundInfo);
 }
 
 ObjCTypeParamList *Sema::actOnObjCTypeParamList(Scope *S,
@@ -750,6 +754,65 @@ static bool checkTypeParamListConsistenc
     ObjCTypeParamDecl *prevTypeParam = prevTypeParams->begin()[i];
     ObjCTypeParamDecl *newTypeParam = newTypeParams->begin()[i];
 
+    // Check for consistency of the variance.
+    if (newTypeParam->getVariance() != prevTypeParam->getVariance()) {
+      if (newTypeParam->getVariance() == ObjCTypeParamVariance::Invariant &&
+          newContext != TypeParamListContext::Definition) {
+        // When the new type parameter is invariant and is not part
+        // of the definition, just propagate the variance.
+        newTypeParam->setVariance(prevTypeParam->getVariance());
+      } else if (prevTypeParam->getVariance() 
+                   == ObjCTypeParamVariance::Invariant &&
+                 !(isa<ObjCInterfaceDecl>(prevTypeParam->getDeclContext()) &&
+                   cast<ObjCInterfaceDecl>(prevTypeParam->getDeclContext())
+                     ->getDefinition() == prevTypeParam->getDeclContext())) {
+        // When the old parameter is invariant and was not part of the
+        // definition, just ignore the difference because it doesn't
+        // matter.
+      } else {
+        {
+          // Diagnose the conflict and update the second declaration.
+          SourceLocation diagLoc = newTypeParam->getVarianceLoc();
+          if (diagLoc.isInvalid())
+            diagLoc = newTypeParam->getLocStart();
+
+          auto diag = S.Diag(diagLoc,
+                             diag::err_objc_type_param_variance_conflict)
+                        << static_cast<unsigned>(newTypeParam->getVariance())
+                        << newTypeParam->getDeclName()
+                        << static_cast<unsigned>(prevTypeParam->getVariance())
+                        << prevTypeParam->getDeclName();
+          switch (prevTypeParam->getVariance()) {
+          case ObjCTypeParamVariance::Invariant:
+            diag << FixItHint::CreateRemoval(newTypeParam->getVarianceLoc());
+            break;
+
+          case ObjCTypeParamVariance::Covariant:
+          case ObjCTypeParamVariance::Contravariant: {
+            StringRef newVarianceStr
+               = prevTypeParam->getVariance() == ObjCTypeParamVariance::Covariant
+                   ? "__covariant"
+                   : "__contravariant";
+            if (newTypeParam->getVariance()
+                  == ObjCTypeParamVariance::Invariant) {
+              diag << FixItHint::CreateInsertion(newTypeParam->getLocStart(),
+                                                 (newVarianceStr + " ").str());
+            } else {
+              diag << FixItHint::CreateReplacement(newTypeParam->getVarianceLoc(),
+                                               newVarianceStr);
+            }
+          }
+          }
+        }
+
+        S.Diag(prevTypeParam->getLocation(), diag::note_objc_type_param_here)
+          << prevTypeParam->getDeclName();
+
+        // Override the variance.
+        newTypeParam->setVariance(prevTypeParam->getVariance());
+      }
+    }
+
     // If the bound types match, there's nothing to do.
     if (S.Context.hasSameType(prevTypeParam->getUnderlyingType(),
                               newTypeParam->getUnderlyingType()))
@@ -876,6 +939,8 @@ ActOnStartClassInterface(Scope *S, Sourc
             ObjCTypeParamDecl::Create(
               Context,
               CurContext,
+              typeParam->getVariance(),
+              SourceLocation(),
               typeParam->getIndex(),
               SourceLocation(),
               typeParam->getIdentifier(),

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Mon Jul  6 22:58:54 2015
@@ -904,7 +904,9 @@ void ASTDeclReader::VisitObjCMethodDecl(
 
 void ASTDeclReader::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) {
   VisitTypedefNameDecl(D);
+  D->Variance = Record[Idx++];
   D->Index = Record[Idx++];
+  D->VarianceLoc = ReadSourceLocation(Record, Idx);
   D->ColonLoc = ReadSourceLocation(Record, Idx);
 }
 

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Mon Jul  6 22:58:54 2015
@@ -581,7 +581,9 @@ void ASTDeclWriter::VisitObjCMethodDecl(
 
 void ASTDeclWriter::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) {
   VisitTypedefNameDecl(D);
+  Record.push_back(D->Variance);
   Record.push_back(D->Index);
+  Writer.AddSourceLocation(D->VarianceLoc, Record);
   Writer.AddSourceLocation(D->ColonLoc, Record);
 
   Code = serialization::DECL_OBJC_TYPE_PARAM;

Modified: cfe/trunk/test/Index/comment-objc-parameterized-classes.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/comment-objc-parameterized-classes.m?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/test/Index/comment-objc-parameterized-classes.m (original)
+++ cfe/trunk/test/Index/comment-objc-parameterized-classes.m Mon Jul  6 22:58:54 2015
@@ -13,7 +13,7 @@
 @interface NSObject
 @end
 
-// CHECK: <Declaration>@interface A <T : id, U : NSObject *> : NSObject
+// CHECK: <Declaration>@interface A <__covariant T : id, U : NSObject *> : NSObject
 /// A
- at interface A<T : id, U : NSObject *> : NSObject
+ at interface A<__covariant T : id, U : NSObject *> : NSObject
 @end

Modified: cfe/trunk/test/PCH/objc_parameterized_classes.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/objc_parameterized_classes.m?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/test/PCH/objc_parameterized_classes.m (original)
+++ cfe/trunk/test/PCH/objc_parameterized_classes.m Mon Jul  6 22:58:54 2015
@@ -12,11 +12,11 @@ __attribute__((objc_root_class))
 @interface NSObject
 @end
 
- at interface PC1<T, U : NSObject *> : NSObject
+ at interface PC1<__covariant T, U : NSObject *> : NSObject
 // expected-note at -2{{type parameter 'U' declared here}}
 @end
 
- at interface PC1<T, U : NSObject *> (Cat1)
+ at interface PC1<__covariant T, U : NSObject *> (Cat1)
 @end
 
 typedef PC1<id, NSObject *> PC1Specialization1;
@@ -34,4 +34,12 @@ typedef PC1Specialization1<id, NSObject
 
 typedef PC1Specialization2<id, NSObject *> PC1Specialization4; // expected-error{{already-specialized class type 'PC1Specialization2' (aka 'PC1Specialization1<NSObject>')}}
 
+ at interface NSString : NSObject
+ at end
+
+void testCovariance(PC1<NSObject *, NSObject *> *pc1a,
+                    PC1<NSString *, NSObject *> *pc1b) {
+  pc1a = pc1b;
+}
+
 #endif

Modified: cfe/trunk/test/SemaObjC/parameterized_classes.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/parameterized_classes.m?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/parameterized_classes.m (original)
+++ cfe/trunk/test/SemaObjC/parameterized_classes.m Mon Jul  6 22:58:54 2015
@@ -4,6 +4,10 @@
 #  error Compiler does not support Objective-C generics?
 #endif
 
+#if !__has_feature(objc_generics_variance)
+#  error Compiler does not support co- and contr-variance?
+#endif
+
 @protocol NSObject // expected-note{{'NSObject' declared here}}
 @end
 
@@ -315,3 +319,39 @@ void testSpecializedTypePrinting() {
 
 @interface NSFoo : PC1<NSObject *, NSObject *> // okay
 @end
+
+// --------------------------------------------------------------------------
+// Co- and contra-variance.
+// --------------------------------------------------------------------------
+ at class Variance1<T, U>;
+
+ at class Variance1<__covariant T, __contravariant U>;
+
+ at interface Variance1<__covariant T, __contravariant U> : NSObject // expected-note 2{{declared here}}
+ at end
+
+ at interface Variance1<T, U> () // okay, inferred
+ at end
+
+ at interface Variance1<T, U> (Cat1) // okay, inferred
+ at end
+
+ at class Variance1<T, U>; // okay, inferred
+
+ at interface Variance1<__covariant T, __contravariant U> () // okay, matches
+ at end
+
+ at interface Variance1<__covariant T, __contravariant U> (Cat2) // okay, matches
+ at end
+
+ at class Variance1<__covariant T, __contravariant U>; // okay, matches
+
+ at interface Variance1<__contravariant X, // expected-error{{contravariant type parameter 'X' conflicts with previous covariant type parameter 'T'}}
+                     __covariant Y> () // expected-error{{covariant type parameter 'Y' conflicts with previous contravariant type parameter 'U'}}
+ at end
+
+ at class Variance2<__covariant T, __contravariant U>; // expected-note 2{{declared here}}
+
+ at interface Variance2<__contravariant T, // expected-error{{contravariant type parameter 'T' conflicts with previous covariant type parameter 'T'}}
+                     U> : NSObject // expected-error{{invariant type parameter 'U' conflicts with previous contravariant type parameter 'U'}}
+ at end

Modified: cfe/trunk/test/SemaObjC/parameterized_classes_subst.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/parameterized_classes_subst.m?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/parameterized_classes_subst.m (original)
+++ cfe/trunk/test/SemaObjC/parameterized_classes_subst.m Mon Jul  6 22:58:54 2015
@@ -343,6 +343,36 @@ void test_implicit_conversions(NSArray<N
   array = mutStringArray;
 }
 
+ at interface NSCovariant1<__covariant T>
+ at end
+
+ at interface NSContravariant1<__contravariant T>
+ at end
+
+void test_variance(NSCovariant1<NSString *> *covariant1,
+                   NSCovariant1<NSMutableString *> *covariant2,
+                   NSCovariant1<NSString *(^)(void)> *covariant3,
+                   NSCovariant1<NSMutableString *(^)(void)> *covariant4,
+                   NSCovariant1<id> *covariant5,
+                   NSCovariant1<id<NSCopying>> *covariant6,
+                   NSContravariant1<NSString *> *contravariant1,
+                   NSContravariant1<NSMutableString *> *contravariant2) {
+  covariant1 = covariant2; // okay
+  covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *> *' from 'NSCovariant1<NSString *> *'}}
+
+  covariant3 = covariant4; // okay
+  covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *(^)(void)> *' from 'NSCovariant1<NSString *(^)(void)> *'}}
+
+  covariant5 = covariant1; // okay
+  covariant1 = covariant5; // okay: id is promiscuous
+
+  covariant5 = covariant3; // okay
+  covariant3 = covariant5; // okay
+
+  contravariant1 = contravariant2; // expected-warning{{incompatible pointer types assigning to 'NSContravariant1<NSString *> *' from 'NSContravariant1<NSMutableString *> *'}}
+  contravariant2 = contravariant1; // okay
+}
+
 // --------------------------------------------------------------------------
 // Ternary operator
 // --------------------------------------------------------------------------

Modified: cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm?rev=241549&r1=241548&r2=241549&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm (original)
+++ cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm Mon Jul  6 22:58:54 2015
@@ -15,6 +15,9 @@ __attribute__((objc_root_class))
 @interface NSString : NSObject <NSCopying>
 @end
 
+ at interface NSMutableString : NSString
+ at end
+
 @interface NSNumber : NSObject <NSCopying>
 @end
 
@@ -288,6 +291,36 @@ void test_implicit_conversions(NSArray<N
   array = mutStringArray;
 }
 
+ at interface NSCovariant1<__covariant T>
+ at end
+
+ at interface NSContravariant1<__contravariant T>
+ at end
+
+void test_variance(NSCovariant1<NSString *> *covariant1,
+                   NSCovariant1<NSMutableString *> *covariant2,
+                   NSCovariant1<NSString *(^)(void)> *covariant3,
+                   NSCovariant1<NSMutableString *(^)(void)> *covariant4,
+                   NSCovariant1<id> *covariant5,
+                   NSCovariant1<id<NSCopying>> *covariant6,
+                   NSContravariant1<NSString *> *contravariant1,
+                   NSContravariant1<NSMutableString *> *contravariant2) {
+  covariant1 = covariant2; // okay
+  covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *> *' from 'NSCovariant1<NSString *> *'}}
+
+  covariant3 = covariant4; // okay
+  covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1<NSMutableString *(^)()> *' from 'NSCovariant1<NSString *(^)()> *'}}
+
+  covariant5 = covariant1; // okay
+  covariant1 = covariant5; // okay: id is promiscuous
+
+  covariant5 = covariant3; // okay
+  covariant3 = covariant5; // okay
+
+  contravariant1 = contravariant2; // expected-warning{{incompatible pointer types assigning to 'NSContravariant1<NSString *> *' from 'NSContravariant1<NSMutableString *> *'}}
+  contravariant2 = contravariant1; // okay
+}
+
 // --------------------------------------------------------------------------
 // Ternary operator
 // --------------------------------------------------------------------------





More information about the cfe-commits mailing list