From: John McCall Date: Tue, 31 Jul 2012 05:14:30 +0000 (+0000) Subject: Introduce new queries on ObjCRuntime for how to interpret subscripts X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1503f0ddab0057e33efa21393fc0577580d6287a;p=clang Introduce new queries on ObjCRuntime for how to interpret subscripts on object pointers and whether pointer arithmetic on object pointers is supported. Make ObjFW interpret subscripts as pseudo-objects. Based on a patch by Jonathan Schleifer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161028 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/ObjCRuntime.h b/include/clang/Basic/ObjCRuntime.h index da3ecca0f8..b24fe7cd91 100644 --- a/include/clang/Basic/ObjCRuntime.h +++ b/include/clang/Basic/ObjCRuntime.h @@ -170,6 +170,34 @@ public: llvm_unreachable("bad kind"); } + /// \brief Does this runtime allow sizeof or alignof on object types? + bool allowsSizeofAlignof() const { + return isFragile(); + } + + /// \brief Does this runtime allow pointer arithmetic on objects? + /// + /// This covers +, -, ++, --, and (if isSubscriptPointerArithmetic() + /// yields true) []. + bool allowsPointerArithmetic() const { + switch (getKind()) { + case FragileMacOSX: + case GCC: + return true; + case MacOSX: + case iOS: + case GNUstep: + case ObjFW: + return false; + } + llvm_unreachable("bad kind"); + } + + /// \brief Is subscripting pointer arithmetic? + bool isSubscriptPointerArithmetic() const { + return allowsPointerArithmetic(); + } + /// \brief Does this runtime provide an objc_terminate function? /// /// This is used in handlers for exceptions during the unwind process; diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index 9993f448f7..166bb9a2c0 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -653,8 +653,11 @@ class CGObjCGNUstep : public CGObjCGNU { } }; -/// Class used when targeting the ObjFW runtime. -class CGObjCObjFW: public CGObjCGCC { +/// The ObjFW runtime, which closely follows the GCC runtime's +/// compiler ABI. Support here is due to Jonathan Schleifer, the +/// ObjFW maintainer. +class CGObjCObjFW : public CGObjCGCC { + /// Emit class references unconditionally as direct symbol references. virtual llvm::Value *GetClassNamed(CGBuilderTy &Builder, const std::string &Name, bool isWeak) { if (isWeak) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 19259b30ec..f13408e632 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2902,8 +2902,9 @@ static bool CheckObjCTraitOperandConstraints(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, UnaryExprOrTypeTrait TraitKind) { - // Reject sizeof(interface) and sizeof(interface) in 64-bit mode. - if (S.LangOpts.ObjCRuntime.isNonFragile() && T->isObjCObjectType()) { + // Reject sizeof(interface) and sizeof(interface) if the + // runtime doesn't allow it. + if (!S.LangOpts.ObjCRuntime.allowsSizeofAlignof() && T->isObjCObjectType()) { S.Diag(Loc, diag::err_sizeof_nonfragile_interface) << T << (TraitKind == UETT_SizeOf) << ArgRange; @@ -3193,6 +3194,22 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, return BuildUnaryOp(S, OpLoc, Opc, Input); } +/// \brief Diagnose if arithmetic on the given ObjC pointer is illegal. +/// +/// \return true on error +static bool checkArithmeticOnObjCPointer(Sema &S, + SourceLocation opLoc, + Expr *op) { + assert(op->getType()->isObjCObjectPointerType()); + if (S.LangOpts.ObjCRuntime.allowsPointerArithmetic()) + return false; + + S.Diag(opLoc, diag::err_arithmetic_nonfragile_interface) + << op->getType()->castAs()->getPointeeType() + << op->getSourceRange(); + return true; +} + ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, Expr *Idx, SourceLocation RLoc) { @@ -3223,7 +3240,6 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, return CreateBuiltinArraySubscriptExpr(Base, LLoc, Idx, RLoc); } - ExprResult Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, Expr *Idx, SourceLocation RLoc) { @@ -3261,13 +3277,21 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); } else if (const ObjCObjectPointerType *PTy = - LHSTy->getAs()) { + LHSTy->getAs()) { BaseExpr = LHSExp; IndexExpr = RHSExp; - Result = BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0); - if (!Result.isInvalid()) - return Owned(Result.take()); + + // Use custom logic if this should be the pseudo-object subscript + // expression. + if (!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic()) + return BuildObjCSubscriptExpression(RLoc, BaseExpr, IndexExpr, 0, 0); + ResultType = PTy->getPointeeType(); + if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) { + Diag(LLoc, diag::err_subscript_nonfragile_interface) + << ResultType << BaseExpr->getSourceRange(); + return ExprError(); + } } else if (const PointerType *PTy = RHSTy->getAs()) { // Handle the uncommon case of "123[Ptr]". BaseExpr = RHSExp; @@ -3279,6 +3303,11 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = RHSExp; IndexExpr = LHSExp; ResultType = PTy->getPointeeType(); + if (!LangOpts.ObjCRuntime.allowsPointerArithmetic()) { + Diag(LLoc, diag::err_subscript_nonfragile_interface) + << ResultType << BaseExpr->getSourceRange(); + return ExprError(); + } } else if (const VectorType *VTy = LHSTy->getAs()) { BaseExpr = LHSExp; // vectors: V[123] IndexExpr = RHSExp; @@ -3351,13 +3380,6 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, diag::err_subscript_incomplete_type, BaseExpr)) return ExprError(); - // Diagnose bad cases where we step over interface counts. - if (ResultType->isObjCObjectType() && LangOpts.ObjCRuntime.isNonFragile()) { - Diag(LLoc, diag::err_subscript_nonfragile_interface) - << ResultType << BaseExpr->getSourceRange(); - return ExprError(); - } - assert(VK == VK_RValue || LangOpts.CPlusPlus || !ResultType.isCForbiddenLValueType()); @@ -6173,17 +6195,12 @@ static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, /// \returns True if pointer has incomplete type static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, Expr *Operand) { - if ((Operand->getType()->isPointerType() && - !Operand->getType()->isDependentType()) || - Operand->getType()->isObjCObjectPointerType()) { - QualType PointeeTy = Operand->getType()->getPointeeType(); - if (S.RequireCompleteType( - Loc, PointeeTy, - diag::err_typecheck_arithmetic_incomplete_type, - PointeeTy, Operand->getSourceRange())) - return true; - } - return false; + assert(Operand->getType()->isAnyPointerType() && + !Operand->getType()->isDependentType()); + QualType PointeeTy = Operand->getType()->getPointeeType(); + return S.RequireCompleteType(Loc, PointeeTy, + diag::err_typecheck_arithmetic_incomplete_type, + PointeeTy, Operand->getSourceRange()); } /// \brief Check the validity of an arithmetic pointer operand. @@ -6254,26 +6271,14 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, return !S.getLangOpts().CPlusPlus; } - if (checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) return false; - if (checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) return false; + if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) + return false; + if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) + return false; return true; } -/// \brief Check bad cases where we step over interface counts. -static bool checkArithmethicPointerOnNonFragileABI(Sema &S, - SourceLocation OpLoc, - Expr *Op) { - assert(Op->getType()->isAnyPointerType()); - QualType PointeeTy = Op->getType()->getPointeeType(); - if (!PointeeTy->isObjCObjectType() || S.LangOpts.ObjCRuntime.isFragile()) - return true; - - S.Diag(OpLoc, diag::err_arithmetic_nonfragile_interface) - << PointeeTy << Op->getSourceRange(); - return false; -} - /// diagnoseStringPlusInt - Emit a warning when adding an integer to a string /// literal. static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, @@ -6350,13 +6355,26 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 return compType; } - // Put any potential pointer into PExp - Expr* PExp = LHS.get(), *IExp = RHS.get(); - if (IExp->getType()->isAnyPointerType()) - std::swap(PExp, IExp); + // Type-checking. Ultimately the pointer's going to be in PExp; + // note that we bias towards the LHS being the pointer. + Expr *PExp = LHS.get(), *IExp = RHS.get(); - if (!PExp->getType()->isAnyPointerType()) - return InvalidOperands(Loc, LHS, RHS); + bool isObjCPointer; + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + std::swap(PExp, IExp); + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + return InvalidOperands(Loc, LHS, RHS); + } + } + assert(PExp->getType()->isAnyPointerType()); if (!IExp->getType()->isIntegerType()) return InvalidOperands(Loc, LHS, RHS); @@ -6364,8 +6382,7 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) return QualType(); - // Diagnose bad cases where we step over interface counts. - if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, PExp)) + if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) return QualType(); // Check array bounds for pointer arithemtic @@ -6414,7 +6431,8 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, QualType lpointee = LHS.get()->getType()->getPointeeType(); // Diagnose bad cases where we step over interface counts. - if (!checkArithmethicPointerOnNonFragileABI(*this, Loc, LHS.get())) + if (LHS.get()->getType()->isObjCObjectPointerType() && + checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) return QualType(); // The result type of a pointer-int computation is the pointer type. @@ -7746,14 +7764,16 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, S.Diag(OpLoc, diag::warn_increment_bool) << Op->getSourceRange(); } else if (ResType->isRealType()) { // OK! - } else if (ResType->isAnyPointerType()) { + } else if (ResType->isPointerType()) { // C99 6.5.2.4p2, 6.5.6p2 if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) return QualType(); - - // Diagnose bad cases where we step over interface counts. - else if (!checkArithmethicPointerOnNonFragileABI(S, OpLoc, Op)) - return QualType(); + } else if (ResType->isObjCObjectPointerType()) { + // On modern runtimes, ObjC pointer arithmetic is forbidden. + // Otherwise, we just need a complete type. + if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || + checkArithmeticOnObjCPointer(S, OpLoc, Op)) + return QualType(); } else if (ResType->isAnyComplexType()) { // C99 does not support ++/-- on complex types, we allow as an extension. S.Diag(OpLoc, diag::ext_integer_increment_complex) diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index d57329b69e..638a30f13a 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -575,27 +575,33 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { return MaybeBindToTemporary(BoxedExpr); } +/// Build an ObjC subscript pseudo-object expression, given that +/// that's supported by the runtime. ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, Expr *IndexExpr, ObjCMethodDecl *getterMethod, ObjCMethodDecl *setterMethod) { - // Subscripting is only supported in the non-fragile ABI. - if (LangOpts.ObjCRuntime.isFragile()) - return ExprError(); + assert(!LangOpts.ObjCRuntime.isSubscriptPointerArithmetic()); - // If the expression is type-dependent, there's nothing for us to do. - assert ((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && - "base or index cannot have dependent type here"); + // We can't get dependent types here; our callers should have + // filtered them out. + assert((!BaseExpr->isTypeDependent() && !IndexExpr->isTypeDependent()) && + "base or index cannot have dependent type here"); + + // Filter out placeholders in the index. In theory, overloads could + // be preserved here, although that might not actually work correctly. ExprResult Result = CheckPlaceholderExpr(IndexExpr); if (Result.isInvalid()) return ExprError(); IndexExpr = Result.get(); - // Perform lvalue-to-rvalue conversion. + // Perform lvalue-to-rvalue conversion on the base. Result = DefaultLvalueConversion(BaseExpr); if (Result.isInvalid()) return ExprError(); BaseExpr = Result.get(); + + // Build the pseudo-object expression. return Owned(ObjCSubscriptRefExpr::Create(Context, BaseExpr, IndexExpr,