diff options
author | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:58:54 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2015-07-07 03:58:54 +0000 |
commit | 1ac1b63c9ca898e66aae9775cca6045082907b9a (patch) | |
tree | b9196b185b0185c84e60f99881aaffd69f4da93d /clang/lib/AST | |
parent | ab209d83be5dadff4f17364a71f323b89e3c63f8 (diff) | |
download | bcm5719-llvm-1ac1b63c9ca898e66aae9775cca6045082907b9a.tar.gz bcm5719-llvm-1ac1b63c9ca898e66aae9775cca6045082907b9a.zip |
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.
llvm-svn: 241549
Diffstat (limited to 'clang/lib/AST')
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 55 | ||||
-rw-r--r-- | clang/lib/AST/ASTDumper.cpp | 13 | ||||
-rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 2 | ||||
-rw-r--r-- | clang/lib/AST/DeclObjC.cpp | 18 | ||||
-rw-r--r-- | clang/lib/AST/DeclPrinter.cpp | 13 |
5 files changed, 91 insertions, 10 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 58d703a67ed..fb9630180dc 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<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::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 3dec23d0065..c95922b141e 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 d264e962beb..8460030b4b2 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 714824b02a2..da4bc549cf4 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 3e5e7c0c355..3202d8c7543 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()) { |