[cfe-commits] r155082 - in /cfe/trunk: docs/ include/clang/AST/ include/clang/Basic/ include/clang/Parse/ include/clang/Sema/ include/clang/Serialization/ lib/AST/ lib/CodeGen/ lib/Lex/ lib/Parse/ lib/Rewrite/ lib/Sema/ lib/Serialization/ lib/StaticAnalyzer/Core/ test/CodeGenObjC/ test/Parser/ test/Rewriter/ test/SemaObjC/ test/SemaTemplate/ tools/libclang/

Douglas Gregor dgregor at apple.com
Mon Apr 30 18:01:09 PDT 2012


On Apr 18, 2012, at 5:25 PM, Patrick Beard <pcbeard at mac.com> wrote:

> Author: pcbeard
> Date: Wed Apr 18 19:25:12 2012
> New Revision: 155082
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=155082&view=rev
> Log:
> Implements boxed expressions for Objective-C. <rdar://problem/10194391>

Cool. Comments below…

> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=155082&r1=155081&r2=155082&view=diff
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Apr 18 19:25:12 2012
> @@ -1518,6 +1518,10 @@
>   "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">;

Diagnostics should start with a lower-case letter (except for proper nouns, of course).

> Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=155082&r1=155081&r2=155082&view=diff
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Wed Apr 18 19:25:12 2012
> @@ -111,7 +111,7 @@
>       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 @@
> /// \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 @@
>   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));
> +  }

The creation of a stub NSNumber is a little unnerving, because this declaration isn't being added to the scope at all. If we were to later see a definition of NSNumber, it wouldn't be associated with this synthesized NSNumber, which seems rather inconsistent. At the very least, shouldn't this NSNumber declaration be turned into a definition with startDefinition()?

> -/// 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);
> -    
> -    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();
> -    }
> -  }
> -  
> +  // compute the effective range of the literal, including the leading '@'.
> +  SourceRange SR(AtLoc, Number->getSourceRange().getEnd());
> +    
>   // Determine the type of the literal.
>   QualType NumberType = Number->getType();
>   if (CharacterLiteral *Char = dyn_cast<CharacterLiteral>(Number)) {
> @@ -249,29 +264,23 @@
>     }
>   }
> 
> -  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);

PerformImplicitConversion has the nasty habit of suppressing errors. You should use PerformCopyInitialization here (with the appropriate method parameter as the entity). 

> 
> +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());

You don't need to canonicalize the type of ValueExpr to inspect it via getAs. In fact, we'd rather you not do that, because while getCanonicalType() removes *all* sugar from the type, getAs will remove the minimal amount of sugar necessary.

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

Similar comments about implicitly creating NSString. Shouldn't this "look for a class named 'foo' and synthesize it if not found" behavior be factored out into a static function?


> +      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)) {

Ah, so isa<> doesn't look through sugar. You'll can use ->isBuiltinType() here.

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

Okay.

> +  // Convert the expression to the type that the parameter requires.
> +  QualType ArgType = BoxingMethod->param_begin()[0]->getType();
> +  ExprResult ConvertedValueExpr = PerformImplicitConversion(ValueExpr, ArgType,
> +                                                            AA_Sending);

PerformCopyInitialization again here.

	- Doug





More information about the cfe-commits mailing list