mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-10 18:11:19 +00:00
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
This commit is contained in:
parent
ab209d83be
commit
1ac1b63c9c
@ -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 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<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; }
|
||||
|
||||
|
@ -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<
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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()) {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
@interface A<T : id, U : NSObject *> : NSObject
|
||||
@interface A<__covariant T : id, U : NSObject *> : NSObject
|
||||
@end
|
||||
|
@ -12,11 +12,11 @@ __attribute__((objc_root_class))
|
||||
@interface NSObject
|
||||
@end
|
||||
|
||||
@interface PC1<T, U : NSObject *> : NSObject
|
||||
@interface PC1<__covariant T, U : NSObject *> : NSObject
|
||||
// expected-note@-2{{type parameter 'U' declared here}}
|
||||
@end
|
||||
|
||||
@interface PC1<T, U : NSObject *> (Cat1)
|
||||
@interface PC1<__covariant T, U : NSObject *> (Cat1)
|
||||
@end
|
||||
|
||||
typedef PC1<id, NSObject *> PC1Specialization1;
|
||||
@ -34,4 +34,12 @@ typedef PC1Specialization1<id, NSObject *> PC1Specialization3; // expected-error
|
||||
|
||||
typedef PC1Specialization2<id, NSObject *> PC1Specialization4; // expected-error{{already-specialized class type 'PC1Specialization2' (aka 'PC1Specialization1<NSObject>')}}
|
||||
|
||||
@interface NSString : NSObject
|
||||
@end
|
||||
|
||||
void testCovariance(PC1<NSObject *, NSObject *> *pc1a,
|
||||
PC1<NSString *, NSObject *> *pc1b) {
|
||||
pc1a = pc1b;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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.
|
||||
// --------------------------------------------------------------------------
|
||||
@class Variance1<T, U>;
|
||||
|
||||
@class Variance1<__covariant T, __contravariant U>;
|
||||
|
||||
@interface Variance1<__covariant T, __contravariant U> : NSObject // expected-note 2{{declared here}}
|
||||
@end
|
||||
|
||||
@interface Variance1<T, U> () // okay, inferred
|
||||
@end
|
||||
|
||||
@interface Variance1<T, U> (Cat1) // okay, inferred
|
||||
@end
|
||||
|
||||
@class Variance1<T, U>; // 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
|
||||
|
@ -343,6 +343,36 @@ void test_implicit_conversions(NSArray<NSString *> *stringArray,
|
||||
array = mutStringArray;
|
||||
}
|
||||
|
||||
@interface NSCovariant1<__covariant T>
|
||||
@end
|
||||
|
||||
@interface NSContravariant1<__contravariant T>
|
||||
@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
|
||||
// --------------------------------------------------------------------------
|
||||
|
@ -15,6 +15,9 @@ __attribute__((objc_root_class))
|
||||
@interface NSString : NSObject <NSCopying>
|
||||
@end
|
||||
|
||||
@interface NSMutableString : NSString
|
||||
@end
|
||||
|
||||
@interface NSNumber : NSObject <NSCopying>
|
||||
@end
|
||||
|
||||
@ -288,6 +291,36 @@ void test_implicit_conversions(NSArray<NSString *> *stringArray,
|
||||
array = mutStringArray;
|
||||
}
|
||||
|
||||
@interface NSCovariant1<__covariant T>
|
||||
@end
|
||||
|
||||
@interface NSContravariant1<__contravariant T>
|
||||
@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
|
||||
// --------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user