diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:05:56 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:05:56 +0000 |
commit | ebcb57a8d298862c65043e88b2429591ab3c58d3 (patch) | |
tree | adbe4e0a60340ec2858e4b8d74d7bb0f23887d67 | |
parent | 9f86af897458b3b44e26b9e06a857f626f71a692 (diff) |
Add clang support for new Objective-C literal syntax for NSDictionary, NSArray,
NSNumber, and boolean literals. This includes both Sema and Codegen support.
Included is also support for new Objective-C container subscripting.
My apologies for the large patch. It was very difficult to break apart.
The patch introduces changes to the driver as well to cause clang to link
in additional runtime support when needed to support the new language features.
Docs are forthcoming to document the implementation and behavior of these features.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152137 91177308-0d34-0410-b5e6-96231b3b80d8
89 files changed, 5092 insertions, 68 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 695db67f24..67876170e4 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -564,6 +564,7 @@ public: CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; CanQualType PseudoObjectTy, ARCUnbridgedCastTy; CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy; + CanQualType ObjCBuiltinBoolTy; // Types for deductions in C++0x [stmt.ranged]'s desugaring. Built on demand. mutable QualType AutoDeductTy; // Deduction against 'auto'. diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index 6e8c60adcf..c3a6880a26 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -18,6 +18,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/SelectorLocationsKind.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Sema/Ownership.h" namespace clang { class IdentifierInfo; @@ -56,6 +57,281 @@ public: child_range children() { return child_range(&String, &String+1); } }; +/// ObjCBoolLiteralExpr - Objective-C Boolean Literal. +/// +class ObjCBoolLiteralExpr : public Expr { + bool Value; + SourceLocation Loc; +public: + ObjCBoolLiteralExpr(bool val, QualType Ty, SourceLocation l) : + Expr(ObjCBoolLiteralExprClass, Ty, VK_RValue, OK_Ordinary, false, false, + false, false), Value(val), Loc(l) {} + + explicit ObjCBoolLiteralExpr(EmptyShell Empty) + : Expr(ObjCBoolLiteralExprClass, Empty) { } + + bool getValue() const { return Value; } + void setValue(bool V) { Value = V; } + + SourceRange getSourceRange() const { return SourceRange(Loc); } + + SourceLocation getLocation() const { return Loc; } + void setLocation(SourceLocation L) { Loc = L; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCBoolLiteralExprClass; + } + static bool classof(const ObjCBoolLiteralExpr *) { return true; } + + // Iterators + child_range children() { return child_range(); } +}; + +/// ObjCNumericLiteral - used for objective-c numeric literals; +/// as in: @42 or @true (c++/objc++) or @__yes (c/objc) +class ObjCNumericLiteral : public Expr { + /// Number - expression AST node for the numeric literal + Stmt *Number; + ObjCMethodDecl *ObjCNumericLiteralMethod; + SourceLocation AtLoc; +public: + ObjCNumericLiteral(Stmt *NL, QualType T, ObjCMethodDecl *method, + SourceLocation L) + : Expr(ObjCNumericLiteralClass, T, VK_RValue, OK_Ordinary, + false, false, false, false), Number(NL), + ObjCNumericLiteralMethod(method), AtLoc(L) {} + explicit ObjCNumericLiteral(EmptyShell Empty) + : Expr(ObjCNumericLiteralClass, Empty) {} + + Expr *getNumber() { return cast<Expr>(Number); } + const Expr *getNumber() const { return cast<Expr>(Number); } + + ObjCMethodDecl *getObjCNumericLiteralMethod() const { + return ObjCNumericLiteralMethod; + } + + SourceLocation getAtLoc() const { return AtLoc; } + + SourceRange getSourceRange() const { + return SourceRange(AtLoc, Number->getSourceRange().getEnd()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCNumericLiteralClass; + } + static bool classof(const ObjCNumericLiteral *) { return true; } + + // Iterators + child_range children() { return child_range(&Number, &Number+1); } + + friend class ASTStmtReader; +}; + +/// ObjCArrayLiteral - used for objective-c array containers; as in: +/// @[@"Hello", NSApp, [NSNumber numberWithInt:42]]; +class ObjCArrayLiteral : public Expr { + unsigned NumElements; + SourceRange Range; + ObjCMethodDecl *ArrayWithObjectsMethod; + + ObjCArrayLiteral(llvm::ArrayRef<Expr *> Elements, + QualType T, ObjCMethodDecl * Method, + SourceRange SR); + + explicit ObjCArrayLiteral(EmptyShell Empty, unsigned NumElements) + : Expr(ObjCArrayLiteralClass, Empty), NumElements(NumElements) {} + +public: + static ObjCArrayLiteral *Create(ASTContext &C, + llvm::ArrayRef<Expr *> Elements, + QualType T, ObjCMethodDecl * Method, + SourceRange SR); + + static ObjCArrayLiteral *CreateEmpty(ASTContext &C, unsigned NumElements); + + SourceRange getSourceRange() const { return Range; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCArrayLiteralClass; + } + static bool classof(const ObjCArrayLiteral *) { return true; } + + /// \brief Retrieve elements of array of literals. + Expr **getElements() { return reinterpret_cast<Expr **>(this + 1); } + + /// \brief Retrieve elements of array of literals. + const Expr * const *getElements() const { + return reinterpret_cast<const Expr * const*>(this + 1); + } + + /// getNumElements - Return number of elements of objective-c array literal. + unsigned getNumElements() const { return NumElements; } + + /// getExpr - Return the Expr at the specified index. + Expr *getElement(unsigned Index) { + assert((Index < NumElements) && "Arg access out of range!"); + return cast<Expr>(getElements()[Index]); + } + const Expr *getElement(unsigned Index) const { + assert((Index < NumElements) && "Arg access out of range!"); + return cast<Expr>(getElements()[Index]); + } + + ObjCMethodDecl *getArrayWithObjectsMethod() const { + return ArrayWithObjectsMethod; + } + + // Iterators + child_range children() { + return child_range((Stmt **)getElements(), + (Stmt **)getElements() + NumElements); + } + + friend class ASTStmtReader; +}; + +/// \brief An element in an Objective-C dictionary literal. +/// +struct ObjCDictionaryElement { + /// \brief The key for the dictionary element. + Expr *Key; + + /// \brief The value of the dictionary element. + Expr *Value; + + /// \brief The location of the ellipsis, if this is a pack expansion. + SourceLocation EllipsisLoc; + + /// \brief The number of elements this pack expansion will expand to, if + /// this is a pack expansion and is known. + llvm::Optional<unsigned> NumExpansions; + + /// \brief Determines whether this dictionary element is a pack expansion. + bool isPackExpansion() const { return EllipsisLoc.isValid(); } +}; + +/// ObjCDictionaryLiteral - AST node to represent objective-c dictionary +/// literals; as in: @{@"name" : NSUserName(), @"date" : [NSDate date] }; +class ObjCDictionaryLiteral : public Expr { + /// \brief Key/value pair used to store the key and value of a given element. + /// + /// Objects of this type are stored directly after the expression. + struct KeyValuePair { + Expr *Key; + Expr *Value; + }; + + /// \brief Data that describes an element that is a pack expansion, used if any + /// of the elements in the dictionary literal are pack expansions. + struct ExpansionData { + /// \brief The location of the ellipsis, if this element is a pack + /// expansion. + SourceLocation EllipsisLoc; + + /// \brief If non-zero, the number of elements that this pack + /// expansion will expand to (+1). + unsigned NumExpansionsPlusOne; + }; + + /// \brief The number of elements in this dictionary literal. + unsigned NumElements : 31; + + /// \brief Determine whether this dictionary literal has any pack expansions. + /// + /// If the dictionary literal has pack expansions, then there will + /// be an array of pack expansion data following the array of + /// key/value pairs, which provide the locations of the ellipses (if + /// any) and number of elements in the expansion (if known). If + /// there are no pack expansions, we optimize away this storage. + unsigned HasPackExpansions : 1; + + SourceRange Range; + ObjCMethodDecl *DictWithObjectsMethod; + + ObjCDictionaryLiteral(ArrayRef<ObjCDictionaryElement> VK, + bool HasPackExpansions, + QualType T, ObjCMethodDecl *method, + SourceRange SR); + + explicit ObjCDictionaryLiteral(EmptyShell Empty, unsigned NumElements, + bool HasPackExpansions) + : Expr(ObjCDictionaryLiteralClass, Empty), NumElements(NumElements), + HasPackExpansions(HasPackExpansions) {} + + KeyValuePair *getKeyValues() { + return reinterpret_cast<KeyValuePair *>(this + 1); + } + + const KeyValuePair *getKeyValues() const { + return reinterpret_cast<const KeyValuePair *>(this + 1); + } + + ExpansionData *getExpansionData() { + if (!HasPackExpansions) + return 0; + + return reinterpret_cast<ExpansionData *>(getKeyValues() + NumElements); + } + + const ExpansionData *getExpansionData() const { + if (!HasPackExpansions) + return 0; + + return reinterpret_cast<const ExpansionData *>(getKeyValues()+NumElements); + } + +public: + static ObjCDictionaryLiteral *Create(ASTContext &C, + ArrayRef<ObjCDictionaryElement> VK, + bool HasPackExpansions, + QualType T, ObjCMethodDecl *method, + SourceRange SR); + + static ObjCDictionaryLiteral *CreateEmpty(ASTContext &C, + unsigned NumElements, + bool HasPackExpansions); + + /// getNumElements - Return number of elements of objective-c dictionary + /// literal. + unsigned getNumElements() const { return NumElements; } + + ObjCDictionaryElement getKeyValueElement(unsigned Index) const { + assert((Index < NumElements) && "Arg access out of range!"); + const KeyValuePair &KV = getKeyValues()[Index]; + ObjCDictionaryElement Result = { KV.Key, KV.Value, SourceLocation(), + llvm::Optional<unsigned>() }; + if (HasPackExpansions) { + const ExpansionData &Expansion = getExpansionData()[Index]; + Result.EllipsisLoc = Expansion.EllipsisLoc; + if (Expansion.NumExpansionsPlusOne > 0) + Result.NumExpansions = Expansion.NumExpansionsPlusOne - 1; + } + return Result; + } + + ObjCMethodDecl *getDictWithObjectsMethod() const + { return DictWithObjectsMethod; } + + SourceRange getSourceRange() const { return Range; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCDictionaryLiteralClass; + } + static bool classof(const ObjCDictionaryLiteral *) { return true; } + + // Iterators + child_range children() { + // Note: we're taking advantage of the layout of the KeyValuePair struct + // here. If that struct changes, this code will need to change as well. + return child_range(reinterpret_cast<Stmt **>(this + 1), + reinterpret_cast<Stmt **>(this + 1) + NumElements * 2); + } + + friend class ASTStmtReader; + friend class ASTStmtWriter; +}; + + /// ObjCEncodeExpr, used for @encode in Objective-C. @encode has the same type /// and behavior as StringLiteral except that the string initializer is obtained /// from ASTContext with the encoding type as an argument. @@ -430,6 +706,88 @@ private: void setLocation(SourceLocation L) { IdLoc = L; } void setReceiverLocation(SourceLocation Loc) { ReceiverLoc = Loc; } }; + +/// ObjCSubscriptRefExpr - used for array and dictionary subscripting. +/// array[4] = array[3]; dictionary[key] = dictionary[alt_key]; +/// +class ObjCSubscriptRefExpr : public Expr { + // Location of ']' in an indexing expression. + SourceLocation RBracket; + // array/dictionary base expression. + // for arrays, this is a numeric expression. For dictionaries, this is + // an objective-c object pointer expression. + enum { BASE, KEY, END_EXPR }; + Stmt* SubExprs[END_EXPR]; + + ObjCMethodDecl *GetAtIndexMethodDecl; + + // For immutable objects this is null. When ObjCSubscriptRefExpr is to read + // an indexed object this is null too. + ObjCMethodDecl *SetAtIndexMethodDecl; + +public: + + ObjCSubscriptRefExpr(Expr *base, Expr *key, QualType T, + ExprValueKind VK, ExprObjectKind OK, + ObjCMethodDecl *getMethod, + ObjCMethodDecl *setMethod, SourceLocation RB) + : Expr(ObjCSubscriptRefExprClass, T, VK, OK, + base->isTypeDependent() || key->isTypeDependent(), + base->isValueDependent() || key->isValueDependent(), + base->isInstantiationDependent() || key->isInstantiationDependent(), + (base->containsUnexpandedParameterPack() || + key->containsUnexpandedParameterPack())), + RBracket(RB), + GetAtIndexMethodDecl(getMethod), + SetAtIndexMethodDecl(setMethod) + {SubExprs[BASE] = base; SubExprs[KEY] = key;} + + explicit ObjCSubscriptRefExpr(EmptyShell Empty) + : Expr(ObjCSubscriptRefExprClass, Empty) {} + + static ObjCSubscriptRefExpr *Create(ASTContext &C, + Expr *base, + Expr *key, QualType T, + ObjCMethodDecl *getMethod, + ObjCMethodDecl *setMethod, + SourceLocation RB); + + SourceLocation getRBracket() const { return RBracket; } + void setRBracket(SourceLocation RB) { RBracket = RB; } + SourceRange getSourceRange() const { + return SourceRange(SubExprs[BASE]->getLocStart(), RBracket); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCSubscriptRefExprClass; + } + static bool classof(const ObjCSubscriptRefExpr *) { return true; } + + Expr *getBaseExpr() const { return cast<Expr>(SubExprs[BASE]); } + void setBaseExpr(Stmt *S) { SubExprs[BASE] = S; } + + Expr *getKeyExpr() const { return cast<Expr>(SubExprs[KEY]); } + void setKeyExpr(Stmt *S) { SubExprs[KEY] = S; } + + ObjCMethodDecl *getAtIndexMethodDecl() const { + return GetAtIndexMethodDecl; + } + + ObjCMethodDecl *setAtIndexMethodDecl() const { + return SetAtIndexMethodDecl; + } + + bool isArraySubscriptRefExpr() const { + return getKeyExpr()->getType()->isIntegralOrEnumerationType(); + } + + child_range children() { + return child_range(SubExprs, SubExprs+END_EXPR); + } +private: + friend class ASTStmtReader; +}; + /// \brief An expression that sends a message to the given Objective-C /// object or class. diff --git a/include/clang/AST/NSAPI.h b/include/clang/AST/NSAPI.h new file mode 100644 index 0000000000..222c8dbacc --- /dev/null +++ b/include/clang/AST/NSAPI.h @@ -0,0 +1,151 @@ +//===--- NSAPI.h - NSFoundation APIs ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_NSAPI_H +#define LLVM_CLANG_AST_NSAPI_H + +#include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/Optional.h" + +namespace clang { + class ASTContext; + class QualType; + +// \brief Provides info and caches identifiers/selectors for NSFoundation API. +class NSAPI { +public: + explicit NSAPI(ASTContext &Ctx); + + ASTContext &getASTContext() const { return Ctx; } + + enum NSClassIdKindKind { + ClassId_NSString, + ClassId_NSArray, + ClassId_NSMutableArray, + ClassId_NSDictionary, + ClassId_NSMutableDictionary, + ClassId_NSNumber + }; + static const unsigned NumClassIds = 6; + + enum NSStringMethodKind { + NSStr_stringWithString, + NSStr_initWithString + }; + static const unsigned NumNSStringMethods = 2; + + IdentifierInfo *getNSClassId(NSClassIdKindKind K) const; + + /// \brief The Objective-C NSString selectors. + Selector getNSStringSelector(NSStringMethodKind MK) const; + + /// \brief Enumerates the NSArray methods used to generate literals. + enum NSArrayMethodKind { + NSArr_array, + NSArr_arrayWithArray, + NSArr_arrayWithObject, + NSArr_arrayWithObjects, + NSArr_arrayWithObjectsCount, + NSArr_initWithArray, + NSArr_initWithObjects, + NSArr_objectAtIndex, + NSMutableArr_replaceObjectAtIndex + }; + static const unsigned NumNSArrayMethods = 9; + + /// \brief The Objective-C NSArray selectors. + Selector getNSArraySelector(NSArrayMethodKind MK) const; + + /// \brief Return NSArrayMethodKind if \arg Sel is such a selector. + llvm::Optional<NSArrayMethodKind> getNSArrayMethodKind(Selector Sel); + + /// \brief Enumerates the NSDictionary methods used to generate literals. + enum NSDictionaryMethodKind { + NSDict_dictionary, + NSDict_dictionaryWithDictionary, + NSDict_dictionaryWithObjectForKey, + NSDict_dictionaryWithObjectsForKeys, + NSDict_dictionaryWithObjectsForKeysCount, + NSDict_dictionaryWithObjectsAndKeys, + NSDict_initWithDictionary, + NSDict_initWithObjectsAndKeys, + NSDict_objectForKey, + NSMutableDict_setObjectForKey + }; + static const unsigned NumNSDictionaryMethods = 10; + + /// \brief The Objective-C NSDictionary selectors. + Selector getNSDictionarySelector(NSDictionaryMethodKind MK) const; + + /// \brief Return NSDictionaryMethodKind if \arg Sel is such a selector. + llvm::Optional<NSDictionaryMethodKind> + getNSDictionaryMethodKind(Selector Sel); + + /// \brief Enumerates the NSNumber methods used to generate literals. + enum NSNumberLiteralMethodKind { + NSNumberWithChar, + NSNumberWithUnsignedChar, + NSNumberWithShort, + NSNumberWithUnsignedShort, + NSNumberWithInt, + NSNumberWithUnsignedInt, + NSNumberWithLong, + NSNumberWithUnsignedLong, + NSNumberWithLongLong, + NSNumberWithUnsignedLongLong, + NSNumberWithFloat, + NSNumberWithDouble, + NSNumberWithBool, + NSNumberWithInteger, + NSNumberWithUnsignedInteger + }; + static const unsigned NumNSNumberLiteralMethods = 15; + + /// \brief The Objective-C NSNumber selectors used to create NSNumber literals. + /// \param Instance if true it will return the selector for the init* method + /// otherwise it will return the selector for the number* method. + Selector getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, + bool Instance) const; + + bool isNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, + Selector Sel) const { + return Sel == getNSNumberLiteralSelector(MK, false) || + Sel == getNSNumberLiteralSelector(MK, true); + } + + /// \brief Return NSNumberLiteralMethodKind if \arg Sel is such a selector. + llvm::Optional<NSNumberLiteralMethodKind> + getNSNumberLiteralMethodKind(Selector Sel) const; + + /// \brief Determine the appropriate NSNumber factory method kind for a + /// literal of the given type. + static llvm::Optional<NSNumberLiteralMethodKind> + getNSNumberFactoryMethodKind(QualType T); + +private: + ASTContext &Ctx; + + mutable IdentifierInfo *ClassIds[NumClassIds]; + + mutable Selector NSStringSelectors[NumNSStringMethods]; + + /// \brief The selectors for Objective-C NSArray methods. + mutable Selector NSArraySelectors[NumNSArrayMethods]; + + /// \brief The selectors for Objective-C NSDictionary methods. + mutable Selector NSDictionarySelectors[NumNSDictionaryMethods]; + + /// \brief The Objective-C NSNumber selectors used to create NSNumber literals. + mutable Selector NSNumberClassSelectors[NumNSNumberLiteralMethods]; + mutable Selector NSNumberInstanceSelectors[NumNSNumberLiteralMethods]; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_NSAPI_H diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index a1214a9d25..aeb9d0508f 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2041,11 +2041,13 @@ DEF_TRAVERSE_STMT(DesignatedInitExpr, { }) DEF_TRAVERSE_STMT(ExtVectorElementExpr, { }) DEF_TRAVERSE_STMT(GNUNullExpr, { }) DEF_TRAVERSE_STMT(ImplicitValueInitExpr, { }) +DEF_TRAVERSE_STMT(ObjCBoolLiteralExpr, { }) DEF_TRAVERSE_STMT(ObjCEncodeExpr, { }) DEF_TRAVERSE_STMT(ObjCIsaExpr, { }) DEF_TRAVERSE_STMT(ObjCIvarRefExpr, { }) DEF_TRAVERSE_STMT(ObjCMessageExpr, { }) DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { }) +DEF_TRAVERSE_STMT(ObjCSubscriptRefExpr, { }) DEF_TRAVERSE_STMT(ObjCProtocolExpr, { }) DEF_TRAVERSE_STMT(ObjCSelectorExpr, { }) DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, { }) @@ -2103,7 +2105,10 @@ DEF_TRAVERSE_STMT(FloatingLiteral, { }) DEF_TRAVERSE_STMT(ImaginaryLiteral, { }) DEF_TRAVERSE_STMT(StringLiteral, { }) DEF_TRAVERSE_STMT(ObjCStringLiteral, { }) - +DEF_TRAVERSE_STMT(ObjCNumericLiteral, { }) +DEF_TRAVERSE_STMT(ObjCArrayLiteral, { }) +DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, { }) + // Traverse OpenCL: AsType, Convert. DEF_TRAVERSE_STMT(AsTypeExpr, { }) diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 4c92520a76..7ef3e2b2ec 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -141,6 +141,8 @@ protected: friend class CallExpr; // ctor friend class OffsetOfExpr; // ctor friend class ObjCMessageExpr; // ctor + friend class ObjCArrayLiteral; // ctor + friend class ObjCDictionaryLiteral; // ctor friend class ShuffleVectorExpr; // ctor friend class ParenListExpr; // ctor friend class CXXUnresolvedConstructExpr; // ctor diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index cb59cbad4f..c40ff03174 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -379,3 +379,13 @@ def Microsoft : DiagGroup<"microsoft">; def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">; def ObjCProtocolMethodImpl : DiagGroup<"objc-protocol-method-implementation">; + +// ObjC API warning groups. +def ObjCRedundantLiteralUse : DiagGroup<"objc-redundant-literal-use">; +def ObjCRedundantAPIUse : DiagGroup<"objc-redundant-api-use", [ + ObjCRedundantLiteralUse + ]>; + +def ObjCCocoaAPI : DiagGroup<"objc-cocoa-api", [ + ObjCRedundantAPIUse + ]>; diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index a78c0a1e54..a6c22db3e8 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -36,7 +36,7 @@ namespace clang { DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + 100, DIAG_START_LEX = DIAG_START_SERIALIZATION + 120, DIAG_START_PARSE = DIAG_START_LEX + 300, - DIAG_START_AST = DIAG_START_PARSE + 350, + DIAG_START_AST = DIAG_START_PARSE + 400, DIAG_START_SEMA = DIAG_START_AST + 100, DIAG_START_ANALYSIS = DIAG_START_SEMA + 3000, DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100 diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 69a6b988a9..fa54990c16 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -331,6 +331,8 @@ def warn_objc_protocol_qualifier_missing_id : Warning< def err_objc_unknown_at : Error<"expected an Objective-C directive after '@'">; def err_illegal_super_cast : Error< "cannot cast 'super' (it isn't an expression)">; +def err_nsnumber_nonliteral_unary : Error< + "@%0 must be followed by a number to form an NSNumber object">; let CategoryName = "ARC Parse Issue" in { def err_arc_bridge_retain : Error< @@ -410,6 +412,8 @@ def warn_cxx98_compat_noexcept_decl : Warning< InGroup<CXX98Compat>, DefaultIgnore; def err_expected_catch : Error<"expected catch">; def err_expected_lbrace_or_comma : Error<"expected '{' or ','">; +def err_expected_rbrace_or_comma : Error<"expected '}' or ','">; +def err_expected_rsquare_or_comma : Error<"expected ']' or ','">; def err_using_namespace_in_class : Error< "'using namespace' is not allowed in classes">; def err_destructor_tilde_identifier : Error< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8499ec0b2a..6ea8962322 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -542,6 +542,7 @@ def warn_strict_multiple_method_decl : Warning< "multiple methods named %0 found">, InGroup<StrictSelector>, DefaultIgnore; def warn_accessor_property_type_mismatch : Warning< "type of property %0 does not match type of accessor %1">; +def not_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; @@ -1484,6 +1485,39 @@ def err_attr_objc_ownership_redundant : Error< "the type %0 is already explicitly ownership-qualified">; def err_attribute_not_string : Error< "argument to %0 attribute was not a string literal">; +def err_undeclared_nsnumber : Error< + "NSNumber must be available to use Objective-C literals">; +def err_invalid_nsnumber_type : Error< + "%0 is not a valid literal type for NSNumber">; +def err_undeclared_nsarray : Error< + "NSArray must be available to use Objective-C array literals">; +def err_undeclared_nsdictionary : Error< + "NSDictionary must be available to use Objective-C dictionary " + "literals">; +def err_undeclared_arraywithobjects : Error< + "declaration of %0 is missing in NSArray class">; +def err_undeclared_dictwithobjects : Error< + "declaration of %0 is missing in NSDictionary class">; +def err_undeclared_nsnumber_method : Error< + "declaration of %0 is missing in NSNumber class">; +def err_objc_literal_method_sig : Error< + "literal construction method %0 has incompatible signature">; +def note_objc_literal_method_param : Note< + "%select{first|second|third}0 parameter has unexpected type %1 " + "(should be %2)">; +def note_objc_literal_method_return : Note< + "method returns unexpected type %0 (should be an object type)">; +def err_invalid_collection_element : Error< + "collection element of type %0 is not an Objective-C object">; +def err_box_literal_collection : Error< + "%select{string|character|boolean|numeric}0 literal must be prefixed by '@' " + "in a collection">; + +let CategoryName = "Cocoa API Issue" in { +def warn_objc_redundant_literal_use : Warning< + "using %0 with a literal is redundant">, InGroup<ObjCRedundantLiteralUse>; +} + def err_only_annotate_after_access_spec : Error< "access specifier can only have annotation attributes">; def err_attribute_section_invalid_for_target : Error< @@ -2642,6 +2676,8 @@ def err_template_id_not_a_type : Error< def note_template_declared_here : Note< "%select{function template|class template|type alias template|template template parameter}0 " "%1 declared here">; +def note_parameter_type : Note< + "parameter of type %0 is declared here">; // C++11 Variadic Templates def err_template_param_pack_default_arg : Error< @@ -3709,6 +3745,33 @@ def err_invalid_property_name : Error< "%0 is not a valid property name (accessing an object of type %1)">; def err_getter_not_found : Error< "expected getter method not found on object of type %0">; +def err_objc_subscript_method_not_found : Error< + "expected method to %select{read|write}1 %select{dictionary|array}2 element not " + "found on object of type %0">; +def err_objc_subscript_index_type : Error< + "method index parameter type %0 is not integral type">; +def err_objc_subscript_key_type : Error< + "method key parameter type %0 is not object type">; +def err_objc_subscript_dic_object_type : Error< + "method object parameter type %0 is not object type">; +def err_objc_subscript_object_type : Error< + "cannot assign to this %select{dictionary|array}1 because assigning method's 2nd parameter" + " of type %0 is not an objective-C pointer type">; +def err_objc_subscript_base_type : Error< + "%select{dictionary|array}1 subscript base type %0 is not an Objective-C object">; +def err_objc_multiple_subscript_type_conversion : Error< + "indexing expression is invalid because subscript type %0 has " + "multiple type conversion functions">; +def err_objc_subscript_type_conversion : Error< + "indexing expression is invalid because subscript type %0 is not an intergal" + "or objective-C pointer type">; +def err_objc_indexing_method_result_type : Error< + "method for accessing %select{dictionary|array}1 element must have Objective-C" + " object return type instead of %0">; +def err_objc_index_incomplete_class_type : Error< + "objective-C index expression has incomplete class type %0">; +def err_illegal_container_subscripting_op : Error< + "illegal operation on objective-c container subscripting">; def err_property_not_found_forward_class : Error< "property %0 cannot be found in forward class object %1">; def err_property_not_as_forward_class : Error< @@ -4438,6 +4501,9 @@ def warn_unused_voidptr : Warning< def warn_unused_property_expr : Warning< "property access result unused - getters should not be used for side effects">, InGroup<UnusedValue>; +def warn_unused_container_subscript_expr : Warning< + "container access result unused - container access should not be used for side effects">, + InGroup<UnusedValue>; def warn_unused_call : Warning< "ignoring return value of function declared with %0 attribute">, InGroup<UnusedValue>; diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index d68afc737d..20cbc29ea0 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -124,6 +124,7 @@ BENIGN_LANGOPT(InlineVisibilityHidden , 1, 0, "hidden default visibility for inl BENIGN_LANGOPT(ParseUnknownAnytype, 1, 0, "__unknown_anytype") BENIGN_LANGOPT(DebuggerSupport , 1, 0, "debugger support") BENIGN_LANGOPT(DebuggerCastResultToId, 1, 0, "for 'po' in the debugger, cast the result to id if it is of unknown type") +BENIGN_LANGOPT(DebuggerObjCLiteral , 1, 0, "debugger objective-C literals and subscripting support") BENIGN_LANGOPT(AddressSanitizer , 1, 0, "AddressSanitizer enabled") BENIGN_LANGOPT(ThreadSanitizer , 1, 0, "ThreadSanitizer enabled") diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 390e5e5f4d..ec0cd333f8 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -113,7 +113,12 @@ namespace clang { /// An Objective C property is a logical field of an Objective-C /// object which is read and written via Objective C method calls. - OK_ObjCProperty + OK_ObjCProperty, + + /// An Objective C array/dictionary subscripting which reads an object + /// or writes at the subscripted array/dictionary element via + /// Objective C method calls. + OK_ObjCSubscript }; // \brief Describes the kind of template specialization that a diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 07bb227b82..5018ac1901 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -132,6 +132,9 @@ def LambdaExpr : DStmt<Expr>; // Obj-C Expressions. def ObjCStringLiteral : DStmt<Expr>; +def ObjCNumericLiteral : DStmt<Expr>; +def ObjCArrayLiteral : DStmt<Expr>; +def ObjCDictionaryLiteral : DStmt<Expr>; def ObjCEncodeExpr : DStmt<Expr>; def ObjCMessageExpr : DStmt<Expr>; def ObjCSelectorExpr : DStmt<Expr>; @@ -140,6 +143,8 @@ def ObjCIvarRefExpr : DStmt<Expr>; def ObjCPropertyRefExpr : DStmt<Expr>; def ObjCIsaExpr : DStmt<Expr>; def ObjCIndirectCopyRestoreExpr : DStmt<Expr>; +def ObjCBoolLiteralExpr : DStmt<Expr>; +def ObjCSubscriptRefExpr : DStmt<Expr>; // Obj-C ARC Expressions. def ObjCBridgedCastExpr : DStmt<ExplicitCastExpr>; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index bdceab48a0..d7e081f834 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -258,6 +258,9 @@ KEYWORD(_Generic , KEYALL) KEYWORD(_Imaginary , KEYALL) KEYWORD(_Static_assert , KEYALL) KEYWORD(__func__ , KEYALL) +KEYWORD(__objc_yes , KEYALL) +KEYWORD(__objc_no , KEYALL) + // C++ 2.11p1: Keywords. KEYWORD(asm , KEYCXX|KEYGNU) diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index e896e6a4c2..93e63dee61 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -661,6 +661,8 @@ def fdebugger_support : Flag<"-fdebugger-support">, HelpText<"Enable special debugger support behavior">; def fdebugger_cast_result_to_id : Flag<"-fdebugger-cast-result-to-id">, HelpText<"Enable casting unknown expression results to id">; +def fdebugger_objc_literal : Flag<"-fdebugger-objc-literal">, + HelpText<"Enable special debugger support for objective-C subscripting and literals">; def fdeprecated_macro : Flag<"-fdeprecated-macro">, HelpText<"Defines the __DEPRECATED macro">; def fno_deprecated_macro : Flag<"-fno-deprecated-macro">, diff --git a/include/clang/Driver/ObjCRuntime.h b/include/clang/Driver/ObjCRuntime.h index 5516460433..094873a7d1 100644 --- a/include/clang/Driver/ObjCRuntime.h +++ b/include/clang/Driver/ObjCRuntime.h @@ -30,6 +30,9 @@ public: /// True if the runtime supports ARC zeroing __weak. unsigned HasWeak : 1; + /// \brief True if the runtime supports subscripting methods. + unsigned HasSubscripting : 1; + /// True if the runtime provides the following entrypoint: /// void objc_terminate(void); /// If available, this will be called instead of abort() when an @@ -37,7 +40,7 @@ public: unsigned HasTerminate : 1; ObjCRuntime() : RuntimeKind(NeXT), HasARC(false), HasWeak(false), - HasTerminate(false) {} + HasSubscripting(false), HasTerminate(false) {} }; } diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index d524a8f12d..b269ddb045 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -463,6 +463,7 @@ def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">, Group<f_Group>; def fno_objc_infer_related_result_type : Flag< "-fno-objc-infer-related-result-type">, Group<f_Group>; +def fobjc_link_runtime: Flag<"-fobjc-link-runtime">, Group<f_Group>; // Objective-C ABI options. def fobjc_abi_version_EQ : Joined<"-fobjc-abi-version=">, Group<f_Group>; diff --git a/include/clang/Frontend/PreprocessorOptions.h b/include/clang/Frontend/PreprocessorOptions.h index 652c24d298..4a0de96382 100644 --- a/include/clang/Frontend/PreprocessorOptions.h +++ b/include/clang/Frontend/PreprocessorOptions.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_FRONTEND_PREPROCESSOROPTIONS_H_ #define LLVM_CLANG_FRONTEND_PREPROCESSOROPTIONS_H_ +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include <cassert> #include <string> diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index a2a0c6db76..cf8f978c9e 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1362,6 +1362,8 @@ private: ExprResult ParseStringLiteralExpression(bool AllowUserDefinedLiteral = false); ExprResult ParseGenericSelectionExpression(); + + ExprResult ParseObjCBoolLiteral(); //===--------------------------------------------------------------------===// // C++ Expressions @@ -1490,6 +1492,11 @@ private: // Objective-C Expressions ExprResult ParseObjCAtExpression(SourceLocation AtLocation); ExprResult ParseObjCStringLiteral(SourceLocation AtLoc); + ExprResult ParseObjCCharacterLiteral(SourceLocation AtLoc); + ExprResult ParseObjCNumericLiteral(SourceLocation AtLoc); + ExprResult ParseObjCBooleanLiteral(SourceLocation AtLoc, bool ArgValue); + ExprResult ParseObjCArrayLiteral(SourceLocation AtLoc); + ExprResult ParseObjCDictionaryLiteral(SourceLocation AtLoc); ExprResult ParseObjCEncodeExpression(SourceLocation AtLoc); ExprResult ParseObjCSelectorExpression(SourceLocation AtLoc); ExprResult ParseObjCProtocolExpression(SourceLocation AtLoc); @@ -1503,7 +1510,7 @@ private: SourceLocation LBracloc, SourceLocation SuperLoc, ParsedType ReceiverType, ExprArg ReceiverExpr); bool ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr); - + //===--------------------------------------------------------------------===// // C99 6.8: Statements and Blocks. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6289b9f3a1..bc7ecf5edd 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -25,9 +25,11 @@ #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/NSAPI.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TemplateKinds.h" @@ -511,11 +513,34 @@ public: /// \brief The MSVC "_GUID" struct, which is defined in MSVC header files. RecordDecl *MSVCGuidDecl; + /// \brief Caches identifiers/selectors for NSFoundation APIs. + llvm::OwningPtr<NSAPI> NSAPIObj; + + /// \brief The declaration of the Objective-C NSNumber class. + ObjCInterfaceDecl *NSNumberDecl; + + /// \brief The Objective-C NSNumber methods used to create NSNumber literals. + ObjCMethodDecl *NSNumberLiteralMethods[NSAPI::NumNSNumberLiteralMethods]; + + /// \brief The declaration of the Objective-C NSArray class. + ObjCInterfaceDecl *NSArrayDecl; + + /// \brief The declaration of the arrayWithObjects:count: method. + ObjCMethodDecl *ArrayWithObjectsMethod; + + /// \brief The declaration of the Objective-C NSDictionary class. + ObjCInterfaceDecl *NSDictionaryDecl; + + /// \brief The declaration of the dictionaryWithObjects:forKeys:count: method. + ObjCMethodDecl *DictionaryWithObjectsMethod; + + /// \brief id<NSCopying> type. + QualType QIDNSCopying; + /// A flag to remember whether the implicit forms of operator new and delete /// have been declared. bool GlobalNewDeleteDeclared; - /// A flag that is set when parsing a -dealloc method and no [super dealloc] /// call was found yet. bool ObjCShouldCallSuperDealloc; @@ -1500,6 +1525,12 @@ public: const PartialDiagnostic &AmbigNote, const PartialDiagnostic &ConvDiag, bool AllowScopedEnumerations); + enum ObjCSubscriptKind { + OS_Array, + OS_Dictionary, + OS_Error + }; + ObjCSubscriptKind CheckSubscriptingKind(Expr *FromE); ExprResult PerformObjectMemberConversion(Expr *From, NestedNameSpecifier *Qualifier, @@ -2515,6 +2546,7 @@ public: NamedDecl *D); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); + ExprResult ActOnIntegerConstant(SourceLocation Loc, uint64_t Val); ExprResult ActOnNumericConstant(const Token &Tok); ExprResult ActOnCharacterConstant(const Token &Tok); ExprResult ActOnParenExpr(SourceLocation L, SourceLocation R, Expr *E); @@ -3232,6 +3264,10 @@ public: /// ActOnCXXBoolLiteral - Parse {true,false} literals. ExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind); + + + /// ActOnObjCBoolLiteral - Parse {__objc_yes,__objc_no} literals. + ExprResult ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind); /// ActOnCXXNullPtrLiteral - Parse 'nullptr'. ExprResult ActOnCXXNullPtrLiteral(SourceLocation Loc); @@ -3664,7 +3700,26 @@ public: ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, Expr **Strings, unsigned NumStrings); - + + ExprResult BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S); + + /// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the + /// numeric literal expression. Type of the expression will be "NSNumber *" + /// or "id" if NSNumber is unavailable. + ExprResult BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number); + ExprResult ActOnObjCBoolLiteral(SourceLocation AtLoc, SourceLocation ValueLoc, + bool Value); + ExprResult BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements); + + ExprResult BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, + Expr *IndexExpr, + ObjCMethodDecl *getterMethod, + ObjCMethodDecl *setterMethod); + + ExprResult BuildObjCDictionaryLiteral(SourceRange SR, + ObjCDictionaryElement *Elements, + unsigned NumElements); + ExprResult BuildObjCEncodeExpression(SourceLocation AtLoc, TypeSourceInfo *EncodedTypeInfo, SourceLocation RParenLoc); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 7de6b4589a..0af1d3b0d1 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1065,6 +1065,12 @@ namespace clang { /// \brief An ObjCStringLiteral record. EXPR_OBJC_STRING_LITERAL, + + EXPR_OBJC_NUMERIC_LITERAL, + EXPR_OBJC_ARRAY_LITERAL, + EXPR_OBJC_DICTIONARY_LITERAL, + + /// \brief An ObjCEncodeExpr record. EXPR_OBJC_ENCODE, /// \brief An ObjCSelectorExpr record. @@ -1075,6 +1081,8 @@ namespace clang { EXPR_OBJC_IVAR_REF_EXPR, /// \brief An ObjCPropertyRefExpr record. EXPR_OBJC_PROPERTY_REF_EXPR, + /// \brief An ObjCSubscriptRefExpr record. + EXPR_OBJC_SUBSCRIPT_REF_EXPR, /// \brief UNUSED EXPR_OBJC_KVC_REF_EXPR, /// \brief An ObjCMessageExpr record. @@ -1098,6 +1106,8 @@ namespace clang { STMT_OBJC_AT_THROW, /// \brief An ObjCAutoreleasePoolStmt record. STMT_OBJC_AUTORELEASE_POOL, + /// \brief A ObjCBoolLiteralExpr record. + EXPR_OBJC_BOOL_LITERAL, // C++ diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 40d43cd0b7..e122df9d23 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -477,7 +477,10 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId); InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass); InitBuiltinType(ObjCBuiltinSelTy, BuiltinType::ObjCSel); - + + // Builtin type for __objc_yes and __objc_no + ObjCBuiltinBoolTy = SignedCharTy; + ObjCConstantStringType = QualType(); // void * type diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 651bcc4857..716459a930 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -33,6 +33,7 @@ add_clang_library(clangAST MicrosoftCXXABI.cpp MicrosoftMangle.cpp NestedNameSpecifier.cpp + NSAPI.cpp ParentMap.cpp RecordLayout.cpp RecordLayoutBuilder.cpp diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 9c9c7baf8e..e35091a101 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2089,6 +2089,16 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { // specs. case ObjCMessageExprClass: case ObjCPropertyRefExprClass: + case ObjCSubscriptRefExprClass: + return CT_Can; + + // All the ObjC literals that are implemented as calls are + // potentially throwing unless we decide to close off that + // possibility. + case ObjCArrayLiteralClass: + case ObjCBoolLiteralExprClass: + case ObjCDictionaryLiteralClass: + case ObjCNumericLiteralClass: return CT_Can; // Many other things have subexpressions, so we have to test those. @@ -3637,6 +3647,117 @@ BlockDeclRefExpr::BlockDeclRefExpr(VarDecl *d, QualType t, ExprValueKind VK, ExprBits.InstantiationDependent = InstantiationDependent; } +ObjCArrayLiteral::ObjCArrayLiteral(llvm::ArrayRef<Expr *> Elements, + QualType T, ObjCMethodDecl *Method, + SourceRange SR) + : Expr(ObjCArrayLiteralClass, T, VK_RValue, OK_Ordinary, + false, false, false, false), + NumElements(Elements.size()), Range(SR), ArrayWithObjectsMethod(Method) +{ + Expr **SaveElements = getElements(); + for (unsigned I = 0, N = Elements.size(); I != N; ++I) { + if (Elements[I]->isTypeDependent() || Elements[I]->isValueDependent()) + ExprBits.ValueDependent = true; + if (Elements[I]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; + if (Elements[I]->containsUnexpandedParameterPack()) + ExprBits.ContainsUnexpandedParameterPack = true; + + SaveElements[I] = Elements[I]; + } +} + +ObjCArrayLiteral *ObjCArrayLiteral::Create(ASTContext &C, + llvm::ArrayRef<Expr *> Elements, + QualType T, ObjCMethodDecl * Method, + SourceRange SR) { + void *Mem = C.Allocate(sizeof(ObjCArrayLiteral) + + Elements.size() * sizeof(Expr *)); + return new (Mem) ObjCArrayLiteral(Elements, T, Method, SR); +} + +ObjCArrayLiteral *ObjCArrayLiteral::CreateEmpty(ASTContext &C, + unsigned NumElements) { + + void *Mem = C.Allocate(sizeof(ObjCArrayLiteral) + + NumElements * sizeof(Expr *)); + return new (Mem) ObjCArrayLiteral(EmptyShell(), NumElements); +} + +ObjCDictionaryLiteral::ObjCDictionaryLiteral( + ArrayRef<ObjCDictionaryElement> VK, + bool HasPackExpansions, + QualType T, ObjCMethodDecl *method, + SourceRange SR) + : Expr(ObjCDictionaryLiteralClass, T, VK_RValue, OK_Ordinary, false, false, + false, false), + NumElements(VK.size()), HasPackExpansions(HasPackExpansions), Range(SR), + DictWithObjectsMethod(method) +{ + KeyValuePair *KeyValues = getKeyValues(); + ExpansionData *Expansions = getExpansionData(); + for (unsigned I = 0; I < NumElements; I++) { + if (VK[I].Key->isTypeDependent() || VK[I].Key->isValueDependent() || + VK[I].Value->isTypeDependent() || VK[I].Value->isValueDependent()) + ExprBits.ValueDependent = true; + if (VK[I].Key->isInstantiationDependent() || + VK[I].Value->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; + if (VK[I].EllipsisLoc.isInvalid() && + (VK[I].Key->containsUnexpandedParameterPack() || + VK[I].Value->containsUnexpandedParameterPack())) + ExprBits.ContainsUnexpandedParameterPack = true; + + KeyValues[I].Key = VK[I].Key; + KeyValues[I].Value = VK[I].Value; + if (Expansions) { + Expansions[I].EllipsisLoc = VK[I].EllipsisLoc; + if (VK[I].NumExpansions) + Expansions[I].NumExpansionsPlusOne = *VK[I].NumExpansions + 1; + else + Expansions[I].NumExpansionsPlusOne = 0; + } + } +} + +ObjCDictionaryLiteral * +ObjCDictionaryLiteral::Create(ASTContext &C, + ArrayRef<ObjCDictionaryElement> VK, + bool HasPackExpansions, + QualType T, ObjCMethodDecl *method, + SourceRange SR) { + unsigned ExpansionsSize = 0; + if (HasPackExpansions) + ExpansionsSize = sizeof(ExpansionData) * VK.size(); + + void *Mem = C.Allocate(sizeof(ObjCDictionaryLiteral) + + sizeof(KeyValuePair) * VK.size() + ExpansionsSize); + return new (Mem) ObjCDictionaryLiteral(VK, HasPackExpansions, T, method, SR); +} + +ObjCDictionaryLiteral * +ObjCDictionaryLiteral::CreateEmpty(ASTContext &C, unsigned NumElements, + bool HasPackExpansions) { + unsigned ExpansionsSize = 0; + if (HasPackExpansions) + ExpansionsSize = sizeof(ExpansionData) * NumElements; + void *Mem = C.Allocate(sizeof(ObjCDictionaryLiteral) + + sizeof(KeyValuePair) * NumElements + ExpansionsSize); + return new (Mem) ObjCDictionaryLiteral(EmptyShell(), NumElements, + HasPackExpansions); +} + +ObjCSubscriptRefExpr *ObjCSubscriptRefExpr::Create(ASTContext &C, + Expr *base, + Expr *key, QualType T, + ObjCMethodDecl *getMethod, + ObjCMethodDecl *setMethod, + SourceLocation RB) { + void *Mem = C.Allocate(sizeof(ObjCSubscriptRefExpr)); + return new (Mem) ObjCSubscriptRefExpr(base, key, T, VK_LValue, + OK_ObjCSubscript, + getMethod, setMethod, RB); +} AtomicExpr::AtomicExpr(SourceLocation BLoc, Expr **args, unsigned nexpr, QualType t, AtomicOp op, SourceLocation RP) diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index 1b04428fae..693e28c8a4 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -108,6 +108,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // __func__ and friends are too. case Expr::PredefinedExprClass: // Property references are lvalues + case Expr::ObjCSubscriptRefExprClass: case Expr::ObjCPropertyRefExprClass: // C++ [expr.typeid]p1: The result of a typeid expression is an lvalue of... case Expr::CXXTypeidExprClass: @@ -157,6 +158,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCSelectorExprClass: case Expr::ObjCProtocolExprClass: case Expr::ObjCStringLiteralClass: + case Expr::ObjCNumericLiteralClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCBoolLiteralExprClass: case Expr::ParenListExprClass: case Expr::SizeOfPackExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index bf91d8e24e..1b15cb1d68 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -3058,6 +3058,8 @@ public: bool VisitUnaryAddrOf(const UnaryOperator *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } + bool VisitObjCNumericLiteral(const ObjCNumericLiteral *E) + { return Success(E); } bool VisitAddrLabelExpr(const AddrLabelExpr *E) { return Success(E); } bool VisitCallExpr(const CallExpr *E); @@ -4051,6 +4053,10 @@ public: return Success(E->getValue(), E); } + bool VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E) { + return Success(E->getValue(), E); + } + // Note, GNU defines __null as an integer, not a pointer. bool VisitGNUNullExpr(const GNUNullExpr *E) { return ZeroInitialization(E); @@ -6256,12 +6262,16 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXDependentScopeMemberExprClass: case Expr::UnresolvedMemberExprClass: case Expr::ObjCStringLiteralClass: + case Expr::ObjCNumericLiteralClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCEncodeExprClass: case Expr::ObjCMessageExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCProtocolExprClass: case Expr::ObjCIvarRefExprClass: case Expr::ObjCPropertyRefExprClass: + case Expr::ObjCSubscriptRefExprClass: case Expr::ObjCIsaExprClass: case Expr::ShuffleVectorExprClass: case Expr::BlockExprClass: @@ -6294,6 +6304,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { return CheckICE(cast<GenericSelectionExpr>(E)->getResultExpr(), Ctx); case Expr::IntegerLiteralClass: case Expr::CharacterLiteralClass: + case Expr::ObjCBoolLiteralExprClass: case Expr::CXXBoolLiteralExprClass: case Expr::CXXScalarValueInitExprClass: case Expr::UnaryTypeTraitExprClass: diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index b7b04434d8..ea6e8b2e23 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2374,6 +2374,10 @@ recurse: case Expr::ObjCProtocolExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCStringLiteralClass: + case Expr::ObjCNumericLiteralClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCSubscriptRefExprClass: case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::OffsetOfExprClass: case Expr::PredefinedExprClass: @@ -2814,6 +2818,13 @@ recurse: Out << 'E'; break; + // FIXME. __objc_yes/__objc_no are mangled same as true/false + case Expr::ObjCBoolLiteralExprClass: + Out << "Lb"; + Out << (cast<ObjCBoolLiteralExpr>(E)->getValue() ? '1' : '0'); + Out << 'E'; + break; + case Expr::CXXBoolLiteralExprClass: Out << "Lb"; Out << (cast<CXXBoolLiteralExpr>(E)->getValue() ? '1' : '0'); diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp new file mode 100644 index 0000000000..8f03711f4d --- /dev/null +++ b/lib/AST/NSAPI.cpp @@ -0,0 +1,311 @@ +//===--- NSAPI.cpp - NSFoundation APIs ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/NSAPI.h" +#include "clang/AST/ASTContext.h" + +using namespace clang; + +NSAPI::NSAPI(ASTContext &ctx) + : Ctx(ctx), ClassIds() { +} + +IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const { + static const char *ClassName[NumClassIds] = { + "NSString", + "NSArray", + "NSMutableArray", + "NSDictionary", + "NSMutableDictionary", + "NSNumber" + }; + + if (!ClassIds[K]) + return (ClassIds[K] = &Ctx.Idents.get(ClassName[K])); + + return ClassIds[K]; +} + +Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { + if (NSStringSelectors[MK].isNull()) { + Selector Sel; + switch (MK) { + case NSStr_stringWithString: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString")); + break; + case NSStr_initWithString: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString")); + break; + } + return (NSStringSelectors[MK] = Sel); + } + + return NSStringSelectors[MK]; +} + +Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { + if (NSArraySelectors[MK].isNull()) { + Selector Sel; + switch (MK) { + case NSArr_array: + Sel = Ctx.Selectors.getNullarySelector(&Ctx.Idents.get("array")); + break; + case NSArr_arrayWithArray: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithArray")); + break; + case NSArr_arrayWithObject: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithObject")); + break; + case NSArr_arrayWithObjects: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithObjects")); + break; + case NSArr_arrayWithObjectsCount: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("arrayWithObjects"), + &Ctx.Idents.get("count") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + case NSArr_initWithArray: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithArray")); + break; + case NSArr_initWithObjects: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithObjects")); + break; + case NSArr_objectAtIndex: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("objectAtIndex")); + break; + case NSMutableArr_replaceObjectAtIndex: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("replaceObjectAtIndex"), + &Ctx.Idents.get("withObject") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + } + return (NSArraySelectors[MK] = Sel); + } + + return NSArraySelectors[MK]; +} + +llvm::Optional<NSAPI::NSArrayMethodKind> +NSAPI::getNSArrayMethodKind(Selector Sel) { + for (unsigned i = 0; i != NumNSArrayMethods; ++i) { + NSArrayMethodKind MK = NSArrayMethodKind(i); + if (Sel == getNSArraySelector(MK)) + return MK; + } + + return llvm::Optional<NSArrayMethodKind>(); +} + +Selector NSAPI::getNSDictionarySelector( + NSDictionaryMethodKind MK) const { + if (NSDictionarySelectors[MK].isNull()) { + Selector Sel; + switch (MK) { + case NSDict_dictionary: + Sel = Ctx.Selectors.getNullarySelector(&Ctx.Idents.get("dictionary")); + break; + case NSDict_dictionaryWithDictionary: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("dictionaryWithDictionary")); + break; + case NSDict_dictionaryWithObjectForKey: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("dictionaryWithObject"), + &Ctx.Idents.get("forKey") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + case NSDict_dictionaryWithObjectsForKeys: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("dictionaryWithObjects"), + &Ctx.Idents.get("forKeys") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + case NSDict_dictionaryWithObjectsForKeysCount: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("dictionaryWithObjects"), + &Ctx.Idents.get("forKeys"), + &Ctx.Idents.get("count") + }; + Sel = Ctx.Selectors.getSelector(3, KeyIdents); + break; + } + case NSDict_dictionaryWithObjectsAndKeys: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("dictionaryWithObjectsAndKeys")); + break; + case NSDict_initWithDictionary: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("initWithDictionary")); + break; + case NSDict_initWithObjectsAndKeys: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("initWithObjectsAndKeys")); + break; + case NSDict_objectForKey: + Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("objectForKey")); + break; + case NSMutableDict_setObjectForKey: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("setObject"), + &Ctx.Idents.get("forKey") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + } + return (NSDictionarySelectors[MK] = Sel); + } + + return NSDictionarySelectors[MK]; +} + +llvm::Optional<NSAPI::NSDictionaryMethodKind> +NSAPI::getNSDictionaryMethodKind(Selector Sel) { + for (unsigned i = 0; i != NumNSDictionaryMethods; ++i) { + NSDictionaryMethodKind MK = NSDictionaryMethodKind(i); + if (Sel == getNSDictionarySelector(MK)) + return MK; + } + + return llvm::Optional<NSDictionaryMethodKind>(); +} + +Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, + bool Instance) const { + static const char *ClassSelectorName[NumNSNumberLiteralMethods] = { + "numberWithChar", + "numberWithUnsignedChar", + "numberWithShort", + "numberWithUnsignedShort", + "numberWithInt", + "numberWithUnsignedInt", + "numberWithLong", + "numberWithUnsignedLong", + "numberWithLongLong", + "numberWithUnsignedLongLong", + "numberWithFloat", + "numberWithDouble", + "numberWithBool", + "numberWithInteger", + "numberWithUnsignedInteger" + }; + static const char *InstanceSelectorName[NumNSNumberLiteralMethods] = { + "initWithChar", + "initWithUnsignedChar", + "initWithShort", + "initWithUnsignedShort", + "initWithInt", + "initWithUnsignedInt", + "initWithLong", + "initWithUnsignedLong", + "initWithLongLong", + "initWithUnsignedLongLong", + "initWithFloat", + "initWithDouble", + "initWithBool", + "initWithInteger", + "initWithUnsignedInteger" + }; + + Selector *Sels; + const char **Names; + if (Instance) { + Sels = NSNumberInstanceSelectors; + Names = InstanceSelectorName; + } else { + Sels = NSNumberClassSelectors; + Names = ClassSelectorName; + } + + if (Sels[MK].isNull()) + Sels[MK] = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(Names[MK])); + return Sels[MK]; +} + +llvm::Optional<NSAPI::NSNumberLiteralMethodKind> +NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { + for (unsigned i = 0; i != NumNSNumberLiteralMethods; ++i) { + NSNumberLiteralMethodKind MK = NSNumberLiteralMethodKind(i); + if (isNSNumberLiteralSelector(MK, Sel)) + return MK; + } + + return llvm::Optional<NSNumberLiteralMethodKind>(); +} + +llvm::Optional<NSAPI::NSNumberLiteralMethodKind> +NSAPI::getNSNumberFactoryMethodKind(QualType T) { + const BuiltinType *BT = T->getAs<BuiltinType>(); + if (!BT) + return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); + + switch (BT->getKind()) { + case BuiltinType::Char_S: + case BuiltinType::SChar: + return NSAPI::NSNumberWithChar; + case BuiltinType::Char_U: + case BuiltinType::UChar: + return NSAPI::NSNumberWithUnsignedChar; + case BuiltinType::Short: + return NSAPI::NSNumberWithShort; + case BuiltinType::UShort: + return NSAPI::NSNumberWithUnsignedShort; + case BuiltinType::Int: + return NSAPI::NSNumberWithInt; + case BuiltinType::UInt: + return NSAPI::NSNumberWithUnsignedInt; + case BuiltinType::Long: + return NSAPI::NSNumberWithLong; + case BuiltinType::ULong: + return NSAPI::NSNumberWithUnsignedLong; + case BuiltinType::LongLong: + return NSAPI::NSNumberWithLongLong; + case BuiltinType::ULongLong: + return NSAPI::NSNumberWithUnsignedLongLong; + case BuiltinType::Float: + return NSAPI::NSNumberWithFloat; + case BuiltinType::Double: + return NSAPI::NSNumberWithDouble; + case BuiltinType::Bool: + return NSAPI::NSNumberWithBool; + + case BuiltinType::Void: + case BuiltinType::WChar_U: + case BuiltinType::WChar_S: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::Int128: + case BuiltinType::LongDouble: + case BuiltinType::UInt128: + case BuiltinType::NullPtr: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCId: + case BuiltinType::ObjCSel: + case BuiltinType::BoundMember: + case BuiltinType::Dependent: + case BuiltinType::Overload: + case BuiltinType::UnknownAny: + case BuiltinType::ARCUnbridgedCast: + case BuiltinType::Half: + case BuiltinType::PseudoObject: + break; + } + + return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); +} diff --git a/lib/AST/StmtDumper.cpp b/lib/AST/StmtDumper.cpp index 608e8ae46e..5120c8c352 100644 --- a/lib/AST/StmtDumper.cpp +++ b/lib/AST/StmtDumper.cpp @@ -112,6 +112,7 @@ namespace { case OK_Ordinary: break; case OK_BitField: OS << " bitfield"; break; case OK_ObjCProperty: OS << " objcproperty"; break; + case OK_ObjCSubscript: OS << " objcsubscript"; break; case OK_VectorComponent: OS << " vectorcomponent"; break; } } @@ -168,7 +169,9 @@ namespace { void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); void VisitObjCProtocolExpr(ObjCProtocolExpr *Node); void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node); + void VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node); void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node); + void VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Node); }; } @@ -682,6 +685,32 @@ void StmtDumper::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) { OS << " super"; } +void StmtDumper::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) { + DumpExpr(Node); + if (Node->isArraySubscriptRefExpr()) + OS << " Kind=ArraySubscript GetterForArray=\""; + else + OS << " Kind=DictionarySubscript GetterForDictionary=\""; + if (Node->getAtIndexMethodDecl()) + OS << Node->getAtIndexMethodDecl()->getSelector().getAsString(); + else + OS << "(null)"; + + if (Node->isArraySubscriptRefExpr()) + OS << "\" SetterForArray=\""; + else + OS << "\" SetterForDictionary=\""; + if (Node->setAtIndexMethodDecl()) + OS << Node->setAtIndexMethodDecl()->getSelector().getAsString(); + else + OS << "(null)"; +} + +void StmtDumper::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Node) { + DumpExpr(Node); + OS << " " << (Node->getValue() ? "__objc_yes" : "__objc_no"); +} + //===----------------------------------------------------------------------===// // Stmt method implementations //===----------------------------------------------------------------------===// diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 6d3e783f22..cd8b6bb5c2 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -603,6 +603,14 @@ void StmtPrinter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) { OS << Node->getExplicitProperty()->getName(); } +void StmtPrinter::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *Node) { + + PrintExpr(Node->getBaseExpr()); + OS << "["; + PrintExpr(Node->getKeyExpr()); + OS << "]"; +} + void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { switch (Node->getIdentType()) { default: @@ -1646,6 +1654,41 @@ void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { VisitStringLiteral(Node->getString()); } +void StmtPrinter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { + OS << "@"; + Visit(E->getNumber()); +} + +void StmtPrinter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + OS << "@[ "; + StmtRange ch = E->children(); + if (ch.first != ch.second) { + while (1) { + Visit(*ch.first); + ++ch.first; + if (ch.first == ch.second) break; + OS << ", "; + } + } + OS << " ]"; +} + +void StmtPrinter::VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + OS << "@{ "; + for (unsigned I = 0, N = E->getNumElements(); I != N; ++I) { + if (I > 0) + OS << ", "; + + ObjCDictionaryElement Element = E->getKeyValueElement(I); + Visit(Element.Key); + OS << " : "; + Visit(Element.Value); + if (Element.isPackExpansion()) + OS << "..."; + } + OS << " }"; +} + void StmtPrinter::VisitObjCEncodeExpr(ObjCEncodeExpr *Node) { OS << "@encode(" << Node->getEncodedType().getAsString(Policy) << ')'; } @@ -1696,6 +1739,10 @@ void StmtPrinter::VisitObjCMessageExpr(ObjCMessageExpr *Mess) { OS << "]"; } +void StmtPrinter::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Node) { + OS << (Node->getValue() ? "__objc_yes" : "__objc_no"); +} + void StmtPrinter::VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { PrintExpr(E->getSubExpr()); diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index d996925906..7935d6d44d 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -978,6 +978,18 @@ void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } +void StmtProfiler::VisitObjCNumericLiteral(const ObjCNumericLiteral *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitObjCArrayLiteral(const ObjCArrayLiteral *E) { + VisitExpr(E); +} + +void StmtProfiler::VisitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E) { + VisitExpr(E); +} + void StmtProfiler::VisitObjCEncodeExpr(const ObjCEncodeExpr *S) { VisitExpr(S); VisitType(S->getEncodedType()); @@ -1014,6 +1026,12 @@ void StmtProfiler::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *S) { } } +void StmtProfiler::VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr *S) { + VisitExpr(S); + VisitDecl(S->getAtIndexMethodDecl()); + VisitDecl(S->setAtIndexMethodDecl()); +} + void StmtProfiler::VisitObjCMessageExpr(const ObjCMessageExpr *S) { VisitExpr(S); VisitName(S->getSelector()); @@ -1025,6 +1043,11 @@ void StmtProfiler::VisitObjCIsaExpr(const ObjCIsaExpr *S) { ID.AddBoolean(S->isArrow()); } +void StmtProfiler::VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->getValue()); +} + void StmtProfiler::VisitObjCIndirectCopyRestoreExpr( const ObjCIndirectCopyRestoreExpr *S) { VisitExpr(S); diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index f7ab880e6c..d5ef402d22 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -177,6 +177,9 @@ public: Value *VisitCharacterLiteral(const CharacterLiteral *E) { return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue()); } + Value *VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *E) { + return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue()); + } Value *VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) { return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue()); } @@ -519,6 +522,15 @@ public: Value *VisitObjCStringLiteral(const ObjCStringLiteral *E) { return CGF.EmitObjCStringLiteral(E); } + Value *VisitObjCNumericLiteral(ObjCNumericLiteral *E) { + return CGF.EmitObjCNumericLiteral(E); + } + Value *VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + return CGF.EmitObjCArrayLiteral(E); + } + Value *VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + return CGF.EmitObjCDictionaryLiteral(E); + } Value *VisitAsTypeExpr(AsTypeExpr *CE); Value *VisitAtomicExpr(AtomicExpr *AE); }; diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 6d95f17226..6ece9e82b3 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -29,6 +29,10 @@ using namespace CodeGen; typedef llvm::PointerIntPair<llvm::Value*,1,bool> TryEmitResult; static TryEmitResult tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e); +static RValue AdjustRelatedResultType(CodeGenFunction &CGF, + const Expr *E, + const ObjCMethodDecl *Method, + RValue Result); /// Given the address of a variable of pointer type, find the correct /// null to store into it. @@ -47,6 +51,138 @@ llvm::Value *CodeGenFunction::EmitObjCStringLiteral(const ObjCStringLiteral *E) return llvm::ConstantExpr::getBitCast(C, ConvertType(E->getType())); } +/// EmitObjCNumericLiteral - This routine generates code for +/// the appropriate +[NSNumber numberWith<Type>:] method. +/// +llvm::Value *CodeGenFunction::EmitObjCNumericLiteral(const ObjCNumericLiteral *E) { + // Generate the correct selector for this literal's concrete type. + const Expr *NL = E->getNumber(); + // Get the method. + const ObjCMethodDecl *Method = E->getObjCNumericLiteralMethod(); + assert(Method && "NSNumber method is null"); + Selector Sel = Method->getSelector(); + + // Generate a reference to the class pointer, which will be the receiver. + QualType ResultType = E->getType(); // should be NSNumber * + const ObjCObjectPointerType *InterfacePointerType = + ResultType->getAsObjCInterfacePointerType(); + ObjCInterfaceDecl *NSNumberDecl = + InterfacePointerType->getObjectType()->getInterface(); + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); + llvm::Value *Receiver = Runtime.GetClass(Builder, NSNumberDecl); + + const ParmVarDecl *argDecl = *Method->param_begin(); + QualType ArgQT = argDecl->getType().getUnqualifiedType(); + RValue RV = EmitAnyExpr(NL); + CallArgList Args; + Args.add(RV, ArgQT); + + RValue result = Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + ResultType, Sel, Receiver, Args, + NSNumberDecl, Method); + return Builder.CreateBitCast(result.getScalarVal(), + ConvertType(E->getType())); +} + +llvm::Value *CodeGenFunction::EmitObjCCollectionLiteral(const Expr *E, + const ObjCMethodDecl *MethodWithObjects) { + ASTContext &Context = CGM.getContext(); + const ObjCDictionaryLiteral *DLE = 0; + const ObjCArrayLiteral *ALE = dyn_cast<ObjCArrayLiteral>(E); + if (!ALE) + DLE = cast<ObjCDictionaryLiteral>(E); + + // Compute the type of the array we're initializing. + uint64_t NumElements = + ALE ? ALE->getNumElements() : DLE->getNumElements(); + llvm::APInt APNumElements(Context.getTypeSize(Context.getSizeType()), + NumElements); + QualType ElementType = Context.getObjCIdType().withConst(); + QualType ElementArrayType + = Context.getConstantArrayType(ElementType, APNumElements, + ArrayType::Normal, /*IndexTypeQuals=*/0); + + // Allocate the temporary array(s). + llvm::Value *Objects = CreateMemTemp(ElementArrayType, "objects"); + llvm::Value *Keys = 0; + if (DLE) + Keys = CreateMemTemp(ElementArrayType, "keys"); + + // Perform the actual initialialization of the array(s). + for (uint64_t i = 0; i < NumElements; i++) { + if (ALE) { + // Emit the initializer. + const Expr *Rhs = ALE->getElement(i); + LValue LV = LValue::MakeAddr(Builder.CreateStructGEP(Objects, i), + ElementType, + Context.getTypeAlignInChars(Rhs->getType()), + Context); + EmitScalarInit(Rhs, /*D=*/0, LV, /*capturedByInit=*/false); + } else { + // Emit the key initializer. + const Expr *Key = DLE->getKeyValueElement(i).Key; + LValue KeyLV = LValue::MakeAddr(Builder.CreateStructGEP(Keys, i), + ElementType, + Context.getTypeAlignInChars(Key->getType()), + Context); + EmitScalarInit(Key, /*D=*/0, KeyLV, /*capturedByInit=*/false); + + // Emit the value initializer. + const Expr *Value = DLE->getKeyValueElement(i).Value; + LValue ValueLV = LValue::MakeAddr(Builder.CreateStructGEP(Objects, i), + ElementType, + Context.getTypeAlignInChars(Value->getType()), + Context); + EmitScalarInit(Value, /*D=*/0, ValueLV, /*capturedByInit=*/false); + } + } + + // Generate the argument list. + CallArgList Args; + ObjCMethodDecl::param_const_iterator PI = MethodWithObjects->param_begin(); + const ParmVarDecl *argDecl = *PI++; + QualType ArgQT = argDecl->getType().getUnqualifiedType(); + Args.add(RValue::get(Objects), ArgQT); + if (DLE) { + argDecl = *PI++; + ArgQT = argDecl->getType().getUnqualifiedType(); + Args.add(RValue::get(Keys), ArgQT); + } + argDecl = *PI; + ArgQT = argDecl->getType().getUnqualifiedType(); + llvm::Value *Count = + llvm::ConstantInt::get(CGM.getTypes().ConvertType(ArgQT), NumElements); + Args.add(RValue::get(Count), ArgQT); + + // Generate a reference to the class pointer, which will be the receiver. + Selector Sel = MethodWithObjects->getSelector(); + QualType ResultType = E->getType(); + const ObjCObjectPointerType *InterfacePointerType + = ResultType->getAsObjCInterfacePointerType(); + ObjCInterfaceDecl *Class + = InterfacePointerType->getObjectType()->getInterface(); + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); + llvm::Value *Receiver = Runtime.GetClass(Builder, Class); + + // Generate the message send. + RValue result = Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + MethodWithObjects->getResultType(), + Sel, + Receiver, Args, Class, + MethodWithObjects); + return Builder.CreateBitCast(result.getScalarVal(), + ConvertType(E->getType())); +} + +llvm::Value *CodeGenFunction::EmitObjCArrayLiteral(const ObjCArrayLiteral *E) { + return EmitObjCCollectionLiteral(E, E->getArrayWithObjectsMethod()); +} + +llvm::Value *CodeGenFunction::EmitObjCDictionaryLiteral( + const ObjCDictionaryLiteral *E) { + return EmitObjCCollectionLiteral(E, E->getDictWithObjectsMethod()); +} + /// Emit a selector. llvm::Value *CodeGenFunction::EmitObjCSelectorExpr(const ObjCSelectorExpr *E) { // Untyped selector. @@ -884,6 +1020,26 @@ static bool hasTrivialSetExpr(const ObjCPropertyImplDecl *PID) { return false; } +bool UseOptimizedSetter(CodeGenModule &CGM) { + if (CGM.getLangOptions().getGC() != LangOptions::NonGC) + return false; + const TargetInfo &Target = CGM.getContext().getTargetInfo(); + StringRef TargetPlatform = Target.getPlatformName(); + if (TargetPlatform.empty()) + return false; + VersionTuple TargetMinVersion = Target.getPlatformMinVersion(); + + if (TargetPlatform.compare("macosx") || + TargetMinVersion.getMajor() <= 9) + return false; + + unsigned minor = 0; + if (llvm::Optional<unsigned> Minor = TargetMinVersion.getMinor()) + minor = *Minor; + + return (minor >= 8); +} + void CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, @@ -937,13 +1093,27 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, case PropertyImplStrategy::GetSetProperty: case PropertyImplStrategy::SetPropertyAndExpressionGet: { - llvm::Value *setPropertyFn = - CGM.getObjCRuntime().GetPropertySetFunction(); - if (!setPropertyFn) { - CGM.ErrorUnsupported(propImpl, "Obj-C setter requiring atomic copy"); - return; + + llvm::Value *setOptimizedPropertyFn = 0; + llvm::Value *setPropertyFn = 0; + if (UseOptimizedSetter(CGM)) { + // 10.8 code and GC is off + setOptimizedPropertyFn = + CGM.getObjCRuntime().GetOptimizedPropertySetFunction(strategy.isAtomic(), + strategy.isCopy()); + if (!setOptimizedPropertyFn) { + CGM.ErrorUnsupported(propImpl, "Obj-C optimized setter - NYI"); + return; + } } - + else { + setPropertyFn = CGM.getObjCRuntime().GetPropertySetFunction(); + if (!setPropertyFn) { + CGM.ErrorUnsupported(propImpl, "Obj-C setter requiring atomic copy"); + return; + } + } + // Emit objc_setProperty((id) self, _cmd, offset, arg, // <is-atomic>, <is-copy>). llvm::Value *cmd = @@ -958,18 +1128,28 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, CallArgList args; args.add(RValue::get(self), getContext().getObjCIdType()); args.add(RValue::get(cmd), getContext().getObjCSelType()); - args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); - args.add(RValue::get(arg), getContext().getObjCIdType()); - args.add(RValue::get(Builder.getInt1(strategy.isAtomic())), - getContext().BoolTy); - args.add(RValue::get(Builder.getInt1(strategy.isCopy())), - getContext().BoolTy); - // FIXME: We shouldn't need to get the function info here, the runtime - // already should have computed it to build the function. - EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), - setPropertyFn, ReturnValueSlot(), args); + if (setOptimizedPropertyFn) { + args.add(RValue::get(arg), getContext().getObjCIdType()); + args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); + EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args, + FunctionType::ExtInfo(), + RequiredArgs::All), + setOptimizedPropertyFn, ReturnValueSlot(), args); + } else { + args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); + args.add(RValue::get(arg), getContext().getObjCIdType()); + args.add(RValue::get(Builder.getInt1(strategy.isAtomic())), + getContext().BoolTy); + args.add(RValue::get(Builder.getInt1(strategy.isCopy())), + getContext().BoolTy); + // FIXME: We shouldn't need to get the function info here, the runtime + // already should have computed it to build the function. + EmitCall(getTypes().arrangeFunctionCall(getContext().VoidTy, args, + FunctionType::ExtInfo(), + RequiredArgs::All), + setPropertyFn, ReturnValueSlot(), args); + } + return; } diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index 299f44c3ef..16812e2957 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -472,6 +472,8 @@ public: virtual llvm::Function *ModuleInitFunction(); virtual llvm::Constant *GetPropertyGetFunction(); virtual llvm::Constant *GetPropertySetFunction(); + virtual llvm::Constant *GetOptimizedPropertySetFunction(bool atomic, + bool copy); virtual llvm::Constant *GetSetStructFunction(); virtual llvm::Constant *GetCppAtomicObjectFunction(); virtual llvm::Constant *GetGetStructFunction(); @@ -2427,6 +2429,11 @@ llvm::Constant *CGObjCGNU::GetPropertySetFunction() { return SetPropertyFn; } +llvm::Constant *CGObjCGNU::GetOptimizedPropertySetFunction(bool atomic, + bool copy) { + return 0; +} + llvm::Constant *CGObjCGNU::GetGetStructFunction() { return GetStructPropertyFn; } diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index f0682f2de9..a408681aad 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -267,6 +267,41 @@ public: return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } + llvm::Constant *getOptimizedSetPropertyFn(bool atomic, bool copy) { + CodeGen::CodeGenTypes &Types = CGM.getTypes(); + ASTContext &Ctx = CGM.getContext(); + // void objc_setProperty_atomic(id self, SEL _cmd, + // id newValue, ptrdiff_t offset); + // void objc_setProperty_nonatomic(id self, SEL _cmd, + // id newValue, ptrdiff_t offset); + // void objc_setProperty_atomic_copy(id self, SEL _cmd, + // id newValue, ptrdiff_t offset); + // void objc_setProperty_nonatomic_copy(id self, SEL _cmd, + // id newValue, ptrdiff_t offset); + + SmallVector<CanQualType,4> Params; + CanQualType IdType = Ctx.getCanonicalParamType(Ctx.getObjCIdType()); + CanQualType SelType = Ctx.getCanonicalParamType(Ctx.getObjCSelType()); + Params.push_back(IdType); + Params.push_back(SelType); + Params.push_back(IdType); + Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); + llvm::FunctionType *FTy = + Types.GetFunctionType(Types.arrangeFunctionType(Ctx.VoidTy, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); + const char *name; + if (atomic && copy) + name = "objc_setProperty_atomic_copy"; + else if (atomic && !copy) + name = "objc_setProperty_atomic"; + else if (!atomic && copy) + name = "objc_setProperty_nonatomic_copy"; + else + name = "objc_setProperty_nonatomic"; + + return CGM.CreateRuntimeFunction(FTy, name); + } llvm::Constant *getCopyStructFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); @@ -906,7 +941,7 @@ public: CGObjCRuntime(cgm), VMContext(cgm.getLLVMContext()) { } virtual llvm::Constant *GenerateConstantString(const StringLiteral *SL); - + virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD=0); @@ -1087,6 +1122,8 @@ public: virtual llvm::Constant *GetPropertyGetFunction(); virtual llvm::Constant *GetPropertySetFunction(); + virtual llvm::Constant *GetOptimizedPropertySetFunction(bool atomic, + bool copy); virtual llvm::Constant *GetGetStructFunction(); virtual llvm::Constant *GetSetStructFunction(); virtual llvm::Constant *GetCppAtomicObjectFunction(); @@ -1349,6 +1386,11 @@ public: return ObjCTypes.getSetPropertyFn(); } + virtual llvm::Constant *GetOptimizedPropertySetFunction(bool atomic, + bool copy) { + return ObjCTypes.getOptimizedSetPropertyFn(atomic, copy); + } + virtual llvm::Constant *GetSetStructFunction() { return ObjCTypes.getCopyStructFn(); } @@ -1578,6 +1620,10 @@ llvm::Constant *CGObjCCommonMac::GenerateConstantString( CGM.GetAddrOfConstantString(SL)); } +enum { + kCFTaggedObjectID_Integer = (1 << 1) + 1 +}; + /// Generates a message send where the super is the receiver. This is /// a message send to self with special delivery semantics indicating /// which class's method should be called. @@ -2723,6 +2769,11 @@ llvm::Constant *CGObjCMac::GetPropertySetFunction() { return ObjCTypes.getSetPropertyFn(); } +llvm::Constant *CGObjCMac::GetOptimizedPropertySetFunction(bool atomic, + bool copy) { + return ObjCTypes.getOptimizedSetPropertyFn(atomic, copy); +} + llvm::Constant *CGObjCMac::GetGetStructFunction() { return ObjCTypes.getCopyStructFn(); } diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 0f3cb549ec..ccf4d4dfca 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -135,7 +135,7 @@ public: /// Generate a constant string object. virtual llvm::Constant *GenerateConstantString(const StringLiteral *) = 0; - + /// Generate a category. A category contains a list of methods (and /// accompanying metadata) and a list of protocols. virtual void GenerateCategory(const ObjCCategoryImplDecl *OCD) = 0; @@ -202,6 +202,10 @@ public: /// Return the runtime function for setting properties. virtual llvm::Constant *GetPropertySetFunction() = 0; + /// Return the runtime function for optimized setting properties. + virtual llvm::Constant *GetOptimizedPropertySetFunction(bool atomic, + bool copy) = 0; + // API for atomic copying of qualified aggregates in getter. virtual llvm::Constant *GetGetStructFunction() = 0; // API for atomic copying of qualified aggregates in setter. diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index d2eaa68298..833eee2da4 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2238,6 +2238,11 @@ public: llvm::Value *EmitObjCProtocolExpr(const ObjCProtocolExpr *E); llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E); + llvm::Value *EmitObjCNumericLiteral(const ObjCNumericLiteral *E); + llvm::Value *EmitObjCArrayLiteral(const ObjCArrayLiteral *E); + llvm::Value *EmitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E); + llvm::Value *EmitObjCCollectionLiteral(const Expr *E, + const ObjCMethodDecl *MethodWithObjects); llvm::Value *EmitObjCSelectorExpr(const ObjCSelectorExpr *E); RValue EmitObjCMessageExpr(const ObjCMessageExpr *E, ReturnValueSlot Return = ReturnValueSlot()); diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index e125ab78f4..5ff82c7600 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -93,12 +93,17 @@ bool Darwin::hasARCRuntime() const { return !isMacosxVersionLT(10, 7); } +bool Darwin::hasSubscriptingRuntime() const { + return !isTargetIPhoneOS() && !isMacosxVersionLT(10, 8); +} + /// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0. void Darwin::configureObjCRuntime(ObjCRuntime &runtime) const { if (runtime.getKind() != ObjCRuntime::NeXT) return ToolChain::configureObjCRuntime(runtime); runtime.HasARC = runtime.HasWeak = hasARCRuntime(); + runtime.HasSubscripting = hasSubscriptingRuntime(); // So far, objc_terminate is only available in iOS 5. // FIXME: do the simulator logic properly. diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 0d591107de..aee21741dd 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -205,6 +205,7 @@ private: std::string MacosxVersionMin; bool hasARCRuntime() const; + bool hasSubscriptingRuntime() const; private: void AddDeploymentTarget(DerivedArgList &Args) const; @@ -252,6 +253,12 @@ public: return TargetIsIPhoneOSSimulator; } + bool isTargetMacOS() const { + return !isTargetIOSSimulator() && + !isTargetIPhoneOS() && + ARCRuntimeForSimulator == ARCSimulator_None; + } + bool isTargetInitialized() const { return TargetInitialized; } void getTargetVersion(unsigned (&Res)[3]) const { diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 0849c8d620..38f21c0882 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -136,6 +136,13 @@ static bool isObjCAutoRefCount(const ArgList &Args) { return Args.hasFlag(options::OPT_fobjc_arc, options::OPT_fno_objc_arc, false); } +/// \brief Determine whether we are linking the ObjC runtime. +static bool isObjCRuntimeLinked(const ArgList &Args) { + if (isObjCAutoRefCount(Args)) + return true; + return Args.hasArg(options::OPT_fobjc_link_runtime); +} + static void addProfileRT(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, llvm::Triple Triple) { @@ -4025,7 +4032,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-lcrt1.o"); else if (getDarwinToolChain().isMacosxVersionLT(10, 6)) CmdArgs.push_back("-lcrt1.10.5.o"); - else + else if (getDarwinToolChain().isMacosxVersionLT(10, 8)) CmdArgs.push_back("-lcrt1.10.6.o"); // darwin_crt2 spec is empty. @@ -4064,14 +4071,24 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, getDarwinToolChain().AddLinkSearchPathArgs(Args, CmdArgs); - // In ARC, if we don't have runtime support, link in the runtime - // stubs. We have to do this *before* adding any of the normal - // linker inputs so that its initializer gets run first. - if (isObjCAutoRefCount(Args)) { - ObjCRuntime runtime; - getDarwinToolChain().configureObjCRuntime(runtime); - if (!runtime.HasARC) - getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs); + if (isObjCRuntimeLinked(Args)) { + // Avoid linking compatibility stubs on i386 mac. + if (!getDarwinToolChain().isTargetMacOS() || + getDarwinToolChain().getArchName() != "i386") { + // If we don't have ARC or subscripting runtime support, link in the + // runtime stubs. We have to do this *before* adding any of the normal + // linker inputs so that its initializer gets run first. + ObjCRuntime runtime; + getDarwinToolChain().configureObjCRuntime(runtime); + // We use arclite library for both ARC and subscripting support. + if ((!runtime.HasARC && isObjCAutoRefCount(Args)) || + !runtime.HasSubscripting) + getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs); + CmdArgs.push_back("-framework"); + CmdArgs.push_back("Foundation"); + } + // Link libobj. + CmdArgs.push_back("-lobjc"); } AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 777e0db026..5f9f1acf73 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -630,6 +630,10 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("ownership_holds", true) .Case("ownership_returns", true) .Case("ownership_takes", true) + .Case("objc_bool", true) + .Case("objc_subscripting", LangOpts.ObjCNonFragileABI) + .Case("objc_array_literals", LangOpts.ObjC2) + .Case("objc_dictionary_literals", LangOpts.ObjC2) .Case("arc_cf_code_audited", true) // C11 features .Case("c_alignas", LangOpts.C11) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 21a2e573c7..a1c3b05030 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -707,6 +707,10 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, case tok::kw_true: case tok::kw_false: return ParseCXXBoolLiteral(); + + case tok::kw___objc_yes: + case tok::kw___objc_no: + return ParseObjCBoolLiteral(); case tok::kw_nullptr: Diag(Tok, diag::warn_cxx98_compat_nullptr); @@ -2403,3 +2407,12 @@ ExprResult Parser::ParseBlockLiteralExpression() { Actions.ActOnBlockError(CaretLoc, getCurScope()); return move(Result); } + +/// ParseObjCBoolLiteral - This handles the objective-c Boolean literals. +/// +/// '__objc_yes' +/// '__objc_no' +ExprResult Parser::ParseObjCBoolLiteral() { + tok::TokenKind Kind = Tok.getKind(); + return Actions.ActOnObjCBoolLiteral(ConsumeToken(), Kind); +} diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 5ed8ffec4c..959d63171b 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -2009,11 +2009,64 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { cutOffParsing(); return ExprError(); + case tok::minus: + case tok::plus: { + tok::TokenKind Kind = Tok.getKind(); + SourceLocation OpLoc = ConsumeToken(); + + if (!Tok.is(tok::numeric_constant)) { + const char *Symbol = 0; + switch (Kind) { + case tok::minus: Symbol = "-"; break; + case tok::plus: Symbol = "+"; break; + default: llvm_unreachable("missing unary operator case"); + } + Diag(Tok, diag::err_nsnumber_nonliteral_unary) + << Symbol; + return ExprError(); + } + + ExprResult Lit(Actions.ActOnNumericConstant(Tok)); + if (Lit.isInvalid()) { + return move(Lit); + } + SourceLocation EndLoc = ConsumeToken(); // consume the literal token. + + Lit = Actions.ActOnUnaryOp(getCurScope(), OpLoc, Kind, Lit.take()); + if (Lit.isInvalid()) + return move(Lit); + + return ParsePostfixExpressionSuffix( + Actions.BuildObjCNumericLiteral(AtLoc, Lit.take())); + } + case tok::string_literal: // primary-expression: string-literal case tok::wide_string_literal: if (Tok.hasUDSuffix()) return ExprError(Diag(Tok, diag::err_invalid_string_udl)); return ParsePostfixExpressionSuffix(ParseObjCStringLiteral(AtLoc)); + + case tok::char_constant: + return ParsePostfixExpressionSuffix(ParseObjCCharacterLiteral(AtLoc)); + + case tok::numeric_constant: + return ParsePostfixExpressionSuffix(ParseObjCNumericLiteral(AtLoc)); + + case tok::kw_true: // Objective-C++, etc. + case tok::kw___objc_yes: // c/c++/objc/objc++ __objc_yes + return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, true)); + case tok::kw_false: // Objective-C++, etc. + case tok::kw___objc_no: // c/c++/objc/objc++ __objc_no + return ParsePostfixExpressionSuffix(ParseObjCBooleanLiteral(AtLoc, false)); + + case tok::l_square: + // Objective-C array literal + return ParsePostfixExpressionSuffix(ParseObjCArrayLiteral(AtLoc)); + + case tok::l_brace: + // Objective-C dictionary literal + return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc)); + default: if (Tok.getIdentifierInfo() == 0) return ExprError(Diag(AtLoc, diag::err_unexpected_at)); @@ -2491,6 +2544,134 @@ ExprResult Parser::ParseObjCStringLiteral(SourceLocation AtLoc) { AtStrings.size())); } +/// ParseObjCBooleanLiteral - +/// objc-scalar-literal : '@' boolean-keyword +/// ; +/// boolean-keyword: 'true' | 'false' | '__objc_yes' | '__objc_no' +/// ; +ExprResult Parser::ParseObjCBooleanLiteral(SourceLocation AtLoc, + bool ArgValue) { + SourceLocation EndLoc = ConsumeToken(); // consume the keyword. + return Actions.ActOnObjCBoolLiteral(AtLoc, EndLoc, ArgValue); +} + +/// ParseObjCCharacterLiteral - +/// objc-scalar-literal : '@' character-literal +/// ; +ExprResult Parser::ParseObjCCharacterLiteral(SourceLocation AtLoc) { + ExprResult Lit(Actions.ActOnCharacterConstant(Tok)); + if (Lit.isInvalid()) { + return move(Lit); + } + SourceLocation EndLoc = ConsumeToken(); // consume the literal token. + return Owned(Actions.BuildObjCNumericLiteral(AtLoc, Lit.take())); +} + +/// ParseObjCNumericLiteral - +/// objc-scalar-literal : '@' scalar-literal +/// ; +/// scalar-literal : | numeric-constant /* any numeric constant. */ +/// ; +ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) { + ExprResult Lit(Actions.ActOnNumericConstant(Tok)); + if (Lit.isInvalid()) { + return move(Lit); + } + SourceLocation EndLoc = ConsumeToken(); // consume the literal token. + return Owned(Actions.BuildObjCNumericLiteral(AtLoc, Lit.take())); +} + +ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) { + ExprVector ElementExprs(Actions); // array elements. + ConsumeBracket(); // consume the l_square. + + while (Tok.isNot(tok::r_square)) { + // Parse list of array element expressions (all must be id types). + ExprResult Res(ParseAssignmentExpression()); + if (Res.isInvalid()) { + // We must manually skip to a ']', otherwise the expression skipper will + // stop at the ']' when it skips to the ';'. We want it to skip beyond + // the enclosing expression. + SkipUntil(tok::r_square); + return move(Res); + } + + // Parse the ellipsis that indicates a pack expansion. + if (Tok.is(tok::ellipsis)) + Res = Actions.ActOnPackExpansion(Res.get(), ConsumeToken()); + if (Res.isInvalid()) + return true; + + ElementExprs.push_back(Res.release()); + + if (Tok.is(tok::comma)) + ConsumeToken(); // Eat the ','. + else if (Tok.isNot(tok::r_square)) + return ExprError(Diag(Tok, diag::err_expected_rsquare_or_comma)); + } + SourceLocation EndLoc = ConsumeBracket(); // location of ']' + MultiExprArg Args(Actions, ElementExprs.take(), ElementExprs.size()); + return Owned(Actions.BuildObjCArrayLiteral(SourceRange(AtLoc, EndLoc), Args)); +} + +ExprResult Parser::ParseObjCDictionaryLiteral(SourceLocation AtLoc) { + SmallVector<ObjCDictionaryElement, 4> Elements; // dictionary elements. + ConsumeBrace(); // consume the l_square. + while (Tok.isNot(tok::r_brace)) { + // Parse the comma separated key : value expressions. + ExprResult KeyExpr; + { + ColonProtectionRAIIObject X(*this); + KeyExpr = ParseAssignmentExpression(); + if (KeyExpr.isInvalid()) { + // We must manually skip to a '}', otherwise the expression skipper will + // stop at the '}' when it skips to the ';'. We want it to skip beyond + // the enclosing expression. + SkipUntil(tok::r_brace); + return move(KeyExpr); + } + } + + if (Tok.is(tok::colon)) { + ConsumeToken(); + } else { + return ExprError(Diag(Tok, diag::err_expected_colon)); + } + + ExprResult ValueExpr(ParseAssignmentExpression()); + if (ValueExpr.isInvalid()) { + // We must manually skip to a '}', otherwise the expression skipper will + // stop at the '}' when it skips to the ';'. We want it to skip beyond + // the enclosing expression. + SkipUntil(tok::r_brace); + return move(ValueExpr); + } + + // Parse the ellipsis that designates this as a pack expansion. + SourceLocation EllipsisLoc; + if (Tok.is(tok::ellipsis) && getLang().CPlusPlus) + EllipsisLoc = ConsumeToken(); + + // We have a valid expression. Collect it in a vector so we can + // build the argument list. + ObjCDictionaryElement Element = { + KeyExpr.get(), ValueExpr.get(), EllipsisLoc, llvm::Optional<unsigned>() + }; + Elements.push_back(Element); + + if (Tok.is(tok::comma)) + ConsumeToken(); // Eat the ','. + else if (Tok.isNot(tok::r_brace)) + return ExprError(Diag(Tok, diag::err_expected_rbrace_or_comma)); + } + SourceLocation EndLoc = ConsumeBrace(); + + // Create the ObjCDictionaryLiteral. + return Owned(Actions.BuildObjCDictionaryLiteral(SourceRange(AtLoc, EndLoc), + Elements.data(), + Elements.size())); +} + /// objc-encode-expression: /// @encode ( type-name ) ExprResult diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index bbb7af8aef..40308f07b1 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -90,6 +90,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, PackContext(0), MSStructPragmaOn(false), VisContext(0), ExprNeedsCleanups(false), LateTemplateParser(0), OpaqueParser(0), IdResolver(pp), StdInitializerList(0), CXXTypeInfoDecl(0), MSVCGuidDecl(0), + NSNumberDecl(0), NSArrayDecl(0), ArrayWithObjectsMethod(0), + NSDictionaryDecl(0), DictionaryWithObjectsMethod(0), GlobalNewDeleteDeclared(false), ObjCShouldCallSuperDealloc(false), ObjCShouldCallSuperFinalize(false), @@ -102,7 +104,12 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, { TUScope = 0; LoadedExternalKnownNamespaces = false; - + for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I) + NSNumberLiteralMethods[I] = 0; + + if (getLangOptions().ObjC1) + NSAPIObj.reset(new NSAPI(Context)); + if (getLangOptions().CPlusPlus) FieldCollector.reset(new CXXFieldCollector()); diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 8721c36432..802979f61b 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -1528,6 +1528,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, case OK_BitField: inappropriate = "bit-field"; break; case OK_VectorComponent: inappropriate = "vector element"; break; case OK_ObjCProperty: inappropriate = "property expression"; break; + case OK_ObjCSubscript: inappropriate = "container subscripting expression"; + break; } if (inappropriate) { Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_reference) diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 2d2bedeec7..df2768ccc2 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -4392,6 +4392,26 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) { Builder.AddPlaceholderChunk("selector"); Builder.AddChunk(CodeCompletionString::CK_RightParen); Results.AddResult(Result(Builder.TakeString())); + + // @[ objects, ... ] + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,[)); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("objects, ..."); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddChunk(CodeCompletionString::CK_RightBracket); + Results.AddResult(Result(Builder.TakeString())); + + // @{ key : object, ... } + Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,{)); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("key"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddChunk(CodeCompletionString::CK_Colon); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("object, ..."); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddChunk(CodeCompletionString::CK_RightBrace); + Results.AddResult(Result(Builder.TakeString())); } static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ab96f7416f..4751db756a 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2390,14 +2390,18 @@ ExprResult Sema::ActOnCharacterConstant(const Token &Tok) { Tok.getLocation())); } +ExprResult Sema::ActOnIntegerConstant(SourceLocation Loc, uint64_t Val) { + unsigned IntSize = Context.getTargetInfo().getIntWidth(); + return Owned(IntegerLiteral::Create(Context, llvm::APInt(IntSize, Val), + Context.IntTy, Loc)); +} + ExprResult Sema::ActOnNumericConstant(const Token &Tok) { // Fast path for a single digit (which is quite common). A single digit // cannot have a trigraph, escaped newline, radix prefix, or type suffix. if (Tok.getLength() == 1) { const char Val = PP.getSpellingOfSingleCharacterNumericConstant(Tok); - unsigned IntSize = Context.getTargetInfo().getIntWidth(); - return Owned(IntegerLiteral::Create(Context, llvm::APInt(IntSize, Val-'0'), - Context.IntTy, Tok.getLocation())); + return ActOnIntegerConstant(Tok.getLocation(), Val-'0'); } SmallString<512> IntegerBuffer; @@ -2926,7 +2930,8 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, (LHSExp->getType()->isRecordType() || LHSExp->getType()->isEnumeralType() || RHSExp->getType()->isRecordType() || - RHSExp->getType()->isEnumeralType())) { + RHSExp->getType()->isEnumeralType()) && + !LHSExp->getType()->isObjCObjectPointerType()) { return CreateOverloadedArraySubscriptExpr(LLoc, RLoc, Base, Idx); } @@ -2979,6 +2984,9 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, LHSTy->getAs<ObjCObjectPointerType>()) { BaseExpr = LHSExp; IndexExpr = RHSExp; + Result = BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0); + if (!Result.isInvalid()) + return Owned(Result.take()); ResultType = PTy->getPointeeType(); } else if (const ObjCObjectPointerType *PTy = RHSTy->getAs<ObjCObjectPointerType>()) { @@ -11001,3 +11009,12 @@ bool Sema::CheckCaseExpression(Expr *E) { return E->getType()->isIntegralOrEnumerationType(); return false; } + +/// ActOnObjCBoolLiteral - Parse {__objc_yes,__objc_no} literals. +ExprResult +Sema::ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) { + assert((Kind == tok::kw___objc_yes || Kind == tok::kw___objc_no) && + "Unknown Objective-C Boolean value!"); + return Owned(new (Context) ObjCBoolLiteralExpr(Kind == tok::kw___objc_yes, + Context.ObjCBuiltinBoolTy, OpLoc)); +} diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index f6662ba37f..dca7719894 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4456,11 +4456,27 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { } else if (isa<StmtExpr>(E)) { ReturnsRetained = true; + // We hit this case with the lambda conversion-to-block optimization; + // we don't want any extra casts here. + } else if (isa<CastExpr>(E) && + isa<BlockExpr>(cast<CastExpr>(E)->getSubExpr())) { + return Owned(E); + // For message sends and property references, we try to find an // actual method. FIXME: we should infer retention by selector in // cases where we don't have an actual method. - } else if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) { - ObjCMethodDecl *D = Send->getMethodDecl(); + } else { + ObjCMethodDecl *D = 0; + if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) { + D = Send->getMethodDecl(); + } else if (ObjCNumericLiteral *NumLit = dyn_cast<ObjCNumericLiteral>(E)) { + D = NumLit->getObjCNumericLiteralMethod(); + } else if (ObjCArrayLiteral *ArrayLit = dyn_cast<ObjCArrayLiteral>(E)) { + D = ArrayLit->getArrayWithObjectsMethod(); + } else if (ObjCDictionaryLiteral *DictLit + = dyn_cast<ObjCDictionaryLiteral>(E)) { + D = DictLit->getDictWithObjectsMethod(); + } ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>()); @@ -4470,13 +4486,6 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (!ReturnsRetained && D && D->getMethodFamily() == OMF_performSelector) return Owned(E); - } else if (isa<CastExpr>(E) && - isa<BlockExpr>(cast<CastExpr>(E)->getSubExpr())) { - // We hit this case with the lambda conversion-to-block optimization; - // we don't want any extra casts here. - return Owned(E); - } else { - ReturnsRetained = false; } // Don't reclaim an object of Class type. diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 22e432d8dc..0eae2e2692 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -17,6 +17,8 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/Initialization.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Edit/Rewriters.h" +#include "clang/Edit/Commit.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" @@ -70,7 +72,11 @@ ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, Context.getPointerType(Context.CharTy), &StrLocs[0], StrLocs.size()); } + + return BuildObjCStringLiteral(AtLocs[0], S); +} +ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ // Verify that this composite string is acceptable for ObjC strings. if (CheckObjCString(S)) return true; @@ -91,7 +97,7 @@ ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, else NSIdent = &Context.Idents.get(StringClass); - NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLocs[0], + NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, LookupOrdinaryName); if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { Context.setObjCConstantStringInterface(StrIF); @@ -106,7 +112,7 @@ ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, } } else { IdentifierInfo *NSIdent = &Context.Idents.get("NSString"); - NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLocs[0], + NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, LookupOrdinaryName); if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { Context.setObjCConstantStringInterface(StrIF); @@ -131,7 +137,613 @@ ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, } } - return new (Context) ObjCStringLiteral(S, Ty, AtLocs[0]); + return new (Context) ObjCStringLiteral(S, Ty, AtLoc); +} + +/// \brief Retrieve the NSNumber factory method that should be used to create +/// an Objective-C literal for the given type. +static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, + QualType T, QualType ReturnType, + SourceRange Range) { + llvm::Optional<NSAPI::NSNumberLiteralMethodKind> Kind + = S.NSAPIObj->getNSNumberFactoryMethodKind(T); + + if (!Kind) { + S.Diag(Loc, diag::err_invalid_nsnumber_type) + << T << Range; + return 0; + } + + // If we already looked up this method, we're done. + if (S.NSNumberLiteralMethods[*Kind]) + return S.NSNumberLiteralMethods[*Kind]; + + Selector Sel = S.NSAPIObj->getNSNumberLiteralSelector(*Kind, + /*Instance=*/false); + + // Look for the appropriate method within NSNumber. + ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel);; + if (!Method && S.getLangOptions().DebuggerObjCLiteral) { + TypeSourceInfo *ResultTInfo = 0; + Method = ObjCMethodDecl::Create(S.Context, SourceLocation(), SourceLocation(), Sel, + ReturnType, + ResultTInfo, + S.Context.getTranslationUnitDecl(), + false /*Instance*/, false/*isVariadic*/, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, + false); + ParmVarDecl *value = ParmVarDecl::Create(S.Context, Method, + SourceLocation(), SourceLocation(), + &S.Context.Idents.get("value"), + T, /*TInfo=*/0, SC_None, SC_None, 0); + Method->setMethodParams(S.Context, value, ArrayRef<SourceLocation>()); + } + + if (!Method) { + S.Diag(Loc, diag::err_undeclared_nsnumber_method) << Sel; + return 0; + } + + // Make sure the return type is reasonable. + if (!Method->getResultType()->isObjCObjectPointerType()) { + S.Diag(Loc, diag::err_objc_literal_method_sig) + << Sel; + S.Diag(Method->getLocation(), diag::note_objc_literal_method_return) + << Method->getResultType(); + return 0; + } + + // Note: if the parameter type is out-of-line, we'll catch it later in the + // implicit conversion. + + S.NSNumberLiteralMethods[*Kind] = Method; + return Method; +} + +/// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the +/// numeric literal expression. Type of the expression will be "NSNumber *" +/// or "id" if NSNumber is unavailable. +ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { + // Look up the NSNumber class, if we haven't done so already. + if (!NSNumberDecl) { + NamedDecl *IF = LookupSingleName(TUScope, + NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber), + AtLoc, LookupOrdinaryName); + NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + + if (!NSNumberDecl && getLangOptions().DebuggerObjCLiteral) + NSNumberDecl = ObjCInterfaceDecl::Create (Context, + Context.getTranslationUnitDecl(), + SourceLocation(), + NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber), + 0, SourceLocation()); + if (!NSNumberDecl) { + Diag(AtLoc, diag::err_undeclared_nsnumber); + return ExprError(); + } + } + + // Determine the type of the literal. + QualType NumberType = Number->getType(); + if (CharacterLiteral *Char = dyn_cast<CharacterLiteral>(Number)) { + // In C, character literals have type 'int'. That's not the type we want + // to use to determine the Objective-c literal kind. + switch (Char->getKind()) { + case CharacterLiteral::Ascii: + NumberType = Context.CharTy; + break; + + case CharacterLiteral::Wide: + NumberType = Context.getWCharType(); + break; + + case CharacterLiteral::UTF16: + NumberType = Context.Char16Ty; + break; + + case CharacterLiteral::UTF32: + NumberType = Context.Char32Ty; + break; + } + } + + ObjCMethodDecl *Method = 0; + // Look for the appropriate method within NSNumber. + // Construct the literal. + QualType Ty + = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSNumberDecl)); + Method = getNSNumberFactoryMethod(*this, AtLoc, + NumberType, Ty, + Number->getSourceRange()); + + if (!Method) + return ExprError(); + + // Convert the number to the type that the parameter expects. + QualType ElementT = Method->param_begin()[0]->getType(); + ExprResult ConvertedNumber = PerformImplicitConversion(Number, ElementT, + AA_Sending); + if (ConvertedNumber.isInvalid()) + return ExprError(); + Number = ConvertedNumber.get(); + + return MaybeBindToTemporary( + new (Context) ObjCNumericLiteral(Number, Ty, Method, AtLoc)); +} + +ExprResult Sema::ActOnObjCBoolLiteral(SourceLocation AtLoc, + SourceLocation ValueLoc, + bool Value) { + ExprResult Inner; + if (getLangOptions().CPlusPlus) { + Inner = ActOnCXXBoolLiteral(ValueLoc, Value? tok::kw_true : tok::kw_false); + } else { + // C doesn't actually have a way to represent literal values of type + // _Bool. So, we'll use 0/1 and implicit cast to _Bool. + Inner = ActOnIntegerConstant(ValueLoc, Value? 1 : 0); + Inner = ImpCastExprToType(Inner.get(), Context.BoolTy, + CK_IntegralToBoolean); + } + + return BuildObjCNumericLiteral(AtLoc, Inner.get()); +} + +/// \brief Check that the given expression is a valid element of an Objective-C +/// collection literal. +static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, + QualType T) { + // If the expression is type-dependent, there's nothing for us to do. + if (Element->isTypeDependent()) + return Element; + + ExprResult Result = S.CheckPlaceholderExpr(Element); + if (Result.isInvalid()) + return ExprError(); + Element = Result.get(); + + // In C++, check for an implicit conversion to an Objective-C object pointer + // type. + if (S.getLangOptions().CPlusPlus && Element->getType()->isRecordType()) { + InitializedEntity Entity + = InitializedEntity::InitializeParameter(S.Context, T, /*Consumed=*/false); + InitializationKind Kind + = InitializationKind::CreateCopy(Element->getLocStart(), SourceLocation()); + InitializationSequence Seq(S, Entity, Kind, &Element, 1); + if (!Seq.Failed()) + return Seq.Perform(S, Entity, Kind, MultiExprArg(S, &Element, 1)); + } + + Expr *OrigElement = Element; + + // Perform lvalue-to-rvalue conversion. + Result = S.DefaultLvalueConversion(Element); + if (Result.isInvalid()) + return ExprError(); + Element = Result.get(); + + // Make sure that we have an Objective-C pointer type or block. + if (!Element->getType()->isObjCObjectPointerType() && + !Element->getType()->isBlockPointerType()) { + bool Recovered = false; + + // If this is potentially an Objective-C numeric literal, add the '@'. + if (isa<IntegerLiteral>(OrigElement) || + isa<CharacterLiteral>(OrigElement) || + isa<FloatingLiteral>(OrigElement) || + isa<ObjCBoolLiteralExpr>(OrigElement) || + isa<CXXBoolLiteralExpr>(OrigElement)) { + if (S.NSAPIObj->getNSNumberFactoryMethodKind(OrigElement->getType())) { + int Which = isa<CharacterLiteral>(OrigElement) ? 1 + : (isa<CXXBoolLiteralExpr>(OrigElement) || + isa<ObjCBoolLiteralExpr>(OrigElement)) ? 2 + : 3; + + S.Diag(OrigElement->getLocStart(), diag::err_box_literal_collection) + << Which << OrigElement->getSourceRange() + << FixItHint::CreateInsertion(OrigElement->getLocStart(), "@"); + + Result = S.BuildObjCNumericLiteral(OrigElement->getLocStart(), + OrigElement); + if (Result.isInvalid()) + return ExprError(); + + Element = Result.get(); + Recovered = true; + } + } + // If this is potentially an Objective-C string literal, add the '@'. + else if (StringLiteral *String = dyn_cast<StringLiteral>(OrigElement)) { + if (String->isAscii()) { + S.Diag(OrigElement->getLocStart(), diag::err_box_literal_collection) + << 0 << OrigElement->getSourceRange() + << FixItHint::CreateInsertion(OrigElement->getLocStart(), "@"); + + Result = S.BuildObjCStringLiteral(OrigElement->getLocStart(), String); + if (Result.isInvalid()) + return ExprError(); + + Element = Result.get(); + Recovered = true; + } + } + + if (!Recovered) { + S.Diag(Element->getLocStart(), diag::err_invalid_collection_element) + << Element->getType(); + return ExprError(); + } + } + + // Make sure that the element has the type that the container factory + // function expects. + return S.PerformCopyInitialization( + InitializedEntity::InitializeParameter(S.Context, T, + /*Consumed=*/false), + Element->getLocStart(), Element); +} + +ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, + Expr *IndexExpr, + ObjCMethodDecl *getterMethod, + ObjCMethodDecl *setterMethod) { + // Feature support is for modern abi. + if (!LangOpts.ObjCNonFragileABI) + return ExprError(); + // If the expression is type-dependent, there's nothing for us to do. + assert ((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && + "base or index cannot have dependent type here"); + ExprResult Result = CheckPlaceholderExpr(IndexExpr); + if (Result.isInvalid()) + return ExprError(); + IndexExpr = Result.get(); + + // Perform lvalue-to-rvalue conversion. + Result = DefaultLvalueConversion(BaseExpr); + if (Result.isInvalid()) + return ExprError(); + BaseExpr = Result.get(); + return Owned(ObjCSubscriptRefExpr::Create(Context, + BaseExpr, + IndexExpr, + Context.PseudoObjectTy, + getterMethod, + setterMethod, RB)); + +} + +ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { + // Look up the NSArray class, if we haven't done so already. + if (!NSArrayDecl) { + NamedDecl *IF = LookupSingleName(TUScope, + NSAPIObj->getNSClassId(NSAPI::ClassId_NSArray), + SR.getBegin(), + LookupOrdinaryName); + NSArrayDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + if (!NSArrayDecl && getLangOptions().DebuggerObjCLiteral) + NSArrayDecl = ObjCInterfaceDecl::Create (Context, + Context.getTranslationUnitDecl(), + SourceLocation(), + NSAPIObj->getNSClassId(NSAPI::ClassId_NSArray), + 0, SourceLocation()); + + if (!NSArrayDecl) { + Diag(SR.getBegin(), diag::err_undeclared_nsarray); + return ExprError(); + } + } + + // Find the arrayWithObjects:count: method, if we haven't done so already. + QualType IdT = Context.getObjCIdType(); + if (!ArrayWithObjectsMethod) { + Selector + Sel = NSAPIObj->getNSArraySelector(NSAPI::NSArr_arrayWithObjectsCount); + ArrayWithObjectsMethod = NSArrayDecl->lookupClassMethod(Sel); + if (!ArrayWithObjectsMethod && getLangOptions().DebuggerObjCLiteral) { + TypeSourceInfo *ResultTInfo = 0; + ArrayWithObjectsMethod = + ObjCMethodDecl::Create(Context, + SourceLocation(), SourceLocation(), Sel, + IdT, + ResultTInfo, + Context.getTranslationUnitDecl(), + false /*Instance*/, false/*isVariadic*/, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, + false); + SmallVector<ParmVarDecl *, 2> Params; + ParmVarDecl *objects = ParmVarDecl::Create(Context, ArrayWithObjectsMethod, + SourceLocation(), SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(objects); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, ArrayWithObjectsMethod, + SourceLocation(), SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(cnt); + ArrayWithObjectsMethod->setMethodParams(Context, Params, + ArrayRef<SourceLocation>()); + + + } + + if (!ArrayWithObjectsMethod) { + Diag(SR.getBegin(), diag::err_undeclared_arraywithobjects) << Sel; + return ExprError(); + } + } + + // Make sure the return type is reasonable. + if (!ArrayWithObjectsMethod->getResultType()->isObjCObjectPointerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << ArrayWithObjectsMethod->getSelector(); + Diag(ArrayWithObjectsMethod->getLocation(), + diag::note_objc_literal_method_return) + << ArrayWithObjectsMethod->getResultType(); + return ExprError(); + } + + // Dig out the type that all elements should be converted to. + QualType T = ArrayWithObjectsMethod->param_begin()[0]->getType(); + const PointerType *PtrT = T->getAs<PointerType>(); + if (!PtrT || + !Context.hasSameUnqualifiedType(PtrT->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << ArrayWithObjectsMethod->getSelector(); + Diag(ArrayWithObjectsMethod->param_begin()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << T + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + T = PtrT->getPointeeType(); + + // Check that the 'count' parameter is integral. + if (!ArrayWithObjectsMethod->param_begin()[1]->getType()->isIntegerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << ArrayWithObjectsMethod->getSelector(); + Diag(ArrayWithObjectsMethod->param_begin()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 + << ArrayWithObjectsMethod->param_begin()[1]->getType() + << "integral"; + return ExprError(); + } + + // Check that each of the elements provided is valid in a collection literal, + // performing conversions as necessary. + Expr **ElementsBuffer = Elements.get(); + for (unsigned I = 0, N = Elements.size(); I != N; ++I) { + ExprResult Converted = CheckObjCCollectionLiteralElement(*this, + ElementsBuffer[I], + T); + if (Converted.isInvalid()) + return ExprError(); + + ElementsBuffer[I] = Converted.get(); + } + + QualType Ty + = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSArrayDecl)); + + return MaybeBindToTemporary( + ObjCArrayLiteral::Create(Context, + llvm::makeArrayRef(Elements.get(), + Elements.size()), + Ty, ArrayWithObjectsMethod, SR)); +} + +ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, + ObjCDictionaryElement *Elements, + unsigned NumElements) { + // Look up the NSDictionary class, if we haven't done so already. + if (!NSDictionaryDecl) { + NamedDecl *IF = LookupSingleName(TUScope, + NSAPIObj->getNSClassId(NSAPI::ClassId_NSDictionary), + SR.getBegin(), LookupOrdinaryName); + NSDictionaryDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + if (!NSDictionaryDecl && getLangOptions().DebuggerObjCLiteral) + NSDictionaryDecl = ObjCInterfaceDecl::Create (Context, + Context.getTranslationUnitDecl(), + SourceLocation(), + NSAPIObj->getNSClassId(NSAPI::ClassId_NSDictionary), + 0, SourceLocation()); + + if (!NSDictionaryDecl) { + Diag(SR.getBegin(), diag::err_undeclared_nsdictionary); + return ExprError(); + } + } + + // Find the dictionaryWithObjects:forKeys:count: method, if we haven't done + // so already. + QualType IdT = Context.getObjCIdType(); + if (!DictionaryWithObjectsMethod) { + Selector Sel = NSAPIObj->getNSDictionarySelector( + NSAPI::NSDict_dictionaryWithObjectsForKeysCount); + DictionaryWithObjectsMethod = NSDictionaryDecl->lookupClassMethod(Sel); + if (!DictionaryWithObjectsMethod && getLangOptions().DebuggerObjCLiteral) { + DictionaryWithObjectsMethod = + ObjCMethodDecl::Create(Context, + SourceLocation(), SourceLocation(), Sel, + IdT, + 0 /*TypeSourceInfo */, + Context.getTranslationUnitDecl(), + false /*Instance*/, false/*isVariadic*/, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, + false); + SmallVector<ParmVarDecl *, 3> Params; + ParmVarDecl *objects = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, + SourceLocation(), SourceLocation(), + &Context.Idents.get("objects"), + Context.getPointerType(IdT), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(objects); + ParmVarDecl *keys = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, + SourceLocation(), SourceLocation(), + &Context.Idents.get("keys"), + Context.getPointerType(IdT), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(keys); + ParmVarDecl *cnt = ParmVarDecl::Create(Context, DictionaryWithObjectsMethod, + SourceLocation(), SourceLocation(), + &Context.Idents.get("cnt"), + Context.UnsignedLongTy, + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(cnt); + DictionaryWithObjectsMethod->setMethodParams(Context, Params, + ArrayRef<SourceLocation>()); + } + + if (!DictionaryWithObjectsMethod) { + Diag(SR.getBegin(), diag::err_undeclared_dictwithobjects) << Sel; + return ExprError(); + } + } + + // Make sure the return type is reasonable. + if (!DictionaryWithObjectsMethod->getResultType()->isObjCObjectPointerType()){ + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << DictionaryWithObjectsMethod->getSelector(); + Diag(DictionaryWithObjectsMethod->getLocation(), + diag::note_objc_literal_method_return) + << DictionaryWithObjectsMethod->getResultType(); + return ExprError(); + } + + // Dig out the type that all values should be converted to. + QualType ValueT = DictionaryWithObjectsMethod->param_begin()[0]->getType(); + const PointerType *PtrValue = ValueT->getAs<PointerType>(); + if (!PtrValue || + !Context.hasSameUnqualifiedType(PtrValue->getPointeeType(), IdT)) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << DictionaryWithObjectsMethod->getSelector(); + Diag(DictionaryWithObjectsMethod->param_begin()[0]->getLocation(), + diag::note_objc_literal_method_param) + << 0 << ValueT + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + ValueT = PtrValue->getPointeeType(); + + // Dig out the type that all keys should be converted to. + QualType KeyT = DictionaryWithObjectsMethod->param_begin()[1]->getType(); + const PointerType *PtrKey = KeyT->getAs<PointerType>(); + if (!PtrKey || + !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + IdT)) { + bool err = true; + if (PtrKey) { + if (QIDNSCopying.isNull()) { + // key argument of selector is id<NSCopying>? + if (ObjCProtocolDecl *NSCopyingPDecl = + LookupProtocol(&Context.Idents.get("NSCopying"), SR.getBegin())) { + ObjCProtocolDecl *PQ[] = {NSCopyingPDecl}; + QIDNSCopying = + Context.getObjCObjectType(Context.ObjCBuiltinIdTy, + (ObjCProtocolDecl**) PQ,1); + QIDNSCopying = Context.getObjCObjectPointerType(QIDNSCopying); + } + } + if (!QIDNSCopying.isNull()) + err = !Context.hasSameUnqualifiedType(PtrKey->getPointeeType(), + QIDNSCopying); + } + + if (err) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << DictionaryWithObjectsMethod->getSelector(); + Diag(DictionaryWithObjectsMethod->param_begin()[1]->getLocation(), + diag::note_objc_literal_method_param) + << 1 << KeyT + << Context.getPointerType(IdT.withConst()); + return ExprError(); + } + } + KeyT = PtrKey->getPointeeType(); + + // Check that the 'count' parameter is integral. + if (!DictionaryWithObjectsMethod->param_begin()[2]->getType() + ->isIntegerType()) { + Diag(SR.getBegin(), diag::err_objc_literal_method_sig) + << DictionaryWithObjectsMethod->getSelector(); + Diag(DictionaryWithObjectsMethod->param_begin()[2]->getLocation(), + diag::note_objc_literal_method_param) + << 2 + << DictionaryWithObjectsMethod->param_begin()[2]->getType() + << "integral"; + return ExprError(); + } + + // Check that each of the keys and values provided is valid in a collection + // literal, performing conversions as necessary. + bool HasPackExpansions = false; + for (unsigned I = 0, N = NumElements; I != N; ++I) { + // Check the key. + ExprResult Key = CheckObjCCollectionLiteralElement(*this, Elements[I].Key, + KeyT); + if (Key.isInvalid()) + return ExprError(); + + // Check the value. + ExprResult Value + = CheckObjCCollectionLiteralElement(*this, Elements[I].Value, ValueT); + if (Value.isInvalid()) + return ExprError(); + + Elements[I].Key = Key.get(); + Elements[I].Value = Value.get(); + + if (Elements[I].EllipsisLoc.isInvalid()) + continue; + + if (!Elements[I].Key->containsUnexpandedParameterPack() && + !Elements[I].Value->containsUnexpandedParameterPack()) { + Diag(Elements[I].EllipsisLoc, + diag::err_pack_expansion_without_parameter_packs) + << SourceRange(Elements[I].Key->getLocStart(), + Elements[I].Value->getLocEnd()); + return ExprError(); + } + + HasPackExpansions = true; + } + + + QualType Ty + = Context.getObjCObjectPointerType( + Context.getObjCInterfaceType(NSDictionaryDecl)); + return MaybeBindToTemporary( + ObjCDictionaryLiteral::Create(Context, + llvm::makeArrayRef(Elements, + NumElements), + HasPackExpansions, + Ty, + DictionaryWithObjectsMethod, SR)); } ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc, @@ -1030,6 +1642,50 @@ ExprResult Sema::BuildClassMessageImplicit(QualType ReceiverType, } +static void applyCocoaAPICheck(Sema &S, const ObjCMessageExpr *Msg, + unsigned DiagID, + bool (*refactor)(const ObjCMessageExpr *, + const NSAPI &, edit::Commit &)) { + SourceLocation MsgLoc = Msg->getExprLoc(); + if (S.Diags.getDiagnosticLevel(DiagID, MsgLoc) == DiagnosticsEngine::Ignored) + return; + + SourceManager &SM = S.SourceMgr; + edit::Commit ECommit(SM, S.LangOpts); + if (refactor(Msg,*S.NSAPIObj, ECommit)) { + DiagnosticBuilder Builder = S.Diag(MsgLoc, DiagID) + << Msg->getSelector() << Msg->getSourceRange(); + // FIXME: Don't emit diagnostic at all if fixits are non-commitable. + if (!ECommit.isCommitable()) + return; + for (edit::Commit::edit_iterator + I = ECommit.edit_begin(), E = ECommit.edit_end(); I != E; ++I) { + const edit::Commit::Edit &Edit = *I; + switch (Edit.Kind) { + case edit::Commit::Act_Insert: + Builder.AddFixItHint(FixItHint::CreateInsertion(Edit.OrigLoc, + Edit.Text, + Edit.BeforePrev)); + break; + case edit::Commit::Act_InsertFromRange: + Builder.AddFixItHint( + FixItHint::CreateInsertionFromRange(Edit.OrigLoc, + Edit.getInsertFromRange(SM), + Edit.BeforePrev)); + break; + case edit::Commit::Act_Remove: + Builder.AddFixItHint(FixItHint::CreateRemoval(Edit.getFileRange(SM))); + break; + } + } + } +} + +static void checkCocoaAPI(Sema &S, const ObjCMessageExpr *Msg) { + applyCocoaAPICheck(S, Msg, diag::warn_objc_redundant_literal_use, + edit::rewriteObjCRedundantCallWithLiteral); +} + /// \brief Build an Objective-C class message expression. /// /// This routine takes care of both normal class messages and @@ -1146,18 +1802,21 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, return ExprError(); // Construct the appropriate ObjCMessageExpr. - Expr *Result; + ObjCMessageExpr *Result; if (SuperLoc.isValid()) Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, SuperLoc, /*IsInstanceSuper=*/false, ReceiverType, Sel, SelectorLocs, Method, makeArrayRef(Args, NumArgs), RBracLoc, isImplicit); - else + else { Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, ReceiverTypeInfo, Sel, SelectorLocs, Method, makeArrayRef(Args, NumArgs), RBracLoc, isImplicit); + if (!isImplicit) + checkCocoaAPI(*this, Result); + } return MaybeBindToTemporary(Result); } @@ -1563,11 +2222,14 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, ReceiverType, Sel, SelectorLocs, Method, makeArrayRef(Args, NumArgs), RBracLoc, isImplicit); - else + else { Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, Receiver, Sel, SelectorLocs, Method, makeArrayRef(Args, NumArgs), RBracLoc, isImplicit); + if (!isImplicit) + checkCocoaAPI(*this, Result); + } if (getLangOptions().ObjCAutoRefCount) { // In ARC, annotate delegate init calls. diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index d5f1523f62..c51eb3f495 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -130,6 +130,28 @@ namespace { } }; + struct ObjCSubscriptRefRebuilder : Rebuilder<ObjCSubscriptRefRebuilder> { + Expr *NewBase; + Expr *NewKeyExpr; + ObjCSubscriptRefRebuilder(Sema &S, Expr *newBase, Expr *newKeyExpr) + : Rebuilder<ObjCSubscriptRefRebuilder>(S), + NewBase(newBase), NewKeyExpr(newKeyExpr) {} + + typedef ObjCSubscriptRefExpr specific_type; + Expr *rebuildSpecific(ObjCSubscriptRefExpr *refExpr) { + assert(refExpr->getBaseExpr()); + assert(refExpr->getKeyExpr()); + + return new (S.Context) + ObjCSubscriptRefExpr(NewBase, + NewKeyExpr, + refExpr->getType(), refExpr->getValueKind(), + refExpr->getObjectKind(),refExpr->getAtIndexMethodDecl(), + refExpr->setAtIndexMethodDecl(), + refExpr->getRBracket()); + } + }; + class PseudoOpBuilder { public: Sema &S; @@ -215,6 +237,39 @@ namespace { ExprResult buildGet(); ExprResult buildSet(Expr *op, SourceLocation, bool); }; + + /// A PseudoOpBuilder for Objective-C array/dictionary indexing. + class ObjCSubscriptOpBuilder : public PseudoOpBuilder { + ObjCSubscriptRefExpr *RefExpr; + OpaqueValueExpr *InstanceBase; + OpaqueValueExpr *InstanceKey; + ObjCMethodDecl *AtIndexGetter; + Selector AtIndexGetterSelector; + + ObjCMethodDecl *AtIndexSetter; + Selector AtIndexSetterSelector; + + public: + ObjCSubscriptOpBuilder(Sema &S, ObjCSubscriptRefExpr *refExpr) : + PseudoOpBuilder(S, refExpr->getSourceRange().getBegin()), + RefExpr(refExpr), + InstanceBase(0), InstanceKey(0), + AtIndexGetter(0), AtIndexSetter(0) { } + + ExprResult buildRValueOperation(Expr *op); + ExprResult buildAssignmentOperation(Scope *Sc, + SourceLocation opLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS); + Expr *rebuildAndCaptureObject(Expr *syntacticBase); + + bool findAtIndexGetter(); + bool findAtIndexSetter(); + + ExprResult buildGet(); + ExprResult buildSet(Expr *op, SourceLocation, bool); + }; + } /// Capture the given expression in an OpaqueValueExpr. @@ -718,6 +773,438 @@ ObjCPropertyOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, return PseudoOpBuilder::buildIncDecOperation(Sc, opcLoc, opcode, op); } +// ObjCSubscript build stuff. +// + +/// objective-c subscripting-specific behavior for doing lvalue-to-rvalue +/// conversion. +/// FIXME. Remove this routine if it is proven that no additional +/// specifity is needed. +ExprResult ObjCSubscriptOpBuilder::buildRValueOperation(Expr *op) { + ExprResult result = PseudoOpBuilder::buildRValueOperation(op); + if (result.isInvalid()) return ExprError(); + return result; +} + +/// objective-c subscripting-specific behavior for doing assignments. +ExprResult +ObjCSubscriptOpBuilder::buildAssignmentOperation(Scope *Sc, + SourceLocation opcLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS) { + assert(BinaryOperator::isAssignmentOp(opcode)); + // There must be a method to do the Index'ed assignment. + if (!findAtIndexSetter()) + return ExprError(); + + // Verify that we can do a compound assignment. + if (opcode != BO_Assign && !findAtIndexGetter()) + return ExprError(); + + ExprResult result = + PseudoOpBuilder::buildAssignmentOperation(Sc, opcLoc, opcode, LHS, RHS); + if (result.isInvalid()) return ExprError(); + + // Various warnings about objc Index'ed assignments in ARC. + if (S.getLangOptions().ObjCAutoRefCount && InstanceBase) { + S.checkRetainCycles(InstanceBase->getSourceExpr(), RHS); + S.checkUnsafeExprAssigns(opcLoc, LHS, RHS); + } + + return result; +} + +/// Capture the base object of an Objective-C Index'ed expression. +Expr *ObjCSubscriptOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) { + assert(InstanceBase == 0); + + // Capture base expression in an OVE and rebuild the syntactic + // form to use the OVE as its base expression. + InstanceBase = capture(RefExpr->getBaseExpr()); + InstanceKey = capture(RefExpr->getKeyExpr()); + + syntacticBase = + ObjCSubscriptRefRebuilder(S, InstanceBase, + InstanceKey).rebuild(syntacticBase); + + return syntacticBase; +} + +/// CheckSubscriptingKind - This routine decide what type +/// of indexing represented by "FromE" is being done. +Sema::ObjCSubscriptKind + Sema::CheckSubscriptingKind(Expr *FromE) { + // If the expression already has integral or enumeration type, we're golden. + QualType T = FromE->getType(); + if (T->isIntegralOrEnumerationType()) + return OS_Array; + + // If we don't have a class type in C++, there's no way we can get an + // expression of integral or enumeration type. + const RecordType *RecordTy = T->getAs<RecordType>(); + if (!RecordTy) + // All other scalar cases are assumed to be dictionary indexing which + // caller handles, with diagnostics if needed. + return OS_Dictionary; + if (!getLangOptions().CPlusPlus || RecordTy->isIncompleteType()) { + // No indexing can be done. Issue diagnostics and quit. + Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion) + << FromE->getType(); + return OS_Error; + } + + // We must have a complete class type. + if (RequireCompleteType(FromE->getExprLoc(), T, + PDiag(diag::err_objc_index_incomplete_class_type) + << FromE->getSourceRange())) + return OS_Error; + + // Look for a conversion to an integral, enumeration type, or + // objective-C pointer type. + UnresolvedSet<4> ViableConversions; + UnresolvedSet<4> ExplicitConversions; + const UnresolvedSetImpl *Conversions + = cast<CXXRecordDecl>(RecordTy->getDecl())->getVisibleConversionFunctions(); + + int NoIntegrals=0, NoObjCIdPointers=0; + SmallVector<CXXConversionDecl *, 4> ConversionDecls; + + for (UnresolvedSetImpl::iterator I = Conversions->begin(), + E = Conversions->end(); + I != E; + ++I) { + if (CXXConversionDecl *Conversion + = dyn_cast<CXXConversionDecl>((*I)->getUnderlyingDecl())) { + QualType CT = Conversion->getConversionType().getNonReferenceType(); + if (CT->isIntegralOrEnumerationType()) { + ++NoIntegrals; + ConversionDecls.push_back(Conversion); + } + else if (CT->isObjCIdType() ||CT->isBlockPointerType()) { + ++NoObjCIdPointers; + ConversionDecls.push_back(Conversion); + } + } + } + if (NoIntegrals ==1 && NoObjCIdPointers == 0) + return OS_Array; + if (NoIntegrals == 0 && NoObjCIdPointers == 1) + return OS_Dictionary; + if (NoIntegrals == 0 && NoObjCIdPointers == 0) { + // No conversion function was found. Issue diagnostic and return. + Diag(FromE->getExprLoc(), diag::err_objc_subscript_type_conversion) + << FromE->getType(); + return OS_Error; + } + Diag(FromE->getExprLoc(), diag::err_objc_multiple_subscript_type_conversion) + << FromE->getType(); + for (unsigned int i = 0; i < ConversionDecls.size(); i++) + Diag(ConversionDecls[i]->getLocation(), diag::not_conv_function_declared_at); + + return OS_Error; +} + +bool ObjCSubscriptOpBuilder::findAtIndexGetter() { + if (AtIndexGetter) + return true; + + Expr *BaseExpr = RefExpr->getBaseExpr(); + QualType BaseT = BaseExpr->getType(); + + QualType ResultType; + if (const ObjCObjectPointerType *PTy = + BaseT->getAs<ObjCObjectPointerType>()) { + ResultType = PTy->getPointeeType(); + if (const ObjCObjectType *iQFaceTy = + ResultType->getAsObjCQualifiedInterfaceType()) + ResultType = iQFaceTy->getBaseType(); + } + Sema::ObjCSubscriptKind Res = + S.CheckSubscriptingKind(RefExpr->getKeyExpr()); + if (Res == Sema::OS_Error) + return false; + bool arrayRef = (Res == Sema::OS_Array); + + if (ResultType.isNull()) { + S.Diag(BaseExpr->getExprLoc(), diag::err_objc_subscript_base_type) + << BaseExpr->getType() << arrayRef; + return false; + } + if (!arrayRef) { + // dictionary subscripting. + // - (id)objectForKeyedSubscript:(id)key; + IdentifierInfo *KeyIdents[] = { + &S.Context.Idents.get("objectForKeyedSubscript") + }; + AtIndexGetterSelector = S.Context.Selectors.getSelector(1, KeyIdents); + } + else { + // - (id)objectAtIndexedSubscript:(size_t)index; + IdentifierInfo *KeyIdents[] = { + &S.Context.Idents.get("objectAtIndexedSubscript") + }; + + AtIndexGetterSelector = S.Context.Selectors.getSelector(1, KeyIdents); + } + + AtIndexGetter = S.LookupMethodInObjectType(AtIndexGetterSelector, ResultType, + true /*instance*/); + bool receiverIdType = (BaseT->isObjCIdType() || + BaseT->isObjCQualifiedIdType()); + + if (!AtIndexGetter && S.getLangOptions().DebuggerObjCLiteral) { + AtIndexGetter = ObjCMethodDecl::Create(S.Context, SourceLocation(), + SourceLocation(), AtIndexGetterSelector, + S.Context.getObjCIdType() /*ReturnType*/, + 0 /*TypeSourceInfo */, + S.Context.getTranslationUnitDecl(), + true /*Instance*/, false/*isVariadic*/, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, + false); + ParmVarDecl *Argument = ParmVarDecl::Create(S.Context, AtIndexGetter, + SourceLocation(), SourceLocation(), + arrayRef ? &S.Context.Idents.get("index") + : &S.Context.Idents.get("key"), + arrayRef ? S.Context.UnsignedLongTy + : S.Context.getObjCIdType(), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + AtIndexGetter->setMethodParams(S.Context, Argument, + ArrayRef<SourceLocation>()); + } + + if (!AtIndexGetter) { + if (!receiverIdType) { + S.Diag(BaseExpr->getExprLoc(), diag::err_objc_subscript_method_not_found) + << BaseExpr->getType() << 0 << arrayRef; + return false; + } + AtIndexGetter = + S.LookupInstanceMethodInGlobalPool(AtIndexGetterSelector, + RefExpr->getSourceRange(), + true, false); + } + + if (AtIndexGetter) { + QualType T = AtIndexGetter->param_begin()[0]->getType(); + if ((arrayRef && !T->isIntegralOrEnumerationType()) || + (!arrayRef && !T->isObjCObjectPointerType())) { + S.Diag(RefExpr->getKeyExpr()->getExprLoc(), + arrayRef ? diag::err_objc_subscript_index_type + : diag::err_objc_subscript_key_type) << T; + S.Diag(AtIndexGetter->param_begin()[0]->getLocation(), + diag::note_parameter_type) << T; + return false; + } + QualType R = AtIndexGetter->getResultType(); + if (!R->isObjCObjectPointerType()) { + S.Diag(RefExpr->getKeyExpr()->getExprLoc(), + diag::err_objc_indexing_method_result_type) << R << arrayRef; + S.Diag(AtIndexGetter->getLocation(), diag::note_method_declared_at) << + AtIndexGetter->getDeclName(); + } + } + return true; +} + +bool ObjCSubscriptOpBuilder::findAtIndexSetter() { + if (AtIndexSetter) + return true; + + Expr *BaseExpr = RefExpr->getBaseExpr(); + QualType BaseT = BaseExpr->getType(); + + QualType ResultType; + if (const ObjCObjectPointerType *PTy = + BaseT->getAs<ObjCObjectPointerType>()) { + ResultType = PTy->getPointeeType(); + if (const ObjCObjectType *iQFaceTy = + ResultType->getAsObjCQualifiedInterfaceType()) + ResultType = iQFaceTy->getBaseType(); + } + + Sema::ObjCSubscriptKind Res = + S.CheckSubscriptingKind(RefExpr->getKeyExpr()); + if (Res == Sema::OS_Error) + return false; + bool arrayRef = (Res == Sema::OS_Array); + + if (ResultType.isNull()) { + S.Diag(BaseExpr->getExprLoc(), diag::err_objc_subscript_base_type) + << BaseExpr->getType() << arrayRef; + return false; + } + + if (!arrayRef) { + // dictionary subscripting. + // - (void)setObject:(id)object forKeyedSubscript:(id)key; + IdentifierInfo *KeyIdents[] = { + &S.Context.Idents.get("setObject"), + &S.Context.Idents.get("forKeyedSubscript") + }; + AtIndexSetterSelector = S.Context.Selectors.getSelector(2, KeyIdents); + } + else { + // - (void)setObject:(id)object atIndexedSubscript:(NSInteger)index; + IdentifierInfo *KeyIdents[] = { + &S.Context.Idents.get("setObject"), + &S.Context.Idents.get("atIndexedSubscript") + }; + AtIndexSetterSelector = S.Context.Selectors.getSelector(2, KeyIdents); + } + AtIndexSetter = S.LookupMethodInObjectType(AtIndexSetterSelector, ResultType, + true /*instance*/); + + bool receiverIdType = (BaseT->isObjCIdType() || + BaseT->isObjCQualifiedIdType()); + + if (!AtIndexSetter && S.getLangOptions().DebuggerObjCLiteral) { + TypeSourceInfo *ResultTInfo = 0; + QualType ReturnType = S.Context.VoidTy; + AtIndexSetter = ObjCMethodDecl::Create(S.Context, SourceLocation(), + SourceLocation(), AtIndexSetterSelector, + ReturnType, + ResultTInfo, + S.Context.getTranslationUnitDecl(), + true /*Instance*/, false/*isVariadic*/, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, /*isDefined=*/false, + ObjCMethodDecl::Required, + false); + SmallVector<ParmVarDecl *, 2> Params; + ParmVarDecl *object = ParmVarDecl::Create(S.Context, AtIndexSetter, + SourceLocation(), SourceLocation(), + &S.Context.Idents.get("object"), + S.Context.getObjCIdType(), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(object); + ParmVarDecl *key = ParmVarDecl::Create(S.Context, AtIndexSetter, + SourceLocation(), SourceLocation(), + arrayRef ? &S.Context.Idents.get("index") + : &S.Context.Idents.get("key"), + arrayRef ? S.Context.UnsignedLongTy + : S.Context.getObjCIdType(), + /*TInfo=*/0, + SC_None, + SC_None, + 0); + Params.push_back(key); + AtIndexSetter->setMethodParams(S.Context, Params, ArrayRef<SourceLocation>()); + } + + if (!AtIndexSetter) { + if (!receiverIdType) { + S.Diag(BaseExpr->getExprLoc(), + diag::err_objc_subscript_method_not_found) + << BaseExpr->getType() << 1 << arrayRef; + return false; + } + AtIndexSetter = + S.LookupInstanceMethodInGlobalPool(AtIndexSetterSelector, + RefExpr->getSourceRange(), + true, false); + } + + bool err = false; + if (AtIndexSetter && arrayRef) { + QualType T = AtIndexSetter->param_begin()[1]->getType(); + if (!T->isIntegralOrEnumerationType()) { + S.Diag(RefExpr->getKeyExpr()->getExprLoc(), + diag::err_objc_subscript_index_type) << T; + S.Diag(AtIndexSetter->param_begin()[1]->getLocation(), + diag::note_parameter_type) << T; + err = true; + } + T = AtIndexSetter->param_begin()[0]->getType(); + if (!T->isObjCObjectPointerType()) { + S.Diag(RefExpr->getBaseExpr()->getExprLoc(), + diag::err_objc_subscript_object_type) << T << arrayRef; + S.Diag(AtIndexSetter->param_begin()[0]->getLocation(), + diag::note_parameter_type) << T; + err = true; + } + } + else if (AtIndexSetter && !arrayRef) + for (unsigned i=0; i <2; i++) { + QualType T = AtIndexSetter->param_begin()[i]->getType(); + if (!T->isObjCObjectPointerType()) { + if (i == 1) + S.Diag(RefExpr->getKeyExpr()->getExprLoc(), + diag::err_objc_subscript_key_type) << T; + else + S.Diag(RefExpr->getBaseExpr()->getExprLoc(), + diag::err_objc_subscript_dic_object_type) << T; + S.Diag(AtIndexSetter->param_begin()[i]->getLocation(), + diag::note_parameter_type) << T; + err = true; + } + } + + return !err; +} + +// Get the object at "Index" position in the container. +// [BaseExpr objectAtIndexedSubscript : IndexExpr]; +ExprResult ObjCSubscriptOpBuilder::buildGet() { + if (!findAtIndexGetter()) + return ExprError(); + + QualType receiverType = InstanceBase->getType(); + + // Build a message-send. + ExprResult msg; + Expr *Index = InstanceKey; + + // Arguments. + Expr *args[] = { Index }; + assert(InstanceBase); + msg = S.BuildInstanceMessageImplicit(InstanceBase, receiverType, + GenericLoc, + AtIndexGetterSelector, AtIndexGetter, + MultiExprArg(args, 1)); + return msg; +} + +/// Store into the container the "op" object at "Index"'ed location +/// by building this messaging expression: +/// - (void)setObject:(id)object atIndexedSubscript:(NSInteger)index; +/// \param bindSetValueAsResult - If true, capture the actual +/// value being set as the value of the property operation. +ExprResult ObjCSubscriptOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, + bool captureSetValueAsResult) { + if (!findAtIndexSetter()) + return ExprError(); + + QualType receiverType = InstanceBase->getType(); + Expr *Index = InstanceKey; + + // Arguments. + Expr *args[] = { op, Index }; + + // Build a message-send. + ExprResult msg = S.BuildInstanceMessageImplicit(InstanceBase, receiverType, + GenericLoc, + AtIndexSetterSelector, + AtIndexSetter, + MultiExprArg(args, 2)); + + if (!msg.isInvalid() && captureSetValueAsResult) { + ObjCMessageExpr *msgExpr = + cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit()); + Expr *arg = msgExpr->getArg(0); + msgExpr->setArg(0, captureValueAsResult(arg)); + } + + return msg; +} + //===----------------------------------------------------------------------===// // General Sema routines. //===----------------------------------------------------------------------===// @@ -728,6 +1215,11 @@ ExprResult Sema::checkPseudoObjectRValue(Expr *E) { = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { ObjCPropertyOpBuilder builder(*this, refExpr); return builder.buildRValueOperation(E); + } + else if (ObjCSubscriptRefExpr *refExpr + = dyn_cast<ObjCSubscriptRefExpr>(opaqueRef)) { + ObjCSubscriptOpBuilder builder(*this, refExpr); + return builder.buildRValueOperation(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -747,6 +1239,9 @@ ExprResult Sema::checkPseudoObjectIncDec(Scope *Sc, SourceLocation opcLoc, = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { ObjCPropertyOpBuilder builder(*this, refExpr); return builder.buildIncDecOperation(Sc, opcLoc, opcode, op); + } else if (isa<ObjCSubscriptRefExpr>(opaqueRef)) { + Diag(opcLoc, diag::err_illegal_container_subscripting_op); + return ExprError(); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -772,6 +1267,10 @@ ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { ObjCPropertyOpBuilder builder(*this, refExpr); return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); + } else if (ObjCSubscriptRefExpr *refExpr + = dyn_cast<ObjCSubscriptRefExpr>(opaqueRef)) { + ObjCSubscriptOpBuilder builder(*this, refExpr); + return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -786,6 +1285,12 @@ static Expr *stripOpaqueValuesFromPseudoObjectRef(Sema &S, Expr *E) { = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { OpaqueValueExpr *baseOVE = cast<OpaqueValueExpr>(refExpr->getBase()); return ObjCPropertyRefRebuilder(S, baseOVE->getSourceExpr()).rebuild(E); + } else if (ObjCSubscriptRefExpr *refExpr + = dyn_cast<ObjCSubscriptRefExpr>(opaqueRef)) { + OpaqueValueExpr *baseOVE = cast<OpaqueValueExpr>(refExpr->getBaseExpr()); + OpaqueValueExpr *keyOVE = cast<OpaqueValueExpr>(refExpr->getKeyExpr()); + return ObjCSubscriptRefRebuilder(S, baseOVE->getSourceExpr(), + keyOVE->getSourceExpr()).rebuild(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index cad6d674bf..b9022e6200 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -199,8 +199,12 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { Diag(Loc, diag::warn_unused_result) << R1 << R2; return; } - } else if (isa<PseudoObjectExpr>(E)) { - DiagID = diag::warn_unused_property_expr; + } else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) { + const Expr *Source = POE->getSyntacticForm(); + if (isa<ObjCSubscriptRefExpr>(Source)) + DiagID = diag::warn_unused_container_subscript_expr; + else + DiagID = diag::warn_unused_property_expr; } else if (const CXXFunctionalCastExpr *FC = dyn_cast<CXXFunctionalCastExpr>(E)) { if (isa<CXXConstructExpr>(FC->getSubExpr()) || diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 3a547ddf68..d661d866c4 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -93,6 +93,22 @@ namespace { return inherited::TraverseTemplateName(Template); } + /// \brief Suppress traversal into Objective-C container literal + /// elements that are pack expansions. + bool TraverseObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + if (!E->containsUnexpandedParameterPack()) + return true; + + for (unsigned I = 0, N = E->getNumElements(); I != N; ++I) { + ObjCDictionaryElement Element = E->getKeyValueElement(I); + if (Element.isPackExpansion()) + continue; + + TraverseStmt(Element.Key); + TraverseStmt(Element.Value); + } + return true; + } //------------------------------------------------------------------------ // Pruning the search for unexpanded parameter packs. //------------------------------------------------------------------------ diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 363e2c408d..0a0f5c7c32 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2210,7 +2210,35 @@ public: OperatorLoc, Pack, PackLoc, RParenLoc); } - + + /// \brief Build a new Objective-C array literal. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildObjCArrayLiteral(SourceRange Range, + Expr **Elements, unsigned NumElements) { + return getSema().BuildObjCArrayLiteral(Range, + MultiExprArg(Elements, NumElements)); + } + + ExprResult RebuildObjCSubscriptRefExpr(SourceLocation RB, + Expr *Base, Expr *Key, + ObjCMethodDecl *getterMethod, + ObjCMethodDecl *setterMethod) { + return getSema().BuildObjCSubscriptExpression(RB, Base, Key, + getterMethod, setterMethod); + } + + /// \brief Build a new Objective-C dictionary literal. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildObjCDictionaryLiteral(SourceRange Range, + ObjCDictionaryElement *Elements, + unsigned NumElements) { + return getSema().BuildObjCDictionaryLiteral(Range, Elements, NumElements); + } + /// \brief Build a new Objective-C @encode expression. /// /// By default, performs semantic analysis to build the new expression. @@ -8253,7 +8281,159 @@ TreeTransform<Derived>::TransformMaterializeTemporaryExpr( template<typename Derived> ExprResult TreeTransform<Derived>::TransformObjCStringLiteral(ObjCStringLiteral *E) { - return SemaRef.Owned(E); + return SemaRef.MaybeBindToTemporary(E); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { + return SemaRef.MaybeBindToTemporary(E); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformObjCNumericLiteral(ObjCNumericLiteral *E) { + return SemaRef.MaybeBindToTemporary(E); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformObjCArrayLiteral(ObjCArrayLiteral *E) { + // Transform each of the elements. + llvm::SmallVector<Expr *, 8> Elements; + bool ArgChanged = false; + if (getDerived().TransformExprs(E->getElements(), E->getNumElements(), + /*IsCall=*/false, Elements, &ArgChanged)) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && !ArgChanged) + return SemaRef.MaybeBindToTemporary(E); + + return getDerived().RebuildObjCArrayLiteral(E->getSourceRange(), + Elements.data(), + Elements.size()); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformObjCDictionaryLiteral( + ObjCDictionaryLiteral *E) { + // Transform each of the elements. + llvm::SmallVector<ObjCDictionaryElement, 8> Elements; + bool ArgChanged = false; + for (unsigned I = 0, N = E->getNumElements(); I != N; ++I) { + ObjCDictionaryElement OrigElement = E->getKeyValueElement(I); + + if (OrigElement.isPackExpansion()) { + // This key/value element is a pack expansion. + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + getSema().collectUnexpandedParameterPacks(OrigElement.Key, Unexpanded); + getSema().collectUnexpandedParameterPacks(OrigElement.Value, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + + // Determine whether the set of unexpanded parameter packs can + // and should be expanded. + bool Expand = true; + bool RetainExpansion = false; + llvm::Optional<unsigned> OrigNumExpansions = OrigElement.NumExpansions; + llvm::Optional<unsigned> NumExpansions = OrigNumExpansions; + SourceRange PatternRange(OrigElement.Key->getLocStart(), + OrigElement.Value->getLocEnd()); + if (getDerived().TryExpandParameterPacks(OrigElement.EllipsisLoc, + PatternRange, + Unexpanded, + Expand, RetainExpansion, + NumExpansions)) + return ExprError(); + + if (!Expand) { + // The transform has determined that we should perform a simple + // transformation on the pack expansion, producing another pack + // expansion. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1); + ExprResult Key = getDerived().TransformExpr(OrigElement.Key); + if (Key.isInvalid()) + return ExprError(); + + if (Key.get() != OrigElement.Key) + ArgChanged = true; + + ExprResult Value = getDerived().TransformExpr(OrigElement.Value); + if (Value.isInvalid()) + return ExprError(); + + if (Value.get() != OrigElement.Value) + ArgChanged = true; + + ObjCDictionaryElement Expansion = { + Key.get(), Value.get(), OrigElement.EllipsisLoc, NumExpansions + }; + Elements.push_back(Expansion); + continue; + } + + // Record right away that the argument was changed. This needs + // to happen even if the array expands to nothing. + ArgChanged = true; + + // The transform has determined that we should perform an elementwise + // expansion of the pattern. Do so. + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); + ExprResult Key = getDerived().TransformExpr(OrigElement.Key); + if (Key.isInvalid()) + return ExprError(); + + ExprResult Value = getDerived().TransformExpr(OrigElement.Value); + if (Value.isInvalid()) + return ExprError(); + + ObjCDictionaryElement Element = { + Key.get(), Value.get(), SourceLocation(), NumExpansions + }; + + // If any unexpanded parameter packs remain, we still have a + // pack expansion. + if (Key.get()->containsUnexpandedParameterPack() || + Value.get()->containsUnexpandedParameterPack()) + Element.EllipsisLoc = OrigElement.EllipsisLoc; + + Elements.push_back(Element); + } + + // We've finished with this pack expansion. + continue; + } + + // Transform and check key. + ExprResult Key = getDerived().TransformExpr(OrigElement.Key); + if (Key.isInvalid()) + return ExprError(); + + if (Key.get() != OrigElement.Key) + ArgChanged = true; + + // Transform and check value. + ExprResult Value + = getDerived().TransformExpr(OrigElement.Value); + if (Value.isInvalid()) + return ExprError(); + + if (Value.get() != OrigElement.Value) + ArgChanged = true; + + ObjCDictionaryElement Element = { + Key.get(), Value.get(), SourceLocation(), llvm::Optional<unsigned>() + }; + Elements.push_back(Element); + } + + if (!getDerived().AlwaysRebuild() && !ArgChanged) + return SemaRef.MaybeBindToTemporary(E); + + return getDerived().RebuildObjCDictionaryLiteral(E->getSourceRange(), + Elements.data(), + Elements.size()); } template<typename Derived> @@ -8436,6 +8616,30 @@ TreeTransform<Derived>::TransformObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { template<typename Derived> ExprResult +TreeTransform<Derived>::TransformObjCSubscriptRefExpr(ObjCSubscriptRefExpr *E) { + // Transform the base expression. + ExprResult Base = getDerived().TransformExpr(E->getBaseExpr()); + if (Base.isInvalid()) + return ExprError(); + + // Transform the key expression. + ExprResult Key = getDerived().TransformExpr(E->getKeyExpr()); + if (Key.isInvalid()) + return ExprError(); + + // If nothing changed, just retain the existing expression. + if (!getDerived().AlwaysRebuild() && + Key.get() == E->getKeyExpr() && Base.get() == E->getBaseExpr()) + return SemaRef.Owned(E); + + return getDerived().RebuildObjCSubscriptRefExpr(E->getRBracket(), + Base.get(), Key.get(), + E->getAtIndexMethodDecl(), + E->setAtIndexMethodDecl()); +} + +template<typename Derived> +ExprResult TreeTransform<Derived>::TransformObjCIsaExpr(ObjCIsaExpr *E) { // Transform the base expression. ExprResult Base = getDerived().TransformExpr(E->getBase()); diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index c586dbfe73..479805e7e5 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -823,6 +823,45 @@ void ASTStmtReader::VisitObjCStringLiteral(ObjCStringLiteral *E) { E->setAtLoc(ReadSourceLocation(Record, Idx)); } +void ASTStmtReader::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { + VisitExpr(E); + // could be one of several IntegerLiteral, FloatLiteral, etc. + E->Number = Reader.ReadSubStmt(); + E->ObjCNumericLiteralMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->AtLoc = ReadSourceLocation(Record, Idx); +} + +void ASTStmtReader::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + VisitExpr(E); + unsigned NumElements = Record[Idx++]; + assert(NumElements == E->getNumElements() && "Wrong number of elements"); + Expr **Elements = E->getElements(); + for (unsigned I = 0, N = NumElements; I != N; ++I) + Elements[I] = Reader.ReadSubExpr(); + E->ArrayWithObjectsMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->Range = ReadSourceRange(Record, Idx); +} + +void ASTStmtReader::VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + VisitExpr(E); + unsigned NumElements = Record[Idx++]; + assert(NumElements == E->getNumElements() && "Wrong number of elements"); + bool HasPackExpansions = Record[Idx++]; + assert(HasPackExpansions == E->HasPackExpansions &&"Pack expansion mismatch"); + ObjCDictionaryLiteral::KeyValuePair *KeyValues = E->getKeyValues(); + ObjCDictionaryLiteral::ExpansionData *Expansions = E->getExpansionData(); + for (unsigned I = 0; I != NumElements; ++I) { + KeyValues[I].Key = Reader.ReadSubExpr(); + KeyValues[I].Value = Reader.ReadSubExpr(); + if (HasPackExpansions) { + Expansions[I].EllipsisLoc = ReadSourceLocation(Record, Idx); + Expansions[I].NumExpansionsPlusOne = Record[Idx++]; + } + } + E->DictWithObjectsMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->Range = ReadSourceRange(Record, Idx); +} + void ASTStmtReader::VisitObjCEncodeExpr(ObjCEncodeExpr *E) { VisitExpr(E); E->setEncodedTypeSourceInfo(GetTypeSourceInfo(Record, Idx)); @@ -878,6 +917,15 @@ void ASTStmtReader::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { } } +void ASTStmtReader::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *E) { + VisitExpr(E); + E->setRBracket(ReadSourceLocation(Record, Idx)); + E->setBaseExpr(Reader.ReadSubExpr()); + E->setKeyExpr(Reader.ReadSubExpr()); + E->GetAtIndexMethodDecl = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->SetAtIndexMethodDecl = ReadDeclAs<ObjCMethodDecl>(Record, Idx); +} + void ASTStmtReader::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); assert(Record[Idx] == E->getNumArgs()); @@ -980,6 +1028,12 @@ void ASTStmtReader::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { S->setThrowLoc(ReadSourceLocation(Record, Idx)); } +void ASTStmtReader::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { + VisitExpr(E); + E->setValue(Record[Idx++]); + E->setLocation(ReadSourceLocation(Record, Idx)); +} + //===----------------------------------------------------------------------===// // C++ Expressions and Statements //===----------------------------------------------------------------------===// @@ -1834,6 +1888,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_OBJC_STRING_LITERAL: S = new (Context) ObjCStringLiteral(Empty); break; + case EXPR_OBJC_NUMERIC_LITERAL: + S = new (Context) ObjCNumericLiteral(Empty); + break; + case EXPR_OBJC_ARRAY_LITERAL: + S = ObjCArrayLiteral::CreateEmpty(Context, + Record[ASTStmtReader::NumExprFields]); + break; + case EXPR_OBJC_DICTIONARY_LITERAL: + S = ObjCDictionaryLiteral::CreateEmpty(Context, + Record[ASTStmtReader::NumExprFields], + Record[ASTStmtReader::NumExprFields + 1]); + break; case EXPR_OBJC_ENCODE: S = new (Context) ObjCEncodeExpr(Empty); break; @@ -1849,6 +1915,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_OBJC_PROPERTY_REF_EXPR: S = new (Context) ObjCPropertyRefExpr(Empty); break; + case EXPR_OBJC_SUBSCRIPT_REF_EXPR: + S = new (Context) ObjCSubscriptRefExpr(Empty); + break; case EXPR_OBJC_KVC_REF_EXPR: llvm_unreachable("mismatching AST file"); case EXPR_OBJC_MESSAGE_EXPR: @@ -1888,6 +1957,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case STMT_OBJC_AUTORELEASE_POOL: S = new (Context) ObjCAutoreleasePoolStmt(Empty); break; + case EXPR_OBJC_BOOL_LITERAL: + S = new (Context) ObjCBoolLiteralExpr(Empty); + break; case STMT_SEH_EXCEPT: S = new (Context) SEHExceptStmt(Empty); break; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index f497a038b3..88c1f70021 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -696,6 +696,9 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(EXPR_BLOCK_DECL_REF); RECORD(EXPR_GENERIC_SELECTION); RECORD(EXPR_OBJC_STRING_LITERAL); + RECORD(EXPR_OBJC_NUMERIC_LITERAL); + RECORD(EXPR_OBJC_ARRAY_LITERAL); + RECORD(EXPR_OBJC_DICTIONARY_LITERAL); RECORD(EXPR_OBJC_ENCODE); RECORD(EXPR_OBJC_SELECTOR_EXPR); RECORD(EXPR_OBJC_PROTOCOL_EXPR); @@ -709,6 +712,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(STMT_OBJC_AT_TRY); RECORD(STMT_OBJC_AT_SYNCHRONIZED); RECORD(STMT_OBJC_AT_THROW); + RECORD(EXPR_OBJC_BOOL_LITERAL); RECORD(EXPR_CXX_OPERATOR_CALL); RECORD(EXPR_CXX_CONSTRUCT); RECORD(EXPR_CXX_STATIC_CAST); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 22fdfe162a..76bbc850c2 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -753,7 +753,6 @@ void ASTStmtWriter::VisitPseudoObjectExpr(PseudoObjectExpr *E) { i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) { Writer.AddStmt(*i); } - Code = serialization::EXPR_PSEUDO_OBJECT; } @@ -770,7 +769,6 @@ void ASTStmtWriter::VisitAtomicExpr(AtomicExpr *E) { } Writer.AddSourceLocation(E->getBuiltinLoc(), Record); Writer.AddSourceLocation(E->getRParenLoc(), Record); - Code = serialization::EXPR_ATOMIC; } @@ -785,6 +783,46 @@ void ASTStmtWriter::VisitObjCStringLiteral(ObjCStringLiteral *E) { Code = serialization::EXPR_OBJC_STRING_LITERAL; } +void ASTStmtWriter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { + VisitExpr(E); + Writer.AddStmt(E->getNumber()); + Writer.AddDeclRef(E->getObjCNumericLiteralMethod(), Record); + Writer.AddSourceLocation(E->getAtLoc(), Record); + Code = serialization::EXPR_OBJC_NUMERIC_LITERAL; +} + +void ASTStmtWriter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + VisitExpr(E); + Record.push_back(E->getNumElements()); + for (unsigned i = 0; i < E->getNumElements(); i++) + Writer.AddStmt(E->getElement(i)); + Writer.AddDeclRef(E->getArrayWithObjectsMethod(), Record); + Writer.AddSourceRange(E->getSourceRange(), Record); + Code = serialization::EXPR_OBJC_ARRAY_LITERAL; +} + +void ASTStmtWriter::VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + VisitExpr(E); + Record.push_back(E->getNumElements()); + Record.push_back(E->HasPackExpansions); + for (unsigned i = 0; i < E->getNumElements(); i++) { + ObjCDictionaryElement Element = E->getKeyValueElement(i); + Writer.AddStmt(Element.Key); + Writer.AddStmt(Element.Value); + if (E->HasPackExpansions) { + Writer.AddSourceLocation(Element.EllipsisLoc, Record); + unsigned NumExpansions = 0; + if (Element.NumExpansions) + NumExpansions = *Element.NumExpansions + 1; + Record.push_back(NumExpansions); + } + } + + Writer.AddDeclRef(E->getDictWithObjectsMethod(), Record); + Writer.AddSourceRange(E->getSourceRange(), Record); + Code = serialization::EXPR_OBJC_DICTIONARY_LITERAL; +} + void ASTStmtWriter::VisitObjCEncodeExpr(ObjCEncodeExpr *E) { VisitExpr(E); Writer.AddTypeSourceInfo(E->getEncodedTypeSourceInfo(), Record); @@ -844,6 +882,17 @@ void ASTStmtWriter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { Code = serialization::EXPR_OBJC_PROPERTY_REF_EXPR; } +void ASTStmtWriter::VisitObjCSubscriptRefExpr(ObjCSubscriptRefExpr *E) { + VisitExpr(E); + Writer.AddSourceLocation(E->getRBracket(), Record); + Writer.AddStmt(E->getBaseExpr()); + Writer.AddStmt(E->getKeyExpr()); + Writer.AddDeclRef(E->getAtIndexMethodDecl(), Record); + Writer.AddDeclRef(E->setAtIndexMethodDecl(), Record); + + Code = serialization::EXPR_OBJC_SUBSCRIPT_REF_EXPR; +} + void ASTStmtWriter::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); Record.push_back(E->getNumArgs()); @@ -945,6 +994,13 @@ void ASTStmtWriter::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { Code = serialization::STMT_OBJC_AT_THROW; } +void ASTStmtWriter::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { + VisitExpr(E); + Record.push_back(E->getValue()); + Writer.AddSourceLocation(E->getLocation(), Record); + Code = serialization::EXPR_OBJC_BOOL_LITERAL; +} + //===----------------------------------------------------------------------===// // C++ Expressions and Statements. //===----------------------------------------------------------------------===// diff --git a/test/CodeGenObjC/Inputs/literal-support.h b/test/CodeGenObjC/Inputs/literal-support.h new file mode 100644 index 0000000000..5680a20c9f --- /dev/null +++ b/test/CodeGenObjC/Inputs/literal-support.h @@ -0,0 +1,35 @@ +#ifndef OBJC_LITERAL_SUPPORT_H +#define OBJC_LITERAL_SUPPORT_H + +typedef unsigned char BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; +@end + +@interface NSArray +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +@end + +#endif // OBJC_LITERAL_SUPPORT_H diff --git a/test/CodeGenObjC/arc-literals.m b/test/CodeGenObjC/arc-literals.m new file mode 100644 index 0000000000..203c2ad1ee --- /dev/null +++ b/test/CodeGenObjC/arc-literals.m @@ -0,0 +1,121 @@ +// RUN: %clang_cc1 -I %S/Inputs -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +#include "literal-support.h" + +// Check the various selector names we'll be using, in order. + +// CHECK: c"numberWithInt:\00" +// CHECK: c"numberWithUnsignedInt:\00" +// CHECK: c"numberWithUnsignedLongLong:\00" +// CHECK: c"numberWithChar:\00" +// CHECK: c"arrayWithObjects:count:\00" +// CHECK: c"dictionaryWithObjects:forKeys:count:\00" +// CHECK: c"prop\00" + +// CHECK: define void @test_numeric() +void test_numeric() { + // CHECK: {{call.*objc_msgSend.*i32 17}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id ilit = @17; + // CHECK: {{call.*objc_msgSend.*i32 25}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id ulit = @25u; + // CHECK: {{call.*objc_msgSend.*i64 42}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id ulllit = @42ull; + // CHECK: {{call.*objc_msgSend.*i8 signext 97}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id charlit = @'a'; + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @test_array +void test_array(id a, id b) { + // Retaining parameters + // CHECK: call i8* @objc_retain(i8* + // CHECK: call i8* @objc_retain(i8* + + // Constructing the array + // CHECK: getelementptr inbounds [2 x i8*]* [[OBJECTS:%[A-Za-z0-9]+]], i32 0, i32 0 + // CHECK: store i8* + // CHECK: getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 1 + // CHECK: store i8* + + // CHECK: {{call i8*.*objc_msgSend.*i64 2}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id arr = @[a, b]; + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @test_dictionary +void test_dictionary(id k1, id o1, id k2, id o2) { + // Retaining parameters + // CHECK: call i8* @objc_retain(i8* + // CHECK: call i8* @objc_retain(i8* + // CHECK: call i8* @objc_retain(i8* + // CHECK: call i8* @objc_retain(i8* + + // Constructing the arrays + // CHECK: getelementptr inbounds [2 x i8*]* [[KEYS:%[A-Za-z0-9]+]], i32 0, i32 0 + // CHECK: store i8* + // CHECK: getelementptr inbounds [2 x i8*]* [[OBJECTS:%[A-Za-z0-9]+]], i32 0, i32 0 + // CHECK: store i8* + // CHECK: getelementptr inbounds [2 x i8*]* [[KEYS]], i32 0, i32 1 + // CHECK: store i8* + // CHECK: getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 1 + // CHECK: store i8* + + // Constructing the dictionary + // CHECK: {{call i8.*@objc_msgSend}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id dict = @{ k1 : o1, k2 : o2 }; + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +@interface A +@end + +@interface B +@property (retain) A* prop; +@end + +// CHECK: define void @test_property +void test_property(B *b) { + // Retain parameter + // CHECK: call i8* @objc_retain + + // Invoke 'prop' + // CHECK: load i8** @"\01L_OBJC_SELECTOR_REFERENCES + // CHECK: {{call.*@objc_msgSend}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + + // Invoke arrayWithObjects:count: + // CHECK: load i8** @"\01L_OBJC_SELECTOR_REFERENCES + // CHECK: {{call.*objc_msgSend}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id arr = @[ b.prop ]; + + // Release b.prop + // CHECK: call void @objc_release + + // Destroy arr + // CHECK: call void @objc_release + + // Destroy b + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjC/objc-arc-container-subscripting.m b/test/CodeGenObjC/objc-arc-container-subscripting.m new file mode 100644 index 0000000000..892491630e --- /dev/null +++ b/test/CodeGenObjC/objc-arc-container-subscripting.m @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fobjc-arc -emit-llvm -triple x86_64-apple-darwin -o - %s | FileCheck %s + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(int)index; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +id func() { + NSMutableArray *array; + array[3] = 0; + return array[3]; +} + +// CHECK: [[call:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK: [[SIX:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[call]]) nounwind +// CHECK: [[ARRAY:%.*]] = load %0** +// CHECK: [[ARRAY_CASTED:%.*]] = bitcast{{.*}}[[ARRAY]] to i8* +// CHECK: call void @objc_release(i8* [[ARRAY_CASTED]]) +// CHECK: [[EIGHT:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[SIX]]) nounwind +// CHECK: ret i8* [[EIGHT]] + diff --git a/test/CodeGenObjC/objc-container-subscripting-1.m b/test/CodeGenObjC/objc-container-subscripting-1.m new file mode 100644 index 0000000000..91b7f468ea --- /dev/null +++ b/test/CodeGenObjC/objc-container-subscripting-1.m @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -o - %s | FileCheck %s + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +@end + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +int main() { + NSMutableArray *array; + id val; + + id oldObject = array[10]; +// CHECK: [[ARR:%.*]] = load {{%.*}} [[array:%.*]], align 8 +// CHECK-NEXT: [[SEL:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" +// CHECK-NEXT: [[ARRC:%.*]] = bitcast {{%.*}} [[ARR]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i32)*)(i8* [[ARRC]], i8* [[SEL]], i32 10) +// CHECK-NEXT: store i8* [[CALL]], i8** [[OLDOBJ:%.*]], align 8 + + val = (array[10] = oldObject); +// CHECK: [[THREE:%.*]] = load {{%.*}} [[array:%.*]], align 8 +// CHECK-NEXT: [[FOUR:%.*]] = load i8** [[oldObject:%.*]], align 8 +// CHECK-NEXT: [[FIVE:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_2" +// CHECK-NEXT: [[SIX:%.*]] = bitcast {{%.*}} [[THREE]] to i8* +// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*, i32)*)(i8* [[SIX]], i8* [[FIVE]], i8* [[FOUR]], i32 10) +// CHECK-NEXT: store i8* [[FOUR]], i8** [[val:%.*]] + + NSMutableDictionary *dictionary; + id key; + id newObject; + oldObject = dictionary[key]; +// CHECK: [[SEVEN:%.*]] = load {{%.*}} [[DICTIONARY:%.*]], align 8 +// CHECK-NEXT: [[EIGHT:%.*]] = load i8** [[KEY:%.*]], align 8 +// CHECK-NEXT: [[TEN:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_4" +// CHECK-NEXT: [[ELEVEN:%.*]] = bitcast {{%.*}} [[SEVEN]] to i8* +// CHECK-NEXT: [[CALL1:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i8*)*)(i8* [[ELEVEN]], i8* [[TEN]], i8* [[EIGHT]]) +// CHECK-NEXT: store i8* [[CALL1]], i8** [[oldObject:%.*]], align 8 + + + val = (dictionary[key] = newObject); +// CHECK: [[TWELVE:%.*]] = load {{%.*}} [[DICTIONARY]], align 8 +// CHECK-NEXT: [[THIRTEEN:%.*]] = load i8** [[KEY]], align 8 +// CHECK-NEXT: [[FOURTEEN:%.*]] = load i8** [[NEWOBJECT:%.*]], align 8 +// CHECK-NEXT: [[SIXTEEN:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_6" +// CHECK-NEXT: [[SEVENTEEN:%.*]] = bitcast {{%.*}} [[TWELVE]] to i8* +// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*, i8*)*)(i8* [[SEVENTEEN]], i8* [[SIXTEEN]], i8* [[FOURTEEN]], i8* [[THIRTEEN]]) +// CHECK-NEXT: store i8* [[FOURTEEN]], i8** [[val:%.*]] +} + diff --git a/test/CodeGenObjC/objc-container-subscripting.m b/test/CodeGenObjC/objc-container-subscripting.m new file mode 100644 index 0000000000..fd8f8effac --- /dev/null +++ b/test/CodeGenObjC/objc-container-subscripting.m @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin %s -o /dev/null + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +#if __has_feature(objc_subscripting) +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +#endif +@end + +#if __has_feature(objc_subscripting) +@interface XNSMutableArray +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +#endif +@end + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@class NSString; + +int main() { + NSMutableArray<P> * array; + id oldObject = array[10]; + + array[10] = oldObject; + + id unknown_array; + oldObject = unknown_array[1]; + + unknown_array[1] = oldObject; + + NSMutableDictionary *dictionary; + NSString *key; + id newObject; + oldObject = dictionary[key]; + dictionary[key] = newObject; // replace oldObject with newObject + +} + diff --git a/test/CodeGenObjC/objc-dictionary-literal.m b/test/CodeGenObjC/objc-dictionary-literal.m new file mode 100644 index 0000000000..b335582ed2 --- /dev/null +++ b/test/CodeGenObjC/objc-dictionary-literal.m @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin10 -fblocks -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fblocks -emit-llvm %s -o /dev/null +// rdar://10614657 + +@interface NSNumber ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithInt:(int)value; +@end + +@protocol NSCopying @end +typedef unsigned long NSUInteger; + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt; +@end + +@interface NSString<NSCopying> +@end + +int main() { + NSDictionary *dict = @{ @"name":@666 }; + NSDictionary *dict1 = @{ @"name":@666 }; + NSDictionary *dict2 = @{ @"name":@666 }; + return 0; +} diff --git a/test/CodeGenObjC/objc-literal-debugger-test.m b/test/CodeGenObjC/objc-literal-debugger-test.m new file mode 100644 index 0000000000..389ef2248a --- /dev/null +++ b/test/CodeGenObjC/objc-literal-debugger-test.m @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdebugger-objc-literal -emit-llvm -o - %s | FileCheck %s + +int main() { + id l = @'a'; + l = @'a'; + l = @42; + l = @-42; + l = @42u; + l = @3.141592654f; + l = @__objc_yes; + l = @__objc_no; + l = @{ @"name":@666 }; + l = @[ @"foo", @"bar" ]; +} + +// CHECK: declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind diff --git a/test/CodeGenObjC/objc-literal-tests.m b/test/CodeGenObjC/objc-literal-tests.m new file mode 100644 index 0000000000..c513d49611 --- /dev/null +++ b/test/CodeGenObjC/objc-literal-tests.m @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin10 -fblocks -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin10 -fblocks -emit-llvm %s -o - | FileCheck %s +// rdar://10111397 + +#if __has_feature(objc_bool) +#define YES __objc_yes +#define NO __objc_no +#else +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#endif + +#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 +typedef unsigned long NSUInteger; +typedef long NSInteger; +#else +typedef unsigned int NSUInteger; +typedef int NSInteger; +#endif +typedef signed char BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) +#if __has_feature(objc_array_literals) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; ++ (NSNumber *)numberWithInteger:(NSInteger)value ; ++ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value ; +#endif +@end + +@interface NSDate ++ (NSDate *) date; +@end + +#if __has_feature(objc_dictionary_literals) +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt; +@end +#endif + +id NSUserName(); + +// CHECK: define i32 @main() nounwind +int main() { + // CHECK: call{{.*}}@objc_msgSend{{.*}}i8 signext 97 + NSNumber *aNumber = @'a'; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i32 42 + NSNumber *fortyTwo = @42; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i32 -42 + NSNumber *negativeFortyTwo = @-42; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i32 42 + NSNumber *positiveFortyTwo = @+42; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i32 42 + NSNumber *fortyTwoUnsigned = @42u; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i64 42 + NSNumber *fortyTwoLong = @42l; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i64 42 + NSNumber *fortyTwoLongLong = @42ll; + // CHECK: call{{.*}}@objc_msgSend{{.*}}float 0x400921FB60000000 + NSNumber *piFloat = @3.141592654f; + // CHECK: call{{.*}}@objc_msgSend{{.*}}double 0x400921FB54411744 + NSNumber *piDouble = @3.1415926535; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i8 signext 1 + NSNumber *yesNumber = @__objc_yes; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i8 signext 0 + NSNumber *noNumber = @__objc_no; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i8 signext 1 + NSNumber *yesNumber1 = @YES; + // CHECK: call{{.*}}@objc_msgSend{{.*}}i8 signext 0 + NSNumber *noNumber1 = @NO; +NSDictionary *dictionary = @{@"name" : NSUserName(), + @"date" : [NSDate date] }; + return __objc_yes == __objc_no; +} + +// rdar://10579122 +typedef BOOL (^foo)(void); +extern void bar(foo a); + +void baz(void) { + bar(^(void) { return YES; }); +} diff --git a/test/CodeGenObjC/optimized-setter.m b/test/CodeGenObjC/optimized-setter.m new file mode 100644 index 0000000000..0e1b388595 --- /dev/null +++ b/test/CodeGenObjC/optimized-setter.m @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 %s -emit-llvm -triple x86_64-apple-macosx10.8.0 -o - | FileCheck %s +// rdar://10179974 + +@interface I +// void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset); +// objc_setProperty(..., NO, NO) +@property (nonatomic, retain) id nonatomicProperty; + +// void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset); +// objc_setProperty(..., NO, YES) +@property (nonatomic, copy) id nonatomicPropertyCopy; + +// void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset); +// objc_setProperty(..., YES, NO) +@property (retain) id atomicProperty; + +// void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset); +// objc_setProperty(..., YES, YES) +@property (copy) id atomicPropertyCopy; +@end + +@implementation I +@synthesize nonatomicProperty; +@synthesize nonatomicPropertyCopy; +@synthesize atomicProperty; +@synthesize atomicPropertyCopy; +@end + +// CHECK: call void @objc_setProperty_nonatomic +// CHECK: call void @objc_setProperty_nonatomic_copy +// CHECK: call void @objc_setProperty_atomic +// CHECK: call void @objc_setProperty_atomic_copy + diff --git a/test/CodeGenObjCXX/Inputs/literal-support.h b/test/CodeGenObjCXX/Inputs/literal-support.h new file mode 100644 index 0000000000..5680a20c9f --- /dev/null +++ b/test/CodeGenObjCXX/Inputs/literal-support.h @@ -0,0 +1,35 @@ +#ifndef OBJC_LITERAL_SUPPORT_H +#define OBJC_LITERAL_SUPPORT_H + +typedef unsigned char BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; +@end + +@interface NSArray +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +@end + +#endif // OBJC_LITERAL_SUPPORT_H diff --git a/test/CodeGenObjCXX/literals.mm b/test/CodeGenObjCXX/literals.mm new file mode 100644 index 0000000000..b8946fa3f8 --- /dev/null +++ b/test/CodeGenObjCXX/literals.mm @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -I %S/Inputs -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -fexceptions -fobjc-exceptions -fcxx-exceptions -fobjc-arc-exceptions -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +#include "literal-support.h" + +struct X { + X(); + ~X(); + operator id() const; +}; + +struct Y { + Y(); + ~Y(); + operator id() const; +}; + +// CHECK: define void @_Z10test_arrayv +void test_array() { + // CHECK: [[OBJECTS:%[a-zA-Z0-9.]+]] = alloca [2 x i8*] + + // Initializing first element + // CHECK: [[ELEMENT0:%[a-zA-Z0-9.]+]] = getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 0 + // CHECK-NEXT: call void @_ZN1XC1Ev + // CHECK-NEXT: [[OBJECT0:%[a-zA-Z0-9.]+]] = invoke i8* @_ZNK1XcvP11objc_objectEv + // CHECK: [[RET0:%[a-zA-Z0-9.]+]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[OBJECT0]]) + // CHECK: store i8* [[RET0]], i8** [[ELEMENT0]] + + // Initializing the second element + // CHECK: [[ELEMENT1:%[a-zA-Z0-9.]+]] = getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 1 + // CHECK-NEXT: invoke void @_ZN1YC1Ev + // CHECK: [[OBJECT1:%[a-zA-Z0-9.]+]] = invoke i8* @_ZNK1YcvP11objc_objectEv + // CHECK: [[RET1:%[a-zA-Z0-9.]+]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[OBJECT1]]) + // CHECK: store i8* [[RET1]], i8** [[ELEMENT1]] + + // Build the array + // CHECK: {{invoke.*@objc_msgSend}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id arr = @[ X(), Y() ]; + + // Destroy temporaries + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NOT: ret void + // CHECK: invoke void @_ZN1YD1Ev + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NEXT: call void @_ZN1XD1Ev + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NEXT: ret void + + // Check cleanups + // CHECK: call void @objc_release + // CHECK-NOT: call void @objc_release + // CHECK: invoke void @_ZN1YD1Ev + // CHECK: call void @objc_release + // CHECK-NOT: call void @objc_release + // CHECK: invoke void @_ZN1XD1Ev + // CHECK-NOT: call void @objc_release + // CHECK: unreachable +} + +// CHECK: define weak_odr void @_Z24test_array_instantiationIiEvv +template<typename T> +void test_array_instantiation() { + // CHECK: [[OBJECTS:%[a-zA-Z0-9.]+]] = alloca [2 x i8*] + + // Initializing first element + // CHECK: [[ELEMENT0:%[a-zA-Z0-9.]+]] = getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 0 + // CHECK-NEXT: call void @_ZN1XC1Ev + // CHECK-NEXT: [[OBJECT0:%[a-zA-Z0-9.]+]] = invoke i8* @_ZNK1XcvP11objc_objectEv + // CHECK: [[RET0:%[a-zA-Z0-9.]+]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[OBJECT0]]) + // CHECK: store i8* [[RET0]], i8** [[ELEMENT0]] + + // Initializing the second element + // CHECK: [[ELEMENT1:%[a-zA-Z0-9.]+]] = getelementptr inbounds [2 x i8*]* [[OBJECTS]], i32 0, i32 1 + // CHECK-NEXT: invoke void @_ZN1YC1Ev + // CHECK: [[OBJECT1:%[a-zA-Z0-9.]+]] = invoke i8* @_ZNK1YcvP11objc_objectEv + // CHECK: [[RET1:%[a-zA-Z0-9.]+]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[OBJECT1]]) + // CHECK: store i8* [[RET1]], i8** [[ELEMENT1]] + + // Build the array + // CHECK: {{invoke.*@objc_msgSend}} + // CHECK: call i8* @objc_retainAutoreleasedReturnValue + id arr = @[ X(), Y() ]; + + // Destroy temporaries + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NOT: ret void + // CHECK: invoke void @_ZN1YD1Ev + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NEXT: call void @_ZN1XD1Ev + // CHECK-NOT: ret void + // CHECK: call void @objc_release + // CHECK-NEXT: ret void + + // Check cleanups + // CHECK: call void @objc_release + // CHECK-NOT: call void @objc_release + // CHECK: invoke void @_ZN1YD1Ev + // CHECK: call void @objc_release + // CHECK-NOT: call void @objc_release + // CHECK: invoke void @_ZN1XD1Ev + // CHECK-NOT: call void @objc_release + // CHECK: unreachable +} + +template void test_array_instantiation<int>(); + diff --git a/test/CodeGenObjCXX/objc-container-subscripting-1.mm b/test/CodeGenObjCXX/objc-container-subscripting-1.mm new file mode 100644 index 0000000000..c0dd0f8bae --- /dev/null +++ b/test/CodeGenObjCXX/objc-container-subscripting-1.mm @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -o - %s | FileCheck %s + +typedef unsigned int size_t; +@protocol P @end +@protocol NSCopying @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +@end + +struct S { + operator unsigned int (); + operator id (); +}; + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id<NSCopying>)key; +- (void)setObject:(id)object forKeyedSubscript:(id<NSCopying>)key; +@end + +int main() { + NSMutableArray<P> * array; + S s; + id oldObject = array[(int)s]; + + NSMutableDictionary<P> *dict; + dict[(id)s] = oldObject; + oldObject = dict[(id)s]; + +} + +template <class T> void test2(NSMutableArray *a) { + a[10] = 0; +} +template void test2<int>(NSMutableArray*); +// CHECK: define weak_odr void @_Z5test2IiEvP14NSMutableArray +// CHECK: @objc_msgSend +// CHECK: ret void + + +template <class T> void test3(NSMutableArray *a) { + a[sizeof(T)] = 0; +} + +template void test3<int>(NSMutableArray*); +// CHECK: define weak_odr void @_Z5test3IiEvP14NSMutableArray +// CHECK: @objc_msgSend +// CHECK: ret void + diff --git a/test/CodeGenObjCXX/objc-container-subscripting.mm b/test/CodeGenObjCXX/objc-container-subscripting.mm new file mode 100644 index 0000000000..dfe48e9d6d --- /dev/null +++ b/test/CodeGenObjCXX/objc-container-subscripting.mm @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -o - %s | FileCheck %s + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +@end + +struct S { + operator unsigned int (); + operator id (); +}; + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +int main() { + NSMutableArray<P> * array; + S s; + id oldObject = array[(int)s]; + + NSMutableDictionary<P> *dict; + dict[(id)s] = oldObject; + oldObject = dict[(id)s]; + +} + +template <class T> void test2(NSMutableArray *a) { + a[10] = 0; +} +template void test2<int>(NSMutableArray*); +// CHECK: define weak_odr void @_Z5test2IiEvP14NSMutableArray +// CHECK: @objc_msgSend +// CHECK: ret void + + +template <class T> void test3(NSMutableArray *a) { + a[sizeof(T)] = 0; +} + +template void test3<int>(NSMutableArray*); +// CHECK: define weak_odr void @_Z5test3IiEvP14NSMutableArray +// CHECK: @objc_msgSend +// CHECK: ret void + +// CHECK: define void @_Z11static_dataP14NSMutableArray +void static_data(NSMutableArray *array) { + // CHECK: call i32 @__cxa_guard_acquire + // CHECK: {{call i8*.*@objc_msgSend }} + // CHECK: call void @__cxa_guard_release + static id x = array[4]; + // CHECK: ret void +} diff --git a/test/Driver/arclite-link.c b/test/Driver/arclite-link.c new file mode 100644 index 0000000000..9cf1efe987 --- /dev/null +++ b/test/Driver/arclite-link.c @@ -0,0 +1,8 @@ +// RUN: touch %t.o +// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.7 %t.o 2>&1 | FileCheck -check-prefix=CHECK-ARCLITE-OSX %s +// RUN: %clang -### -target x86_64-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.8 %t.o 2>&1 | FileCheck -check-prefix=CHECK-NOARCLITE %s +// RUN: %clang -### -target i386-apple-darwin10 -fobjc-link-runtime -mmacosx-version-min=10.7 %t.o 2>&1 | FileCheck -check-prefix=CHECK-NOARCLITE %s + +// CHECK-ARCLITE-OSX: libarclite_macosx.a +// CHECK-ARCLITE-OSX: -lobjc +// CHECK-NOARCLITE-NOT: libarclite diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c index 056d34706a..3206f654d5 100644 --- a/test/Driver/darwin-ld.c +++ b/test/Driver/darwin-ld.c @@ -117,3 +117,7 @@ // LINK_VERSION_MIN: "-macosx_version_min" "10.6.0" // LINK_VERSION_MIN: {{ld(.exe)?"}} // LINK_VERSION_MIN: "-macosx_version_min" "10.7.0" + +// RUN: %clang -target x86_64-apple-darwin12 -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=LINK_NO_CRT1 %s < %t.log +// LINK_NO_CRT1-NOT: crt diff --git a/test/Driver/rewrite-objc.m b/test/Driver/rewrite-objc.m index 9e779f76b1..30883f3bf5 100644 --- a/test/Driver/rewrite-objc.m +++ b/test/Driver/rewrite-objc.m @@ -3,7 +3,7 @@ // TEST0: clang{{.*}}" "-cc1" // TEST0: "-rewrite-objc" // FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead. -// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime-has-arc" "-fobjc-runtime-has-weak" "-fobjc-fragile-abi" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option" +// TEST0: "-fmessage-length" "0" "-stack-protector" "1" "-mstackrealign" "-fblocks" "-fobjc-runtime-has-arc" "-fobjc-runtime-has-weak" "-fobjc-fragile-abi" "-fobjc-default-synthesize-properties" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fexceptions" "-fdiagnostics-show-option" // TEST0: rewrite-objc.m" // RUN: not %clang -ccc-no-clang -target unknown -rewrite-objc %s -o - -### 2>&1 | \ diff --git a/test/PCH/objc_container.h b/test/PCH/objc_container.h new file mode 100644 index 0000000000..c83f90238b --- /dev/null +++ b/test/PCH/objc_container.h @@ -0,0 +1,26 @@ +@protocol P @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(unsigned int)index; +- (void)setObject:(id)object atIndexedSubscript:(unsigned int)index; +@end + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +void all() { + NSMutableArray *array; + id oldObject = array[10]; + + array[10] = oldObject; + + NSMutableDictionary *dictionary; + id key; + id newObject; + oldObject = dictionary[key]; + + dictionary[key] = newObject; +} + diff --git a/test/PCH/objc_container.m b/test/PCH/objc_container.m new file mode 100644 index 0000000000..1e59054a2e --- /dev/null +++ b/test/PCH/objc_container.m @@ -0,0 +1,20 @@ +// Test this without pch. +// RUN: %clang_cc1 -include %S/objc_container.h -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -x objective-c -emit-pch -o %t %S/objc_container.h +// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s +// RUN: %clang -cc1 -include-pch %t -ast-print %s | FileCheck -check-prefix=PRINT %s +// RUN: %clang -cc1 -include-pch %t -emit-llvm -o - %s | FileCheck -check-prefix=IR %s + +// CHECK-PRINT: id oldObject = array[10]; +// CHECK-PRINT: array[10] = oldObject; +// CHECK-PRINT: oldObject = dictionary[key]; +// CHECK-PRINT: dictionary[key] = newObject; + +// CHECK-IR: define void @all() nounwind +// CHECK-IR: {{call.*objc_msgSend}} +// CHECK-IR: {{call.*objc_msgSend}} +// CHECK-IR: {{call.*objc_msgSend}} +// CHECK-IR: {{call.*objc_msgSend}} +// CHECK-IR: ret void diff --git a/test/PCH/objc_literals.m b/test/PCH/objc_literals.m new file mode 100644 index 0000000000..cce3173bba --- /dev/null +++ b/test/PCH/objc_literals.m @@ -0,0 +1,66 @@ +// RUN: %clang -cc1 -emit-pch -o %t %s +// RUN: %clang -cc1 -include-pch %t -verify %s +// RUN: %clang -cc1 -include-pch %t -ast-print %s | FileCheck -check-prefix=PRINT %s +// RUN: %clang -cc1 -include-pch %t -emit-llvm -o - %s | FileCheck -check-prefix=IR %s + +#ifndef HEADER +#define HEADER + +typedef unsigned char BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; +@end + +@interface NSArray +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +@end + +// CHECK-IR: define internal void @test_numeric_literals() +static inline void test_numeric_literals() { + // CHECK-PRINT: id intlit = @17 + // CHECK-IR: {{call.*17}} + id intlit = @17; + // CHECK-PRINT: id floatlit = @17.45 + // CHECK-IR: {{call.*1.745}} + id floatlit = @17.45; +} + +static inline void test_array_literals() { + // CHECK-PRINT: id arraylit = @[ @17, @17.45 + id arraylit = @[@17, @17.45]; +} + +static inline void test_dictionary_literals() { + // CHECK-PRINT: id dictlit = @{ @17 : {{@17.45[^,]*}}, @"hello" : @"world" }; + id dictlit = @{@17 : @17.45, @"hello" : @"world" }; +} + +#else +void test_all() { + test_numeric_literals(); + test_array_literals(); + test_dictionary_literals(); +} +#endif diff --git a/test/PCH/objc_literals.mm b/test/PCH/objc_literals.mm new file mode 100644 index 0000000000..8ef3351150 --- /dev/null +++ b/test/PCH/objc_literals.mm @@ -0,0 +1,65 @@ +// RUN: %clang -cc1 -emit-pch -x objective-c++ -std=c++0x -o %t %s +// RUN: %clang -cc1 -include-pch %t -x objective-c++ -std=c++0x -verify %s +// RUN: %clang -cc1 -include-pch %t -x objective-c++ -std=c++0x -ast-print %s | FileCheck -check-prefix=PRINT %s +// RUN: %clang -cc1 -include-pch %t -x objective-c++ -std=c++0x -emit-llvm -o - %s | FileCheck -check-prefix=IR %s + +#ifndef HEADER +#define HEADER + +typedef unsigned char BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; +@end + +@interface NSArray +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +@end + +template<typename T, typename U> +struct pair { + T first; + U second; +}; + +template<typename T, typename U> +pair<T, U> make_pair(const T& first, const U& second) { + return { first, second }; +} + +// CHECK-IR: define linkonce_odr void @_Z29variadic_dictionary_expansionIJP8NSStringS1_EJP8NSNumberS3_EEvDp4pairIT_T0_E +template<typename ...Ts, typename ... Us> +void variadic_dictionary_expansion(pair<Ts, Us>... key_values) { + // CHECK-PRINT: id dict = @{ key_values.first : key_values.second... }; + // CHECK-IR: {{call.*objc_msgSend}} + // CHECK-IR: ret void + id dict = @{ key_values.first : key_values.second ... }; +} + +#else +void test_all() { + variadic_dictionary_expansion(make_pair(@"Seventeen", @17), + make_pair(@"YES", @true)); +} +#endif diff --git a/test/PCH/subscripting-literals.m b/test/PCH/subscripting-literals.m new file mode 100644 index 0000000000..1675373441 --- /dev/null +++ b/test/PCH/subscripting-literals.m @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o %t.nopch.ll %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o %t.pch.ll %s -include-pch %t.pch +// RUN: diff %t.nopch.ll %t.pch.ll + +#ifndef HEADER +#define HEADER + +@interface NSArray +- (id)objectAtIndexedSubscript:(int)index; ++ (id)arrayWithObjects:(id *)objects count:(unsigned)count; +@end + +@interface NSMutableArray : NSArray +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +@interface NSDictionary +- (id)objectForKeyedSubscript:(id)key; ++ (id)dictionaryWithObjects:(id *)objects forKeys:(id *)keys count:(unsigned)count; +@end + +@interface NSMutableDictionary : NSDictionary +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@interface NSNumber ++ (NSNumber *)numberWithInt:(int)value; +@end + +@class NSString; + +id testArray(int idx, id p) { + NSMutableArray *array; + array[idx] = p; + NSArray *arr = @[ p, @7 ]; + return array[idx]; +} + +void testDict(NSString *key, id newObject, id oldObject) { + NSMutableDictionary *dictionary; + oldObject = dictionary[key]; + dictionary[key] = newObject; + NSDictionary *dict = @{ key: newObject, key: oldObject }; +} + +#endif diff --git a/test/SemaObjC/cocoa-api-usage.m b/test/SemaObjC/cocoa-api-usage.m new file mode 100644 index 0000000000..85e21154a9 --- /dev/null +++ b/test/SemaObjC/cocoa-api-usage.m @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -fsyntax-only -Wobjc-cocoa-api -verify +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %s -fsyntax-only -Wobjc-cocoa-api -verify +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c %s.fixed -fsyntax-only +// RUN: cp %s %t.m +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %t.m -fixit -Wobjc-cocoa-api +// RUN: diff %s.fixed %t.m +// RUN: cp %s %t.m +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %t.m -fixit -Wobjc-cocoa-api +// RUN: diff %s.fixed %t.m + +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSString : NSObject ++ (id)stringWithString:(NSString *)string; +- (id)initWithString:(NSString *)aString; +@end + +@interface NSArray : NSObject +- (id)objectAtIndex:(unsigned long)index; +- (id)objectAtIndexedSubscript:(int)index; +@end + +@interface NSArray (NSArrayCreation) ++ (id)array; ++ (id)arrayWithObject:(id)anObject; ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; ++ (id)arrayWithObjects:(id)firstObj, ...; ++ (id)arrayWithArray:(NSArray *)array; + +- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt; +- (id)initWithObjects:(id)firstObj, ...; +- (id)initWithArray:(NSArray *)array; + +- (id)objectAtIndex:(unsigned long)index; +@end + +@interface NSMutableArray : NSArray +- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +@interface NSDictionary : NSObject +- (id)objectForKeyedSubscript:(id)key; +@end + +@interface NSDictionary (NSDictionaryCreation) ++ (id)dictionary; ++ (id)dictionaryWithObject:(id)object forKey:(id)key; ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; ++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; ++ (id)dictionaryWithDictionary:(NSDictionary *)dict; ++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +- (id)initWithObjectsAndKeys:(id)firstObject, ...; +- (id)initWithDictionary:(NSDictionary *)otherDictionary; +- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)objectForKey:(id)aKey; +@end + +@interface NSMutableDictionary : NSDictionary +- (void)setObject:(id)anObject forKey:(id)aKey; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithInt:(int)value; +@end + +#define M(x) (x) +#define PAIR(x) @#x, [NSNumber numberWithInt:(x)] +#define TWO(x) ((x), (x)) + +void foo() { + NSString *str = M([NSString stringWithString:@"foo"]); // expected-warning {{redundant}} + str = [[NSString alloc] initWithString:@"foo"]; + NSArray *arr = [NSArray arrayWithArray:@[str]]; // expected-warning {{redundant}} + NSDictionary *dict = [NSDictionary dictionaryWithDictionary:@{str: arr}]; // expected-warning {{redundant}} +} diff --git a/test/SemaObjC/cocoa-api-usage.m.fixed b/test/SemaObjC/cocoa-api-usage.m.fixed new file mode 100644 index 0000000000..55e060a068 --- /dev/null +++ b/test/SemaObjC/cocoa-api-usage.m.fixed @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -fsyntax-only -Wobjc-cocoa-api -verify +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %s -fsyntax-only -Wobjc-cocoa-api -verify +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c %s.fixed -fsyntax-only +// RUN: cp %s %t.m +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %t.m -fixit -Wobjc-cocoa-api +// RUN: diff %s.fixed %t.m +// RUN: cp %s %t.m +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc %t.m -fixit -Wobjc-cocoa-api +// RUN: diff %s.fixed %t.m + +typedef signed char BOOL; +#define nil ((void*) 0) + +@interface NSObject ++ (id)alloc; +@end + +@interface NSString : NSObject ++ (id)stringWithString:(NSString *)string; +- (id)initWithString:(NSString *)aString; +@end + +@interface NSArray : NSObject +- (id)objectAtIndex:(unsigned long)index; +- (id)objectAtIndexedSubscript:(int)index; +@end + +@interface NSArray (NSArrayCreation) ++ (id)array; ++ (id)arrayWithObject:(id)anObject; ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; ++ (id)arrayWithObjects:(id)firstObj, ...; ++ (id)arrayWithArray:(NSArray *)array; + +- (id)initWithObjects:(const id [])objects count:(unsigned long)cnt; +- (id)initWithObjects:(id)firstObj, ...; +- (id)initWithArray:(NSArray *)array; + +- (id)objectAtIndex:(unsigned long)index; +@end + +@interface NSMutableArray : NSArray +- (void)replaceObjectAtIndex:(unsigned long)index withObject:(id)anObject; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +@interface NSDictionary : NSObject +- (id)objectForKeyedSubscript:(id)key; +@end + +@interface NSDictionary (NSDictionaryCreation) ++ (id)dictionary; ++ (id)dictionaryWithObject:(id)object forKey:(id)key; ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; ++ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...; ++ (id)dictionaryWithDictionary:(NSDictionary *)dict; ++ (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)initWithObjects:(const id [])objects forKeys:(const id [])keys count:(unsigned long)cnt; +- (id)initWithObjectsAndKeys:(id)firstObject, ...; +- (id)initWithDictionary:(NSDictionary *)otherDictionary; +- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys; + +- (id)objectForKey:(id)aKey; +@end + +@interface NSMutableDictionary : NSDictionary +- (void)setObject:(id)anObject forKey:(id)aKey; +- (void)setObject:(id)object forKeyedSubscript:(id)key; +@end + +@interface NSNumber : NSObject +@end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithInt:(int)value; +@end + +#define M(x) (x) +#define PAIR(x) @#x, [NSNumber numberWithInt:(x)] +#define TWO(x) ((x), (x)) + +void foo() { + NSString *str = M(@"foo"); // expected-warning {{redundant}} + str = [[NSString alloc] initWithString:@"foo"]; + NSArray *arr = @[str]; // expected-warning {{redundant}} + NSDictionary *dict = @{str: arr}; // expected-warning {{redundant}} +} diff --git a/test/SemaObjC/invalid-code.m b/test/SemaObjC/invalid-code.m index 8378ed761c..7b6591205b 100644 --- a/test/SemaObjC/invalid-code.m +++ b/test/SemaObjC/invalid-code.m @@ -2,7 +2,8 @@ // rdar://6124613 void test1() { - void *p = @1; // expected-error {{unexpected '@' in program}} + void *xyzzy = 0; + void *p = @xyzzy; // expected-error {{unexpected '@' in program}} } // <rdar://problem/7495713> diff --git a/test/SemaObjC/objc-array-literal.m b/test/SemaObjC/objc-array-literal.m new file mode 100644 index 0000000000..9f59316219 --- /dev/null +++ b/test/SemaObjC/objc-array-literal.m @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://10111397 + +#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 +typedef unsigned long NSUInteger; +#else +typedef unsigned int NSUInteger; +#endif + +@class NSString; + +extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); + +@class NSFastEnumerationState; + +@protocol NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id [])buffer count:(NSUInteger)len; + +@end + +@interface NSNumber ++ (NSNumber *)numberWithInt:(int)value; +@end + +@interface NSArray <NSFastEnumeration> ++ (id)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt; +@end + + +int main() { + NSArray *array = @[@"Hello", @"There", @"How Are You", [NSNumber numberWithInt:42]]; + + for (id string in array) + NSLog(@"%@\n", string); + + NSArray *array1 = @["Forgot"]; // expected-error {{string literal must be prefixed by '@' in a collection}} + + const char *blah; + NSArray *array2 = @[blah]; // expected-error{{collection element of type 'const char *' is not an Objective-C object}} +} diff --git a/test/SemaObjC/objc-container-subscripting-1.m b/test/SemaObjC/objc-container-subscripting-1.m new file mode 100644 index 0000000000..a58a7c3bda --- /dev/null +++ b/test/SemaObjC/objc-container-subscripting-1.m @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +@end + +@interface XNSMutableArray +@end + +int main() { +id array; +id oldObject = array[10]; // expected-warning {{instance method '-objectAtIndexedSubscript:' not found (return type defaults to 'id')}} + +array[10] = 0; // expected-warning {{instance method '-setObject:atIndexedSubscript:' not found (return type defaults to 'id')}} + +id<P> p_array; +oldObject = p_array[10]; // expected-warning {{instance method '-objectAtIndexedSubscript:' not found (return type defaults to 'id')}} + +p_array[10] = 0; // expected-warning {{instance method '-setObject:atIndexedSubscript:' not found (return type defaults to 'id')}} +} + diff --git a/test/SemaObjC/objc-container-subscripting-2.m b/test/SemaObjC/objc-container-subscripting-2.m new file mode 100644 index 0000000000..2564975f4d --- /dev/null +++ b/test/SemaObjC/objc-container-subscripting-2.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(size_t)index; +- (void)setObject:(id)object atIndexedSubscript:(size_t)index; +@end + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(size_t)key; +@end + +id func() { + NSMutableArray *array; + float f; + array[f] = array; // expected-error {{expected method to write dictionary element not found on object of type 'NSMutableArray *'}} + return array[3.14]; // expected-error {{expected method to read dictionary element not found on object of type 'NSMutableArray *'}} +} + +void test_unused() { + NSMutableArray *array; + array[10]; // expected-warning {{container access result unused - container access should not be used for side effects}} + + NSMutableDictionary *dict; + dict[array]; // expected-warning {{container access result unused - container access should not be used for side effects}} +} + diff --git a/test/SemaObjC/objc-container-subscripting-3.m b/test/SemaObjC/objc-container-subscripting-3.m new file mode 100644 index 0000000000..5fd1a10915 --- /dev/null +++ b/test/SemaObjC/objc-container-subscripting-3.m @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://10904488 + +@interface Test +- (int)objectAtIndexedSubscript:(int)index; // expected-note {{method 'objectAtIndexedSubscript:' declared here}} +- (void)setObject:(int)object atIndexedSubscript:(int)index; // expected-note {{parameter of type 'int' is declared here}} +@end + +@interface NSMutableDictionary +- (int)objectForKeyedSubscript:(id)key; // expected-note {{method 'objectForKeyedSubscript:' declared here}} +- (void)setObject:(int)object forKeyedSubscript:(id)key; // expected-note {{parameter of type 'int' is declared here}} +@end + +int main() { + Test *array; + int i = array[10]; // expected-error {{method for accessing array element must have Objective-C object return type instead of 'int'}} + array[2] = i; // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'int' is not an objective-C pointer type}} + + NSMutableDictionary *dict; + id key, val; + val = dict[key]; // expected-error {{method for accessing dictionary element must have Objective-C object return type instead of 'int'}} \ + // expected-warning {{incompatible integer to pointer conversion assigning to 'id' from 'int'}} + dict[key] = val; // expected-error {{method object parameter type 'int' is not object type}} +} + diff --git a/test/SemaObjC/objc-container-subscripting.m b/test/SemaObjC/objc-container-subscripting.m new file mode 100644 index 0000000000..4125bc634a --- /dev/null +++ b/test/SemaObjC/objc-container-subscripting.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef unsigned int size_t; +@protocol P @end + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(double)index; // expected-note {{parameter of type 'double' is declared here}} +- (void)setObject:(id *)object atIndexedSubscript:(void *)index; // expected-note {{parameter of type 'void *' is declared here}} \ + // expected-note {{parameter of type 'id *' is declared here}} +@end +@interface I @end + +int main() { + NSMutableArray<P> * array; + id oldObject = array[10]; // expected-error {{method index parameter type 'double' is not integral type}} + array[3] = 0; // expected-error {{method index parameter type 'void *' is not integral type}} \ + // expected-error {{cannot assign to this array because assigning method's 2nd parameter of type 'id *' is not an objective-C pointer type}} + + I* iarray; + iarray[3] = 0; // expected-error {{expected method to write array element not found on object of type 'I *'}} + I* p = iarray[4]; // expected-error {{expected method to read array element not found on object of type 'I *'}} + + oldObject = array[10]++; // expected-error {{illegal operation on objective-c container subscripting}} + oldObject = array[10]--; // expected-error {{illegal operation on objective-c container subscripting}} + oldObject = --array[10]; // expected-error {{illegal operation on objective-c container subscripting}} +} + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id*)key; // expected-note {{parameter of type 'id *' is declared here}} +- (void)setObject:(void*)object forKeyedSubscript:(id*)key; // expected-note {{parameter of type 'void *' is declared here}} \ + // expected-note {{parameter of type 'id *' is declared here}} +@end +@class NSString; + +void testDict() { + NSMutableDictionary *dictionary; + NSString *key; + id newObject, oldObject; + oldObject = dictionary[key]; // expected-error {{method key parameter type 'id *' is not object type}} + dictionary[key] = newObject; // expected-error {{method object parameter type 'void *' is not object type}} \ + // expected-error {{method key parameter type 'id *' is not object type}} +} diff --git a/test/SemaObjC/objc-literal-nsnumber.m b/test/SemaObjC/objc-literal-nsnumber.m new file mode 100644 index 0000000000..db6183e6bb --- /dev/null +++ b/test/SemaObjC/objc-literal-nsnumber.m @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s +// rdar://10111397 + +#if __LP64__ +typedef unsigned long NSUInteger; +#else +typedef unsigned int NSUInteger; +#endif + +@interface NSObject ++ (NSObject*)nsobject; +@end + +@interface NSNumber : NSObject ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithFloat:(float)value; +@end + +int main() { + NSNumber * N = @3.1415926535; // expected-error {{declaration of 'numberWithDouble:' is missing in NSNumber class}} + NSNumber *noNumber = @__objc_yes; // expected-error {{declaration of 'numberWithBool:' is missing in NSNumber class}} + NSNumber * NInt = @1000; + NSNumber * NLongDouble = @1000.0l; // expected-error{{'long double' is not a valid literal type for NSNumber}} + id character = @ 'a'; + + NSNumber *NNegativeInt = @-1000; + NSNumber *NPositiveInt = @+1000; + NSNumber *NNegativeFloat = @-1000.1f; + NSNumber *NPositiveFloat = @+1000.1f; + + int five = 5; + @-five; // expected-error{{@- must be followed by a number to form an NSNumber object}} + @+five; // expected-error{{@+ must be followed by a number to form an NSNumber object}} +} + +// Dictionary test +@class NSDictionary; + +NSDictionary *err() { + return @{@"name" : @"value"}; // expected-error {{declaration of 'dictionaryWithObjects:forKeys:count:' is missing in NSDictionary class}} +} + +@interface NSDate : NSObject ++ (NSDate *) date; +@end + +@protocol NSCopying +- copy; +@end + +@interface NSDictionary : NSObject ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt; +@end + +@interface NSString<NSCopying> +@end + +id NSUserName(); + +int Int(); + +NSDictionary * blocks() { + return @{ @"task" : ^ { return 17; } }; +} + +NSDictionary * warn() { + NSDictionary *dictionary = @{@"name" : NSUserName(), + @"date" : [NSDate date], + @"name2" : @"other", + NSObject.nsobject : @"nsobject" }; // expected-warning{{passing 'NSObject *' to parameter of incompatible type 'const id<NSCopying>'}} + NSDictionary *dictionary2 = @{@"name" : Int()}; // expected-error {{collection element of type 'int' is not an Objective-C object}} + + NSObject *o; + NSDictionary *dictionary3 = @{o : o, // expected-warning{{passing 'NSObject *' to parameter of incompatible type 'const id<NSCopying>'}} + @"date" : [NSDate date] }; + return dictionary3; +} diff --git a/test/SemaObjC/objc-literal-sig.m b/test/SemaObjC/objc-literal-sig.m new file mode 100644 index 0000000000..fb5c79fd84 --- /dev/null +++ b/test/SemaObjC/objc-literal-sig.m @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +typedef _Bool BOOL; + +@interface NSNumber @end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (int)numberWithBool:(BOOL)value; // expected-note{{method returns unexpected type 'int' (should be an object type)}} +@end + +@interface NSArray +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const int [])objects // expected-note{{first parameter has unexpected type 'const int *' (should be 'const id *')}} + count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects + forKeys:(const int [])keys // expected-note{{second parameter has unexpected type 'const int *' (should be 'const id *')}} + count:(unsigned long)cnt; +@end + +void test_sig() { + (void)@__objc_yes; // expected-error{{literal construction method 'numberWithBool:' has incompatible signature}} + id array = @[ @17 ]; // expected-error{{literal construction method 'arrayWithObjects:count:' has incompatible signature}} + id dict = @{ @"hello" : @17 }; // expected-error{{literal construction method 'dictionaryWithObjects:forKeys:count:' has incompatible signature}} +} diff --git a/test/SemaObjC/sizeof-interface.m b/test/SemaObjC/sizeof-interface.m index dd8c1fdca0..dc99befeef 100644 --- a/test/SemaObjC/sizeof-interface.m +++ b/test/SemaObjC/sizeof-interface.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -verify -fsyntax-only %s -@class I0; // expected-note 3{{forward declaration of class here}} +@class I0; // expected-note 2{{forward declaration of class here}} // rdar://6811884 int g0 = sizeof(I0); // expected-error{{invalid application of 'sizeof' to an incomplete type 'I0'}} @@ -9,7 +9,7 @@ int g0 = sizeof(I0); // expected-error{{invalid application of 'sizeof' to an in void *g3(I0 *P) { P = P+5; // expected-error {{arithmetic on a pointer to an incomplete type 'I0'}} - return &P[4]; // expected-error{{subscript of pointer to incomplete type 'I0'}} + return &P[4]; // expected-error{{expected method to read array element not found on object of type 'I0 *'}} } @@ -55,7 +55,7 @@ int bar(I0 *P) { P = 5+P; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size in non-fragile ABI}} P = P-5; // expected-error {{arithmetic on pointer to interface 'I0', which is not a constant size in non-fragile ABI}} - return P[4].x[2]; // expected-error {{subscript requires size of interface 'I0', which is not constant in non-fragile ABI}} + return P[4].x[2]; // expected-error {{expected method to read array element not found on object of type 'I0 *'}} } diff --git a/test/SemaObjCXX/literals.mm b/test/SemaObjCXX/literals.mm new file mode 100644 index 0000000000..1f6782abbe --- /dev/null +++ b/test/SemaObjCXX/literals.mm @@ -0,0 +1,178 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x -fblocks %s + +typedef unsigned char BOOL; + +@protocol NSCopying +- copy; +@end + +@interface NSObject +@end + +@interface NSNumber : NSObject <NSCopying> +-copy; +@end + +@interface NSNumber (NSNumberCreation) ++ (NSNumber *)numberWithChar:(char)value; ++ (NSNumber *)numberWithUnsignedChar:(unsigned char)value; ++ (NSNumber *)numberWithShort:(short)value; ++ (NSNumber *)numberWithUnsignedShort:(unsigned short)value; ++ (NSNumber *)numberWithInt:(int)value; ++ (NSNumber *)numberWithUnsignedInt:(unsigned int)value; ++ (NSNumber *)numberWithLong:(long)value; ++ (NSNumber *)numberWithUnsignedLong:(unsigned long)value; ++ (NSNumber *)numberWithLongLong:(long long)value; ++ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value; ++ (NSNumber *)numberWithFloat:(float)value; ++ (NSNumber *)numberWithDouble:(double)value; ++ (NSNumber *)numberWithBool:(BOOL)value; +@end + +@interface NSArray : NSObject <NSCopying> +-copy; +@end + +@interface NSArray (NSArrayCreation) ++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt; +@end + +@interface NSDictionary ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(unsigned long)cnt; +@end + +template<typename T> +struct ConvertibleTo { + operator T(); +}; + +template<typename T> +struct ExplicitlyConvertibleTo { + explicit operator T(); +}; + +template<typename T> +class PrivateConvertibleTo { +private: + operator T(); // expected-note{{declared private here}} +}; + +template<typename T> ConvertibleTo<T> makeConvertible(); + +struct X { + ConvertibleTo<id> x; + ConvertibleTo<id> get(); +}; + +template<typename T> T test_numeric_instantiation() { + return @-17.42; +} + +template id test_numeric_instantiation(); + +void test_convertibility(ConvertibleTo<NSArray*> toArray, + ConvertibleTo<id> toId, + ConvertibleTo<int (^)(int)> toBlock, + ConvertibleTo<int> toInt, + ExplicitlyConvertibleTo<NSArray *> toArrayExplicit) { + id array = @[ + toArray, + toId, + toBlock, + toInt // expected-error{{collection element of type 'ConvertibleTo<int>' is not an Objective-C object}} + ]; + id array2 = @[ toArrayExplicit ]; // expected-error{{collection element of type 'ExplicitlyConvertibleTo<NSArray *>' is not an Objective-C object}} + + id array3 = @[ + makeConvertible<id>(), + makeConvertible<id>, // expected-error{{collection element of type 'ConvertibleTo<id> ()' is not an Objective-C object}} + ]; + + X x; + id array4 = @[ x.x ]; + id array5 = @[ x.get ]; // expected-error{{reference to non-static member function must be called}} + id array6 = @[ PrivateConvertibleTo<NSArray*>() ]; // expected-error{{operator NSArray *' is a private member of 'PrivateConvertibleTo<NSArray *>'}} +} + +template<typename T> +void test_array_literals(T t) { + id arr = @[ @17, t ]; // expected-error{{collection element of type 'int' is not an Objective-C object}} +} + +template void test_array_literals(id); +template void test_array_literals(NSArray*); +template void test_array_literals(int); // expected-note{{in instantiation of function template specialization 'test_array_literals<int>' requested here}} + +template<typename T, typename U> +void test_dictionary_literals(T t, U u) { + NSObject *object; + id dict = @{ + @17 : t, // expected-error{{collection element of type 'int' is not an Objective-C object}} + u : @42 // expected-error{{collection element of type 'int' is not an Objective-C object}} + }; + + id dict2 = @{ + object : @"object" // expected-error{{cannot initialize a parameter of type 'const id<NSCopying>' with an rvalue of type 'NSObject *'}} + }; +} + +template void test_dictionary_literals(id, NSArray*); +template void test_dictionary_literals(NSArray*, id); +template void test_dictionary_literals(int, id); // expected-note{{in instantiation of function template specialization 'test_dictionary_literals<int, id>' requested here}} +template void test_dictionary_literals(id, int); // expected-note{{in instantiation of function template specialization 'test_dictionary_literals<id, int>' requested here}} + +template<typename ...Args> +void test_bad_variadic_array_literal(Args ...args) { + id arr1 = @[ args ]; // expected-error{{initializer contains unexpanded parameter pack 'args'}} +} + +template<typename ...Args> +void test_variadic_array_literal(Args ...args) { + id arr1 = @[ args... ]; // expected-error{{collection element of type 'int' is not an Objective-C object}} +} +template void test_variadic_array_literal(id); +template void test_variadic_array_literal(id, NSArray*); +template void test_variadic_array_literal(id, int, NSArray*); // expected-note{{in instantiation of function template specialization 'test_variadic_array_literal<id, int, NSArray *>' requested here}} + +template<typename ...Args> +void test_bad_variadic_dictionary_literal(Args ...args) { + id dict = @{ args : @17 }; // expected-error{{initializer contains unexpanded parameter pack 'args'}} +} + +// Test array literal pack expansions. +template<typename T, typename U> +struct pair { + T first; + U second; +}; + +template<typename T, typename ...Ts, typename ... Us> +void test_variadic_dictionary_expansion(T t, pair<Ts, Us>... key_values) { + id dict = @{ + t : key_values.second ..., // expected-error{{collection element of type 'int' is not an Objective-C object}} + key_values.first : key_values.second ..., // expected-error{{collection element of type 'float' is not an Objective-C object}} + key_values.second : t ... + }; +} + +template void test_variadic_dictionary_expansion(id, + pair<NSNumber*, id>, + pair<id, ConvertibleTo<id>>); +template void test_variadic_dictionary_expansion(NSNumber *, // expected-note{{in instantiation of function template specialization}} + pair<NSNumber*, int>, + pair<id, ConvertibleTo<id>>); +template void test_variadic_dictionary_expansion(NSNumber *, // expected-note{{in instantiation of function template specialization}} + pair<NSNumber*, id>, + pair<float, ConvertibleTo<id>>); + +// Test parsing +struct key { + static id value; +}; + +id key; +id value; + +void test_dictionary_colon() { + id dict = @{ key : value }; +} diff --git a/test/SemaObjCXX/objc-container-subscripting.mm b/test/SemaObjCXX/objc-container-subscripting.mm new file mode 100644 index 0000000000..ccbc45e3b4 --- /dev/null +++ b/test/SemaObjCXX/objc-container-subscripting.mm @@ -0,0 +1,138 @@ +// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fsyntax-only -std=c++11 -verify %s + +@class NSArray; + +@interface NSMutableDictionary +- (id)objectForKeyedSubscript:(id)key; +- (void)setObject:(id)object forKeyedSubscript:(id)key; // expected-note {{passing argument to parameter 'object' here}} +@end + +template<typename T, typename U, typename O> +void test_dictionary_subscripts(T base, U key, O obj) { + base[key] = obj; // expected-error {{expected method to write array element not found on object of type 'NSMutableDictionary *'}} \ + // expected-error {{cannot initialize a parameter of type 'id' with an lvalue of type 'int'}} + obj = base[key]; // expected-error {{expected method to read array element not found on object of type 'NSMutableDictionary *'}} \ + // expected-error {{assigning to 'int' from incompatible type 'id'}} + +} + +template void test_dictionary_subscripts(NSMutableDictionary*, id, NSArray *ns); + +template void test_dictionary_subscripts(NSMutableDictionary*, NSArray *ns, id); + +template void test_dictionary_subscripts(NSMutableDictionary*, int, id); // expected-note {{in instantiation of function template specialization 'test_dictionary_subscripts<NSMutableDictionary *, int, id>' requested here}} + +template void test_dictionary_subscripts(NSMutableDictionary*, id, int); // expected-note {{in instantiation of function template specialization 'test_dictionary_subscripts<NSMutableDictionary *, id, int>' requested here}} + + +@interface NSMutableArray +- (id)objectAtIndexedSubscript:(int)index; +- (void)setObject:(id)object atIndexedSubscript:(int)index; +@end + +template<typename T, typename U, typename O> +void test_array_subscripts(T base, U index, O obj) { + base[index] = obj; // expected-error {{expected method to write dictionary element not found on object of type 'NSMutableArray *'}} + obj = base[index]; // expected-error {{expected method to read dictionary element not found on object of type 'NSMutableArray *'}} +} + +template void test_array_subscripts(NSMutableArray *, int, id); +template void test_array_subscripts(NSMutableArray *, short, id); +enum E { e }; + +template void test_array_subscripts(NSMutableArray *, E, id); + +template void test_array_subscripts(NSMutableArray *, double, id); // expected-note {{in instantiation of function template specialization 'test_array_subscripts<NSMutableArray *, double, id>' requested here}} + +template<typename T> +struct ConvertibleTo { + operator T(); +}; + +template<typename T> +struct ExplicitlyConvertibleTo { + explicit operator T(); +}; + +template<typename T> ConvertibleTo<T> makeConvertible(); + +struct X { + ConvertibleTo<id> x; + ConvertibleTo<id> get(); +}; + +NSMutableArray *test_array_convertibility(ConvertibleTo<NSMutableArray*> toArray, + ConvertibleTo<id> toId, + ConvertibleTo<int (^)(int)> toBlock, + ConvertibleTo<int> toInt, + ExplicitlyConvertibleTo<NSMutableArray *> toArrayExplicit) { + id array; + + array[1] = toArray; + + array[4] = array[1]; + + toArrayExplicit[2] = toId; // expected-error {{type 'ExplicitlyConvertibleTo<NSMutableArray *>' does not provide a subscript operator}} + + return array[toInt]; + +} + +id test_dict_convertibility(ConvertibleTo<NSMutableDictionary*> toDict, + ConvertibleTo<id> toId, + ConvertibleTo<int (^)(int)> toBlock, + ConvertibleTo<int> toInt, + ExplicitlyConvertibleTo<NSMutableDictionary *> toDictExplicit) { + + + NSMutableDictionary *Dict; + id Id; + Dict[toId] = toBlock; + + Dict[toBlock] = toBlock; + + Dict[toBlock] = Dict[toId] = Dict[toBlock]; + + Id = toDictExplicit[toId] = Id; // expected-error {{no viable overloaded operator[] for type 'ExplicitlyConvertibleTo<NSMutableDictionary *>'}} + + return Dict[toBlock]; +} + + +template<typename ...Args> +void test_bad_variadic_array_subscripting(Args ...args) { + id arr1; + arr1[3] = args; // expected-error {{expression contains unexpanded parameter pack 'args'}} +} + +template<typename ...Args> +void test_variadic_array_subscripting(Args ...args) { + id arr[] = {args[3]...}; // which means: {a[3], b[3], c[3]}; +} + +template void test_variadic_array_subscripting(id arg1, NSMutableArray* arg2, id arg3); + +@class Key; + +template<typename Index, typename ...Args> +void test_variadic_dictionary_subscripting(Index I, Args ...args) { + id arr[] = {args[I]...}; // which means: {a[3], b[3], c[3]}; +} + +template void test_variadic_dictionary_subscripting(Key *key, id arg1, NSMutableDictionary* arg2, id arg3); + +template<int N> +id get(NSMutableArray *array) { + return array[N]; // array[N] should be a value- and instantiation-dependent ObjCSubscriptRefExpr +} + +struct WeirdIndex { + operator int(); // expected-note {{type conversion function declared here}} + operator id(); // expected-note {{type conversion function declared here}} +}; + +id FUNC(WeirdIndex w) { + NSMutableArray *array; + return array[w]; // expected-error {{indexing expression is invalid because subscript type 'WeirdIndex' has multiple type conversion functions}} +} + |