diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index ac48f31e72a2..fc8e95884695 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -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 is a subtype of X when + /// the type parameter is covariant and T is a subtype of U. + Covariant, + /// The parameter is contravariant, e.g., X is a subtype of X + /// 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 TypedefNameDecl { void anchor() override; /// Index of this type parameter in the type parameter list. - unsigned Index : 16; + unsigned Index : 14; - // The location of the ':', which will be valid when the bound was - // explicitly specified. + /// The variance of the type parameter. + unsigned Variance : 2; + + /// 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(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(Variance); + } + + /// Set the variance of this type parameter. + void setVariance(ObjCTypeParamVariance variance) { + Variance = static_cast(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; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e606cbd25898..3b2d56ce3a0c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7782,6 +7782,10 @@ def err_objc_type_param_bound_conflict : Error< "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< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 6c0a6414f95c..7a91c9f502b7 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -527,7 +527,9 @@ KEYWORD(__bridge_retained , KEYARC) 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. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index fc6cb7d550e8..0590f4362889 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 58d703a67ed7..fb9630180dca 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6943,20 +6943,62 @@ void getIntersectionOfProtocols(ASTContext &Context, 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(); + const ObjCObjectPointerType *rhsOPT = rhs->getAs(); + if (lhsOPT && rhsOPT) + return ctx.canAssignObjCInterfaces(lhsOPT, rhsOPT); + + // Two block pointers. + const BlockPointerType *lhsBlock = lhs->getAs(); + const BlockPointerType *rhsBlock = rhs->getAs(); + 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 lhsArgs, +static bool sameObjCTypeArgs(ASTContext &ctx, + const ObjCInterfaceDecl *iface, + ArrayRef lhsArgs, ArrayRef 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::areCommonBaseCompatible( 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::areCommonBaseCompatible( 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(const ObjCObjectType *LHS, // 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; } diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp index 3dec23d00653..c95922b141e0 100644 --- a/clang/lib/AST/ASTDumper.cpp +++ b/clang/lib/AST/ASTDumper.cpp @@ -1475,6 +1475,19 @@ void ASTDumper::VisitObjCMethodDecl(const ObjCMethodDecl *D) { 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()); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index d264e962beb2..8460030b4b20 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3452,6 +3452,8 @@ Decl *ASTNodeImporter::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { ObjCTypeParamDecl *Result = ObjCTypeParamDecl::Create( Importer.getToContext(), DC, + D->getVariance(), + Importer.Import(D->getVarianceLoc()), D->getIndex(), Importer.Import(D->getLocation()), Name.getAsIdentifierInfo(), diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 714824b02a25..da4bc549cf42 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1198,28 +1198,36 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { 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); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 3e5e7c0c3551..3202d8c75436 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -974,6 +974,19 @@ void DeclPrinter::PrintObjCTypeParams(ObjCTypeParamList *Params) { 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()) { diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 6790908fe4ba..64ce8c918258 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1108,6 +1108,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .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) diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 0f90b6846855..4f16d47dfa75 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -416,11 +416,15 @@ static void addContextSensitiveTypeNullability(Parser &P, /// '<' 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::parseObjCTypeParamListOrProtocolRefs( 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::parseObjCTypeParamListOrProtocolRefs( 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::parseObjCTypeParamListOrProtocolRefs( // Create the type parameter. DeclResult typeParam = Actions.actOnObjCTypeParam(getCurScope(), + variance, + varianceLoc, typeParams.size(), paramName, paramLoc, diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 74fbec23545c..e67955e8e121 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -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(Scope *S, unsigned index, } // 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 checkTypeParamListConsistency(Sema &S, 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(prevTypeParam->getDeclContext()) && + cast(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(newTypeParam->getVariance()) + << newTypeParam->getDeclName() + << static_cast(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, SourceLocation AtInterfaceLoc, ObjCTypeParamDecl::Create( Context, CurContext, + typeParam->getVariance(), + SourceLocation(), typeParam->getIndex(), SourceLocation(), typeParam->getIdentifier(), diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index c54c5697e59d..52019fcd328a 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -904,7 +904,9 @@ void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { 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); } diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 4d3fb78b0801..371aea8088a0 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -581,7 +581,9 @@ void ASTDeclWriter::VisitObjCMethodDecl(ObjCMethodDecl *D) { 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; diff --git a/clang/test/Index/comment-objc-parameterized-classes.m b/clang/test/Index/comment-objc-parameterized-classes.m index 173fcecf3315..5a928509dfe6 100644 --- a/clang/test/Index/comment-objc-parameterized-classes.m +++ b/clang/test/Index/comment-objc-parameterized-classes.m @@ -13,7 +13,7 @@ @interface NSObject @end -// CHECK: @interface A <T : id, U : NSObject *> : NSObject +// CHECK: @interface A <__covariant T : id, U : NSObject *> : NSObject /// A -@interface A : NSObject +@interface A<__covariant T : id, U : NSObject *> : NSObject @end diff --git a/clang/test/PCH/objc_parameterized_classes.m b/clang/test/PCH/objc_parameterized_classes.m index 616d4a2659d7..0c1885871045 100644 --- a/clang/test/PCH/objc_parameterized_classes.m +++ b/clang/test/PCH/objc_parameterized_classes.m @@ -12,11 +12,11 @@ __attribute__((objc_root_class)) @interface NSObject @end -@interface PC1 : NSObject +@interface PC1<__covariant T, U : NSObject *> : NSObject // expected-note@-2{{type parameter 'U' declared here}} @end -@interface PC1 (Cat1) +@interface PC1<__covariant T, U : NSObject *> (Cat1) @end typedef PC1 PC1Specialization1; @@ -34,4 +34,12 @@ typedef PC1Specialization1 PC1Specialization3; // expected-error typedef PC1Specialization2 PC1Specialization4; // expected-error{{already-specialized class type 'PC1Specialization2' (aka 'PC1Specialization1')}} +@interface NSString : NSObject +@end + +void testCovariance(PC1 *pc1a, + PC1 *pc1b) { + pc1a = pc1b; +} + #endif diff --git a/clang/test/SemaObjC/parameterized_classes.m b/clang/test/SemaObjC/parameterized_classes.m index eb3e122e820d..644fe3a329af 100644 --- a/clang/test/SemaObjC/parameterized_classes.m +++ b/clang/test/SemaObjC/parameterized_classes.m @@ -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 // okay @end + +// -------------------------------------------------------------------------- +// Co- and contra-variance. +// -------------------------------------------------------------------------- +@class Variance1; + +@class Variance1<__covariant T, __contravariant U>; + +@interface Variance1<__covariant T, __contravariant U> : NSObject // expected-note 2{{declared here}} +@end + +@interface Variance1 () // okay, inferred +@end + +@interface Variance1 (Cat1) // okay, inferred +@end + +@class Variance1; // okay, inferred + +@interface Variance1<__covariant T, __contravariant U> () // okay, matches +@end + +@interface Variance1<__covariant T, __contravariant U> (Cat2) // okay, matches +@end + +@class Variance1<__covariant T, __contravariant U>; // okay, matches + +@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'}} +@end + +@class Variance2<__covariant T, __contravariant U>; // expected-note 2{{declared here}} + +@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'}} +@end diff --git a/clang/test/SemaObjC/parameterized_classes_subst.m b/clang/test/SemaObjC/parameterized_classes_subst.m index dc243069ca1b..f90ee9093592 100644 --- a/clang/test/SemaObjC/parameterized_classes_subst.m +++ b/clang/test/SemaObjC/parameterized_classes_subst.m @@ -343,6 +343,36 @@ void test_implicit_conversions(NSArray *stringArray, array = mutStringArray; } +@interface NSCovariant1<__covariant T> +@end + +@interface NSContravariant1<__contravariant T> +@end + +void test_variance(NSCovariant1 *covariant1, + NSCovariant1 *covariant2, + NSCovariant1 *covariant3, + NSCovariant1 *covariant4, + NSCovariant1 *covariant5, + NSCovariant1> *covariant6, + NSContravariant1 *contravariant1, + NSContravariant1 *contravariant2) { + covariant1 = covariant2; // okay + covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} + + covariant3 = covariant4; // okay + covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} + + 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 *' from 'NSContravariant1 *'}} + contravariant2 = contravariant1; // okay +} + // -------------------------------------------------------------------------- // Ternary operator // -------------------------------------------------------------------------- diff --git a/clang/test/SemaObjCXX/parameterized_classes_subst.mm b/clang/test/SemaObjCXX/parameterized_classes_subst.mm index 1b4c86edb768..f342ac0224ce 100644 --- a/clang/test/SemaObjCXX/parameterized_classes_subst.mm +++ b/clang/test/SemaObjCXX/parameterized_classes_subst.mm @@ -15,6 +15,9 @@ __attribute__((objc_root_class)) @interface NSString : NSObject @end +@interface NSMutableString : NSString +@end + @interface NSNumber : NSObject @end @@ -288,6 +291,36 @@ void test_implicit_conversions(NSArray *stringArray, array = mutStringArray; } +@interface NSCovariant1<__covariant T> +@end + +@interface NSContravariant1<__contravariant T> +@end + +void test_variance(NSCovariant1 *covariant1, + NSCovariant1 *covariant2, + NSCovariant1 *covariant3, + NSCovariant1 *covariant4, + NSCovariant1 *covariant5, + NSCovariant1> *covariant6, + NSContravariant1 *contravariant1, + NSContravariant1 *contravariant2) { + covariant1 = covariant2; // okay + covariant2 = covariant1; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} + + covariant3 = covariant4; // okay + covariant4 = covariant3; // expected-warning{{incompatible pointer types assigning to 'NSCovariant1 *' from 'NSCovariant1 *'}} + + 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 *' from 'NSContravariant1 *'}} + contravariant2 = contravariant1; // okay +} + // -------------------------------------------------------------------------- // Ternary operator // --------------------------------------------------------------------------