115 changes: 98 additions & 17 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1288,10 +1288,14 @@ class Type : public ExtQualsTypeCommonBase {

unsigned : NumTypeBits;

/// The number of type arguments stored directly on this object type.
unsigned NumTypeArgs : 7;

/// NumProtocols - The number of protocols stored directly on this
/// object type.
unsigned NumProtocols : 32 - NumTypeBits;
unsigned NumProtocols : 7;
};
static_assert(NumTypeBits + 7 + 7 <= 32, "Does not fit in an unsigned");

class ReferenceTypeBitfields {
friend class ReferenceType;
Expand Down Expand Up @@ -1586,6 +1590,7 @@ class Type : public ExtQualsTypeCommonBase {
bool isObjCObjectOrInterfaceType() const;
bool isObjCIdType() const; // id
bool isObjCClassType() const; // Class
bool isBlockCompatibleObjCPointerType(ASTContext &ctx) const;
bool isObjCSelType() const; // Class
bool isObjCBuiltinType() const; // 'id' or 'Class'
bool isObjCARCBridgableType() const;
Expand Down Expand Up @@ -4369,19 +4374,25 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode {
};

/// ObjCObjectType - Represents a class type in Objective C.
/// Every Objective C type is a combination of a base type and a
/// list of protocols.
///
/// Every Objective C type is a combination of a base type, a set of
/// type arguments (optional, for parameterized classes) and a list of
/// protocols.
///
/// Given the following declarations:
/// \code
/// \@class C;
/// \@class C<T>;
/// \@protocol P;
/// \endcode
///
/// 'C' is an ObjCInterfaceType C. It is sugar for an ObjCObjectType
/// with base C and no protocols.
///
/// 'C<P>' is an ObjCObjectType with base C and protocol list [P].
/// 'C<P>' is an unspecialized ObjCObjectType with base C and protocol list [P].
/// 'C<C*>' is a specialized ObjCObjectType with type arguments 'C*' and no
/// protocol list.
/// 'C<C*><P>' is a specialized ObjCObjectType with base C, type arguments 'C*',
/// and protocol list [P].
///
/// 'id' is a TypedefType which is sugar for an ObjCObjectPointerType whose
/// pointee is an ObjCObjectType with base BuiltinType::ObjCIdType
Expand All @@ -4391,8 +4402,10 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode {
/// with base BuiltinType::ObjCIdType and protocol list [P]. Eventually
/// this should get its own sugar class to better represent the source.
class ObjCObjectType : public Type {
// ObjCObjectType.NumProtocols - the number of protocols stored
// ObjCObjectType.NumTypeArgs - the number of type arguments stored
// after the ObjCObjectPointerType node.
// ObjCObjectType.NumProtocols - the number of protocols stored
// after the type arguments of ObjCObjectPointerType node.
//
// These protocols are those written directly on the type. If
// protocol qualifiers ever become additive, the iterators will need
Expand All @@ -4408,17 +4421,24 @@ class ObjCObjectType : public Type {
return const_cast<ObjCObjectType*>(this)->getProtocolStorage();
}

QualType *getTypeArgStorage();
const QualType *getTypeArgStorage() const {
return const_cast<ObjCObjectType *>(this)->getTypeArgStorage();
}

ObjCProtocolDecl **getProtocolStorage();

protected:
ObjCObjectType(QualType Canonical, QualType Base,
ObjCProtocolDecl * const *Protocols, unsigned NumProtocols);
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols);

enum Nonce_ObjCInterface { Nonce_ObjCInterface };
ObjCObjectType(enum Nonce_ObjCInterface)
: Type(ObjCInterface, QualType(), false, false, false, false),
BaseType(QualType(this_(), 0)) {
ObjCObjectTypeBits.NumProtocols = 0;
ObjCObjectTypeBits.NumTypeArgs = 0;
}

public:
Expand Down Expand Up @@ -4452,6 +4472,33 @@ class ObjCObjectType : public Type {
/// really is an interface.
ObjCInterfaceDecl *getInterface() const;

/// Determine whether this object type is "specialized", meaning
/// that it has type arguments.
bool isSpecialized() const;

/// Determine whether this object type was written with type arguments.
bool isSpecializedAsWritten() const {
return ObjCObjectTypeBits.NumTypeArgs > 0;
}

/// Determine whether this object type is "unspecialized", meaning
/// that it has no type arguments.
bool isUnspecialized() const { return !isSpecialized(); }

/// Determine whether this object type is "unspecialized" as
/// written, meaning that it has no type arguments.
bool isUnspecializedAsWritten() const { return !isSpecializedAsWritten(); }

/// Retrieve the type arguments of this object type (semantically).
ArrayRef<QualType> getTypeArgs() const;

/// Retrieve the type arguments of this object type as they were
/// written.
ArrayRef<QualType> getTypeArgsAsWritten() const {
return ArrayRef<QualType>(getTypeArgStorage(),
ObjCObjectTypeBits.NumTypeArgs);
}

typedef ObjCProtocolDecl * const *qual_iterator;
typedef llvm::iterator_range<qual_iterator> qual_range;

Expand Down Expand Up @@ -4491,21 +4538,25 @@ class ObjCObjectTypeImpl : public ObjCObjectType, public llvm::FoldingSetNode {
// will need to be modified.

ObjCObjectTypeImpl(QualType Canonical, QualType Base,
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols)
: ObjCObjectType(Canonical, Base, Protocols, NumProtocols) {}
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols)
: ObjCObjectType(Canonical, Base, typeArgs, protocols) {}

public:
void Profile(llvm::FoldingSetNodeID &ID);
static void Profile(llvm::FoldingSetNodeID &ID,
QualType Base,
ObjCProtocolDecl *const *protocols,
unsigned NumProtocols);
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols);
};

inline QualType *ObjCObjectType::getTypeArgStorage() {
return reinterpret_cast<QualType *>(static_cast<ObjCObjectTypeImpl*>(this)+1);
}

inline ObjCProtocolDecl **ObjCObjectType::getProtocolStorage() {
return reinterpret_cast<ObjCProtocolDecl**>(
static_cast<ObjCObjectTypeImpl*>(this) + 1);
return reinterpret_cast<ObjCProtocolDecl**>(
getTypeArgStorage() + ObjCObjectTypeBits.NumTypeArgs);
}

/// ObjCInterfaceType - Interfaces are the core concept in Objective-C for
Expand Down Expand Up @@ -4556,9 +4607,14 @@ class ObjCInterfaceType : public ObjCObjectType {
};

inline ObjCInterfaceDecl *ObjCObjectType::getInterface() const {
if (const ObjCInterfaceType *T =
getBaseType()->getAs<ObjCInterfaceType>())
return T->getDecl();
QualType baseType = getBaseType();
while (const ObjCObjectType *ObjT = baseType->getAs<ObjCObjectType>()) {
if (const ObjCInterfaceType *T = dyn_cast<ObjCInterfaceType>(ObjT))
return T->getDecl();

baseType = ObjT->getBaseType();
}

return nullptr;
}

Expand Down Expand Up @@ -4653,6 +4709,31 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode {
return getObjectType()->isObjCQualifiedClass();
}

/// Whether this type is specialized, meaning that it has type arguments.
bool isSpecialized() const { return getObjectType()->isSpecialized(); }

/// Whether this type is specialized, meaning that it has type arguments.
bool isSpecializedAsWritten() const {
return getObjectType()->isSpecializedAsWritten();
}

/// Whether this type is unspecialized, meaning that is has no type arguments.
bool isUnspecialized() const { return getObjectType()->isUnspecialized(); }

/// Determine whether this object type is "unspecialized" as
/// written, meaning that it has no type arguments.
bool isUnspecializedAsWritten() const { return !isSpecializedAsWritten(); }

/// Retrieve the type arguments for this type.
ArrayRef<QualType> getTypeArgs() const {
return getObjectType()->getTypeArgs();
}

/// Retrieve the type arguments for this type.
ArrayRef<QualType> getTypeArgsAsWritten() const {
return getObjectType()->getTypeArgsAsWritten();
}

/// An iterator over the qualifiers on the object type. Provided
/// for convenience. This will always iterate over the full set of
/// protocols on a type, not just those provided directly.
Expand Down
87 changes: 63 additions & 24 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,9 +799,11 @@ class AttributedTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
};


struct ObjCProtocolListLocInfo {
SourceLocation LAngleLoc;
SourceLocation RAngleLoc;
struct ObjCObjectTypeLocInfo {
SourceLocation TypeArgsLAngleLoc;
SourceLocation TypeArgsRAngleLoc;
SourceLocation ProtocolLAngleLoc;
SourceLocation ProtocolRAngleLoc;
bool HasBaseTypeAsWritten;
};

Expand All @@ -813,25 +815,59 @@ struct ObjCProtocolListLocInfo {
class ObjCObjectTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
ObjCObjectTypeLoc,
ObjCObjectType,
ObjCProtocolListLocInfo> {
// SourceLocations are stored after Info, one for each Protocol.
ObjCObjectTypeLocInfo> {
// TypeSourceInfo*'s are stored after Info, one for each type argument.
TypeSourceInfo **getTypeArgLocArray() const {
return (TypeSourceInfo**)this->getExtraLocalData();
}

// SourceLocations are stored after the type argument information, one for
// each Protocol.
SourceLocation *getProtocolLocArray() const {
return (SourceLocation*) this->getExtraLocalData();
return (SourceLocation*)(getTypeArgLocArray() + getNumTypeArgs());
}

public:
SourceLocation getLAngleLoc() const {
return this->getLocalData()->LAngleLoc;
SourceLocation getTypeArgsLAngleLoc() const {
return this->getLocalData()->TypeArgsLAngleLoc;
}
void setLAngleLoc(SourceLocation Loc) {
this->getLocalData()->LAngleLoc = Loc;
void setTypeArgsLAngleLoc(SourceLocation Loc) {
this->getLocalData()->TypeArgsLAngleLoc = Loc;
}

SourceLocation getRAngleLoc() const {
return this->getLocalData()->RAngleLoc;
SourceLocation getTypeArgsRAngleLoc() const {
return this->getLocalData()->TypeArgsRAngleLoc;
}
void setRAngleLoc(SourceLocation Loc) {
this->getLocalData()->RAngleLoc = Loc;
void setTypeArgsRAngleLoc(SourceLocation Loc) {
this->getLocalData()->TypeArgsRAngleLoc = Loc;
}

unsigned getNumTypeArgs() const {
return this->getTypePtr()->getTypeArgsAsWritten().size();
}

TypeSourceInfo *getTypeArgTInfo(unsigned i) const {
assert(i < getNumTypeArgs() && "Index is out of bounds!");
return getTypeArgLocArray()[i];
}

void setTypeArgTInfo(unsigned i, TypeSourceInfo *TInfo) {
assert(i < getNumTypeArgs() && "Index is out of bounds!");
getTypeArgLocArray()[i] = TInfo;
}

SourceLocation getProtocolLAngleLoc() const {
return this->getLocalData()->ProtocolLAngleLoc;
}
void setProtocolLAngleLoc(SourceLocation Loc) {
this->getLocalData()->ProtocolLAngleLoc = Loc;
}

SourceLocation getProtocolRAngleLoc() const {
return this->getLocalData()->ProtocolRAngleLoc;
}
void setProtocolRAngleLoc(SourceLocation Loc) {
this->getLocalData()->ProtocolRAngleLoc = Loc;
}

unsigned getNumProtocols() const {
Expand Down Expand Up @@ -865,23 +901,26 @@ class ObjCObjectTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
}

SourceRange getLocalSourceRange() const {
return SourceRange(getLAngleLoc(), getRAngleLoc());
SourceLocation start = getTypeArgsLAngleLoc();
if (start.isInvalid())
start = getProtocolLAngleLoc();
SourceLocation end = getProtocolRAngleLoc();
if (end.isInvalid())
end = getTypeArgsRAngleLoc();
return SourceRange(start, end);
}

void initializeLocal(ASTContext &Context, SourceLocation Loc) {
setHasBaseTypeAsWritten(true);
setLAngleLoc(Loc);
setRAngleLoc(Loc);
for (unsigned i = 0, e = getNumProtocols(); i != e; ++i)
setProtocolLoc(i, Loc);
}
void initializeLocal(ASTContext &Context, SourceLocation Loc);

unsigned getExtraLocalDataSize() const {
return this->getNumProtocols() * sizeof(SourceLocation);
return this->getNumTypeArgs() * sizeof(TypeSourceInfo *)
+ this->getNumProtocols() * sizeof(SourceLocation);
}

unsigned getExtraLocalDataAlignment() const {
return llvm::alignOf<SourceLocation>();
static_assert(alignof(ObjCObjectTypeLoc) >= alignof(TypeSourceInfo *),
"not enough alignment for tail-allocated data");
return llvm::alignOf<TypeSourceInfo *>();
}

QualType getInnerType() const {
Expand Down
5 changes: 2 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1022,12 +1022,11 @@ let CategoryName = "Generics Issue" in {
def err_objc_expected_type_parameter : Error<
"expected type parameter name">;

def err_objc_parameterized_class_without_base : Error<
"parameterized Objective-C class %0 must have a superclass">;

def err_objc_parameterized_implementation : Error<
"@implementation cannot have type parameters">;

def err_objc_type_args_after_protocols : Error<
"protocol qualifiers must precede type arguments">;
}

} // end of Parser diagnostics
30 changes: 30 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7780,6 +7780,36 @@ def err_objc_parameterized_forward_class : Error<
def err_objc_parameterized_forward_class_first : Error<
"class %0 previously declared with type parameters">;

def err_objc_type_arg_missing_star : Error<
"type argument %0 must be a pointer (requires a '*')">;

def err_objc_type_arg_missing : Error<
"no type or protocol named %0">;

def err_objc_protocol_suggest : Error<
"no protocol named %0: did you mean %1?">;

def err_objc_type_args_and_protocols : Error<
"angle brackets contain both a %select{type|protocol}0 (%1) and a "
"%select{protocol|type}0 (%2)">;

def err_objc_type_args_non_class : Error<
"type arguments cannot be applied to non-class type %0">;

def err_objc_type_args_non_parameterized_class : Error<
"type arguments cannot be applied to non-parameterized class %0">;

def err_objc_type_args_specialized_class : Error<
"type arguments cannot be applied to already-specialized class type %0">;

def err_objc_type_args_wrong_arity : Error<
"too %select{many|few}0 type arguments for class %1 (have %2, expected %3)">;
}

def err_objc_type_arg_not_id_compatible : Error<
"type argument %0 is neither an Objective-C object nor a block type">;

def err_objc_type_arg_does_not_match_bound : Error<
"type argument %0 does not satisy the bound (%1) of type parameter %2">;

} // end of sema component.
2 changes: 2 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,8 @@ class Parser : public CodeCompletionHandler {
SourceLocation &LAngleLoc,
SourceLocation &EndProtoLoc);
bool ParseObjCProtocolQualifiers(DeclSpec &DS);
void ParseObjCTypeArgsOrProtocolQualifiers(DeclSpec &DS,
bool warnOnIncompleteProtocols);
void ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
Decl *CDecl);
DeclGroupPtrTy ParseObjCAtProtocolDeclaration(SourceLocation atLoc,
Expand Down
28 changes: 28 additions & 0 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,14 @@ class DeclSpec {
// Scope specifier for the type spec, if applicable.
CXXScopeSpec TypeScope;

/// List of Objective-C type arguments, e.g., in \c NSArray<NSView *>.
ArrayRef<ParsedType> ObjCTypeArgs;

/// Location of the '<' that starts a list of Objective-C type arguments.
SourceLocation ObjCTypeArgsLAngleLoc;
/// Location of the '>' that ends a list of Objective-C type arguments.
SourceLocation ObjCTypeArgsRAngleLoc;

// List of protocol qualifiers for objective-c classes. Used for
// protocol-qualified interfaces "NString<foo>" and protocol-qualified id
// "id<foo>".
Expand Down Expand Up @@ -449,6 +457,7 @@ class DeclSpec {
ObjCQualifiers(nullptr) {
}
~DeclSpec() {
delete [] ObjCTypeArgs.data();
delete [] ProtocolQualifiers;
delete [] ProtocolLocs;
}
Expand Down Expand Up @@ -751,6 +760,25 @@ class DeclSpec {
Attrs.takeAllFrom(attrs);
}

/// Determine whether the declaration specifiers contain Objective-C
/// type arguments.
bool hasObjCTypeArgs() const { return !ObjCTypeArgs.empty(); }

ArrayRef<ParsedType> getObjCTypeArgs() const { return ObjCTypeArgs; }
SourceLocation getObjCTypeArgsLAngleLoc() const {
return ObjCTypeArgsLAngleLoc;
}
SourceLocation getObjCTypeArgsRAngleLoc() const {
return ObjCTypeArgsRAngleLoc;
}
SourceRange getObjCTypeArgsRange() const {
return SourceRange(ObjCTypeArgsLAngleLoc, ObjCTypeArgsRAngleLoc);
}

void setObjCTypeArgs(SourceLocation lAngleLoc,
ArrayRef<ParsedType> args,
SourceLocation rAngleLoc);

typedef Decl * const *ProtocolQualifierListTy;
ProtocolQualifierListTy getProtocolQualifiers() const {
return ProtocolQualifiers;
Expand Down
28 changes: 27 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -7098,17 +7098,30 @@ class Sema {
SourceLocation rAngleLoc);
void popObjCTypeParamList(Scope *S, ObjCTypeParamList *typeParamList);

Decl *ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
Decl *ActOnStartClassInterface(Scope *S,
SourceLocation AtInterfaceLoc,
IdentifierInfo *ClassName,
SourceLocation ClassLoc,
ObjCTypeParamList *typeParamList,
IdentifierInfo *SuperName,
SourceLocation SuperLoc,
ArrayRef<ParsedType> SuperTypeArgs,
SourceRange SuperTypeArgsRange,
Decl * const *ProtoRefs,
unsigned NumProtoRefs,
const SourceLocation *ProtoLocs,
SourceLocation EndProtoLoc,
AttributeList *AttrList);

void ActOnSuperClassOfClassInterface(Scope *S,
SourceLocation AtInterfaceLoc,
ObjCInterfaceDecl *IDecl,
IdentifierInfo *ClassName,
SourceLocation ClassLoc,
IdentifierInfo *SuperName,
SourceLocation SuperLoc,
ArrayRef<ParsedType> SuperTypeArgs,
SourceRange SuperTypeArgsRange);

void ActOnTypedefedProtocols(SmallVectorImpl<Decl *> &ProtocolRefs,
IdentifierInfo *SuperName,
Expand Down Expand Up @@ -7174,6 +7187,19 @@ class Sema {
unsigned NumProtocols,
SmallVectorImpl<Decl *> &Protocols);

/// Given a list of identifiers (and their locations), resolve the
/// names to either Objective-C protocol qualifiers or type
/// arguments, as appropriate. The result will be attached to the
/// given declaration specifiers.
void actOnObjCTypeArgsOrProtocolQualifiers(
Scope *S,
DeclSpec &DS,
SourceLocation lAngleLoc,
ArrayRef<IdentifierInfo *> identifiers,
ArrayRef<SourceLocation> identifierLocs,
SourceLocation rAngleLoc,
bool warnOnIncompleteProtocols);

/// Ensure attributes are consistent with type.
/// \param [in, out] Attributes The attributes to check; they will
/// be modified to be consistent with \p PropertyTy.
Expand Down
92 changes: 66 additions & 26 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3618,45 +3618,85 @@ static void SortAndUniqueProtocols(ObjCProtocolDecl **Protocols,
QualType ASTContext::getObjCObjectType(QualType BaseType,
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols) const {
// If the base type is an interface and there aren't any protocols
// to add, then the interface type will do just fine.
if (!NumProtocols && isa<ObjCInterfaceType>(BaseType))
return BaseType;
return getObjCObjectType(BaseType, { },
llvm::makeArrayRef(Protocols, NumProtocols));
}

QualType ASTContext::getObjCObjectType(
QualType baseType,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols) const {
// If the base type is an interface and there aren't any protocols or
// type arguments to add, then the interface type will do just fine.
if (typeArgs.empty() && protocols.empty() && isa<ObjCInterfaceType>(baseType))
return baseType;

// Look in the folding set for an existing type.
llvm::FoldingSetNodeID ID;
ObjCObjectTypeImpl::Profile(ID, BaseType, Protocols, NumProtocols);
ObjCObjectTypeImpl::Profile(ID, baseType, typeArgs, protocols);
void *InsertPos = nullptr;
if (ObjCObjectType *QT = ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(QT, 0);

// Build the canonical type, which has the canonical base type and
// a sorted-and-uniqued list of protocols.
QualType Canonical;
bool ProtocolsSorted = areSortedAndUniqued(Protocols, NumProtocols);
if (!ProtocolsSorted || !BaseType.isCanonical()) {
if (!ProtocolsSorted) {
SmallVector<ObjCProtocolDecl*, 8> Sorted(Protocols,
Protocols + NumProtocols);
unsigned UniqueCount = NumProtocols;

SortAndUniqueProtocols(&Sorted[0], UniqueCount);
Canonical = getObjCObjectType(getCanonicalType(BaseType),
&Sorted[0], UniqueCount);
// Determine the type arguments to be used for canonicalization,
// which may be explicitly specified here or written on the base
// type.
ArrayRef<QualType> effectiveTypeArgs = typeArgs;
if (effectiveTypeArgs.empty()) {
if (auto baseObject = baseType->getAs<ObjCObjectType>())
effectiveTypeArgs = baseObject->getTypeArgs();
}

// Build the canonical type, which has the canonical base type and a
// sorted-and-uniqued list of protocols and the type arguments
// canonicalized.
QualType canonical;
bool typeArgsAreCanonical = std::all_of(effectiveTypeArgs.begin(),
effectiveTypeArgs.end(),
[&](QualType type) {
return type.isCanonical();
});
bool protocolsSorted = areSortedAndUniqued(protocols.data(),
protocols.size());
if (!typeArgsAreCanonical || !protocolsSorted || !baseType.isCanonical()) {
// Determine the canonical type arguments.
ArrayRef<QualType> canonTypeArgs;
SmallVector<QualType, 4> canonTypeArgsVec;
if (!typeArgsAreCanonical) {
canonTypeArgsVec.reserve(effectiveTypeArgs.size());
for (auto typeArg : effectiveTypeArgs)
canonTypeArgsVec.push_back(getCanonicalType(typeArg));
canonTypeArgs = canonTypeArgsVec;
} else {
Canonical = getObjCObjectType(getCanonicalType(BaseType),
Protocols, NumProtocols);
canonTypeArgs = effectiveTypeArgs;
}

ArrayRef<ObjCProtocolDecl *> canonProtocols;
SmallVector<ObjCProtocolDecl*, 8> canonProtocolsVec;
if (!protocolsSorted) {
canonProtocolsVec.insert(canonProtocolsVec.begin(),
protocols.begin(),
protocols.end());
unsigned uniqueCount = protocols.size();
SortAndUniqueProtocols(&canonProtocolsVec[0], uniqueCount);
canonProtocols = llvm::makeArrayRef(&canonProtocolsVec[0], uniqueCount);
} else {
canonProtocols = protocols;
}

canonical = getObjCObjectType(getCanonicalType(baseType), canonTypeArgs,
canonProtocols);

// Regenerate InsertPos.
ObjCObjectTypes.FindNodeOrInsertPos(ID, InsertPos);
}

unsigned Size = sizeof(ObjCObjectTypeImpl);
Size += NumProtocols * sizeof(ObjCProtocolDecl *);
void *Mem = Allocate(Size, TypeAlignment);
unsigned size = sizeof(ObjCObjectTypeImpl);
size += typeArgs.size() * sizeof(QualType);
size += protocols.size() * sizeof(ObjCProtocolDecl *);
void *mem = Allocate(size, TypeAlignment);
ObjCObjectTypeImpl *T =
new (Mem) ObjCObjectTypeImpl(Canonical, BaseType, Protocols, NumProtocols);
new (mem) ObjCObjectTypeImpl(canonical, baseType, typeArgs, protocols);

Types.push_back(T);
ObjCObjectTypes.InsertNode(T, InsertPos);
Expand Down Expand Up @@ -5921,7 +5961,7 @@ void ASTContext::getObjCEncodingForTypeQualifier(Decl::ObjCDeclQualifier QT,

TypedefDecl *ASTContext::getObjCIdDecl() const {
if (!ObjCIdDecl) {
QualType T = getObjCObjectType(ObjCBuiltinIdTy, nullptr, 0);
QualType T = getObjCObjectType(ObjCBuiltinIdTy, { }, { });
T = getObjCObjectPointerType(T);
ObjCIdDecl = buildImplicitTypedef(T, "id");
}
Expand All @@ -5938,7 +5978,7 @@ TypedefDecl *ASTContext::getObjCSelDecl() const {

TypedefDecl *ASTContext::getObjCClassDecl() const {
if (!ObjCClassDecl) {
QualType T = getObjCObjectType(ObjCBuiltinClassTy, nullptr, 0);
QualType T = getObjCObjectType(ObjCBuiltinClassTy, { }, { });
T = getObjCObjectPointerType(T);
ObjCClassDecl = buildImplicitTypedef(T, "Class");
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/ASTDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,22 @@ break; \
if (const PointerType *Ty = QT->getAs<PointerType>()) {
QT = Context.getPointerType(Desugar(Context, Ty->getPointeeType(),
ShouldAKA));
} else if (const auto *Ty = QT->getAs<ObjCObjectPointerType>()) {
QT = Context.getObjCObjectPointerType(Desugar(Context, Ty->getPointeeType(),
ShouldAKA));
} else if (const LValueReferenceType *Ty = QT->getAs<LValueReferenceType>()) {
QT = Context.getLValueReferenceType(Desugar(Context, Ty->getPointeeType(),
ShouldAKA));
} else if (const RValueReferenceType *Ty = QT->getAs<RValueReferenceType>()) {
QT = Context.getRValueReferenceType(Desugar(Context, Ty->getPointeeType(),
ShouldAKA));
} else if (const auto *Ty = QT->getAs<ObjCObjectType>()) {
if (Ty->getBaseType().getTypePtr() != Ty && !ShouldAKA) {
QualType BaseType = Desugar(Context, Ty->getBaseType(), ShouldAKA);
QT = Context.getObjCObjectType(BaseType, Ty->getTypeArgsAsWritten(),
llvm::makeArrayRef(Ty->qual_begin(),
Ty->getNumProtocols()));
}
}

return QC.apply(Context, QT);
Expand Down
27 changes: 17 additions & 10 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,16 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) {
if (ToBaseType.isNull())
return QualType();

SmallVector<QualType, 4> TypeArgs;
for (auto TypeArg : T->getTypeArgs()) {
QualType ImportedTypeArg = Importer.Import(TypeArg);
if (ImportedTypeArg.isNull())
return QualType();

TypeArgs.push_back(ImportedTypeArg);
}


SmallVector<ObjCProtocolDecl *, 4> Protocols;
for (auto *P : T->quals()) {
ObjCProtocolDecl *Protocol
Expand All @@ -1853,9 +1863,8 @@ QualType ASTNodeImporter::VisitObjCObjectType(const ObjCObjectType *T) {
Protocols.push_back(Protocol);
}

return Importer.getToContext().getObjCObjectType(ToBaseType,
Protocols.data(),
Protocols.size());
return Importer.getToContext().getObjCObjectType(ToBaseType, TypeArgs,
Protocols);
}

QualType
Expand Down Expand Up @@ -3694,13 +3703,11 @@ bool ASTNodeImporter::ImportDefinition(ObjCInterfaceDecl *From,

// If this class has a superclass, import it.
if (From->getSuperClass()) {
ObjCInterfaceDecl *Super = cast_or_null<ObjCInterfaceDecl>(
Importer.Import(From->getSuperClass()));
if (!Super)
TypeSourceInfo *SuperTInfo = Importer.Import(From->getSuperClassTInfo());
if (!SuperTInfo)
return true;

To->setSuperClass(Super);
To->setSuperClassLoc(Importer.Import(From->getSuperClassLoc()));

To->setSuperClass(SuperTInfo);
}

// Import protocols
Expand Down Expand Up @@ -5367,7 +5374,7 @@ TypeSourceInfo *ASTImporter::Import(TypeSourceInfo *FromTSI) {
return nullptr;

return ToContext.getTrivialTypeSourceInfo(T,
FromTSI->getTypeLoc().getLocStart());
Import(FromTSI->getTypeLoc().getLocStart()));
}

Decl *ASTImporter::GetAlreadyImportedOrNull(Decl *FromD) {
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/AST/DeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,33 @@ ObjCTypeParamList *ObjCInterfaceDecl::getTypeParamList() const {
return nullptr;
}

ObjCInterfaceDecl *ObjCInterfaceDecl::getSuperClass() const {
// FIXME: Should make sure no callers ever do this.
if (!hasDefinition())
return nullptr;

if (data().ExternallyCompleted)
LoadExternalDefinition();

if (const ObjCObjectType *superType = getSuperClassType()) {
if (ObjCInterfaceDecl *superDecl = superType->getInterface()) {
if (ObjCInterfaceDecl *superDef = superDecl->getDefinition())
return superDef;

return superDecl;
}
}

return nullptr;
}

SourceLocation ObjCInterfaceDecl::getSuperClassLoc() const {
if (TypeSourceInfo *superTInfo = getSuperClassTInfo())
return superTInfo->getTypeLoc().getLocStart();

return SourceLocation();
}

/// FindPropertyVisibleInPrimaryClass - Finds declaration of the property
/// with name 'PropertyId' in the primary class; including those in protocols
/// (direct or indirect) used by the primary class.
Expand Down
99 changes: 87 additions & 12 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,19 +467,56 @@ const RecordType *Type::getAsUnionType() const {
}

ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base,
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols)
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols)
: Type(ObjCObject, Canonical, false, false, false, false),
BaseType(Base)
{
ObjCObjectTypeBits.NumProtocols = NumProtocols;
assert(getNumProtocols() == NumProtocols &&
ObjCObjectTypeBits.NumTypeArgs = typeArgs.size();
assert(getTypeArgsAsWritten().size() == typeArgs.size() &&
"bitfield overflow in type argument count");
ObjCObjectTypeBits.NumProtocols = protocols.size();
assert(getNumProtocols() == protocols.size() &&
"bitfield overflow in protocol count");
if (NumProtocols)
memcpy(getProtocolStorage(), Protocols,
NumProtocols * sizeof(ObjCProtocolDecl*));
if (!typeArgs.empty())
memcpy(getTypeArgStorage(), typeArgs.data(),
typeArgs.size() * sizeof(QualType));
if (!protocols.empty())
memcpy(getProtocolStorage(), protocols.data(),
protocols.size() * sizeof(ObjCProtocolDecl*));
}

bool ObjCObjectType::isSpecialized() const {
// If we have type arguments written here, the type is specialized.
if (ObjCObjectTypeBits.NumTypeArgs > 0)
return true;

if (!qual_empty()) {
// Otherwise, check whether the base type is specialized.
if (auto objcObject = getBaseType()->getAs<ObjCObjectType>())
return objcObject->isSpecialized();
}

// Not specialized.
return false;
}

ArrayRef<QualType> ObjCObjectType::getTypeArgs() const {
// We have type arguments written on this type.
if (isSpecializedAsWritten())
return getTypeArgsAsWritten();

if (!qual_empty()) {
// Look at the base type, which might have type arguments.
if (auto objcObject = getBaseType()->getAs<ObjCObjectType>())
return objcObject->getTypeArgs();
}

// No type arguments.
return { };
}


const ObjCObjectType *Type::getAsObjCQualifiedInterfaceType() const {
// There is no sugar for ObjCObjectType's, just return the canonical
// type pointer if it is the right class. There is no typedef information to
Expand Down Expand Up @@ -2076,15 +2113,20 @@ QualifierCollector::apply(const ASTContext &Context, const Type *T) const {

void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID,
QualType BaseType,
ObjCProtocolDecl * const *Protocols,
unsigned NumProtocols) {
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols) {
ID.AddPointer(BaseType.getAsOpaquePtr());
for (unsigned i = 0; i != NumProtocols; i++)
ID.AddPointer(Protocols[i]);
ID.AddInteger(typeArgs.size());
for (auto typeArg : typeArgs)
ID.AddPointer(typeArg.getAsOpaquePtr());
ID.AddInteger(protocols.size());
for (auto proto : protocols)
ID.AddPointer(proto);
}

void ObjCObjectTypeImpl::Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getBaseType(), qual_begin(), getNumProtocols());
Profile(ID, getBaseType(), getTypeArgs(),
llvm::makeArrayRef(qual_begin(), getNumProtocols()));
}

namespace {
Expand Down Expand Up @@ -2495,6 +2537,39 @@ Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) {
return None;
}

bool Type::isBlockCompatibleObjCPointerType(ASTContext &ctx) const {
const ObjCObjectPointerType *objcPtr = getAs<ObjCObjectPointerType>();
if (!objcPtr)
return false;

if (objcPtr->isObjCIdType()) {
// id is always okay.
return true;
}

// Blocks are NSObjects.
if (ObjCInterfaceDecl *iface = objcPtr->getInterfaceDecl()) {
if (iface->getIdentifier() != ctx.getNSObjectName())
return false;

// Continue to check qualifiers, below.
} else if (objcPtr->isObjCQualifiedIdType()) {
// Continue to check qualifiers, below.
} else {
return false;
}

// Check protocol qualifiers.
for (ObjCProtocolDecl *proto : objcPtr->quals()) {
// Blocks conform to NSObject and NSCopying.
if (proto->getIdentifier() != ctx.getNSObjectName() &&
proto->getIdentifier() != ctx.getNSCopyingName())
return false;
}

return true;
}

Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
if (isObjCARCImplicitlyUnretainedType())
return Qualifiers::OCL_ExplicitNone;
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/AST/TypeLoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,22 @@ TypeLoc TypeLoc::IgnoreParensImpl(TypeLoc TL) {
return TL;
}

void ObjCObjectTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
setHasBaseTypeAsWritten(true);
setTypeArgsLAngleLoc(Loc);
setTypeArgsRAngleLoc(Loc);
for (unsigned i = 0, e = getNumTypeArgs(); i != e; ++i) {
setTypeArgTInfo(i,
Context.getTrivialTypeSourceInfo(
getTypePtr()->getTypeArgsAsWritten()[i], Loc));
}
setProtocolLAngleLoc(Loc);
setProtocolRAngleLoc(Loc);
for (unsigned i = 0, e = getNumProtocols(); i != e; ++i)
setProtocolLoc(i, Loc);
}

void TypeOfTypeLoc::initializeLocal(ASTContext &Context,
SourceLocation Loc) {
TypeofLikeTypeLoc<TypeOfTypeLoc, TypeOfType, TypeOfTypeLocInfo>
Expand Down
71 changes: 34 additions & 37 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1310,59 +1310,56 @@ void TypePrinter::printObjCInterfaceAfter(const ObjCInterfaceType *T,

void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T,
raw_ostream &OS) {
if (T->qual_empty())
if (T->qual_empty() && T->isUnspecializedAsWritten())
return printBefore(T->getBaseType(), OS);

print(T->getBaseType(), OS, StringRef());
OS << '<';
bool isFirst = true;
for (const auto *I : T->quals()) {
if (isFirst)
isFirst = false;
else
OS << ',';
OS << I->getName();

if (T->isSpecializedAsWritten()) {
bool isFirst = true;
OS << '<';
for (auto typeArg : T->getTypeArgsAsWritten()) {
if (isFirst)
isFirst = false;
else
OS << ",";

print(typeArg, OS, StringRef());
}
OS << '>';
}
OS << '>';

if (!T->qual_empty()) {
bool isFirst = true;
OS << '<';
for (const auto *I : T->quals()) {
if (isFirst)
isFirst = false;
else
OS << ',';
OS << I->getName();
}
OS << '>';
}

spaceBeforePlaceHolder(OS);
}
void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T,
raw_ostream &OS) {
if (T->qual_empty())
if (T->qual_empty() && T->isUnspecializedAsWritten())
return printAfter(T->getBaseType(), OS);
}

void TypePrinter::printObjCObjectPointerBefore(const ObjCObjectPointerType *T,
raw_ostream &OS) {
T->getPointeeType().getLocalQualifiers().print(OS, Policy,
/*appendSpaceIfNonEmpty=*/true);

assert(!T->isObjCSelType());
printBefore(T->getPointeeType(), OS);

if (T->isObjCIdType() || T->isObjCQualifiedIdType())
OS << "id";
else if (T->isObjCClassType() || T->isObjCQualifiedClassType())
OS << "Class";
else
OS << T->getInterfaceDecl()->getName();

if (!T->qual_empty()) {
OS << '<';
for (ObjCObjectPointerType::qual_iterator I = T->qual_begin(),
E = T->qual_end();
I != E; ++I) {
OS << (*I)->getName();
if (I+1 != E)
OS << ',';
}
OS << '>';
}

// If we need to print the pointer, print it now.
if (!T->isObjCIdType() && !T->isObjCQualifiedIdType() &&
!T->isObjCClassType() && !T->isObjCQualifiedClassType()) {
OS << " *"; // Don't forget the implicit pointer.
} else {
spaceBeforePlaceHolder(OS);
if (HasEmptyPlaceHolder)
OS << ' ';
OS << '*';
}
}
void TypePrinter::printObjCObjectPointerAfter(const ObjCObjectPointerType *T,
Expand Down
50 changes: 40 additions & 10 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2886,11 +2886,26 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DS.SetRangeEnd(Tok.getAnnotationEndLoc());
ConsumeToken(); // The typename

// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
// Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
// Objective-C supports type arguments and protocol references
// following an Objective-C object pointer type. Handle either
// one of them.
if (Tok.is(tok::less) && getLangOpts().ObjC1) {
ParseObjCTypeArgsOrProtocolQualifiers(
DS, /*warnOnIncompleteProtocols=*/false);

// An Objective-C object pointer followed by type arguments
// can then be followed again by a set of protocol references, e.g.,
// \c NSArray<NSView><NSTextDelegate>
if (Tok.is(tok::less)) {
if (DS.getProtocolQualifiers()) {
Diag(Tok, diag::err_objc_type_args_after_protocols)
<< SourceRange(DS.getProtocolLAngleLoc(), DS.getLocEnd());
SkipUntil(tok::greater, tok::greatergreater);
} else {
ParseObjCProtocolQualifiers(DS);
}
}
}

continue;
}
Expand Down Expand Up @@ -2997,11 +3012,26 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DS.SetRangeEnd(Tok.getLocation());
ConsumeToken(); // The identifier

// Objective-C supports syntax of the form 'id<proto1,proto2>' where 'id'
// is a specific typedef and 'itf<proto1,proto2>' where 'itf' is an
// Objective-C interface.
if (Tok.is(tok::less) && getLangOpts().ObjC1)
ParseObjCProtocolQualifiers(DS);
// Objective-C supports type arguments and protocol references
// following an Objective-C object pointer type. Handle either
// one of them.
if (Tok.is(tok::less) && getLangOpts().ObjC1) {
ParseObjCTypeArgsOrProtocolQualifiers(
DS, /*warnOnIncompleteProtocols=*/false);

// An Objective-C object pointer followed by type arguments
// can then be followed again by a set of protocol references, e.g.,
// \c NSArray<NSView><NSTextDelegate>
if (Tok.is(tok::less)) {
if (DS.getProtocolQualifiers()) {
Diag(Tok, diag::err_objc_type_args_after_protocols)
<< SourceRange(DS.getProtocolLAngleLoc(), DS.getLocEnd());
SkipUntil(tok::greater, tok::greatergreater);
} else {
ParseObjCProtocolQualifiers(DS);
}
}
}

// Need to support trailing type qualifiers (e.g. "id<p> const").
// If a type specifier follows, it will be diagnosed elsewhere.
Expand Down
146 changes: 129 additions & 17 deletions clang/lib/Parse/ParseObjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void Parser::CheckNestedObjCContexts(SourceLocation AtLoc)
/// @end
///
/// objc-superclass:
/// ':' identifier
/// ':' identifier objc-type-arguments[opt]
///
/// objc-class-interface-attributes:
/// __attribute__((visibility("default")))
Expand Down Expand Up @@ -293,6 +293,7 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
// Parse a class interface.
IdentifierInfo *superClassId = nullptr;
SourceLocation superClassLoc;
DeclSpec superClassDS(AttrFactory);

if (Tok.is(tok::colon)) { // a super class is specified.
ConsumeToken();
Expand All @@ -311,18 +312,12 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
}
superClassId = Tok.getIdentifierInfo();
superClassLoc = ConsumeToken();
} else if (typeParameterList) {
// An objc-type-parameter-list is ambiguous with an objc-protocol-refs
// in an @interface without a specified superclass, so such classes
// are ill-formed. We have determined that we have an
// objc-type-parameter-list but no superclass, so complain and record
// as if we inherited from NSObject.
SourceLocation insertLoc = PP.getLocForEndOfToken(PrevTokLocation);
Diag(insertLoc, diag::err_objc_parameterized_class_without_base)
<< nameId
<< FixItHint::CreateInsertion(insertLoc, " : NSObject");
superClassId = PP.getIdentifierInfo("NSObject");
superClassLoc = Tok.getLocation();

// Type arguments for the superclass or protocol conformances.
if (Tok.is(tok::less)) {
ParseObjCTypeArgsOrProtocolQualifiers(superClassDS,
/*warnOnIncompleteProtocols=*/true);
}
}

// Next, we need to check for any protocol references.
Expand All @@ -338,6 +333,16 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
/*ForObjCContainer=*/true,
&ProtocolIdents[0], ProtocolIdents.size(),
ProtocolRefs);
} else if (auto protocols = superClassDS.getProtocolQualifiers()) {
// We already parsed the protocols named when we thought we had a
// type argument list (for a specialized superclass). Treat them
// as actual protocol references.
unsigned numProtocols = superClassDS.getNumProtocolQualifiers();
ProtocolRefs.append(protocols, protocols + numProtocols);
ProtocolLocs.append(superClassDS.getProtocolLocs(),
superClassDS.getProtocolLocs() + numProtocols);
LAngleLoc = superClassDS.getProtocolLAngleLoc();
EndProtoLoc = superClassDS.getLocEnd();
} else if (Tok.is(tok::less) &&
ParseObjCProtocolReferences(ProtocolRefs, ProtocolLocs, true, true,
LAngleLoc, EndProtoLoc)) {
Expand All @@ -348,8 +353,11 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
Actions.ActOnTypedefedProtocols(ProtocolRefs, superClassId, superClassLoc);

Decl *ClsType =
Actions.ActOnStartClassInterface(AtLoc, nameId, nameLoc, typeParameterList,
superClassId, superClassLoc,
Actions.ActOnStartClassInterface(getCurScope(), AtLoc, nameId, nameLoc,
typeParameterList, superClassId,
superClassLoc,
superClassDS.getObjCTypeArgs(),
superClassDS.getObjCTypeArgsRange(),
ProtocolRefs.data(), ProtocolRefs.size(),
ProtocolLocs.data(),
EndProtoLoc, attrs.getList());
Expand Down Expand Up @@ -1554,15 +1562,119 @@ bool Parser::ParseObjCProtocolQualifiers(DeclSpec &DS) {
SmallVector<Decl *, 8> ProtocolDecl;
SmallVector<SourceLocation, 8> ProtocolLocs;
bool Result = ParseObjCProtocolReferences(ProtocolDecl, ProtocolLocs, false,
false,
LAngleLoc, EndProtoLoc);
false, LAngleLoc, EndProtoLoc);
DS.setProtocolQualifiers(ProtocolDecl.data(), ProtocolDecl.size(),
ProtocolLocs.data(), LAngleLoc);
if (EndProtoLoc.isValid())
DS.SetRangeEnd(EndProtoLoc);
return Result;
}

/// Parse Objective-C type arguments or protocol qualifiers.
///
/// objc-type-arguments:
/// '<' type-name (',' type-name)* '>'
///
void Parser::ParseObjCTypeArgsOrProtocolQualifiers(
DeclSpec &DS,
bool warnOnIncompleteProtocols) {
assert(Tok.is(tok::less) && "Not at the start of type args or protocols");
SourceLocation lAngleLoc = ConsumeToken();

// Whether all of the elements we've parsed thus far are single
// identifiers, which might be types or might be protocols.
bool allSingleIdentifiers = true;
SmallVector<IdentifierInfo *, 4> identifiers;
SmallVector<SourceLocation, 4> identifierLocs;

// Parse a list of comma-separated identifiers, bailing out if we
// see something different.
do {
// Parse a single identifier.
if (Tok.is(tok::identifier) &&
(NextToken().is(tok::comma) ||
NextToken().is(tok::greater) ||
NextToken().is(tok::greatergreater))) {
identifiers.push_back(Tok.getIdentifierInfo());
identifierLocs.push_back(ConsumeToken());
continue;
}

if (Tok.is(tok::code_completion)) {
// FIXME: Also include types here.
SmallVector<IdentifierLocPair, 4> identifierLocPairs;
for (unsigned i = 0, n = identifiers.size(); i != n; ++i) {
identifierLocPairs.push_back(IdentifierLocPair(identifiers[i],
identifierLocs[i]));
}

Actions.CodeCompleteObjCProtocolReferences(identifierLocPairs.data(),
identifierLocPairs.size());
cutOffParsing();
return;
}

allSingleIdentifiers = false;
break;
} while (TryConsumeToken(tok::comma));

// If we parsed an identifier list, semantic analysis sorts out
// whether it refers to protocols or to type arguments.
if (allSingleIdentifiers) {
// Parse the closing '>'.
SourceLocation rAngleLoc;
(void)ParseGreaterThanInTemplateList(rAngleLoc, /*ConsumeLastToken=*/true,
/*ObjCGenericList=*/true);

// Let Sema figure out what we parsed.
Actions.actOnObjCTypeArgsOrProtocolQualifiers(getCurScope(),
DS,
lAngleLoc,
identifiers,
identifierLocs,
rAngleLoc,
warnOnIncompleteProtocols);
return;
}

// We syntactically matched a type argument, so commit to parsing
// type arguments.
SmallVector<ParsedType, 4> typeArgs;

// Convert the identifiers into type arguments.
bool invalid = false;
for (unsigned i = 0, n = identifiers.size(); i != n; ++i) {
ParsedType typeArg
= Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope());
if (typeArg) {
typeArgs.push_back(typeArg);
} else {
invalid = true;
}
}

// Continue parsing type-names.
do {
TypeResult typeArg = ParseTypeName();
if (typeArg.isUsable()) {
typeArgs.push_back(typeArg.get());
} else {
invalid = true;
}
} while (TryConsumeToken(tok::comma));

// Parse the closing '>'.
SourceLocation rAngleLoc;
(void)ParseGreaterThanInTemplateList(rAngleLoc, /*ConsumeLastToken=*/true,
/*ObjCGenericList=*/true);

if (invalid)
return;

// Update the DeclSpec appropriately.
DS.setObjCTypeArgs(lAngleLoc, typeArgs, rAngleLoc);
}

void Parser::HelperActionsForIvarDeclarations(Decl *interfaceDecl, SourceLocation atLoc,
BalancedDelimiterTracker &T,
SmallVectorImpl<Decl *> &AllIvarDecls,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case_typename:
// In Objective-C, we might have a protocol-qualified type.
if (getLangOpts().ObjC1 && NextToken().is(tok::less)) {
// Tentatively parse the
// Tentatively parse the protocol qualifiers.
TentativeParsingAction PA(*this);
ConsumeToken(); // The type token

Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/DeclSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ bool DeclSpec::SetConstexprSpec(SourceLocation Loc, const char *&PrevSpec,
return false;
}


bool DeclSpec::SetConceptSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
if (Concept_specified) {
Expand All @@ -905,6 +906,16 @@ bool DeclSpec::SetConceptSpec(SourceLocation Loc, const char *&PrevSpec,
return false;
}

void DeclSpec::setObjCTypeArgs(SourceLocation lAngleLoc,
ArrayRef<ParsedType> args,
SourceLocation rAngleLoc) {
ParsedType *argsCopy = new ParsedType[args.size()];
memcpy(argsCopy, args.data(), args.size() * sizeof(ParsedType));
ObjCTypeArgs = llvm::makeArrayRef(argsCopy, args.size());
ObjCTypeArgsLAngleLoc = lAngleLoc;
ObjCTypeArgsRAngleLoc = rAngleLoc;
}

void DeclSpec::setProtocolQualifiers(Decl * const *Protos,
unsigned NP,
SourceLocation *ProtoLocs,
Expand Down
549 changes: 468 additions & 81 deletions clang/lib/Sema/SemaDeclObjC.cpp

Large diffs are not rendered by default.

34 changes: 2 additions & 32 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5826,36 +5826,6 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS,
return ResultTy;
}

/// \brief Returns true if QT is quelified-id and implements 'NSObject' and/or
/// 'NSCopying' protocols (and nothing else); or QT is an NSObject and optionally
/// implements 'NSObject' and/or NSCopying' protocols (and nothing else).
static bool isObjCPtrBlockCompatible(Sema &S, ASTContext &C, QualType QT) {
if (QT->isObjCIdType())
return true;

const ObjCObjectPointerType *OPT = QT->getAs<ObjCObjectPointerType>();
if (!OPT)
return false;

if (ObjCInterfaceDecl *ID = OPT->getInterfaceDecl())
if (ID->getIdentifier() != &C.Idents.get("NSObject"))
return false;

ObjCProtocolDecl* PNSCopying =
S.LookupProtocol(&C.Idents.get("NSCopying"), SourceLocation());
ObjCProtocolDecl* PNSObject =
S.LookupProtocol(&C.Idents.get("NSObject"), SourceLocation());

for (auto *Proto : OPT->quals()) {
if ((PNSCopying && declaresSameEntity(Proto, PNSCopying)) ||
(PNSObject && declaresSameEntity(Proto, PNSObject)))
;
else
return false;
}
return true;
}

/// \brief Return the resulting type when the operands are both block pointers.
static QualType checkConditionalBlockPointerCompatibility(Sema &S,
ExprResult &LHS,
Expand Down Expand Up @@ -7008,8 +6978,8 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS,
}

// Only under strict condition T^ is compatible with an Objective-C pointer.
if (RHSType->isBlockPointerType() &&
isObjCPtrBlockCompatible(*this, Context, LHSType)) {
if (RHSType->isBlockPointerType() &&
LHSType->isBlockCompatibleObjCPointerType(Context)) {
maybeExtendBlockObject(*this, RHS);
Kind = CK_BlockPointerToObjCPointerCast;
return Compatible;
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Sema/SemaExprObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,10 @@ ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR,
LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) {
ObjCProtocolDecl *PQ[] = {NSCopyingPDecl};
QIDNSCopying =
Context.getObjCObjectType(Context.ObjCBuiltinIdTy,
(ObjCProtocolDecl**) PQ,1);
Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { },
llvm::makeArrayRef(
(ObjCProtocolDecl**) PQ,
1));
QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying);
}
}
Expand Down
230 changes: 193 additions & 37 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,160 @@ static void diagnoseAndRemoveTypeQualifiers(Sema &S, const DeclSpec &DS,
}
}

/// Apply Objective-C type arguments to the given type.
static QualType applyObjCTypeArgs(Sema &S, SourceLocation loc, QualType type,
ArrayRef<ParsedType> typeArgs,
SourceRange typeArgsRange) {
// We can only apply type arguments to an Objective-C class type.
const auto *objcObjectType = type->getAs<ObjCObjectType>();
if (!objcObjectType || !objcObjectType->getInterface()) {
S.Diag(loc, diag::err_objc_type_args_non_class)
<< type
<< typeArgsRange;
return type;
}

// The class type must be parameterized.
ObjCInterfaceDecl *objcClass = objcObjectType->getInterface();
ObjCTypeParamList *typeParams = objcClass->getTypeParamList();
if (!typeParams) {
S.Diag(loc, diag::err_objc_type_args_non_parameterized_class)
<< objcClass->getDeclName()
<< FixItHint::CreateRemoval(typeArgsRange);
return type;
}

// The type must not already be specialized.
if (objcObjectType->isSpecialized()) {
S.Diag(loc, diag::err_objc_type_args_specialized_class)
<< type
<< FixItHint::CreateRemoval(typeArgsRange);
return type;
}

// Make sure that we have the right number of type arguments.
if (typeArgs.size() != typeParams->size()) {
S.Diag(loc, diag::err_objc_type_args_wrong_arity)
<< (typeArgs.size() < typeParams->size())
<< objcClass->getDeclName()
<< (unsigned)typeArgs.size()
<< (unsigned)typeParams->size();
S.Diag(objcClass->getLocation(), diag::note_previous_decl)
<< objcClass;
return type;
}

// Check the type arguments.
SmallVector<QualType, 4> finalTypeArgs;
for (unsigned i = 0, n = typeArgs.size(); i != n; ++i) {
TypeSourceInfo *typeArgInfo = nullptr;
QualType typeArg = S.GetTypeFromParser(typeArgs[i], &typeArgInfo);
finalTypeArgs.push_back(typeArg);

// Objective-C object pointer types must be substitutable for the bounds.
if (const auto *typeArgObjC = typeArg->getAs<ObjCObjectPointerType>()) {
// Retrieve the bound.
ObjCTypeParamDecl *typeParam = typeParams->begin()[i];
QualType bound = typeParam->getUnderlyingType();
const auto *boundObjC = bound->getAs<ObjCObjectPointerType>();

// Determine whether the type argument is substitutable for the bound.
if (typeArgObjC->isObjCIdType()) {
// When the type argument is 'id', the only acceptable type
// parameter bound is 'id'.
if (boundObjC->isObjCIdType())
continue;
} else if (S.Context.canAssignObjCInterfaces(boundObjC, typeArgObjC)) {
// Otherwise, we follow the assignability rules.
continue;
}

// Diagnose the mismatch.
S.Diag(typeArgInfo->getTypeLoc().getLocStart(),
diag::err_objc_type_arg_does_not_match_bound)
<< typeArg << bound << typeParam->getDeclName();
S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here)
<< typeParam->getDeclName();

return type;
}

// Block pointer types are permitted for unqualified 'id' bounds.
if (typeArg->isBlockPointerType()) {
// Retrieve the bound.
ObjCTypeParamDecl *typeParam = typeParams->begin()[i];
QualType bound = typeParam->getUnderlyingType();
if (bound->isBlockCompatibleObjCPointerType(S.Context))
continue;

// Diagnose the mismatch.
S.Diag(typeArgInfo->getTypeLoc().getLocStart(),
diag::err_objc_type_arg_does_not_match_bound)
<< typeArg << bound << typeParam->getDeclName();
S.Diag(typeParam->getLocation(), diag::note_objc_type_param_here)
<< typeParam->getDeclName();

return type;
}

// Dependent types will be checked at instantiation time.
if (typeArg->isDependentType()) {
continue;
}

// Diagnose non-id-compatible type arguments.
S.Diag(typeArgInfo->getTypeLoc().getLocStart(),
diag::err_objc_type_arg_not_id_compatible)
<< typeArg
<< typeArgInfo->getTypeLoc().getSourceRange();
return type;
}

// Success. Form the specialized type.
return S.Context.getObjCObjectType(type, finalTypeArgs, { });
}

/// Apply Objective-C protocol qualifiers to the given type.
static QualType applyObjCProtocolQualifiers(
Sema &S, SourceLocation loc, SourceRange range, QualType type,
ArrayRef<ObjCProtocolDecl *> protocols,
const SourceLocation *protocolLocs) {
ASTContext &ctx = S.Context;
if (const ObjCObjectType *objT = dyn_cast<ObjCObjectType>(type.getTypePtr())){
// FIXME: Check for protocols to which the class type is already
// known to conform.

return ctx.getObjCObjectType(objT->getBaseType(),
objT->getTypeArgsAsWritten(),
protocols);
}

if (type->isObjCObjectType()) {
// Silently overwrite any existing protocol qualifiers.
// TODO: determine whether that's the right thing to do.

// FIXME: Check for protocols to which the class type is already
// known to conform.
return ctx.getObjCObjectType(type, { }, protocols);
}

// id<protocol-list>
if (type->isObjCIdType()) {
type = ctx.getObjCObjectType(ctx.ObjCBuiltinIdTy, { }, protocols);
return ctx.getObjCObjectPointerType(type);
}

// Class<protocol-list>
if (type->isObjCClassType()) {
type = ctx.getObjCObjectType(ctx.ObjCBuiltinClassTy, { }, protocols);
return ctx.getObjCObjectPointerType(type);
}

S.Diag(loc, diag::err_invalid_protocol_qualifiers)
<< range;
return type;
}

/// \brief Convert the specified declspec to the appropriate type
/// object.
/// \param state Specifies the declarator containing the declaration specifier
Expand Down Expand Up @@ -803,9 +957,10 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
case DeclSpec::TST_unspecified:
// "<proto1,proto2>" is an objc qualified ID with a missing id.
if (DeclSpec::ProtocolQualifierListTy PQ = DS.getProtocolQualifiers()) {
Result = Context.getObjCObjectType(Context.ObjCBuiltinIdTy,
(ObjCProtocolDecl*const*)PQ,
DS.getNumProtocolQualifiers());
Result = Context.getObjCObjectType(Context.ObjCBuiltinIdTy, { },
llvm::makeArrayRef(
(ObjCProtocolDecl*const*)PQ,
DS.getNumProtocolQualifiers()));
Result = Context.getObjCObjectPointerType(Result);
break;
}
Expand Down Expand Up @@ -967,37 +1122,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
DS.getTypeSpecSign() == 0 &&
"Can't handle qualifiers on typedef names yet!");
Result = S.GetTypeFromParser(DS.getRepAsType());
if (Result.isNull())
if (Result.isNull()) {
declarator.setInvalidType(true);
else if (DeclSpec::ProtocolQualifierListTy PQ
= DS.getProtocolQualifiers()) {
if (const ObjCObjectType *ObjT = Result->getAs<ObjCObjectType>()) {
// Silently drop any existing protocol qualifiers.
// TODO: determine whether that's the right thing to do.
if (ObjT->getNumProtocols())
Result = ObjT->getBaseType();

if (DS.getNumProtocolQualifiers())
Result = Context.getObjCObjectType(Result,
(ObjCProtocolDecl*const*) PQ,
DS.getNumProtocolQualifiers());
} else if (Result->isObjCIdType()) {
// id<protocol-list>
Result = Context.getObjCObjectType(Context.ObjCBuiltinIdTy,
(ObjCProtocolDecl*const*) PQ,
DS.getNumProtocolQualifiers());
Result = Context.getObjCObjectPointerType(Result);
} else if (Result->isObjCClassType()) {
// Class<protocol-list>
Result = Context.getObjCObjectType(Context.ObjCBuiltinClassTy,
(ObjCProtocolDecl*const*) PQ,
DS.getNumProtocolQualifiers());
Result = Context.getObjCObjectPointerType(Result);
} else {
S.Diag(DeclLoc, diag::err_invalid_protocol_qualifiers)
<< DS.getSourceRange();
declarator.setInvalidType(true);
}
} else if (S.getLangOpts().OpenCL) {
if (const AtomicType *AT = Result->getAs<AtomicType>()) {
const BuiltinType *BT = AT->getValueType()->getAs<BuiltinType>();
Expand All @@ -1022,6 +1148,21 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
declarator.setInvalidType(true);
}
}
} else {
// Apply Objective-C type arguments.
if (DS.hasObjCTypeArgs()) {
Result = applyObjCTypeArgs(S, DeclLoc, Result, DS.getObjCTypeArgs(),
DS.getObjCTypeArgsRange());
}

// Apply Objective-C protocol qualifiers.
if (DeclSpec::ProtocolQualifierListTy PQ = DS.getProtocolQualifiers()) {
Result = applyObjCProtocolQualifiers(
S, DeclLoc, DS.getSourceRange(), Result,
llvm::makeArrayRef((ObjCProtocolDecl * const *)PQ,
DS.getNumProtocolQualifiers()),
DS.getProtocolLocs());
}
}

// TypeQuals handled by caller.
Expand Down Expand Up @@ -4138,18 +4279,33 @@ namespace {
Visit(TL.getBaseLoc());
}

// Type arguments.
if (TL.getNumTypeArgs() > 0) {
assert(TL.getNumTypeArgs() == DS.getObjCTypeArgs().size());
TL.setTypeArgsLAngleLoc(DS.getObjCTypeArgsLAngleLoc());
TL.setTypeArgsRAngleLoc(DS.getObjCTypeArgsRAngleLoc());
for (unsigned i = 0, n = TL.getNumTypeArgs(); i != n; ++i) {
TypeSourceInfo *typeArgInfo = nullptr;
(void)Sema::GetTypeFromParser(DS.getObjCTypeArgs()[i], &typeArgInfo);
TL.setTypeArgTInfo(i, typeArgInfo);
}
} else {
TL.setTypeArgsLAngleLoc(SourceLocation());
TL.setTypeArgsRAngleLoc(SourceLocation());
}

// Protocol qualifiers.
if (DS.getProtocolQualifiers()) {
assert(TL.getNumProtocols() > 0);
assert(TL.getNumProtocols() == DS.getNumProtocolQualifiers());
TL.setLAngleLoc(DS.getProtocolLAngleLoc());
TL.setRAngleLoc(DS.getSourceRange().getEnd());
TL.setProtocolLAngleLoc(DS.getProtocolLAngleLoc());
TL.setProtocolRAngleLoc(DS.getSourceRange().getEnd());
for (unsigned i = 0, e = DS.getNumProtocolQualifiers(); i != e; ++i)
TL.setProtocolLoc(i, DS.getProtocolLocs()[i]);
} else {
assert(TL.getNumProtocols() == 0);
TL.setLAngleLoc(SourceLocation());
TL.setRAngleLoc(SourceLocation());
TL.setProtocolLAngleLoc(SourceLocation());
TL.setProtocolRAngleLoc(SourceLocation());
}
}
void VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc TL) {
Expand Down
14 changes: 11 additions & 3 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5263,11 +5263,15 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
case TYPE_OBJC_OBJECT: {
unsigned Idx = 0;
QualType Base = readType(*Loc.F, Record, Idx);
unsigned NumTypeArgs = Record[Idx++];
SmallVector<QualType, 4> TypeArgs;
for (unsigned I = 0; I != NumTypeArgs; ++I)
TypeArgs.push_back(readType(*Loc.F, Record, Idx));
unsigned NumProtos = Record[Idx++];
SmallVector<ObjCProtocolDecl*, 4> Protos;
for (unsigned I = 0; I != NumProtos; ++I)
Protos.push_back(ReadDeclAs<ObjCProtocolDecl>(*Loc.F, Record, Idx));
return Context.getObjCObjectType(Base, Protos.data(), NumProtos);
return Context.getObjCObjectType(Base, TypeArgs, Protos);
}

case TYPE_OBJC_OBJECT_POINTER: {
Expand Down Expand Up @@ -5646,8 +5650,12 @@ void TypeLocReader::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
}
void TypeLocReader::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
TL.setHasBaseTypeAsWritten(Record[Idx++]);
TL.setLAngleLoc(ReadSourceLocation(Record, Idx));
TL.setRAngleLoc(ReadSourceLocation(Record, Idx));
TL.setTypeArgsLAngleLoc(ReadSourceLocation(Record, Idx));
TL.setTypeArgsRAngleLoc(ReadSourceLocation(Record, Idx));
for (unsigned i = 0, e = TL.getNumTypeArgs(); i != e; ++i)
TL.setTypeArgTInfo(i, Reader.GetTypeSourceInfo(F, Record, Idx));
TL.setProtocolLAngleLoc(ReadSourceLocation(Record, Idx));
TL.setProtocolRAngleLoc(ReadSourceLocation(Record, Idx));
for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i)
TL.setProtocolLoc(i, ReadSourceLocation(Record, Idx));
}
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,8 +953,7 @@ void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) {
ObjCInterfaceDecl::DefinitionData &Data = ID->data();

// Read the superclass.
Data.SuperClass = ReadDeclAs<ObjCInterfaceDecl>(Record, Idx);
Data.SuperClassLoc = ReadSourceLocation(Record, Idx);
Data.SuperClassTInfo = GetTypeSourceInfo(Record, Idx);

Data.EndLoc = ReadSourceLocation(Record, Idx);
Data.HasDesignatedInitializers = Record[Idx++];
Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ void ASTTypeWriter::VisitObjCInterfaceType(const ObjCInterfaceType *T) {

void ASTTypeWriter::VisitObjCObjectType(const ObjCObjectType *T) {
Writer.AddTypeRef(T->getBaseType(), Record);
Record.push_back(T->getTypeArgs().size());
for (auto TypeArg : T->getTypeArgs())
Writer.AddTypeRef(TypeArg, Record);
Record.push_back(T->getNumProtocols());
for (const auto *I : T->quals())
Writer.AddDeclRef(I, Record);
Expand Down Expand Up @@ -648,8 +651,12 @@ void TypeLocWriter::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) {
}
void TypeLocWriter::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
Record.push_back(TL.hasBaseTypeAsWritten());
Writer.AddSourceLocation(TL.getLAngleLoc(), Record);
Writer.AddSourceLocation(TL.getRAngleLoc(), Record);
Writer.AddSourceLocation(TL.getTypeArgsLAngleLoc(), Record);
Writer.AddSourceLocation(TL.getTypeArgsRAngleLoc(), Record);
for (unsigned i = 0, e = TL.getNumTypeArgs(); i != e; ++i)
Writer.AddTypeSourceInfo(TL.getTypeArgTInfo(i), Record);
Writer.AddSourceLocation(TL.getProtocolLAngleLoc(), Record);
Writer.AddSourceLocation(TL.getProtocolRAngleLoc(), Record);
for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i)
Writer.AddSourceLocation(TL.getProtocolLoc(i), Record);
}
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,8 +604,7 @@ void ASTDeclWriter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
// Write the DefinitionData
ObjCInterfaceDecl::DefinitionData &Data = D->data();

Writer.AddDeclRef(D->getSuperClass(), Record);
Writer.AddSourceLocation(D->getSuperClassLoc(), Record);
Writer.AddTypeSourceInfo(D->getSuperClassTInfo(), Record);
Writer.AddSourceLocation(D->getEndOfDefinitionLoc(), Record);
Record.push_back(Data.HasDesignatedInitializers);

Expand Down
42 changes: 42 additions & 0 deletions clang/test/Index/annotate-parameterized-classes.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@protocol NSObject
@end

@interface NSObject
@end

@interface A<T : id, U : NSObject *> : NSObject
@end

@interface A<T : id, U : NSObject *> (Cat1)
@end

typedef A<id<NSObject>, NSObject *> ASpecialization1;

@interface B<T : id, U : NSObject *> : A<T, U>
@end

// RUN: c-index-test -test-annotate-tokens=%s:7:1:9:1 %s -target x86_64-apple-macosx10.7.0 | FileCheck -check-prefix=CHECK-INTERFACE-DECL %s
// CHECK-INTERFACE-DECL: Identifier: "T" [7:14 - 7:15] TemplateTypeParameter=T:7:14
// FIXME: Should be a type reference
// CHECK-INTERFACE-DECL: Identifier: "id" [7:18 - 7:20] TemplateTypeParameter=T:7:14
// CHECK-INTERFACE-DECL: Identifier: "U" [7:22 - 7:23] TemplateTypeParameter=U:7:22
// FIXME: Should be a class reference
// CHECK-INTERFACE-DECL: Identifier: "NSObject" [7:26 - 7:34] TemplateTypeParameter=U:7:22

// RUN: c-index-test -test-annotate-tokens=%s:10:1:12:1 %s -target x86_64-apple-macosx10.7.0 | FileCheck -check-prefix=CHECK-CATEGORY-DECL %s
// CHECK-CATEGORY-DECL: Identifier: "T" [10:14 - 10:15] TemplateTypeParameter=T:10:14
// FIXME: Should be a type reference
// CHECK-CATEGORY-DECL: Identifier: "id" [10:18 - 10:20] TemplateTypeParameter=T:10:14
// CHECK-CATEGORY-DECL: Identifier: "U" [10:22 - 10:23] TemplateTypeParameter=U:10:22
// FIXME: Should be a class reference
// CHECK-CATEGORY-DECL: Identifier: "NSObject" [10:26 - 10:34] TemplateTypeParameter=U:10:22

// RUN: c-index-test -test-annotate-tokens=%s:13:1:14:1 %s -target x86_64-apple-macosx10.7.0 | FileCheck -check-prefix=CHECK-SPECIALIZATION %s
// CHECK-SPECIALIZATION: Identifier: "id" [13:11 - 13:13] TypeRef=id:0:0
// CHECK-SPECIALIZATION: Identifier: "NSObject" [13:14 - 13:22] ObjCProtocolRef=NSObject:1:11
// CHECK-SPECIALIZATION: Identifier: "NSObject" [13:25 - 13:33] ObjCClassRef=NSObject:4:12

// RUN: c-index-test -test-annotate-tokens=%s:15:1:16:1 %s -target x86_64-apple-macosx10.7.0 | FileCheck -check-prefix=CHECK-SUPER %s
// CHECK-SUPER: Identifier: "A" [15:40 - 15:41] ObjCSuperClassRef=A:7:12
// CHECK-SUPER: Identifier: "T" [15:42 - 15:43] TypeRef=T:15:14
// CHECK-SUPER: Identifier: "U" [15:45 - 15:46] TypeRef=U:15:22
3 changes: 1 addition & 2 deletions clang/test/Index/complete-method-decls.m
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ @implementation I2

// RUN: c-index-test -code-completion-at=%s:85:2 %s | FileCheck -check-prefix=CHECK-CLASSTY %s
// CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text Class<P1>}{RightParen )}{TypedText meth}
// FIXME: It should be "MyObject <P1> *""
// CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text A<P1> *}{RightParen )}{TypedText meth2}
// CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text MyObject<P1> *}{RightParen )}{TypedText meth2}
// CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text MyObjectRef}{RightParen )}{TypedText meth3}

// RUN: c-index-test -code-completion-at=%s:93:2 %s | FileCheck -check-prefix=CHECK-NULLABILITY %s
Expand Down
4 changes: 4 additions & 0 deletions clang/test/PCH/objc_parameterized_classes.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ @interface PC1<T, U : NSObject *> : NSObject
@interface PC1<T, U : NSObject *> (Cat1)
@end

typedef PC1<id, NSObject *> PC1Specialization1;

#else

@interface PC1<T : NSObject *, // expected-error{{type bound 'NSObject *' for type parameter 'T' conflicts with implicit bound 'id}}
Expand All @@ -27,4 +29,6 @@ @interface PC1<T : NSObject *, // expected-error{{type bound 'NSObject *' for ty
// expected-note@15{{type parameter 'U' declared here}}
@end

typedef PC1Specialization1<id, NSObject *> PC1Specialization2; // expected-error{{type arguments cannot be applied to already-specialized class type 'PC1Specialization1' (aka 'PC1<id,NSObject *>')}}

#endif
7 changes: 1 addition & 6 deletions clang/test/Parser/objcxx11-protocol-in-template.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
template<class T> class vector {};
@protocol P @end

#if __cplusplus >= 201103L
// expected-no-diagnostics
#else
// expected-error@14{{a space is required between consecutive right angle brackets}}
// expected-error@15{{a space is required between consecutive right angle brackets}}
#endif
// expected-no-diagnostics

vector<id<P>> v;
vector<vector<id<P>>> v2;
2 changes: 1 addition & 1 deletion clang/test/SemaObjC/interface-1.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

@interface NSWhatever :
NSObject // expected-error {{cannot find interface declaration for 'NSObject'}}
<NSCopying> // expected-error {{cannot find protocol declaration for 'NSCopying'}}
<NSCopying> // expected-error {{no type or protocol named 'NSCopying'}}
@end


Expand Down
147 changes: 140 additions & 7 deletions clang/test/SemaObjC/parameterized_classes.m
Original file line number Diff line number Diff line change
@@ -1,39 +1,43 @@
// RUN: %clang_cc1 %s -verify
// RUN: %clang_cc1 -fblocks %s -verify

@protocol NSObject
@protocol NSObject // expected-note{{'NSObject' declared here}}
@end

@protocol NSCopying // expected-note{{'NSCopying' declared here}}
@end

__attribute__((objc_root_class))
@interface NSObject <NSObject> // expected-note{{'NSObject' defined here}}
@end

@interface NSString : NSObject
@interface NSString : NSObject <NSCopying>
@end

// --------------------------------------------------------------------------
// Parsing parameterized classes.
// --------------------------------------------------------------------------

// Parse type parameters with a bound
@interface PC1<T, U : NSObject*> : NSObject
@interface PC1<T, U : NSObject*> : NSObject // expected-note{{'PC1' declared here}}
// expected-note@-1{{type parameter 'T' declared here}}
// expected-note@-2{{type parameter 'U' declared here}}
// expected-note@-3{{type parameter 'U' declared here}}
@end

// Parse a type parameter with a bound that terminates in '>>'.
@interface PC2<T : id<NSObject>> : NSObject // expected-error{{a space is required between consecutive right angle brackets (use '> >')}}
@interface PC2<T : id<NSObject>> : NSObject
@end

// Parse multiple type parameters.
@interface PC3<T, U : id> : NSObject
@end

// Parse multiple type parameters--grammatically ambiguous with protocol refs.
@interface PC4<T, U, V> : NSObject
@interface PC4<T, U, V> : NSObject // expected-note 2{{'PC4' declared here}}
@end

// Parse a type parameter list without a superclass.
@interface PC5<T : id> // expected-error{{parameterized Objective-C class 'PC5' must have a superclass}}
@interface PC5<T : id>
@end

// Parse a type parameter with name conflicts.
Expand Down Expand Up @@ -92,6 +96,7 @@ @interface PC12<T : NSObject *> : NSObject // expected-note{{type parameter 'T'

// Parameterized forward declaration a class that is not parameterized.
@class NSObject<T>; // expected-error{{forward declaration of non-parameterized class 'NSObject' cannot have type parameters}}
// expected-note@-1{{'NSObject' declared here}}

// Parameterized forward declaration preceding the definition (that is
// not parameterized).
Expand Down Expand Up @@ -190,3 +195,131 @@ void test_PC20_unspecialized(PC20 *pc20) {
ip = [pc20 extMethod: 0]; // expected-warning{{incompatible pointer types assigning to 'int *' from 'X' (aka 'id')}}
[pc20 extMethod: ip]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'Y' (aka 'NSObject *')}}
}

// --------------------------------------------------------------------------
// Parsing type arguments.
// --------------------------------------------------------------------------

typedef NSString * ObjCStringRef; // expected-note{{'ObjCStringRef' declared here}}

// Type arguments with a mix of identifiers and type-names.
typedef PC4<id, NSObject *, NSString *> typeArgs1;

// Type arguments with only identifiers.
typedef PC4<id, id, id> typeArgs2;

// Type arguments with only identifiers; one is ambiguous (resolved as
// types).
typedef PC4<NSObject, id, id> typeArgs3; // expected-error{{type argument 'NSObject' must be a pointer (requires a '*')}}

// Type arguments with only identifiers; one is ambiguous (resolved as
// protocol qualifiers).
typedef PC4<NSObject, NSCopying> protocolQuals1;

// Type arguments and protocol qualifiers.
typedef PC4<id, NSObject *, id><NSObject, NSCopying> typeArgsAndProtocolQuals1;

// Type arguments and protocol qualifiers in the wrong order.
typedef PC4<NSObject, NSCopying><id, NSObject *, id> typeArgsAndProtocolQuals2; // expected-error{{protocol qualifiers must precede type arguments}}

// Type arguments and protocol qualifiers (identifiers).
typedef PC4<id, NSObject, id><NSObject, NSCopying> typeArgsAndProtocolQuals3; // expected-error{{type argument 'NSObject' must be a pointer (requires a '*')}}

// Typo correction: protocol bias.
typedef PC4<NSCopying, NSObjec> protocolQuals2; // expected-error{{cannot find protocol declaration for 'NSObjec'; did you mean 'NSObject'?}}

// Typo correction: type bias.
typedef PC4<id, id, NSObjec> typeArgs4; // expected-error{{unknown class name 'NSObjec'; did you mean 'NSObject'?}}
// expected-error@-1{{type argument 'NSObject' must be a pointer (requires a '*')}}

// Typo correction: bias set by correction itself to a protocol.
typedef PC4<NSObject, NSObject, NSCopyin> protocolQuals3; // expected-error{{cannot find protocol declaration for 'NSCopyin'; did you mean 'NSCopying'?}}

// Typo correction: bias set by correction itself to a type.
typedef PC4<NSObject, NSObject, ObjCStringref> typeArgs5; // expected-error{{unknown type name 'ObjCStringref'; did you mean 'ObjCStringRef'?}}
// expected-error@-1{{type argument 'NSObject' must be a pointer (requires a '*')}}
// expected-error@-2{{type argument 'NSObject' must be a pointer (requires a '*')}}

// Type/protocol conflict.
typedef PC4<NSCopying, ObjCStringRef> typeArgsProtocolQualsConflict1; // expected-error{{angle brackets contain both a type ('ObjCStringRef') and a protocol ('NSCopying')}}

// Handling the '>>' in type argument lists.
typedef PC4<id<NSCopying>, NSObject *, id<NSObject>> typeArgs6;

// --------------------------------------------------------------------------
// Checking type arguments.
// --------------------------------------------------------------------------

@interface PC15<T : id, U : NSObject *, V : id<NSCopying>> : NSObject
// expected-note@-1{{type parameter 'V' declared here}}
// expected-note@-2{{type parameter 'V' declared here}}
// expected-note@-3{{type parameter 'U' declared here}}
@end

typedef PC4<NSString *> tooFewTypeArgs1; // expected-error{{too few type arguments for class 'PC4' (have 1, expected 3)}}

typedef PC4<NSString *, NSString *, NSString *, NSString *> tooManyTypeArgs1; // expected-error{{too many type arguments for class 'PC4' (have 4, expected 3)}}

typedef PC15<int (^)(int, int), // block pointers as 'id'
NSString *, // subclass
NSString *> typeArgs7; // class that conforms to the protocol

typedef PC15<NSObject *, NSObject *, id<NSCopying>> typeArgs8;

typedef PC15<NSObject *, NSObject *,
NSObject *> typeArgs8b; // expected-error{{type argument 'NSObject *' does not satisy the bound ('id<NSCopying>') of type parameter 'V'}}

typedef PC15<id,
id, // expected-error{{type argument 'id' does not satisy the bound ('NSObject *') of type parameter 'U'}}
id> typeArgs9;

typedef PC15<id, NSObject *,
id> typeArgs10; // expected-error{{type argument 'id' does not satisy the bound ('id<NSCopying>') of type parameter 'V'}}

typedef PC15<id,
int (^)(int, int), // okay
id<NSCopying, NSObject>> typeArgs11;

typedef PC15<id, NSString *, int (^)(int, int)> typeArgs12; // okay

typedef NSObject<id, id> typeArgs13; // expected-error{{type arguments cannot be applied to non-parameterized class 'NSObject'}}

typedef id<id, id> typeArgs14; // expected-error{{type arguments cannot be applied to non-class type 'id'}}

typedef PC1<NSObject *, NSString *> typeArgs15;

typedef PC1<NSObject *, NSString *><NSCopying> typeArgsAndProtocolQuals4;

typedef typeArgs15<NSCopying> typeArgsAndProtocolQuals5;

typedef typeArgs15<NSObject *, NSString *> typeArgs16; // expected-error{{type arguments cannot be applied to already-specialized class type 'typeArgs15' (aka 'PC1<NSObject *,NSString *>')}}

typedef typeArgs15<NSObject> typeArgsAndProtocolQuals6;

void testSpecializedTypePrinting() {
int *ip;

ip = (typeArgs15*)0; // expected-warning{{'typeArgs15 *' (aka 'PC1<NSObject *,NSString *> *')}}
ip = (typeArgsAndProtocolQuals4*)0; // expected-warning{{'typeArgsAndProtocolQuals4 *' (aka 'PC1<NSObject *,NSString *><NSCopying> *')}}
ip = (typeArgsAndProtocolQuals5*)0; // expected-warning{{'typeArgsAndProtocolQuals5 *' (aka 'typeArgs15<NSCopying> *')}}
ip = (typeArgsAndProtocolQuals6)0; // expected-error{{used type 'typeArgsAndProtocolQuals6' (aka 'typeArgs15<NSObject>')}}
ip = (typeArgsAndProtocolQuals6*)0;// expected-warning{{'typeArgsAndProtocolQuals6 *' (aka 'typeArgs15<NSObject> *')}}
}

// --------------------------------------------------------------------------
// Specialized superclasses
// --------------------------------------------------------------------------
@interface PC21<T : NSObject *> : PC1<T, T>
@end

@interface PC22<T : NSObject *> : PC1<T> // expected-error{{too few type arguments for class 'PC1' (have 1, expected 2)}}
@end

@interface PC23<T : NSObject *> : PC1<T, U> // expected-error{{unknown type name 'U'}}
@end

@interface PC24<T> : PC1<T, T> // expected-error{{type argument 'T' (aka 'id') does not satisy the bound ('NSObject *') of type parameter 'U'}}
@end

@interface NSFoo : PC1<NSObject *, NSObject *> // okay
@end
26 changes: 26 additions & 0 deletions clang/test/SemaObjCXX/parameterized_classes.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_cc1 -std=c++11 %s -verify

// expected-no-diagnostics
@protocol NSObject
@end

@protocol NSCopying
@end

__attribute__((objc_root_class))
@interface NSObject <NSObject>
@end

@interface NSString : NSObject
@end

// --------------------------------------------------------------------------
// Parsing parameterized classes.
// --------------------------------------------------------------------------
@interface PC1<T, U, V> : NSObject
@end

// --------------------------------------------------------------------------
// Parsing type arguments.
// --------------------------------------------------------------------------
typedef PC1<::NSString *, NSString *, id<NSCopying>> typeArgs1;
44 changes: 43 additions & 1 deletion clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,9 @@ bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) {
TU)))
return true;

if (VisitObjCTypeParamList(ND->getTypeParamList()))
return true;

ObjCCategoryDecl::protocol_loc_iterator PL = ND->protocol_loc_begin();
for (ObjCCategoryDecl::protocol_iterator I = ND->protocol_begin(),
E = ND->protocol_end(); I != E; ++I, ++PL)
Expand Down Expand Up @@ -1080,19 +1083,48 @@ bool CursorVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) {
return false;
}

bool CursorVisitor::VisitObjCTypeParamList(ObjCTypeParamList *typeParamList) {
if (!typeParamList)
return false;

for (auto *typeParam : *typeParamList) {
// Visit the type parameter.
if (Visit(MakeCXCursor(typeParam, TU, RegionOfInterest)))
return true;

// Visit the bound, if it's explicit.
if (typeParam->hasExplicitBound()) {
if (auto TInfo = typeParam->getTypeSourceInfo()) {
if (Visit(TInfo->getTypeLoc()))
return true;
}
}
}

return false;
}

bool CursorVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
if (!D->isThisDeclarationADefinition()) {
// Forward declaration is treated like a reference.
return Visit(MakeCursorObjCClassRef(D, D->getLocation(), TU));
}

// Objective-C type parameters.
if (VisitObjCTypeParamList(D->getTypeParamListAsWritten()))
return true;

// Issue callbacks for super class.
if (D->getSuperClass() &&
Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(),
D->getSuperClassLoc(),
TU)))
return true;

if (TypeSourceInfo *SuperClassTInfo = D->getSuperClassTInfo())
if (Visit(SuperClassTInfo->getTypeLoc()))
return true;

ObjCInterfaceDecl::protocol_loc_iterator PL = D->protocol_loc_begin();
for (ObjCInterfaceDecl::protocol_iterator I = D->protocol_begin(),
E = D->protocol_end(); I != E; ++I, ++PL)
Expand Down Expand Up @@ -1486,6 +1518,11 @@ bool CursorVisitor::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) {
if (TL.hasBaseTypeAsWritten() && Visit(TL.getBaseLoc()))
return true;

for (unsigned I = 0, N = TL.getNumTypeArgs(); I != N; ++I) {
if (Visit(TL.getTypeArgTInfo(I)->getTypeLoc()))
return true;
}

for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) {
if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), TL.getProtocolLoc(I),
TU)))
Expand Down Expand Up @@ -4411,7 +4448,12 @@ static enum CXChildVisitResult GetCursorVisitor(CXCursor cursor,
*BestCursor = getTypeRefedCallExprCursor(*BestCursor);
return CXChildVisit_Recurse;
}


// If we already have an Objective-C superclass reference, don't
// update it further.
if (BestCursor->kind == CXCursor_ObjCSuperClassRef)
return CXChildVisit_Break;

*BestCursor = cursor;
return CXChildVisit_Recurse;
}
Expand Down
1 change: 1 addition & 0 deletions clang/tools/libclang/CursorVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class CursorVisitor : public DeclVisitor<CursorVisitor, bool>,
bool VisitObjCCategoryDecl(ObjCCategoryDecl *ND);
bool VisitObjCProtocolDecl(ObjCProtocolDecl *PID);
bool VisitObjCPropertyDecl(ObjCPropertyDecl *PD);
bool VisitObjCTypeParamList(ObjCTypeParamList *typeParamList);
bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D);
bool VisitObjCImplDecl(ObjCImplDecl *D);
bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D);
Expand Down