From eb382ec1507cf2c8c12d7443d0b67c076223aec6 Mon Sep 17 00:00:00 2001 From: Patrick Beard Date: Thu, 19 Apr 2012 00:25:12 +0000 Subject: [PATCH] Implements boxed expressions for Objective-C. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@155082 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.html | 14 +- docs/ObjectiveCLiterals.html | 100 ++++++- include/clang/AST/ExprObjC.h | 54 ++-- include/clang/AST/RecursiveASTVisitor.h | 2 +- include/clang/Basic/DiagnosticSemaKinds.td | 4 + include/clang/Basic/StmtNodes.td | 2 +- include/clang/Parse/Parser.h | 1 + include/clang/Sema/Sema.h | 21 +- include/clang/Serialization/ASTBitCodes.h | 2 +- lib/AST/ExprClassification.cpp | 2 +- lib/AST/ExprConstant.cpp | 4 +- lib/AST/ItaniumMangle.cpp | 2 +- lib/AST/StmtPrinter.cpp | 4 +- lib/AST/StmtProfile.cpp | 2 +- lib/CodeGen/CGExprScalar.cpp | 4 +- lib/CodeGen/CGObjC.cpp | 38 +-- lib/CodeGen/CodeGenFunction.h | 2 +- lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseObjc.cpp | 26 ++ lib/Rewrite/RewriteModernObjC.cpp | 46 ++-- lib/Sema/SemaExceptionSpec.cpp | 2 +- lib/Sema/SemaExprCXX.cpp | 4 +- lib/Sema/SemaExprObjC.cpp | 247 ++++++++++++++---- lib/Sema/TreeTransform.h | 20 +- lib/Serialization/ASTReaderStmt.cpp | 12 +- lib/Serialization/ASTWriter.cpp | 2 +- lib/Serialization/ASTWriterStmt.cpp | 10 +- lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +- test/CodeGenObjC/objc-literal-debugger-test.m | 16 +- test/Parser/objc-boxing.m | 26 ++ test/Rewriter/objc-modern-boxing.mm | 68 +++++ test/SemaObjC/boxing-illegal-types.m | 17 ++ test/SemaTemplate/instantiate-objc-1.mm | 21 ++ tools/libclang/CXCursor.cpp | 2 +- tools/libclang/IndexBody.cpp | 6 +- 35 files changed, 624 insertions(+), 162 deletions(-) create mode 100644 test/Parser/objc-boxing.m create mode 100644 test/Rewriter/objc-modern-boxing.mm create mode 100644 test/SemaObjC/boxing-illegal-types.m diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 68f0afc1ff..9bae334f19 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -91,7 +91,7 @@
  • Automatic reference counting
  • Enumerations with a fixed underlying type
  • Interoperability with C++11 lambdas
  • -
  • Object Literals and Subscripting
  • +
  • Object Literals and Subscripting
  • Function Overloading in C
  • @@ -1183,10 +1183,18 @@ in Objective-C++, and not in C++ with blocks, due to its use of Objective-C memory management (autorelease).

    -

    Object Literals and Subscripting

    +

    Object Literals and Subscripting

    -

    Clang provides support for Object Literals and Subscripting 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: __has_feature(objc_array_literals) tests the availability of array literals; __has_feature(objc_dictionary_literals) tests the availability of dictionary literals; __has_feature(objc_subscripting) tests the availability of object subscripting.

    +

    Clang provides support for Object Literals +and Subscripting 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: __has_feature(objc_array_literals) +tests the availability of array literals; +__has_feature(objc_dictionary_literals) tests the availability of +dictionary literals; __has_feature(objc_subscripting) tests the +availability of object subscripting.

    Function Overloading in C

    diff --git a/docs/ObjectiveCLiterals.html b/docs/ObjectiveCLiterals.html index 63b523c6df..dc2f2fff09 100644 --- a/docs/ObjectiveCLiterals.html +++ b/docs/ObjectiveCLiterals.html @@ -68,14 +68,15 @@ void main(int argc, const char *argv[]) {

    Discussion

    -NSNumber literals only support literal scalar values after the '@'. Consequently, @INT_MAX works, but @INT_MIN does not, because they are defined like this:

    +NSNumber literals only support literal scalar values after the '@'. Consequently, @INT_MAX works, but @INT_MIN does not, because they are defined like this:

     #define INT_MAX   2147483647  /* max value for an int */
     #define INT_MIN   (-2147483647-1) /* min value for an int */
     
    -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.

    +The definition of INT_MIN is not a simple literal, but a parenthesized expression. Parenthesized +expressions are supported using the boxed expression syntax, which is described in the next section.

    Because NSNumber does not currently support wrapping long double values, the use of a long double NSNumber literal (e.g. @123.23L) will be rejected by the compiler.

    @@ -95,6 +96,94 @@ The compiler implicitly converts __objc_yes and __objc_no@true and @false expressions, which are equivalent to @YES and @NO. + +

    Boxed Expressions

    + + +

    Objective-C provides a new syntax for boxing C expressions:

    + +
    +@( expression )
    +
    + +

    Expressions of scalar (numeric, enumerated, BOOL) and C string pointer types +are supported:

    + +
    +// 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:@":"];
    +
    + +

    Boxed Enums

    + +

    +Cocoa frameworks frequently define constant values using enums. Although enum values are integral, they may not be used directly as boxed literals (this avoids conflicts with future '@'-prefixed Objective-C keywords). Instead, an enum value must be placed inside a boxed expression. The following example demonstrates configuring an AVAudioRecorder using a dictionary that contains a boxed enumeration value: +

    + +
    +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];
    +}
    +
    + +

    +The expression @(AVAudioQualityMax) converts AVAudioQualityMax to an integer type, and boxes the value accordingly. If the enum has a fixed underlying type as in: +

    + +
    +typedef enum : unsigned char { Red, Green, Blue } Color;
    +NSNumber *red = @(Red), *green = @(Green), *blue = @(Blue); // => [NSNumber numberWithUnsignedChar:]
    +
    + +

    +then the fixed underlying type will be used to select the correct NSNumber creation method. +

    + +

    Boxed C Strings

    + +

    +A C string literal prefixed by the '@' token denotes an NSString literal in the same way a numeric literal prefixed by the '@' token denotes an NSNumber literal. When the type of the parenthesized expression is (char *) or (const char *), the result of the boxed expression is a pointer to an NSString object containing equivalent character data. The following example converts C-style command line arguments into NSString objects. +

    + +
    +// 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
    +    }
    +}
    +
    + +

    +As with all C pointers, character pointer expressions can involve arbitrary pointer arithmetic, therefore programmers must ensure that the character data is valid. Passing NULL as the character pointer will raise an exception at runtime. When possible, the compiler will reject NULL character pointers used in boxed expressions. +

    + +

    Availability

    + +

    This feature will be available in clang 3.2. It is not currently available in any Apple compiler.

    Container Literals

    @@ -104,9 +193,11 @@ Objective-C now supports a new expression syntax for creating immutable array an Immutable array expression:

    -

    +
    +
     NSArray *array = @[ @"Hello", NSApp, [NSNumber numberWithInt:42] ];
     
    +
    This creates an NSArray with 3 elements. The comma-separated sub-expressions of an array literal can be any Objective-C object pointer typed expression.

    @@ -309,6 +400,9 @@ Programs test for the new features by using clang's __has_feature checks. Here a Code can use also __has_feature(objc_bool) to check for the availability of numeric literals support. This checks for the new __objc_yes / __objc_no keywords, which enable the use of @YES / @NO literals.

    +

    To check whether boxed expressions are supported, use +__has_feature(objc_boxed_expressions) feature macro.

    + diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index 4bfd12c069..d59662fd93 100644 --- a/include/clang/AST/ExprObjC.h +++ b/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(Number); } - const Expr *getNumber() const { return cast(Number); } + Expr *getSubExpr() { return cast(SubExpr); } + const Expr *getSubExpr() const { return cast(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/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index f1b5171021..a2f192ba83 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/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/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a7c6ca3bbe..86d42bb72c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index e7718cd80c..5abc50683c 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -134,7 +134,7 @@ def LambdaExpr : DStmt; // Obj-C Expressions. def ObjCStringLiteral : DStmt; -def ObjCNumericLiteral : DStmt; +def ObjCBoxedExpr : DStmt; def ObjCArrayLiteral : DStmt; def ObjCDictionaryLiteral : DStmt; def ObjCEncodeExpr : DStmt; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index de62ed2def..9d9a666c9c 100644 --- a/include/clang/Parse/Parser.h +++ b/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/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8ac7c3ee01..e84a554505 100644 --- a/include/clang/Sema/Sema.h +++ b/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/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index f9bb8928a3..f177b2fd59 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/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/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index b091e19a8a..f958aded8d 100644 --- a/lib/AST/ExprClassification.cpp +++ b/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/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 66a88b065c..818548127c 100644 --- a/lib/AST/ExprConstant.cpp +++ b/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/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 0d405f1f57..cf624f6d80 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/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/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 0d1066b7e3..af8e5c792a 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/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/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index e50523ae86..e6b378e16f 100644 --- a/lib/AST/StmtProfile.cpp +++ b/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/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 18891f7492..734531f724 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/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/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index d0aa0f5567..fc274a93a8 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/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:] method. +/// EmitObjCBoxedExpr - This routine generates code to call +/// the appropriate expression boxing method. This will either be +/// one of +[NSNumber numberWith:], 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/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 83f1e2df9f..001a371002 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/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/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index fe7058570e..50388687dc 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/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/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 789a8ae7a9..dd8259964c 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/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/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp index 94fba64e17..b4da50583a 100644 --- a/lib/Rewrite/RewriteModernObjC.cpp +++ b/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 MsgExprs; SmallVector 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()->getInterface(); + // Create a call to objc_getClass(""). 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(":"), etc. // it will be the 2nd argument. SmallVector 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(userExpr)) { + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast(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 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(S)) return RewriteObjCBoolLiteralExpr(BoolLitExpr); - if (ObjCNumericLiteral *NumericLitExpr = dyn_cast(S)) - return RewriteObjCNumericLiteralExpr(NumericLitExpr); + if (ObjCBoxedExpr *BoxedExpr = dyn_cast(S)) + return RewriteObjCBoxedExpr(BoxedExpr); if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast(S)) return RewriteObjCArrayLiteralExpr(ArrayLitExpr); diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 14b24341d0..ec33f0ac8e 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/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/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index af86cb2c43..af0f971c1c 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -4522,8 +4522,8 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { ObjCMethodDecl *D = 0; if (ObjCMessageExpr *Send = dyn_cast(E)) { D = Send->getMethodDecl(); - } else if (ObjCNumericLiteral *NumLit = dyn_cast(E)) { - D = NumLit->getObjCNumericLiteralMethod(); + } else if (ObjCBoxedExpr *BoxedExpr = dyn_cast(E)) { + D = BoxedExpr->getBoxingMethod(); } else if (ObjCArrayLiteral *ArrayLit = dyn_cast(E)) { D = ArrayLit->getArrayWithObjectsMethod(); } else if (ObjCDictionaryLiteral *DictLit diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index b62d56efda..41fd112c71 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/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(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 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(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()); } @@ -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(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(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()) { + 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(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()); + StringWithUTF8StringMethod = M; + } + assert(StringWithUTF8StringMethod && + "StringWithUTF8StringMethod should not be NULL"); + } + + BoxingMethod = StringWithUTF8StringMethod; + BoxedType = NSStringPointer; + } + } else if (isa(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 + // for more details. + + // Check for a top-level character literal. + if (const CharacterLiteral *Char = + dyn_cast(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/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index a66378e517..7387eac667 100644 --- a/lib/Sema/TreeTransform.h +++ b/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::TransformObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { template ExprResult -TreeTransform::TransformObjCNumericLiteral(ObjCNumericLiteral *E) { - return SemaRef.MaybeBindToTemporary(E); +TreeTransform::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 diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 007ecee9f5..73b5ab7ec6 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/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(Record, Idx); - E->AtLoc = ReadSourceLocation(Record, Idx); + E->SubExpr = Reader.ReadSubStmt(); + E->BoxingMethod = ReadDeclAs(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/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 81c0a9dd48..e9c0596bec 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/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/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 1e31211463..89436157df 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/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/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fd9068518..abc6316538 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/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/test/CodeGenObjC/objc-literal-debugger-test.m b/test/CodeGenObjC/objc-literal-debugger-test.m index 389ef2248a..f6a6dfa853 100644 --- a/test/CodeGenObjC/objc-literal-debugger-test.m +++ b/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/test/Parser/objc-boxing.m b/test/Parser/objc-boxing.m new file mode 100644 index 0000000000..a16a137b8f --- /dev/null +++ b/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/test/Rewriter/objc-modern-boxing.mm b/test/Rewriter/objc-modern-boxing.mm new file mode 100644 index 0000000000..9a2ce681d5 --- /dev/null +++ b/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/test/SemaObjC/boxing-illegal-types.m b/test/SemaObjC/boxing-illegal-types.m new file mode 100644 index 0000000000..dcfcee8160 --- /dev/null +++ b/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/test/SemaTemplate/instantiate-objc-1.mm b/test/SemaTemplate/instantiate-objc-1.mm index 2780f8e579..1a1a86885d 100644 --- a/test/SemaTemplate/instantiate-objc-1.mm +++ b/test/SemaTemplate/instantiate-objc-1.mm @@ -46,3 +46,24 @@ template struct EncodeTest { template struct EncodeTest; template struct EncodeTest; template struct EncodeTest; + +// @() boxing expressions. +template 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; +template struct BoxingTest; +template struct BoxingTest; // expected-note {{in instantiation of member function 'BoxingTest::box' requested here}} +template struct BoxingTest; // expected-note {{in instantiation of member function 'BoxingTest::box' requested here}} diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index a298759562..678798f9aa 100644 --- a/tools/libclang/CXCursor.cpp +++ b/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/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 74a8d37d42..239dde21bd 100644 --- a/tools/libclang/IndexBody.cpp +++ b/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(), -- 2.40.0