diff options
author | Patrick Beard <pcbeard@mac.com> | 2012-04-19 00:25:12 +0000 |
---|---|---|
committer | Patrick Beard <pcbeard@mac.com> | 2012-04-19 00:25:12 +0000 |
commit | 0caa39474bfc9ca1b4ce64b3318ddaa1d8975232 (patch) | |
tree | ed4816aeb2e1961c1a24293ddf74e1c8826a0d43 /clang | |
parent | 201ba5fa00c789edd594791176f6771f10e85dd1 (diff) | |
download | bcm5719-llvm-0caa39474bfc9ca1b4ce64b3318ddaa1d8975232.tar.gz bcm5719-llvm-0caa39474bfc9ca1b4ce64b3318ddaa1d8975232.zip |
Implements boxed expressions for Objective-C. <rdar://problem/10194391>
llvm-svn: 155082
Diffstat (limited to 'clang')
35 files changed, 624 insertions, 162 deletions
diff --git a/clang/docs/LanguageExtensions.html b/clang/docs/LanguageExtensions.html index 68f0afc1ffa..9bae334f19b 100644 --- a/clang/docs/LanguageExtensions.html +++ b/clang/docs/LanguageExtensions.html @@ -91,7 +91,7 @@ <li><a href="#objc_arc">Automatic reference counting</a></li> <li><a href="#objc_fixed_enum">Enumerations with a fixed underlying type</a></li> <li><a href="#objc_lambdas">Interoperability with C++11 lambdas</a></li> - <li><a href="#object-literals-subscripting">Object Literals and Subscripting</a></li> + <li><a href="#objc_object_literals_subscripting">Object Literals and Subscripting</a></li> </ul> </li> <li><a href="#overloading-in-c">Function Overloading in C</a></li> @@ -1183,10 +1183,18 @@ in Objective-C++, and not in C++ with blocks, due to its use of Objective-C memory management (autorelease).</p> <!-- ======================================================================= --> -<h2 id="object-literals-subscripting">Object Literals and Subscripting</h2> +<h2 id="objc_object_literals_subscripting">Object Literals and Subscripting</h2> <!-- ======================================================================= --> -<p>Clang provides support for <a href="ObjectiveCLiterals.html">Object Literals and Subscripting</a> in Objective-C, which simplifies common Objective-C programming patterns, makes programs more concise, and improves the safety of container creation. There are several feature macros associated with object literals and subscripting: <code>__has_feature(objc_array_literals)</code> tests the availability of array literals; <code>__has_feature(objc_dictionary_literals)</code> tests the availability of dictionary literals; <code>__has_feature(objc_subscripting)</code> tests the availability of object subscripting.</p> +<p>Clang provides support for <a href="ObjectiveCLiterals.html">Object Literals +and Subscripting</a> in Objective-C, which simplifies common Objective-C +programming patterns, makes programs more concise, and improves the safety of +container creation. There are several feature macros associated with object +literals and subscripting: <code>__has_feature(objc_array_literals)</code> +tests the availability of array literals; +<code>__has_feature(objc_dictionary_literals)</code> tests the availability of +dictionary literals; <code>__has_feature(objc_subscripting)</code> tests the +availability of object subscripting.</p> <!-- ======================================================================= --> <h2 id="overloading-in-c">Function Overloading in C</h2> diff --git a/clang/docs/ObjectiveCLiterals.html b/clang/docs/ObjectiveCLiterals.html index 63b523c6dfc..dc2f2fff09a 100644 --- a/clang/docs/ObjectiveCLiterals.html +++ b/clang/docs/ObjectiveCLiterals.html @@ -68,14 +68,15 @@ void main(int argc, const char *argv[]) { <h3>Discussion</h3> -NSNumber literals only support literal scalar values after the '@'. Consequently, @INT_MAX works, but @INT_MIN does not, because they are defined like this:<p> +NSNumber literals only support literal scalar values after the <code>'@'</code>. Consequently, <code>@INT_MAX</code> works, but <code>@INT_MIN</code> does not, because they are defined like this:<p> <pre> #define INT_MAX 2147483647 /* max value for an int */ #define INT_MIN (-2147483647-1) /* min value for an int */ </pre> -The definition of INT_MIN is not a simple literal, but a parenthesized expression. This is by design, but may be improved in subsequent compiler releases.<p> +The definition of <code>INT_MIN</code> is not a simple literal, but a parenthesized expression. Parenthesized +expressions are supported using the <a href="#objc_boxed_expressions">boxed expression</a> syntax, which is described in the next section.<p> Because <code>NSNumber</code> does not currently support wrapping <code>long double</code> values, the use of a <code>long double NSNumber</code> literal (e.g. <code>@123.23L</code>) will be rejected by the compiler.<p> @@ -95,6 +96,94 @@ The compiler implicitly converts <code>__objc_yes</code> and <code>__objc_no</co Objective-C++ also supports <code>@true</code> and <code>@false</code> expressions, which are equivalent to <code>@YES</code> and <code>@NO</code>. +<!-- ======================================================================= --> +<h2 id="objc_boxed_expressions">Boxed Expressions</h2> +<!-- ======================================================================= --> + +<p>Objective-C provides a new syntax for boxing C expressions:</p> + +<pre> +<code>@( <em>expression</em> )</code> +</pre> + +<p>Expressions of scalar (numeric, enumerated, BOOL) and C string pointer types +are supported:</p> + +<pre> +// numbers. +NSNumber *smallestInt = @(-INT_MAX - 1); +NSNumber *piOverTwo = @(M_PI / 2); + +// enumerated types. +typedef enum { Red, Green, Blue } Color; +NSNumber *favoriteColor = @(Green); + +// strings. +NSString *path = @(getenv("PATH")); +NSArray *pathComponents = [path componentsSeparatedByString:@":"]; +</pre> + +<h3>Boxed Enums</h3> + +<p> +Cocoa frameworks frequently define constant values using <em>enums.</em> Although enum values are integral, they may not be used directly as boxed literals (this avoids conflicts with future <code>'@'</code>-prefixed Objective-C keywords). Instead, an enum value must be placed inside a boxed expression. The following example demonstrates configuring an <code>AVAudioRecorder</code> using a dictionary that contains a boxed enumeration value: +</p> + +<pre> +enum { + AVAudioQualityMin = 0, + AVAudioQualityLow = 0x20, + AVAudioQualityMedium = 0x40, + AVAudioQualityHigh = 0x60, + AVAudioQualityMax = 0x7F +}; + +- (AVAudioRecorder *)recordToFile:(NSURL *)fileURL { + NSDictionary *settings = @{ AVEncoderAudioQualityKey : @(AVAudioQualityMax) }; + return [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:NULL]; +} +</pre> + +<p> +The expression <code>@(AVAudioQualityMax)</code> converts <code>AVAudioQualityMax</code> to an integer type, and boxes the value accordingly. If the enum has a <a href="http://clang.llvm.org/docs/LanguageExtensions.html#objc_fixed_enum">fixed underlying type</a> as in: +</p> + +<pre> +typedef enum : unsigned char { Red, Green, Blue } Color; +NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:] +</pre> + +<p> +then the fixed underlying type will be used to select the correct <code>NSNumber</code> creation method. +</p> + +<h3>Boxed C Strings</h3> + +<p> +A C string literal prefixed by the <code>'@'</code> token denotes an <code>NSString</code> literal in the same way a numeric literal prefixed by the <code>'@'</code> token denotes an <code>NSNumber</code> literal. When the type of the parenthesized expression is <code>(char *)</code> or <code>(const char *)</code>, the result of the boxed expression is a pointer to an <code>NSString</code> object containing equivalent character data. The following example converts C-style command line arguments into <code>NSString</code> objects. +</p> + +<pre> +// Partition command line arguments into positional and option arguments. +NSMutableArray *args = [NSMutableArray new]; +NSMutableDictionary *options = [NSMutableArray new]; +while (--argc) { + const char *arg = *++argv; + if (strncmp(arg, "--", 2) == 0) { + options[@(arg + 2)] = @(*++argv); // --key value + } else { + [args addObject:@(arg)]; // positional argument + } +} +</pre> + +<p> +As with all C pointers, character pointer expressions can involve arbitrary pointer arithmetic, therefore programmers must ensure that the character data is valid. Passing <code>NULL</code> as the character pointer will raise an exception at runtime. When possible, the compiler will reject <code>NULL</code> character pointers used in boxed expressions. +</p> + +<h3>Availability</h3> + +<p>This feature will be available in clang 3.2. It is not currently available in any Apple compiler.</p> <h2>Container Literals</h2> @@ -104,9 +193,11 @@ Objective-C now supports a new expression syntax for creating immutable array an Immutable array expression:<p> - <pre> +<blockquote> +<pre> NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ]; </pre> +</blockquote> This creates an <code>NSArray</code> with 3 elements. The comma-separated sub-expressions of an array literal can be any Objective-C object pointer typed expression.<p> @@ -309,6 +400,9 @@ Programs test for the new features by using clang's __has_feature checks. Here a Code can use also <code>__has_feature(objc_bool)</code> to check for the availability of numeric literals support. This checks for the new <code>__objc_yes / __objc_no</code> keywords, which enable the use of <code>@YES / @NO</code> literals.<p> +<p>To check whether boxed expressions are supported, use +<code>__has_feature(objc_boxed_expressions)</code> feature macro.</p> + </div> </body> </html> diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index 4bfd12c0693..d59662fd930 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -87,43 +87,45 @@ public: 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; +/// ObjCBoxedExpr - used for generalized expression boxing. +/// as in: @(strdup("hello world")) or @(random()) +/// Also used for boxing non-parenthesized numeric literals; +/// as in: @42 or @true (c++/objc++) or @__yes (c/objc). +class ObjCBoxedExpr : public Expr { + Stmt *SubExpr; + ObjCMethodDecl *BoxingMethod; + SourceRange Range; 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) {} + ObjCBoxedExpr(Expr *E, QualType T, ObjCMethodDecl *method, + SourceRange R) + : Expr(ObjCBoxedExprClass, T, VK_RValue, OK_Ordinary, + E->isTypeDependent(), E->isValueDependent(), + E->isInstantiationDependent(), E->containsUnexpandedParameterPack()), + SubExpr(E), BoxingMethod(method), Range(R) {} + explicit ObjCBoxedExpr(EmptyShell Empty) + : Expr(ObjCBoxedExprClass, Empty) {} - Expr *getNumber() { return cast<Expr>(Number); } - const Expr *getNumber() const { return cast<Expr>(Number); } + Expr *getSubExpr() { return cast<Expr>(SubExpr); } + const Expr *getSubExpr() const { return cast<Expr>(SubExpr); } - ObjCMethodDecl *getObjCNumericLiteralMethod() const { - return ObjCNumericLiteralMethod; + ObjCMethodDecl *getBoxingMethod() const { + return BoxingMethod; } - - SourceLocation getAtLoc() const { return AtLoc; } + + SourceLocation getAtLoc() const { return Range.getBegin(); } SourceRange getSourceRange() const LLVM_READONLY { - return SourceRange(AtLoc, Number->getSourceRange().getEnd()); + return Range; } - + static bool classof(const Stmt *T) { - return T->getStmtClass() == ObjCNumericLiteralClass; + return T->getStmtClass() == ObjCBoxedExprClass; } - static bool classof(const ObjCNumericLiteral *) { return true; } + static bool classof(const ObjCBoxedExpr *) { return true; } // Iterators - child_range children() { return child_range(&Number, &Number+1); } - + child_range children() { return child_range(&SubExpr, &SubExpr+1); } + friend class ASTStmtReader; }; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index f1b51710213..a2f192ba83b 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2209,7 +2209,7 @@ DEF_TRAVERSE_STMT(FloatingLiteral, { }) DEF_TRAVERSE_STMT(ImaginaryLiteral, { }) DEF_TRAVERSE_STMT(StringLiteral, { }) DEF_TRAVERSE_STMT(ObjCStringLiteral, { }) -DEF_TRAVERSE_STMT(ObjCNumericLiteral, { }) +DEF_TRAVERSE_STMT(ObjCBoxedExpr, { }) DEF_TRAVERSE_STMT(ObjCArrayLiteral, { }) DEF_TRAVERSE_STMT(ObjCDictionaryLiteral, { }) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a7c6ca3bbef..86d42bb72ce 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1518,6 +1518,10 @@ 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_nsstring : Error< + "cannot box a string value because NSString has not been declared">; +def err_objc_illegal_boxed_expression_type : Error< + "Illegal type %0 used in a boxed expression">; def err_undeclared_nsarray : Error< "NSArray must be available to use Objective-C array literals">; def err_undeclared_nsdictionary : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index e7718cd80cb..5abc50683c9 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -134,7 +134,7 @@ def LambdaExpr : DStmt<Expr>; // Obj-C Expressions. def ObjCStringLiteral : DStmt<Expr>; -def ObjCNumericLiteral : DStmt<Expr>; +def ObjCBoxedExpr : DStmt<Expr>; def ObjCArrayLiteral : DStmt<Expr>; def ObjCDictionaryLiteral : DStmt<Expr>; def ObjCEncodeExpr : DStmt<Expr>; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index de62ed2def5..9d9a666c9c9 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1503,6 +1503,7 @@ private: ExprResult ParseObjCBooleanLiteral(SourceLocation AtLoc, bool ArgValue); ExprResult ParseObjCArrayLiteral(SourceLocation AtLoc); ExprResult ParseObjCDictionaryLiteral(SourceLocation AtLoc); + ExprResult ParseObjCBoxedExpr(SourceLocation AtLoc); ExprResult ParseObjCEncodeExpression(SourceLocation AtLoc); ExprResult ParseObjCSelectorExpression(SourceLocation AtLoc); ExprResult ParseObjCProtocolExpression(SourceLocation AtLoc); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8ac7c3ee01a..e84a554505d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -523,9 +523,21 @@ public: /// \brief The declaration of the Objective-C NSNumber class. ObjCInterfaceDecl *NSNumberDecl; + /// \brief Pointer to NSNumber type (NSNumber *). + QualType NSNumberPointer; + /// \brief The Objective-C NSNumber methods used to create NSNumber literals. ObjCMethodDecl *NSNumberLiteralMethods[NSAPI::NumNSNumberLiteralMethods]; + /// \brief The declaration of the Objective-C NSString class. + ObjCInterfaceDecl *NSStringDecl; + + /// \brief Pointer to NSString type (NSString *). + QualType NSStringPointer; + + /// \brief The declaration of the stringWithUTF8String: method. + ObjCMethodDecl *StringWithUTF8StringMethod; + /// \brief The declaration of the Objective-C NSArray class. ObjCInterfaceDecl *NSArrayDecl; @@ -3848,7 +3860,7 @@ public: ExprResult BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S); - /// BuildObjCNumericLiteral - builds an ObjCNumericLiteral AST node for the + /// BuildObjCNumericLiteral - builds an ObjCBoxedExpr 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); @@ -3856,6 +3868,13 @@ public: bool Value); ExprResult BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements); + // BuildObjCBoxedExpr - builds an ObjCBoxedExpr AST node for the + // '@' prefixed parenthesized expression. The type of the expression will + // either be "NSNumber *" or "NSString *" depending on the type of + // ValueType, which is allowed to be a built-in numeric type or + // "char *" or "const char *". + ExprResult BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr); + ExprResult BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, Expr *IndexExpr, ObjCMethodDecl *getterMethod, diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index f9bb8928a36..f177b2fd599 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1066,7 +1066,7 @@ namespace clang { /// \brief An ObjCStringLiteral record. EXPR_OBJC_STRING_LITERAL, - EXPR_OBJC_NUMERIC_LITERAL, + EXPR_OBJC_BOXED_EXPRESSION, EXPR_OBJC_ARRAY_LITERAL, EXPR_OBJC_DICTIONARY_LITERAL, diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index b091e19a8a9..f958aded8d3 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -158,7 +158,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCSelectorExprClass: case Expr::ObjCProtocolExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoolLiteralExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 66a88b065ce..818548127cf 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3073,7 +3073,7 @@ public: bool VisitUnaryAddrOf(const UnaryOperator *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } - bool VisitObjCNumericLiteral(const ObjCNumericLiteral *E) + bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { return Success(E); } bool VisitAddrLabelExpr(const AddrLabelExpr *E) { return Success(E); } @@ -6501,7 +6501,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXDependentScopeMemberExprClass: case Expr::UnresolvedMemberExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCEncodeExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0d405f1f57f..cf624f6d806 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2390,7 +2390,7 @@ recurse: case Expr::ObjCProtocolExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCSubscriptRefExprClass: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 0d1066b7e32..af8e5c792a4 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1727,9 +1727,9 @@ void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { VisitStringLiteral(Node->getString()); } -void StmtPrinter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void StmtPrinter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { OS << "@"; - Visit(E->getNumber()); + Visit(E->getSubExpr()); } void StmtPrinter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index e50523ae860..e6b378e16f0 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -981,7 +981,7 @@ void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } -void StmtProfiler::VisitObjCNumericLiteral(const ObjCNumericLiteral *E) { +void StmtProfiler::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { VisitExpr(E); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 18891f7492a..734531f724b 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -498,8 +498,8 @@ public: Value *VisitObjCStringLiteral(const ObjCStringLiteral *E) { return CGF.EmitObjCStringLiteral(E); } - Value *VisitObjCNumericLiteral(ObjCNumericLiteral *E) { - return CGF.EmitObjCNumericLiteral(E); + Value *VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + return CGF.EmitObjCBoxedExpr(E); } Value *VisitObjCArrayLiteral(ObjCArrayLiteral *E) { return CGF.EmitObjCArrayLiteral(E); diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index d0aa0f5567a..fc274a93a8a 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -51,36 +51,36 @@ 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. +/// EmitObjCBoxedExpr - This routine generates code to call +/// the appropriate expression boxing method. This will either be +/// one of +[NSNumber numberWith<Type>:], or +[NSString stringWithUTF8String:]. /// llvm::Value * -CodeGenFunction::EmitObjCNumericLiteral(const ObjCNumericLiteral *E) { +CodeGenFunction::EmitObjCBoxedExpr(const ObjCBoxedExpr *E) { // Generate the correct selector for this literal's concrete type. - const Expr *NL = E->getNumber(); + const Expr *SubExpr = E->getSubExpr(); // Get the method. - const ObjCMethodDecl *Method = E->getObjCNumericLiteralMethod(); - assert(Method && "NSNumber method is null"); - Selector Sel = Method->getSelector(); + const ObjCMethodDecl *BoxingMethod = E->getBoxingMethod(); + assert(BoxingMethod && "BoxingMethod is null"); + assert(BoxingMethod->isClassMethod() && "BoxingMethod must be a class method"); + Selector Sel = BoxingMethod->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(); + // Assumes that the method was introduced in the class that should be + // messaged (avoids pulling it out of the result type). CGObjCRuntime &Runtime = CGM.getObjCRuntime(); - llvm::Value *Receiver = Runtime.GetClass(Builder, NSNumberDecl); - - const ParmVarDecl *argDecl = *Method->param_begin(); + const ObjCInterfaceDecl *ClassDecl = BoxingMethod->getClassInterface(); + llvm::Value *Receiver = Runtime.GetClass(Builder, ClassDecl); + + const ParmVarDecl *argDecl = *BoxingMethod->param_begin(); QualType ArgQT = argDecl->getType().getUnqualifiedType(); - RValue RV = EmitAnyExpr(NL); + RValue RV = EmitAnyExpr(SubExpr); CallArgList Args; Args.add(RV, ArgQT); - + RValue result = Runtime.GenerateMessageSend(*this, ReturnValueSlot(), - ResultType, Sel, Receiver, Args, - NSNumberDecl, Method); + BoxingMethod->getResultType(), Sel, Receiver, Args, + ClassDecl, BoxingMethod); return Builder.CreateBitCast(result.getScalarVal(), ConvertType(E->getType())); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 83f1e2df9f4..001a3710020 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2264,7 +2264,7 @@ public: llvm::Value *EmitObjCProtocolExpr(const ObjCProtocolExpr *E); llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E); - llvm::Value *EmitObjCNumericLiteral(const ObjCNumericLiteral *E); + llvm::Value *EmitObjCBoxedExpr(const ObjCBoxedExpr *E); llvm::Value *EmitObjCArrayLiteral(const ObjCArrayLiteral *E); llvm::Value *EmitObjCDictionaryLiteral(const ObjCDictionaryLiteral *E); llvm::Value *EmitObjCCollectionLiteral(const Expr *E, diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index fe7058570ef..50388687dcc 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -634,6 +634,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_subscripting", LangOpts.ObjCNonFragileABI) .Case("objc_array_literals", LangOpts.ObjC2) .Case("objc_dictionary_literals", LangOpts.ObjC2) + .Case("objc_boxed_expressions", LangOpts.ObjC2) .Case("arc_cf_code_audited", true) // C11 features .Case("c_alignas", LangOpts.C11) diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 789a8ae7a93..dd8259964c6 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -2066,6 +2066,10 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { // Objective-C dictionary literal return ParsePostfixExpressionSuffix(ParseObjCDictionaryLiteral(AtLoc)); + case tok::l_paren: + // Objective-C boxed expression + return ParsePostfixExpressionSuffix(ParseObjCBoxedExpr(AtLoc)); + default: if (Tok.getIdentifierInfo() == 0) return ExprError(Diag(AtLoc, diag::err_unexpected_at)); @@ -2580,6 +2584,28 @@ ExprResult Parser::ParseObjCNumericLiteral(SourceLocation AtLoc) { return Owned(Actions.BuildObjCNumericLiteral(AtLoc, Lit.take())); } +/// ParseObjCBoxedExpr - +/// objc-box-expression: +/// @( assignment-expression ) +ExprResult +Parser::ParseObjCBoxedExpr(SourceLocation AtLoc) { + if (Tok.isNot(tok::l_paren)) + return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@"); + + BalancedDelimiterTracker T(*this, tok::l_paren); + T.consumeOpen(); + ExprResult ValueExpr(ParseAssignmentExpression()); + if (T.consumeClose()) + return ExprError(); + + // Wrap the sub-expression in a parenthesized expression, to distinguish + // a boxed expression from a literal. + SourceLocation LPLoc = T.getOpenLocation(), RPLoc = T.getCloseLocation(); + ValueExpr = Actions.ActOnParenExpr(LPLoc, RPLoc, ValueExpr.take()); + return Owned(Actions.BuildObjCBoxedExpr(SourceRange(AtLoc, RPLoc), + ValueExpr.take())); +} + ExprResult Parser::ParseObjCArrayLiteral(SourceLocation AtLoc) { ExprVector ElementExprs(Actions); // array elements. ConsumeBracket(); // consume the l_square. diff --git a/clang/lib/Rewrite/RewriteModernObjC.cpp b/clang/lib/Rewrite/RewriteModernObjC.cpp index 94fba64e17a..b4da50583a1 100644 --- a/clang/lib/Rewrite/RewriteModernObjC.cpp +++ b/clang/lib/Rewrite/RewriteModernObjC.cpp @@ -317,7 +317,7 @@ namespace { Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); - Stmt *RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp); + Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); @@ -2471,7 +2471,7 @@ Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { return PE; } -Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) { +Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { // synthesize declaration of helper functions needed in this routine. if (!SelGetUidFunctionDecl) SynthSelGetUidFunctionDecl(); @@ -2489,13 +2489,12 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) SmallVector<Expr*, 4> MsgExprs; SmallVector<Expr*, 4> ClsExprs; QualType argType = Context->getPointerType(Context->CharTy); - QualType expType = Exp->getType(); - // Create a call to objc_getClass("NSNumber"). It will be th 1st argument. - ObjCInterfaceDecl *Class = - expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + // Create a call to objc_getClass("<BoxingClass>"). It will be the 1st argument. + ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); + ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); - IdentifierInfo *clsName = Class->getIdentifier(); + IdentifierInfo *clsName = BoxingClass->getIdentifier(); ClsExprs.push_back(StringLiteral::Create(*Context, clsName->getName(), StringLiteral::Ascii, false, @@ -2506,12 +2505,11 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) StartLoc, EndLoc); MsgExprs.push_back(Cls); - // Create a call to sel_registerName("numberWithBool:"), etc. + // Create a call to sel_registerName("<BoxingMethod>:"), etc. // it will be the 2nd argument. SmallVector<Expr*, 4> SelExprs; - ObjCMethodDecl *NumericMethod = Exp->getObjCNumericLiteralMethod(); SelExprs.push_back(StringLiteral::Create(*Context, - NumericMethod->getSelector().getAsString(), + BoxingMethod->getSelector().getAsString(), StringLiteral::Ascii, false, argType, SourceLocation())); CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, @@ -2519,25 +2517,25 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) StartLoc, EndLoc); MsgExprs.push_back(SelExp); - // User provided numeric literal is the 3rd, and last, argument. - Expr *userExpr = Exp->getNumber(); - if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) { + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(subExpr)) { QualType type = ICE->getType(); const Expr *SubExpr = ICE->IgnoreParenImpCasts(); CastKind CK = CK_BitCast; if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) CK = CK_IntegralToBoolean; - userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); } - MsgExprs.push_back(userExpr); + MsgExprs.push_back(subExpr); SmallVector<QualType, 4> ArgTypes; ArgTypes.push_back(Context->getObjCIdType()); ArgTypes.push_back(Context->getObjCSelType()); - for (ObjCMethodDecl::param_iterator PI = NumericMethod->param_begin(), - E = NumericMethod->param_end(); PI != E; ++PI) + for (ObjCMethodDecl::param_iterator PI = BoxingMethod->param_begin(), + E = BoxingMethod->param_end(); PI != E; ++PI) ArgTypes.push_back((*PI)->getType()); - + QualType returnType = Exp->getType(); // Get the type, we will need to reference it in a couple spots. QualType msgSendType = MsgSendFlavor->getType(); @@ -2547,13 +2545,13 @@ Stmt *RewriteModernObjC::RewriteObjCNumericLiteralExpr(ObjCNumericLiteral *Exp) VK_LValue, SourceLocation()); CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); // Now do the "normal" pointer to function cast. QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - NumericMethod->isVariadic()); + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + BoxingMethod->isVariadic()); castType = Context->getPointerType(castType); cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, cast); @@ -5214,8 +5212,8 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast<ObjCBoolLiteralExpr>(S)) return RewriteObjCBoolLiteralExpr(BoolLitExpr); - if (ObjCNumericLiteral *NumericLitExpr = dyn_cast<ObjCNumericLiteral>(S)) - return RewriteObjCNumericLiteralExpr(NumericLitExpr); + if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(S)) + return RewriteObjCBoxedExpr(BoxedExpr); if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast<ObjCArrayLiteral>(S)) return RewriteObjCArrayLiteralExpr(ArrayLitExpr); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 14b24341d09..ec33f0ac8e6 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -964,7 +964,7 @@ CanThrowResult Sema::canThrow(const Expr *E) { // possibility. case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: return CT_Can; // Many other things have subexpressions, so we have to test those. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index af86cb2c43e..af0f971c1ca 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4522,8 +4522,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { 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 (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) { + D = BoxedExpr->getBoxingMethod(); } else if (ObjCArrayLiteral *ArrayLit = dyn_cast<ObjCArrayLiteral>(E)) { D = ArrayLit->getArrayWithObjectsMethod(); } else if (ObjCDictionaryLiteral *DictLit diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index b62d56efdab..41fd112c718 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -111,7 +111,7 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ Ty = Context.getObjCIdType(); } } else { - IdentifierInfo *NSIdent = &Context.Idents.get("NSString"); + IdentifierInfo *NSIdent = NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); NamedDecl *IF = LookupSingleName(TUScope, NSIdent, AtLoc, LookupOrdinaryName); if (ObjCInterfaceDecl *StrIF = dyn_cast_or_null<ObjCInterfaceDecl>(IF)) { @@ -143,17 +143,20 @@ ExprResult Sema::BuildObjCStringLiteral(SourceLocation AtLoc, StringLiteral *S){ /// \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) { + QualType NumberType, + bool isLiteral = false, + SourceRange R = SourceRange()) { llvm::Optional<NSAPI::NSNumberLiteralMethodKind> Kind - = S.NSAPIObj->getNSNumberFactoryMethodKind(T); + = S.NSAPIObj->getNSNumberFactoryMethodKind(NumberType); if (!Kind) { - S.Diag(Loc, diag::err_invalid_nsnumber_type) - << T << Range; + if (isLiteral) { + S.Diag(Loc, diag::err_invalid_nsnumber_type) + << NumberType << R; + } return 0; } - + // If we already looked up this method, we're done. if (S.NSNumberLiteralMethods[*Kind]) return S.NSNumberLiteralMethods[*Kind]; @@ -161,23 +164,52 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, Selector Sel = S.NSAPIObj->getNSNumberLiteralSelector(*Kind, /*Instance=*/false); + ASTContext &CX = S.Context; + + // Look up the NSNumber class, if we haven't done so already. It's cached + // in the Sema instance. + if (!S.NSNumberDecl) { + IdentifierInfo *NSNumberId = S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber); + NamedDecl *IF = S.LookupSingleName(S.TUScope, NSNumberId, + Loc, Sema::LookupOrdinaryName); + S.NSNumberDecl = dyn_cast_or_null<ObjCInterfaceDecl>(IF); + if (!S.NSNumberDecl) { + if (S.getLangOpts().DebuggerObjCLiteral) { + // Create a stub definition of NSNumber. + S.NSNumberDecl = ObjCInterfaceDecl::Create (CX, + CX.getTranslationUnitDecl(), + SourceLocation(), NSNumberId, + 0, SourceLocation()); + } else { + // Otherwise, require a declaration of NSNumber. + S.Diag(Loc, diag::err_undeclared_nsnumber); + return 0; + } + } else if (!S.NSNumberDecl->hasDefinition()) { + S.Diag(Loc, diag::err_undeclared_nsnumber); + return 0; + } + + // generate the pointer to NSNumber type. + S.NSNumberPointer = CX.getObjCObjectPointerType(CX.getObjCInterfaceType(S.NSNumberDecl)); + } + // Look for the appropriate method within NSNumber. ObjCMethodDecl *Method = S.NSNumberDecl->lookupClassMethod(Sel);; if (!Method && S.getLangOpts().DebuggerObjCLiteral) { + // create a stub definition this NSNumber factory method. 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); + Method = ObjCMethodDecl::Create(CX, SourceLocation(), SourceLocation(), Sel, + S.NSNumberPointer, ResultTInfo, S.NSNumberDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); ParmVarDecl *value = ParmVarDecl::Create(S.Context, Method, SourceLocation(), SourceLocation(), - &S.Context.Idents.get("value"), - T, /*TInfo=*/0, SC_None, SC_None, 0); + &CX.Idents.get("value"), + NumberType, /*TInfo=*/0, SC_None, SC_None, 0); Method->setMethodParams(S.Context, value, ArrayRef<SourceLocation>()); } @@ -202,29 +234,12 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, 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. +/// BuildObjCNumericLiteral - builds an ObjCBoxedExpr AST node for the +/// numeric literal expression. Type of the expression will be "NSNumber *". 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); + // compute the effective range of the literal, including the leading '@'. + SourceRange SR(AtLoc, Number->getSourceRange().getEnd()); - if (!NSNumberDecl && getLangOpts().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)) { @@ -249,29 +264,23 @@ ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { } } - 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()); - + ObjCMethodDecl *Method = getNSNumberFactoryMethod(*this, AtLoc, NumberType, + true, 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, + QualType ArgType = Method->param_begin()[0]->getType(); + ExprResult ConvertedNumber = PerformImplicitConversion(Number, ArgType, AA_Sending); if (ConvertedNumber.isInvalid()) return ExprError(); Number = ConvertedNumber.get(); return MaybeBindToTemporary( - new (Context) ObjCNumericLiteral(Number, Ty, Method, AtLoc)); + new (Context) ObjCBoxedExpr(Number, NSNumberPointer, Method, SR)); } ExprResult Sema::ActOnObjCBoolLiteral(SourceLocation AtLoc, @@ -385,6 +394,144 @@ static ExprResult CheckObjCCollectionLiteralElement(Sema &S, Expr *Element, Element->getLocStart(), Element); } +ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { + if (ValueExpr->isTypeDependent()) { + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, Context.DependentTy, NULL, SR); + return Owned(BoxedExpr); + } + ObjCMethodDecl *BoxingMethod = NULL; + QualType BoxedType; + // Convert the expression to an RValue, so we can check for pointer types... + ExprResult RValue = DefaultFunctionArrayLvalueConversion(ValueExpr); + if (RValue.isInvalid()) { + return ExprError(); + } + ValueExpr = RValue.get(); + QualType ValueType(ValueExpr->getType().getCanonicalType()); + if (const PointerType *PT = ValueType->getAs<PointerType>()) { + QualType PointeeType = PT->getPointeeType(); + if (Context.hasSameUnqualifiedType(PointeeType, Context.CharTy)) { + + if (!NSStringDecl) { + IdentifierInfo *NSStringId = + NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); + NamedDecl *Decl = LookupSingleName(TUScope, NSStringId, + SR.getBegin(), LookupOrdinaryName); + NSStringDecl = dyn_cast_or_null<ObjCInterfaceDecl>(Decl); + if (!NSStringDecl) { + if (getLangOpts().DebuggerObjCLiteral) { + // Support boxed expressions in the debugger w/o NSString declaration. + NSStringDecl = ObjCInterfaceDecl::Create(Context, + Context.getTranslationUnitDecl(), + SourceLocation(), NSStringId, + 0, SourceLocation()); + } else { + Diag(SR.getBegin(), diag::err_undeclared_nsstring); + return ExprError(); + } + } else if (!NSStringDecl->hasDefinition()) { + Diag(SR.getBegin(), diag::err_undeclared_nsstring); + return ExprError(); + } + assert(NSStringDecl && "NSStringDecl should not be NULL"); + NSStringPointer = + Context.getObjCObjectPointerType(Context.getObjCInterfaceType(NSStringDecl)); + } + + if (!StringWithUTF8StringMethod) { + IdentifierInfo *II = &Context.Idents.get("stringWithUTF8String"); + Selector stringWithUTF8String = Context.Selectors.getUnarySelector(II); + + // Look for the appropriate method within NSString. + StringWithUTF8StringMethod = NSStringDecl->lookupClassMethod(stringWithUTF8String); + if (!StringWithUTF8StringMethod && getLangOpts().DebuggerObjCLiteral) { + // Debugger needs to work even if NSString hasn't been defined. + TypeSourceInfo *ResultTInfo = 0; + ObjCMethodDecl *M = + ObjCMethodDecl::Create(Context, SourceLocation(), SourceLocation(), + stringWithUTF8String, NSStringPointer, + ResultTInfo, NSStringDecl, + /*isInstance=*/false, /*isVariadic=*/false, + /*isSynthesized=*/false, + /*isImplicitlyDeclared=*/true, + /*isDefined=*/false, + ObjCMethodDecl::Required, + /*HasRelatedResultType=*/false); + ParmVarDecl *value = + ParmVarDecl::Create(Context, M, + SourceLocation(), SourceLocation(), + &Context.Idents.get("value"), + Context.getPointerType(Context.CharTy.withConst()), + /*TInfo=*/0, + SC_None, SC_None, 0); + M->setMethodParams(Context, value, ArrayRef<SourceLocation>()); + StringWithUTF8StringMethod = M; + } + assert(StringWithUTF8StringMethod && + "StringWithUTF8StringMethod should not be NULL"); + } + + BoxingMethod = StringWithUTF8StringMethod; + BoxedType = NSStringPointer; + } + } else if (isa<BuiltinType>(ValueType)) { + // The other types we support are numeric, char and BOOL/bool. We could also + // provide limited support for structure types, such as NSRange, NSRect, and + // NSSize. See NSValue (NSValueGeometryExtensions) in <Foundation/NSGeometry.h> + // for more details. + + // Check for a top-level character literal. + if (const CharacterLiteral *Char = + dyn_cast<CharacterLiteral>(ValueExpr->IgnoreParens())) { + // 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: + ValueType = Context.CharTy; + break; + + case CharacterLiteral::Wide: + ValueType = Context.getWCharType(); + break; + + case CharacterLiteral::UTF16: + ValueType = Context.Char16Ty; + break; + + case CharacterLiteral::UTF32: + ValueType = Context.Char32Ty; + break; + } + } + + // FIXME: Do I need to do anything special with BoolTy expressions? + + // Look for the appropriate method within NSNumber. + BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), ValueType); + BoxedType = NSNumberPointer; + } + + if (!BoxingMethod) { + Diag(SR.getBegin(), diag::err_objc_illegal_boxed_expression_type) + << ValueType << ValueExpr->getSourceRange(); + return ExprError(); + } + + // Convert the expression to the type that the parameter requires. + QualType ArgType = BoxingMethod->param_begin()[0]->getType(); + ExprResult ConvertedValueExpr = PerformImplicitConversion(ValueExpr, ArgType, + AA_Sending); + if (ConvertedValueExpr.isInvalid()) + return ExprError(); + ValueExpr = ConvertedValueExpr.get(); + + ObjCBoxedExpr *BoxedExpr = + new (Context) ObjCBoxedExpr(ValueExpr, BoxedType, + BoxingMethod, SR); + return MaybeBindToTemporary(BoxedExpr); +} + ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, Expr *IndexExpr, ObjCMethodDecl *getterMethod, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a66378e5178..7387eac667b 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2225,6 +2225,14 @@ public: RParenLoc); } + /// \brief Build a new Objective-C boxed expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { + return getSema().BuildObjCBoxedExpr(SR, ValueExpr); + } + /// \brief Build a new Objective-C array literal. /// /// By default, performs semantic analysis to build the new expression. @@ -8352,8 +8360,16 @@ TreeTransform<Derived>::TransformObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { template<typename Derived> ExprResult -TreeTransform<Derived>::TransformObjCNumericLiteral(ObjCNumericLiteral *E) { - return SemaRef.MaybeBindToTemporary(E); +TreeTransform<Derived>::TransformObjCBoxedExpr(ObjCBoxedExpr *E) { + ExprResult SubExpr = getDerived().TransformExpr(E->getSubExpr()); + if (SubExpr.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + SubExpr.get() == E->getSubExpr()) + return SemaRef.Owned(E); + + return getDerived().RebuildObjCBoxedExpr(E->getSourceRange(), SubExpr.get()); } template<typename Derived> diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 007ecee9f53..73b5ab7ec6d 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -816,12 +816,12 @@ void ASTStmtReader::VisitObjCStringLiteral(ObjCStringLiteral *E) { E->setAtLoc(ReadSourceLocation(Record, Idx)); } -void ASTStmtReader::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void ASTStmtReader::VisitObjCBoxedExpr(ObjCBoxedExpr *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); + E->SubExpr = Reader.ReadSubStmt(); + E->BoxingMethod = ReadDeclAs<ObjCMethodDecl>(Record, Idx); + E->Range = ReadSourceRange(Record, Idx); } void ASTStmtReader::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { @@ -1888,8 +1888,8 @@ 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); + case EXPR_OBJC_BOXED_EXPRESSION: + S = new (Context) ObjCBoxedExpr(Empty); break; case EXPR_OBJC_ARRAY_LITERAL: S = ObjCArrayLiteral::CreateEmpty(Context, diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 81c0a9dd48a..e9c0596becb 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -696,7 +696,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream, RECORD(EXPR_BLOCK); RECORD(EXPR_GENERIC_SELECTION); RECORD(EXPR_OBJC_STRING_LITERAL); - RECORD(EXPR_OBJC_NUMERIC_LITERAL); + RECORD(EXPR_OBJC_BOXED_EXPRESSION); RECORD(EXPR_OBJC_ARRAY_LITERAL); RECORD(EXPR_OBJC_DICTIONARY_LITERAL); RECORD(EXPR_OBJC_ENCODE); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 1e312114631..89436157df9 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -777,12 +777,12 @@ void ASTStmtWriter::VisitObjCStringLiteral(ObjCStringLiteral *E) { Code = serialization::EXPR_OBJC_STRING_LITERAL; } -void ASTStmtWriter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void ASTStmtWriter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { VisitExpr(E); - Writer.AddStmt(E->getNumber()); - Writer.AddDeclRef(E->getObjCNumericLiteralMethod(), Record); - Writer.AddSourceLocation(E->getAtLoc(), Record); - Code = serialization::EXPR_OBJC_NUMERIC_LITERAL; + Writer.AddStmt(E->getSubExpr()); + Writer.AddDeclRef(E->getBoxingMethod(), Record); + Writer.AddSourceRange(E->getSourceRange(), Record); + Code = serialization::EXPR_OBJC_BOXED_EXPRESSION; } void ASTStmtWriter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fd90685186..abc63165384 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -592,7 +592,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: case Stmt::ObjCSelectorExprClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: diff --git a/clang/test/CodeGenObjC/objc-literal-debugger-test.m b/clang/test/CodeGenObjC/objc-literal-debugger-test.m index 389ef2248a4..f6a6dfa853c 100644 --- a/clang/test/CodeGenObjC/objc-literal-debugger-test.m +++ b/clang/test/CodeGenObjC/objc-literal-debugger-test.m @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdebugger-objc-literal -emit-llvm -o - %s | FileCheck %s int main() { - id l = @'a'; + // object literals. + id l; l = @'a'; l = @42; l = @-42; @@ -11,6 +12,19 @@ int main() { l = @__objc_no; l = @{ @"name":@666 }; l = @[ @"foo", @"bar" ]; + +#if __has_feature(objc_boxed_expressions) + // boxed expressions. + id b; + b = @('a'); + b = @(42); + b = @(-42); + b = @(42u); + b = @(3.141592654f); + b = @(__objc_yes); + b = @(__objc_no); + b = @("hello"); +#endif } // CHECK: declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind diff --git a/clang/test/Parser/objc-boxing.m b/clang/test/Parser/objc-boxing.m new file mode 100644 index 00000000000..a16a137b8f6 --- /dev/null +++ b/clang/test/Parser/objc-boxing.m @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +@interface NSString @end + +@interface NSString (NSStringExtensionMethods) ++ (id)stringWithUTF8String:(const char *)nullTerminatedCString; +@end + +extern char *strdup(const char *str); + +id constant_string() { + return @("boxed constant string."); +} + +id dynamic_string() { + return @(strdup("boxed dynamic string")); +} + +id const_char_pointer() { + return @((const char *)"constant character pointer"); +} + +id missing_parentheses() { + return @(5; // expected-error {{expected ')'}} \ + // expected-note {{to match this '('}} +} diff --git a/clang/test/Rewriter/objc-modern-boxing.mm b/clang/test/Rewriter/objc-modern-boxing.mm new file mode 100644 index 00000000000..9a2ce681d57 --- /dev/null +++ b/clang/test/Rewriter/objc-modern-boxing.mm @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -E %s -o %t.mm +// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o - | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -fblocks -fms-extensions -rewrite-objc %t.mm -o %t-rw.cpp +// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-address-of-temporary -D"Class=void*" -D"id=void*" -D"SEL=void*" -D"__declspec(X)=" %t-rw.cpp -Wno-attributes -Werror + +extern char *strdup(const char *str); +extern "C" void *sel_registerName(const char *); + +typedef signed char BOOL; +typedef long NSInteger; +typedef unsigned long NSUInteger; + +#if __has_feature(objc_bool) +#define YES __objc_yes +#define NO __objc_no +#else +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#endif + +@interface NSNumber ++ (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 ; +@end + +@interface NSString ++ (id)stringWithUTF8String:(const char *)str; +@end + +int main(int argc, const char *argv[]) { + // character. + NSNumber *theLetterZ = @('Z'); // equivalent to [NSNumber numberWithChar:('Z')] + + // integral. + NSNumber *fortyTwo = @(42); // equivalent to [NSNumber numberWithInt:(42)] + NSNumber *fortyTwoUnsigned = @(42U); // equivalent to [NSNumber numberWithUnsignedInt:(42U)] + NSNumber *fortyTwoLong = @(42L); // equivalent to [NSNumber numberWithLong:(42L)] + NSNumber *fortyTwoLongLong = @(42LL); // equivalent to [NSNumber numberWithLongLong:(42LL)] + + // floating point. + NSNumber *piFloat = @(3.141592654F); // equivalent to [NSNumber numberWithFloat:(3.141592654F)] + NSNumber *piDouble = @(3.1415926535); // equivalent to [NSNumber numberWithDouble:(3.1415926535)] + + // Strings. + NSString *duplicateString = @(strdup("Hello")); +} + +// CHECK: NSNumber *theLetterZ = ((NSNumber *(*)(id, SEL, char))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithChar:"), ('Z')); +// CHECK: NSNumber *fortyTwo = ((NSNumber *(*)(id, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), (42)); +// CHECK: NSNumber *fortyTwoUnsigned = ((NSNumber *(*)(id, SEL, unsigned int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithUnsignedInt:"), (42U)); +// CHECK: NSNumber *fortyTwoLong = ((NSNumber *(*)(id, SEL, long))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithLong:"), (42L)); +// CHECK: NSNumber *fortyTwoLongLong = ((NSNumber *(*)(id, SEL, long long))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithLongLong:"), (42LL)); +// CHECK: NSNumber *piFloat = ((NSNumber *(*)(id, SEL, float))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithFloat:"), (3.1415927)); +// CHECK: NSNumber *piDouble = ((NSNumber *(*)(id, SEL, double))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithDouble:"), (3.1415926535)); +// CHECK: NSString *duplicateString = ((NSString *(*)(id, SEL, const char *))(void *)objc_msgSend)(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), (const char *)(strdup("Hello"))); diff --git a/clang/test/SemaObjC/boxing-illegal-types.m b/clang/test/SemaObjC/boxing-illegal-types.m new file mode 100644 index 00000000000..dcfcee81606 --- /dev/null +++ b/clang/test/SemaObjC/boxing-illegal-types.m @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wattributes %s + +typedef struct { + int x, y, z; +} point; + +void testStruct() { + point p = { 0, 0, 0 }; + id boxed = @(p); // expected-error {{Illegal type 'point' used in a boxed expression}} +} + +void testPointers() { + void *null = 0; + id boxed_null = @(null); // expected-error {{Illegal type 'void *' used in a boxed expression}} + int numbers[] = { 0, 1, 2 }; + id boxed_numbers = @(numbers); // expected-error {{Illegal type 'int *' used in a boxed expression}} +} diff --git a/clang/test/SemaTemplate/instantiate-objc-1.mm b/clang/test/SemaTemplate/instantiate-objc-1.mm index 2780f8e5797..1a1a86885d6 100644 --- a/clang/test/SemaTemplate/instantiate-objc-1.mm +++ b/clang/test/SemaTemplate/instantiate-objc-1.mm @@ -46,3 +46,24 @@ template <typename T> struct EncodeTest { template struct EncodeTest<int>; template struct EncodeTest<double>; template struct EncodeTest<wchar_t>; + +// @() boxing expressions. +template <typename T> struct BoxingTest { + static id box(T value) { + return @(value); // expected-error {{Illegal type 'int *' used in a boxed expression}} \ + // expected-error {{Illegal type 'long double' used in a boxed expression}} + } +}; + +@interface NSNumber ++ (NSNumber *)numberWithInt:(int)value; +@end + +@interface NSString ++ (id)stringWithUTF8String:(const char *)str; +@end + +template struct BoxingTest<int>; +template struct BoxingTest<const char *>; +template struct BoxingTest<int *>; // expected-note {{in instantiation of member function 'BoxingTest<int *>::box' requested here}} +template struct BoxingTest<long double>; // expected-note {{in instantiation of member function 'BoxingTest<long double>::box' requested here}} diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index a2987595624..678798f9aa8 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -229,7 +229,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, case Stmt::VAArgExprClass: case Stmt::ObjCArrayLiteralClass: case Stmt::ObjCDictionaryLiteralClass: - case Stmt::ObjCNumericLiteralClass: + case Stmt::ObjCBoxedExprClass: case Stmt::ObjCSubscriptRefExprClass: K = CXCursor_UnexposedExpr; break; diff --git a/clang/tools/libclang/IndexBody.cpp b/clang/tools/libclang/IndexBody.cpp index 74a8d37d42f..239dde21bd4 100644 --- a/clang/tools/libclang/IndexBody.cpp +++ b/clang/tools/libclang/IndexBody.cpp @@ -90,13 +90,13 @@ public: return true; } - bool VisitObjCNumericLiteral(ObjCNumericLiteral *E) { - if (ObjCMethodDecl *MD = E->getObjCNumericLiteralMethod()) + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + if (ObjCMethodDecl *MD = E->getBoxingMethod()) IndexCtx.handleReference(MD, E->getLocStart(), Parent, ParentDC, E, CXIdxEntityRef_Implicit); return true; } - + bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) IndexCtx.handleReference(MD, E->getLocStart(), |