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/Sema/SemaDeclObjC.cpp | |
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/Sema/SemaDeclObjC.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 71 |
1 files changed, 68 insertions, 3 deletions
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 74fbec23545..e67955e8e12 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<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, SourceLocation AtInterfaceLoc, ObjCTypeParamDecl::Create( Context, CurContext, + typeParam->getVariance(), + SourceLocation(), typeParam->getIndex(), SourceLocation(), typeParam->getIdentifier(), |