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