diff options
40 files changed, 1674 insertions, 652 deletions
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 37a9f072af9..5d95cc81c75 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -974,6 +974,14 @@ public: /// declaration context DC. bool Encloses(const DeclContext *DC) const; + /// \brief Find the nearest non-closure ancestor of this context, + /// i.e. the innermost semantic parent of this context which is not + /// a closure. A context may be its own non-closure ancestor. + DeclContext *getNonClosureAncestor(); + const DeclContext *getNonClosureAncestor() const { + return const_cast<DeclContext*>(this)->getNonClosureAncestor(); + } + /// getPrimaryContext - There may be many different /// declarations of the same entity (including forward declarations /// of classes, multiple definitions of namespaces, etc.), each with diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index fd88e1e88e0..3ea590cd84b 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2758,6 +2758,13 @@ public: bool isCompoundAssignmentOp() const { return isCompoundAssignmentOp(getOpcode()); } + static Opcode getOpForCompoundAssignment(Opcode Opc) { + assert(isCompoundAssignmentOp(Opc)); + if (Opc >= BO_XorAssign) + return Opcode(unsigned(Opc) - BO_XorAssign + BO_Xor); + else + return Opcode(unsigned(Opc) - BO_MulAssign + BO_Mul); + } static bool isShiftAssignOp(Opcode Opc) { return Opc == BO_ShlAssign || Opc == BO_ShrAssign; @@ -4251,6 +4258,140 @@ public: child_range children() { return child_range(&SrcExpr, &SrcExpr+1); } }; +/// PseudoObjectExpr - An expression which accesses a pseudo-object +/// l-value. A pseudo-object is an abstract object, accesses to which +/// are translated to calls. The pseudo-object expression has a +/// syntactic form, which shows how the expression was actually +/// written in the source code, and a semantic form, which is a series +/// of expressions to be executed in order which detail how the +/// operation is actually evaluated. Optionally, one of the semantic +/// forms may also provide a result value for the expression. +/// +/// If any of the semantic-form expressions is an OpaqueValueExpr, +/// that OVE is required to have a source expression, and it is bound +/// to the result of that source expression. Such OVEs may appear +/// only in subsequent semantic-form expressions and as +/// sub-expressions of the syntactic form. +/// +/// PseudoObjectExpr should be used only when an operation can be +/// usefully described in terms of fairly simple rewrite rules on +/// objects and functions that are meant to be used by end-developers. +/// For example, under the Itanium ABI, dynamic casts are implemented +/// as a call to a runtime function called __dynamic_cast; using this +/// class to describe that would be inappropriate because that call is +/// not really part of the user-visible semantics, and instead the +/// cast is properly reflected in the AST and IR-generation has been +/// taught to generate the call as necessary. In contrast, an +/// Objective-C property access is semantically defined to be +/// equivalent to a particular message send, and this is very much +/// part of the user model. The name of this class encourages this +/// modelling design. +class PseudoObjectExpr : public Expr { + // PseudoObjectExprBits.NumSubExprs - The number of sub-expressions. + // Always at least two, because the first sub-expression is the + // syntactic form. + + // PseudoObjectExprBits.ResultIndex - The index of the + // sub-expression holding the result. 0 means the result is void, + // which is unambiguous because it's the index of the syntactic + // form. Note that this is therefore 1 higher than the value passed + // in to Create, which is an index within the semantic forms. + // Note also that ASTStmtWriter assumes this encoding. + + Expr **getSubExprsBuffer() { return reinterpret_cast<Expr**>(this + 1); } + const Expr * const *getSubExprsBuffer() const { + return reinterpret_cast<const Expr * const *>(this + 1); + } + + friend class ASTStmtReader; + + PseudoObjectExpr(QualType type, ExprValueKind VK, + Expr *syntactic, ArrayRef<Expr*> semantic, + unsigned resultIndex); + + PseudoObjectExpr(EmptyShell shell, unsigned numSemanticExprs); + + unsigned getNumSubExprs() const { + return PseudoObjectExprBits.NumSubExprs; + } + +public: + /// NoResult - A value for the result index indicating that there is + /// no semantic result. + enum { NoResult = ~0U }; + + static PseudoObjectExpr *Create(ASTContext &Context, Expr *syntactic, + ArrayRef<Expr*> semantic, + unsigned resultIndex); + + static PseudoObjectExpr *Create(ASTContext &Context, EmptyShell shell, + unsigned numSemanticExprs); + + /// Return the syntactic form of this expression, i.e. the + /// expression it actually looks like. Likely to be expressed in + /// terms of OpaqueValueExprs bound in the semantic form. + Expr *getSyntacticForm() { return getSubExprsBuffer()[0]; } + const Expr *getSyntacticForm() const { return getSubExprsBuffer()[0]; } + + /// Return the index of the result-bearing expression into the semantics + /// expressions, or PseudoObjectExpr::NoResult if there is none. + unsigned getResultExprIndex() const { + if (PseudoObjectExprBits.ResultIndex == 0) return NoResult; + return PseudoObjectExprBits.ResultIndex - 1; + } + + /// Return the result-bearing expression, or null if there is none. + Expr *getResultExpr() { + if (PseudoObjectExprBits.ResultIndex == 0) + return 0; + return getSubExprsBuffer()[PseudoObjectExprBits.ResultIndex]; + } + const Expr *getResultExpr() const { + return const_cast<PseudoObjectExpr*>(this)->getResultExpr(); + } + + unsigned getNumSemanticExprs() const { return getNumSubExprs() - 1; } + + typedef Expr * const *semantics_iterator; + typedef const Expr * const *const_semantics_iterator; + semantics_iterator semantics_begin() { + return getSubExprsBuffer() + 1; + } + const_semantics_iterator semantics_begin() const { + return getSubExprsBuffer() + 1; + } + semantics_iterator semantics_end() { + return getSubExprsBuffer() + getNumSubExprs(); + } + const_semantics_iterator semantics_end() const { + return getSubExprsBuffer() + getNumSubExprs(); + } + Expr *getSemanticExpr(unsigned index) { + assert(index + 1 < getNumSubExprs()); + return getSubExprsBuffer()[index + 1]; + } + const Expr *getSemanticExpr(unsigned index) const { + return const_cast<PseudoObjectExpr*>(this)->getSemanticExpr(index); + } + + SourceLocation getExprLoc() const { + return getSyntacticForm()->getExprLoc(); + } + SourceRange getSourceRange() const { + return getSyntacticForm()->getSourceRange(); + } + + child_range children() { + Stmt **cs = reinterpret_cast<Stmt**>(getSubExprsBuffer()); + return child_range(cs, cs + getNumSubExprs()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == PseudoObjectExprClass; + } + static bool classof(const PseudoObjectExpr *) { return true; } +}; + /// AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*, /// __atomic_load, __atomic_store, and __atomic_compare_exchange_*, for the /// similarly-named C++0x instructions. All of these instructions take one diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 9b65063d35a..409f83019ff 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1874,6 +1874,23 @@ TraverseGenericSelectionExpr(GenericSelectionExpr *S) { return true; } +// PseudoObjectExpr is a special case because of the wierdness with +// syntactic expressions and opaque values. +template<typename Derived> +bool RecursiveASTVisitor<Derived>:: +TraversePseudoObjectExpr(PseudoObjectExpr *S) { + TRY_TO(WalkUpFromPseudoObjectExpr(S)); + TRY_TO(TraverseStmt(S->getSyntacticForm())); + for (PseudoObjectExpr::semantics_iterator + i = S->semantics_begin(), e = S->semantics_end(); i != e; ++i) { + Expr *sub = *i; + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) + sub = OVE->getSourceExpr(); + TRY_TO(TraverseStmt(sub)); + } + return true; +} + DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, { // This is called for code like 'return T()' where T is a built-in // (i.e. non-class) type. diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 8840719413d..83836b717b2 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -146,6 +146,7 @@ protected: friend class CXXUnresolvedConstructExpr; // ctor friend class CXXDependentScopeMemberExpr; // ctor friend class OverloadExpr; // ctor + friend class PseudoObjectExpr; // ctor friend class AtomicExpr; // ctor unsigned : NumStmtBits; @@ -184,6 +185,18 @@ protected: unsigned NumPreArgs : 1; }; + class PseudoObjectExprBitfields { + friend class PseudoObjectExpr; + friend class ASTStmtReader; // deserialization + + unsigned : NumExprBits; + + // These don't need to be particularly wide, because they're + // strictly limited by the forms of expressions we permit. + unsigned NumSubExprs : 8; + unsigned ResultIndex : 32 - 8 - NumExprBits; + }; + class ObjCIndirectCopyRestoreExprBitfields { friend class ObjCIndirectCopyRestoreExpr; unsigned : NumExprBits; @@ -201,6 +214,7 @@ protected: DeclRefExprBitfields DeclRefExprBits; CastExprBitfields CastExprBits; CallExprBitfields CallExprBits; + PseudoObjectExprBitfields PseudoObjectExprBits; ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; }; diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 02aec8cfef5..6bb38d0ce7d 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -77,6 +77,7 @@ def ImplicitValueInitExpr : DStmt<Expr>; def ParenListExpr : DStmt<Expr>; def VAArgExpr : DStmt<Expr>; def GenericSelectionExpr : DStmt<Expr>; +def PseudoObjectExpr : DStmt<Expr>; // Atomic expressions def AtomicExpr : DStmt<Expr>; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 982c31d2932..fc97d290463 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1000,6 +1000,8 @@ namespace clang { EXPR_BLOCK_DECL_REF, /// \brief A GenericSelectionExpr record. EXPR_GENERIC_SELECTION, + /// \brief A PseudoObjectExpr record. + EXPR_PSEUDO_OBJECT, /// \brief An AtomicExpr record. EXPR_ATOMIC, diff --git a/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index bf2517f1198..6eb82093cf8 100644 --- a/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -160,12 +160,14 @@ private: if (!E) return false; E = E->IgnoreParenCasts(); + + // Also look through property-getter sugar. + if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) + E = pseudoOp->getResultExpr()->IgnoreImplicit(); + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); - if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) - return propE->getGetterSelector() == DelegateSel; - return false; } diff --git a/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp b/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp index 69fb2e8949e..3bfa15c6e0a 100644 --- a/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/clang/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -236,7 +236,15 @@ private: } } - if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getSubExpr())){ + Expr *subExpr = E->getSubExpr(); + + // Look through pseudo-object expressions. + if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(subExpr)) { + subExpr = pseudo->getResultExpr(); + assert(subExpr && "no result for pseudo-object of non-void type?"); + } + + if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(subExpr)) { if (implCE->getCastKind() == CK_ARCConsumeObject) return rewriteToBridgedCast(E, OBC_BridgeRetained); if (implCE->getCastKind() == CK_ARCReclaimReturnedObject) diff --git a/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp b/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp index 7c533bcf3a0..d1f08aac28c 100644 --- a/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp +++ b/clang/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp @@ -78,6 +78,15 @@ public: return true; } + bool VisitPseudoObjectExpr(PseudoObjectExpr *POE) { + if (isZeroingPropIvar(POE) && isRemovable(POE)) { + Transaction Trans(Pass.TA); + Pass.TA.removeStmt(POE); + } + + return true; + } + bool VisitBinaryOperator(BinaryOperator *BOE) { if (isZeroingPropIvar(BOE) && isRemovable(BOE)) { Transaction Trans(Pass.TA); @@ -142,17 +151,21 @@ private: } bool isZeroingPropIvar(Expr *E) { - BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E); - if (!BOE) return false; + E = E->IgnoreParens(); + if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) + return isZeroingPropIvar(BO); + if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E)) + return isZeroingPropIvar(PO); + return false; + } + bool isZeroingPropIvar(BinaryOperator *BOE) { if (BOE->getOpcode() == BO_Comma) return isZeroingPropIvar(BOE->getLHS()) && isZeroingPropIvar(BOE->getRHS()); if (BOE->getOpcode() != BO_Assign) - return false; - - ASTContext &Ctx = Pass.Ctx; + return false; Expr *LHS = BOE->getLHS(); if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) { @@ -172,25 +185,38 @@ private: if (!IvarBacksPropertySynthesis) return false; } - else if (ObjCPropertyRefExpr *PropRefExp = dyn_cast<ObjCPropertyRefExpr>(LHS)) { - // TODO: Using implicit property decl. - if (PropRefExp->isImplicitProperty()) - return false; - if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { - if (!SynthesizedProperties.count(PDecl)) - return false; - } - } else return false; - Expr *RHS = BOE->getRHS(); - bool RHSIsNull = RHS->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull); - if (RHSIsNull) + return isZero(BOE->getRHS()); + } + + bool isZeroingPropIvar(PseudoObjectExpr *PO) { + BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm()); + if (!BO) return false; + if (BO->getOpcode() != BO_Assign) return false; + + ObjCPropertyRefExpr *PropRefExp = + dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens()); + if (!PropRefExp) return false; + + // TODO: Using implicit property decl. + if (PropRefExp->isImplicitProperty()) + return false; + + if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { + if (!SynthesizedProperties.count(PDecl)) + return false; + } + + return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr()); + } + + bool isZero(Expr *E) { + if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull)) return true; - return isZeroingPropIvar(RHS); + return isZeroingPropIvar(E); } }; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index a4daea22872..4c31bbd3d76 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -640,7 +640,11 @@ void Decl::CheckAccessDeclContext() const { } DeclContext *Decl::getNonClosureContext() { - DeclContext *DC = getDeclContext(); + return getDeclContext()->getNonClosureAncestor(); +} + +DeclContext *DeclContext::getNonClosureAncestor() { + DeclContext *DC = this; // This is basically "while (DC->isClosure()) DC = DC->getParent();" // except that it's significantly more efficient to cast to a known diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 32399736887..6eb6116b01b 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1694,6 +1694,19 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, R1 = getSourceRange(); return true; + case PseudoObjectExprClass: { + const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(this); + + // Only complain about things that have the form of a getter. + if (isa<UnaryOperator>(PO->getSyntacticForm()) || + isa<BinaryOperator>(PO->getSyntacticForm())) + return false; + + Loc = getExprLoc(); + R1 = getSourceRange(); + return true; + } + case StmtExprClass: { // Statement exprs don't logically have side effects themselves, but are // sometimes used in macros in ways that give them a type that is unused. @@ -2598,6 +2611,9 @@ Expr::isNullPointerConstant(ASTContext &Ctx, } else if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(this)) { return M->GetTemporaryExpr()->isNullPointerConstant(Ctx, NPC); + } else if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(this)) { + if (const Expr *Source = OVE->getSourceExpr()) + return Source->isNullPointerConstant(Ctx, NPC); } // C++0x nullptr_t is always a null pointer constant. @@ -3306,6 +3322,72 @@ const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { return cast<OpaqueValueExpr>(e); } +PseudoObjectExpr *PseudoObjectExpr::Create(ASTContext &Context, EmptyShell sh, + unsigned numSemanticExprs) { + void *buffer = Context.Allocate(sizeof(PseudoObjectExpr) + + (1 + numSemanticExprs) * sizeof(Expr*), + llvm::alignOf<PseudoObjectExpr>()); + return new(buffer) PseudoObjectExpr(sh, numSemanticExprs); +} + +PseudoObjectExpr::PseudoObjectExpr(EmptyShell shell, unsigned numSemanticExprs) + : Expr(PseudoObjectExprClass, shell) { + PseudoObjectExprBits.NumSubExprs = numSemanticExprs + 1; +} + +PseudoObjectExpr *PseudoObjectExpr::Create(ASTContext &C, Expr *syntax, + ArrayRef<Expr*> semantics, + unsigned resultIndex) { + assert(syntax && "no syntactic expression!"); + assert(semantics.size() && "no semantic expressions!"); + + QualType type; + ExprValueKind VK; + if (resultIndex == NoResult) { + type = C.VoidTy; + VK = VK_RValue; + } else { + assert(resultIndex < semantics.size()); + type = semantics[resultIndex]->getType(); + VK = semantics[resultIndex]->getValueKind(); + assert(semantics[resultIndex]->getObjectKind() == OK_Ordinary); + } + + void *buffer = C.Allocate(sizeof(PseudoObjectExpr) + + (1 + semantics.size()) * sizeof(Expr*), + llvm::alignOf<PseudoObjectExpr>()); + return new(buffer) PseudoObjectExpr(type, VK, syntax, semantics, + resultIndex); +} + +PseudoObjectExpr::PseudoObjectExpr(QualType type, ExprValueKind VK, + Expr *syntax, ArrayRef<Expr*> semantics, + unsigned resultIndex) + : Expr(PseudoObjectExprClass, type, VK, OK_Ordinary, + /*filled in at end of ctor*/ false, false, false, false) { + PseudoObjectExprBits.NumSubExprs = semantics.size() + 1; + PseudoObjectExprBits.ResultIndex = resultIndex + 1; + + for (unsigned i = 0, e = semantics.size() + 1; i != e; ++i) { + Expr *E = (i == 0 ? syntax : semantics[i-1]); + getSubExprsBuffer()[i] = E; + + if (E->isTypeDependent()) + ExprBits.TypeDependent = true; + if (E->isValueDependent()) + ExprBits.ValueDependent = true; + if (E->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; + if (E->containsUnexpandedParameterPack()) + ExprBits.ContainsUnexpandedParameterPack = true; + + if (isa<OpaqueValueExpr>(E)) + assert(cast<OpaqueValueExpr>(E)->getSourceExpr() != 0 && + "opaque-value semantic expressions for pseudo-object " + "operations must have sources"); + } +} + //===----------------------------------------------------------------------===// // ExprIterator. //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 594ae69ec40..1251b96fbac 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -232,6 +232,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { return ClassifyExprValueKind(Lang, E, cast<OpaqueValueExpr>(E)->getValueKind()); + // Pseudo-object expressions can produce l-values with reference magic. + case Expr::PseudoObjectExprClass: + return ClassifyExprValueKind(Lang, E, + cast<PseudoObjectExpr>(E)->getValueKind()); + // Implicit casts are lvalues if they're lvalue casts. Other than that, we // only specifically record class temporaries. case Expr::ImplicitCastExprClass: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3011b7f25fa..9c9e473f7b5 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3450,6 +3450,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::AsTypeExprClass: case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::MaterializeTemporaryExprClass: + case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: return ICEDiag(2, E->getLocStart()); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 889cf513bba..b2e1d20ca01 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2257,6 +2257,7 @@ recurse: case Expr::CXXNoexceptExprClass: case Expr::CUDAKernelCallExprClass: case Expr::AsTypeExprClass: + case Expr::PseudoObjectExprClass: case Expr::AtomicExprClass: { // As bad as this diagnostic is, it's better than crashing. diff --git a/clang/lib/AST/StmtDumper.cpp b/clang/lib/AST/StmtDumper.cpp index 54e62fe35b2..22483431998 100644 --- a/clang/lib/AST/StmtDumper.cpp +++ b/clang/lib/AST/StmtDumper.cpp @@ -148,6 +148,7 @@ namespace { void VisitCompoundAssignOperator(CompoundAssignOperator *Node); void VisitAddrLabelExpr(AddrLabelExpr *Node); void VisitBlockExpr(BlockExpr *Node); + void VisitOpaqueValueExpr(OpaqueValueExpr *Node); // C++ void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node); @@ -524,6 +525,15 @@ void StmtDumper::VisitBlockExpr(BlockExpr *Node) { DumpSubTree(block->getBody()); } +void StmtDumper::VisitOpaqueValueExpr(OpaqueValueExpr *Node) { + DumpExpr(Node); + + if (Expr *Source = Node->getSourceExpr()) { + OS << '\n'; + DumpSubTree(Source); + } +} + // GNU extensions. void StmtDumper::VisitAddrLabelExpr(AddrLabelExpr *Node) { diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 5b1654c9240..04617bf9ac8 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1027,6 +1027,10 @@ void StmtPrinter::VisitVAArgExpr(VAArgExpr *Node) { OS << ")"; } +void StmtPrinter::VisitPseudoObjectExpr(PseudoObjectExpr *Node) { + PrintExpr(Node->getSyntacticForm()); +} + void StmtPrinter::VisitAtomicExpr(AtomicExpr *Node) { const char *Name = 0; switch (Node->getOp()) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index db97e59804e..5a6b771b9a8 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -475,6 +475,15 @@ void StmtProfiler::VisitGenericSelectionExpr(const GenericSelectionExpr *S) { } } +void StmtProfiler::VisitPseudoObjectExpr(const PseudoObjectExpr *S) { + VisitExpr(S); + for (PseudoObjectExpr::const_semantics_iterator + i = S->semantics_begin(), e = S->semantics_end(); i != e; ++i) + // Normally, we would not profile the source expressions of OVEs. + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(*i)) + Visit(OVE->getSourceExpr()); +} + void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) { VisitExpr(S); ID.AddInteger(S->getOp()); diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 48888f672ea..73879214f34 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -344,6 +344,7 @@ private: CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S); CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); CFGBlock *VisitReturnStmt(ReturnStmt *R); + CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, AddStmtChoice asc); CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc); @@ -981,6 +982,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::OpaqueValueExprClass: return Block; + case Stmt::PseudoObjectExprClass: + return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S)); + case Stmt::ReturnStmtClass: return VisitReturnStmt(cast<ReturnStmt>(S)); @@ -1907,6 +1911,31 @@ CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { return NYS(); } +CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + autoCreateBlock(); + + // Add the PseudoObject as the last thing. + appendStmt(Block, E); + + CFGBlock *lastBlock = Block; + + // Before that, evaluate all of the semantics in order. In + // CFG-land, that means appending them in reverse order. + for (unsigned i = E->getNumSemanticExprs(); i != 0; ) { + Expr *Semantic = E->getSemanticExpr(--i); + + // If the semantic is an opaque value, we're being asked to bind + // it to its source expression. + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Semantic)) + Semantic = OVE->getSourceExpr(); + + if (CFGBlock *B = Visit(Semantic)) + lastBlock = B; + } + + return lastBlock; +} + CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { CFGBlock *LoopSuccessor = NULL; diff --git a/clang/lib/Analysis/LiveVariables.cpp b/clang/lib/Analysis/LiveVariables.cpp index cd6f09f2a74..f0dbc532b2e 100644 --- a/clang/lib/Analysis/LiveVariables.cpp +++ b/clang/lib/Analysis/LiveVariables.cpp @@ -288,6 +288,18 @@ void TransferFunctions::Visit(Stmt *S) { } break; } + case Stmt::PseudoObjectExprClass: { + // A pseudo-object operation only directly consumes its result + // expression. + Expr *child = cast<PseudoObjectExpr>(S)->getResultExpr(); + if (!child) return; + if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child)) + child = OV->getSourceExpr(); + child = child->IgnoreParens(); + val.liveStmts = LV.SSetFact.add(val.liveStmts, child); + return; + } + // FIXME: These cases eventually shouldn't be needed. case Stmt::ExprWithCleanupsClass: { S = cast<ExprWithCleanups>(S)->getSubExpr(); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 9ad3ae8352e..d7371141f32 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -672,6 +672,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitStringLiteralLValue(cast<StringLiteral>(E)); case Expr::ObjCEncodeExprClass: return EmitObjCEncodeExprLValue(cast<ObjCEncodeExpr>(E)); + case Expr::PseudoObjectExprClass: + return EmitPseudoObjectLValue(cast<PseudoObjectExpr>(E)); case Expr::BlockDeclRefExprClass: return EmitBlockDeclRefLValue(cast<BlockDeclRefExpr>(E)); @@ -2768,3 +2770,86 @@ void CodeGenFunction::SetFPAccuracy(llvm::Value *Val, unsigned AccuracyN, cast<llvm::Instruction>(Val)->setMetadata(llvm::LLVMContext::MD_fpaccuracy, Node); } + +namespace { + struct LValueOrRValue { + LValue LV; + RValue RV; + }; +} + +static LValueOrRValue emitPseudoObjectExpr(CodeGenFunction &CGF, + const PseudoObjectExpr *E, + bool forLValue, + AggValueSlot slot) { + llvm::SmallVector<CodeGenFunction::OpaqueValueMappingData, 4> opaques; + + // Find the result expression, if any. + const Expr *resultExpr = E->getResultExpr(); + LValueOrRValue result; + + for (PseudoObjectExpr::const_semantics_iterator + i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) { + const Expr *semantic = *i; + + // If this semantic expression is an opaque value, bind it + // to the result of its source expression. + if (const OpaqueValueExpr *ov = dyn_cast<OpaqueValueExpr>(semantic)) { + + // If this is the result expression, we may need to evaluate + // directly into the slot. + typedef CodeGenFunction::OpaqueValueMappingData OVMA; + OVMA opaqueData; + if (ov == resultExpr && ov->isRValue() && !forLValue && + CodeGenFunction::hasAggregateLLVMType(ov->getType()) && + !ov->getType()->isAnyComplexType()) { + CGF.EmitAggExpr(ov->getSourceExpr(), slot); + + LValue LV = CGF.MakeAddrLValue(slot.getAddr(), ov->getType()); + opaqueData = OVMA::bind(CGF, ov, LV); + result.RV = slot.asRValue(); + + // Otherwise, emit as normal. + } else { + opaqueData = OVMA::bind(CGF, ov, ov->getSourceExpr()); + + // If this is the result, also evaluate the result now. + if (ov == resultExpr) { + if (forLValue) + result.LV = CGF.EmitLValue(ov); + else + result.RV = CGF.EmitAnyExpr(ov, slot); + } + } + + opaques.push_back(opaqueData); + + // Otherwise, if the expression is the result, evaluate it + // and remember the result. + } else if (semantic == resultExpr) { + if (forLValue) + result.LV = CGF.EmitLValue(semantic); + else + result.RV = CGF.EmitAnyExpr(semantic, slot); + + // Otherwise, evaluate the expression in an ignored context. + } else { + CGF.EmitIgnoredExpr(semantic); + } + } + + // Unbind all the opaques now. + for (unsigned i = 0, e = opaques.size(); i != e; ++i) + opaques[i].unbind(CGF); + + return result; +} + +RValue CodeGenFunction::EmitPseudoObjectRValue(const PseudoObjectExpr *E, + AggValueSlot slot) { + return emitPseudoObjectExpr(*this, E, false, slot).RV; +} + +LValue CodeGenFunction::EmitPseudoObjectLValue(const PseudoObjectExpr *E) { + return emitPseudoObjectExpr(*this, E, true, AggValueSlot::ignored()).LV; +} diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 0fa143391d6..ffbc2e0732f 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -148,6 +148,15 @@ public: void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E); void VisitOpaqueValueExpr(OpaqueValueExpr *E); + void VisitPseudoObjectExpr(PseudoObjectExpr *E) { + if (E->isGLValue()) { + LValue LV = CGF.EmitPseudoObjectLValue(E); + return EmitFinalDestCopy(E, LV); + } + + CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType())); + } + void VisitVAArgExpr(VAArgExpr *E); void EmitInitializationToLValue(Expr *E, LValue Address); diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index b6c416bc355..c09278c14ed 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -137,6 +137,10 @@ public: return CGF.getOpaqueRValueMapping(E).getComplexVal(); } + ComplexPairTy VisitPseudoObjectExpr(PseudoObjectExpr *E) { + return CGF.EmitPseudoObjectRValue(E).getComplexVal(); + } + // FIXME: CompoundLiteralExpr ComplexPairTy EmitCast(CastExpr::CastKind CK, Expr *Op, QualType DestTy); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 50c5057f3eb..83ca159aa5b 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -197,6 +197,10 @@ public: return llvm::ConstantInt::get(ConvertType(E->getType()),E->getPackLength()); } + Value *VisitPseudoObjectExpr(PseudoObjectExpr *E) { + return CGF.EmitPseudoObjectRValue(E).getScalarVal(); + } + Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) { if (E->isGLValue()) return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E)); diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 0cd98ee319b..f164103723d 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -2231,6 +2231,59 @@ static bool shouldEmitSeparateBlockRetain(const Expr *e) { return true; } +/// Try to emit a PseudoObjectExpr at +1. +/// +/// This massively duplicates emitPseudoObjectRValue. +static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF, + const PseudoObjectExpr *E) { + llvm::SmallVector<CodeGenFunction::OpaqueValueMappingData, 4> opaques; + + // Find the result expression. + const Expr *resultExpr = E->getResultExpr(); + assert(resultExpr); + TryEmitResult result; + + for (PseudoObjectExpr::const_semantics_iterator + i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) { + const Expr *semantic = *i; + + // If this semantic expression is an opaque value, bind it + // to the result of its source expression. + if (const OpaqueValueExpr *ov = dyn_cast<OpaqueValueExpr>(semantic)) { + typedef CodeGenFunction::OpaqueValueMappingData OVMA; + OVMA opaqueData; + + // If this semantic is the result of the pseudo-object + // expression, try to evaluate the source as +1. + if (ov == resultExpr) { + assert(!OVMA::shouldBindAsLValue(ov)); + result = tryEmitARCRetainScalarExpr(CGF, ov->getSourceExpr()); + opaqueData = OVMA::bind(CGF, ov, RValue::get(result.getPointer())); + + // Otherwise, just bind it. + } else { + opaqueData = OVMA::bind(CGF, ov, ov->getSourceExpr()); + } + opaques.push_back(opaqueData); + + // Otherwise, if the expression is the result, evaluate it + // and remember the result. + } else if (semantic == resultExpr) { + result = tryEmitARCRetainScalarExpr(CGF, semantic); + + // Otherwise, evaluate the expression in an ignored context. + } else { + CGF.EmitIgnoredExpr(semantic); + } + } + + // Unbind all the opaques now. + for (unsigned i = 0, e = opaques.size(); i != e; ++i) + opaques[i].unbind(CGF); + + return result; +} + static TryEmitResult tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { // Look through cleanups. @@ -2356,6 +2409,17 @@ tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { llvm::Value *result = emitARCRetainCall(CGF, e); if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); return TryEmitResult(result, true); + + // Look through pseudo-object expressions. + } else if (const PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) { + TryEmitResult result + = tryEmitARCRetainPseudoObject(CGF, pseudo); + if (resultType) { + llvm::Value *value = result.getPointer(); + value = CGF.Builder.CreateBitCast(value, resultType); + result.setPointer(value); + } + return result; } // Conservatively halt the search at any other expression kind. diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h index 489e600b3dd..bc94e8ebc9b 100644 --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -27,6 +27,7 @@ namespace clang { class ObjCPropertyRefExpr; namespace CodeGen { + class AggValueSlot; class CGBitFieldInfo; /// RValue - This trivial value class is used to represent the result of an @@ -452,7 +453,7 @@ public: RValue asRValue() const { return RValue::getAggregate(getAddr(), isVolatile()); } - + void setZeroed(bool V = true) { ZeroedFlag = V; } IsZeroed_t isZeroed() const { return IsZeroed_t(ZeroedFlag); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 4940e21c8be..0b31ce43a90 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -950,20 +950,86 @@ public: public: PeepholeProtection() : Inst(0) {} - }; + }; - /// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr. - class OpaqueValueMapping { - CodeGenFunction &CGF; + /// A non-RAII class containing all the information about a bound + /// opaque value. OpaqueValueMapping, below, is a RAII wrapper for + /// this which makes individual mappings very simple; using this + /// class directly is useful when you have a variable number of + /// opaque values or don't want the RAII functionality for some + /// reason. + class OpaqueValueMappingData { const OpaqueValueExpr *OpaqueValue; bool BoundLValue; CodeGenFunction::PeepholeProtection Protection; + OpaqueValueMappingData(const OpaqueValueExpr *ov, + bool boundLValue) + : OpaqueValue(ov), BoundLValue(boundLValue) {} public: + OpaqueValueMappingData() : OpaqueValue(0) {} + static bool shouldBindAsLValue(const Expr *expr) { return expr->isGLValue() || expr->getType()->isRecordType(); } + static OpaqueValueMappingData bind(CodeGenFunction &CGF, + const OpaqueValueExpr *ov, + const Expr *e) { + if (shouldBindAsLValue(ov)) + return bind(CGF, ov, CGF.EmitLValue(e)); + return bind(CGF, ov, CGF.EmitAnyExpr(e)); + } + + static OpaqueValueMappingData bind(CodeGenFunction &CGF, + const OpaqueValueExpr *ov, + const LValue &lv) { + assert(shouldBindAsLValue(ov)); + CGF.OpaqueLValues.insert(std::make_pair(ov, lv)); + return OpaqueValueMappingData(ov, true); + } + + static OpaqueValueMappingData bind(CodeGenFunction &CGF, + const OpaqueValueExpr *ov, + const RValue &rv) { + assert(!shouldBindAsLValue(ov)); + CGF.OpaqueRValues.insert(std::make_pair(ov, rv)); + + OpaqueValueMappingData data(ov, false); + + // Work around an extremely aggressive peephole optimization in + // EmitScalarConversion which assumes that all other uses of a + // value are extant. + data.Protection = CGF.protectFromPeepholes(rv); + + return data; + } + + bool isValid() const { return OpaqueValue != 0; } + void clear() { OpaqueValue = 0; } + + void unbind(CodeGenFunction &CGF) { + assert(OpaqueValue && "no data to unbind!"); + + if (BoundLValue) { + CGF.OpaqueLValues.erase(OpaqueValue); + } else { + CGF.OpaqueRValues.erase(OpaqueValue); + CGF.unprotectFromPeepholes(Protection); + } + } + }; + + /// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr. + class OpaqueValueMapping { + CodeGenFunction &CGF; + OpaqueValueMappingData Data; + + public: + static bool shouldBindAsLValue(const Expr *expr) { + return OpaqueValueMappingData::shouldBindAsLValue(expr); + } + /// Build the opaque value mapping for the given conditional /// operator if it's the GNU ?: extension. This is a common /// enough pattern that the convenience operator is really @@ -971,75 +1037,34 @@ public: /// OpaqueValueMapping(CodeGenFunction &CGF, const AbstractConditionalOperator *op) : CGF(CGF) { - if (isa<ConditionalOperator>(op)) { - OpaqueValue = 0; - BoundLValue = false; + if (isa<ConditionalOperator>(op)) + // Leave Data empty. return; - } const BinaryConditionalOperator *e = cast<BinaryConditionalOperator>(op); - init(e->getOpaqueValue(), e->getCommon()); + Data = OpaqueValueMappingData::bind(CGF, e->getOpaqueValue(), + e->getCommon()); } OpaqueValueMapping(CodeGenFunction &CGF, const OpaqueValueExpr *opaqueValue, LValue lvalue) - : CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(true) { - assert(opaqueValue && "no opaque value expression!"); - assert(shouldBindAsLValue(opaqueValue)); - initLValue(lvalue); + : CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, lvalue)) { } OpaqueValueMapping(CodeGenFunction &CGF, const OpaqueValueExpr *opaqueValue, RValue rvalue) - : CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(false) { - assert(opaqueValue && "no opaque value expression!"); - assert(!shouldBindAsLValue(opaqueValue)); - initRValue(rvalue); + : CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, rvalue)) { } void pop() { - assert(OpaqueValue && "mapping already popped!"); - popImpl(); - OpaqueValue = 0; + Data.unbind(CGF); + Data.clear(); } ~OpaqueValueMapping() { - if (OpaqueValue) popImpl(); - } - - private: - void popImpl() { - if (BoundLValue) - CGF.OpaqueLValues.erase(OpaqueValue); - else { - CGF.OpaqueRValues.erase(OpaqueValue); - CGF.unprotectFromPeepholes(Protection); - } - } - - void init(const OpaqueValueExpr *ov, const Expr *e) { - OpaqueValue = ov; - BoundLValue = shouldBindAsLValue(ov); - assert(BoundLValue == shouldBindAsLValue(e) - && "inconsistent expression value kinds!"); - if (BoundLValue) - initLValue(CGF.EmitLValue(e)); - else - initRValue(CGF.EmitAnyExpr(e)); - } - - void initLValue(const LValue &lv) { - CGF.OpaqueLValues.insert(std::make_pair(OpaqueValue, lv)); - } - - void initRValue(const RValue &rv) { - // Work around an extremely aggressive peephole optimization in - // EmitScalarConversion which assumes that all other uses of a - // value are extant. - Protection = CGF.protectFromPeepholes(rv); - CGF.OpaqueRValues.insert(std::make_pair(OpaqueValue, rv)); + if (Data.isValid()) Data.unbind(CGF); } }; @@ -2015,6 +2040,10 @@ public: LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); LValue EmitOpaqueValueLValue(const OpaqueValueExpr *e); + RValue EmitPseudoObjectRValue(const PseudoObjectExpr *e, + AggValueSlot slot = AggValueSlot::ignored()); + LValue EmitPseudoObjectLValue(const PseudoObjectExpr *e); + llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); LValue EmitLValueForAnonRecordField(llvm::Value* Base, diff --git a/clang/lib/Rewrite/RewriteObjC.cpp b/clang/lib/Rewrite/RewriteObjC.cpp index 1af9a6ce041..428418a09d9 100644 --- a/clang/lib/Rewrite/RewriteObjC.cpp +++ b/clang/lib/Rewrite/RewriteObjC.cpp @@ -138,12 +138,6 @@ namespace { llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; - // This maps a property to it's assignment statement. - llvm::DenseMap<Expr *, BinaryOperator *> PropSetters; - // This maps a property to it's synthesied message expression. - // This allows us to rewrite chained getters (e.g. o.a.b.c). - llvm::DenseMap<Expr *, Stmt *> PropGetters; - // This maps an original source AST to it's rewritten form. This allows // us to avoid rewriting the same node twice (which is very uncommon). // This is needed to support some of the exotic property rewriting. @@ -154,6 +148,19 @@ namespace { VarDecl *GlobalVarDecl; bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; static const int OBJC_ABI_VERSION = 7; public: @@ -186,7 +193,7 @@ namespace { return; // We can't rewrite the same node twice. if (DisableReplaceStmt) - return; // Used when rewriting the assignment of a property setter. + return; // If replacement succeeded or warning disabled return with no warning. if (!Rewrite.ReplaceStmt(Old, New)) { @@ -200,6 +207,9 @@ namespace { } void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + if (DisableReplaceStmt) + return; + // Measure the old text. int Size = Rewrite.getRangeSize(SrcRange); if (Size == -1) { @@ -282,18 +292,14 @@ namespace { // Expression Rewriting. Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); - void CollectPropertySetters(Stmt *S); Stmt *CurrentBody; ParentMap *PropParentMap; // created lazily. Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); - Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, SourceLocation OrigStart, - bool &replaced); - Stmt *RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced); - Stmt *RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr); - Stmt *RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt, - SourceRange SrcRange); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); @@ -1281,184 +1287,173 @@ void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { "/* @end */"); } -Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt, - SourceRange SrcRange) { - ObjCMethodDecl *OMD = 0; - QualType Ty; - Selector Sel; - Stmt *Receiver = 0; - bool Super = false; - QualType SuperTy; - SourceLocation SuperLocation; - SourceLocation SelectorLoc; - // Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr or ObjCImplicitSetterGetterRefExpr. - // This allows us to reuse all the fun and games in SynthMessageExpr(). - if (ObjCPropertyRefExpr *PropRefExpr = - dyn_cast<ObjCPropertyRefExpr>(BinOp->getLHS())) { - SelectorLoc = PropRefExpr->getLocation(); - if (PropRefExpr->isExplicitProperty()) { - ObjCPropertyDecl *PDecl = PropRefExpr->getExplicitProperty(); - OMD = PDecl->getSetterMethodDecl(); - Ty = PDecl->getType(); - Sel = PDecl->getSetterName(); - } else { - OMD = PropRefExpr->getImplicitPropertySetter(); - Sel = OMD->getSelector(); - Ty = (*OMD->param_begin())->getType(); - } - Super = PropRefExpr->isSuperReceiver(); - if (!Super) { - Receiver = PropRefExpr->getBase(); - } else { - SuperTy = PropRefExpr->getSuperReceiverType(); - SuperLocation = PropRefExpr->getReceiverLocation(); +Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base, *RHS; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = 0; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); } + + // Rebuild the RHS. + RHS = cast<BinaryOperator>(PseudoOp->getSyntacticForm())->getRHS(); + RHS = cast<OpaqueValueExpr>(RHS)->getSourceExpr(); + RHS = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(RHS)); + } + + // TODO: avoid this copy. + SmallVector<SourceLocation, 1> SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc()); + break; } - - assert(OMD && "RewritePropertyOrImplicitSetter - null OMD"); - - ObjCMessageExpr *MsgExpr; - if (Super) - MsgExpr = ObjCMessageExpr::Create(*Context, - Ty.getNonReferenceType(), - Expr::getValueKindForType(Ty), - /*FIXME?*/SourceLocation(), - SuperLocation, - /*IsInstanceSuper=*/true, - SuperTy, - Sel, SelectorLoc, OMD, - newStmt, - /*FIXME:*/SourceLocation()); - else { - // FIXME. Refactor this into common code with that in - // RewritePropertyOrImplicitGetter - assert(Receiver && "RewritePropertyOrImplicitSetter - null Receiver"); - if (Expr *Exp = dyn_cast<Expr>(Receiver)) - if (PropGetters[Exp]) - // This allows us to handle chain/nested property/implicit getters. - Receiver = PropGetters[Exp]; - - MsgExpr = ObjCMessageExpr::Create(*Context, - Ty.getNonReferenceType(), - Expr::getValueKindForType(Ty), - /*FIXME: */SourceLocation(), - cast<Expr>(Receiver), - Sel, SelectorLoc, OMD, - newStmt, - /*FIXME:*/SourceLocation()); - } - Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr); - // Now do the actual rewrite. - ReplaceStmtWithRange(BinOp, ReplacingStmt, SrcRange); - //delete BinOp; - // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references - // to things that stay around. - Context->Deallocate(MsgExpr); - return ReplacingStmt; + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; } -Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr) { - // Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr or ImplicitGetter. - // This allows us to reuse all the fun and games in SynthMessageExpr(). - Stmt *Receiver = 0; - ObjCMethodDecl *OMD = 0; - QualType Ty; - Selector Sel; - bool Super = false; - QualType SuperTy; - SourceLocation SuperLocation; - SourceLocation SelectorLoc; - if (ObjCPropertyRefExpr *PropRefExpr = - dyn_cast<ObjCPropertyRefExpr>(PropOrGetterRefExpr)) { - SelectorLoc = PropRefExpr->getLocation(); - if (PropRefExpr->isExplicitProperty()) { - ObjCPropertyDecl *PDecl = PropRefExpr->getExplicitProperty(); - OMD = PDecl->getGetterMethodDecl(); - Ty = PDecl->getType(); - Sel = PDecl->getGetterName(); - } else { - OMD = PropRefExpr->getImplicitPropertyGetter(); - Sel = OMD->getSelector(); - Ty = OMD->getResultType(); - } - Super = PropRefExpr->isSuperReceiver(); - if (!Super) - Receiver = PropRefExpr->getBase(); - else { - SuperTy = PropRefExpr->getSuperReceiverType(); - SuperLocation = PropRefExpr->getReceiverLocation(); +Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = 0; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); } } - - assert (OMD && "RewritePropertyOrImplicitGetter - OMD is null"); - - ObjCMessageExpr *MsgExpr; - if (Super) - MsgExpr = ObjCMessageExpr::Create(*Context, - Ty.getNonReferenceType(), - Expr::getValueKindForType(Ty), - PropOrGetterRefExpr->getLocStart(), - SuperLocation, - /*IsInstanceSuper=*/true, - SuperTy, - Sel, SelectorLoc, OMD, - ArrayRef<Expr*>(), - PropOrGetterRefExpr->getLocEnd()); - else { - assert (Receiver && "RewritePropertyOrImplicitGetter - Receiver is null"); - if (Expr *Exp = dyn_cast<Expr>(Receiver)) - if (PropGetters[Exp]) - // This allows us to handle chain/nested property/implicit getters. - Receiver = PropGetters[Exp]; - MsgExpr = ObjCMessageExpr::Create(*Context, - Ty.getNonReferenceType(), - Expr::getValueKindForType(Ty), - PropOrGetterRefExpr->getLocStart(), - cast<Expr>(Receiver), - Sel, SelectorLoc, OMD, - ArrayRef<Expr*>(), - PropOrGetterRefExpr->getLocEnd()); - } - - Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr, MsgExpr->getLocStart(), - MsgExpr->getLocEnd()); - - if (!PropParentMap) - PropParentMap = new ParentMap(CurrentBody); - bool NestedPropertyRef = false; - Stmt *Parent = PropParentMap->getParent(PropOrGetterRefExpr); - ImplicitCastExpr*ICE=0; - if (Parent) - if ((ICE = dyn_cast<ImplicitCastExpr>(Parent))) { - assert((ICE->getCastKind() == CK_GetObjCProperty) - && "RewritePropertyOrImplicitGetter"); - Parent = PropParentMap->getParent(Parent); - NestedPropertyRef = (Parent && isa<ObjCPropertyRefExpr>(Parent)); - } - if (NestedPropertyRef) { - // We stash away the ReplacingStmt since actually doing the - // replacement/rewrite won't work for nested getters (e.g. obj.p.i) - PropGetters[ICE] = ReplacingStmt; - // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references - // to things that stay around. - Context->Deallocate(MsgExpr); - return PropOrGetterRefExpr; // return the original... - } else { - ReplaceStmt(PropOrGetterRefExpr, ReplacingStmt); - // delete PropRefExpr; elsewhere... - // NOTE: We don't want to call MsgExpr->Destroy(), as it holds references - // to things that stay around. - Context->Deallocate(MsgExpr); - return ReplacingStmt; + + // Intentionally empty. + SmallVector<SourceLocation, 1> SelLocs; + SmallVector<Expr*, 1> Args; + + ObjCMessageExpr *NewMsg; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc()); + break; } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; } -Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, - SourceLocation OrigStart, - bool &replaced) { +Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + ObjCIvarDecl *D = IV->getDecl(); - const Expr *BaseExpr = IV->getBase(); + + Expr *Replacement = IV; if (CurMethodDef) { if (BaseExpr->getType()->isObjCObjectPointerType()) { const ObjCInterfaceType *iFaceDecl = @@ -1483,25 +1478,19 @@ Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, CK_BitCast, IV->getBase()); // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), - IV->getBase()->getLocEnd(), + ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), castExpr); - replaced = true; if (IV->isFreeIvar() && CurMethodDef->getClassInterface() == iFaceDecl->getDecl()) { MemberExpr *ME = new (Context) MemberExpr(PE, true, D, IV->getLocation(), D->getType(), VK_LValue, OK_Ordinary); - // delete IV; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return ME; + Replacement = ME; + } else { + IV->setBase(PE); } - // Get the new text - // Cannot delete IV->getBase(), since PE points to it. - // Replace the old base with the cast. This is important when doing - // embedded rewrites. For example, [newInv->_container addObject:0]. - IV->setBase(PE); - return IV; } } else { // we are outside a method. assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method"); @@ -1532,36 +1521,15 @@ Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, // Don't forget the parens to enforce the proper binding. ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), IV->getBase()->getLocEnd(), castExpr); - replaced = true; // Cannot delete IV->getBase(), since PE points to it. // Replace the old base with the cast. This is important when doing // embedded rewrites. For example, [newInv->_container addObject:0]. IV->setBase(PE); - return IV; } } - return IV; -} -Stmt *RewriteObjC::RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced) { - for (Stmt::child_range CI = S->children(); CI; ++CI) { - if (*CI) { - Stmt *newStmt = RewriteObjCNestedIvarRefExpr(*CI, replaced); - if (newStmt) - *CI = newStmt; - } - } - if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { - SourceRange OrigStmtRange = S->getSourceRange(); - Stmt *newStmt = RewriteObjCIvarRefExpr(IvarRefExpr, OrigStmtRange.getBegin(), - replaced); - return newStmt; - } - if (ObjCMessageExpr *MsgRefExpr = dyn_cast<ObjCMessageExpr>(S)) { - Stmt *newStmt = SynthMessageExpr(MsgRefExpr); - return newStmt; - } - return S; + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; } /// SynthCountByEnumWithState - To print: @@ -4753,6 +4721,9 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { return CondExpr; } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { CPT = IRE->getType()->getAs<BlockPointerType>(); + } else if (const PseudoObjectExpr *POE + = dyn_cast<PseudoObjectExpr>(BlockExp)) { + CPT = POE->getType()->castAs<BlockPointerType>(); } else { assert(1 && "RewriteBlockClass: Bad type"); } @@ -5580,26 +5551,6 @@ bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { // Function Body / Expression rewriting //===----------------------------------------------------------------------===// -// This is run as a first "pass" prior to RewriteFunctionBodyOrGlobalInitializer(). -// The allows the main rewrite loop to associate all ObjCPropertyRefExprs with -// their respective BinaryOperator. Without this knowledge, we'd need to rewrite -// the ObjCPropertyRefExpr twice (once as a getter, and later as a setter). -// Since the rewriter isn't capable of rewriting rewritten code, it's important -// we get this right. -void RewriteObjC::CollectPropertySetters(Stmt *S) { - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) - CollectPropertySetters(*CI); - - if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) { - if (BinOp->isAssignmentOp()) { - if (isa<ObjCPropertyRefExpr>(BinOp->getLHS())) - PropSetters[BinOp->getLHS()] = BinOp; - } - } -} - Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S) || isa<ForStmt>(S)) @@ -5609,46 +5560,28 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { ObjCBcLabelNo.push_back(++BcLabelCount); } + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) { + if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + SourceRange OrigStmtRange = S->getSourceRange(); // Perform a bottom up rewrite of all children. for (Stmt::child_range CI = S->children(); CI; ++CI) if (*CI) { - Stmt *newStmt; - Stmt *ChildStmt = (*CI); - if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(ChildStmt)) { - Expr *OldBase = IvarRefExpr->getBase(); - bool replaced = false; - newStmt = RewriteObjCNestedIvarRefExpr(ChildStmt, replaced); - if (replaced) { - if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(newStmt)) - ReplaceStmt(OldBase, IRE->getBase()); - else - ReplaceStmt(ChildStmt, newStmt); - } - } - else - newStmt = RewriteFunctionBodyOrGlobalInitializer(ChildStmt); + Stmt *childStmt = (*CI); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); if (newStmt) { - if (Expr *PropOrImplicitRefExpr = dyn_cast<Expr>(ChildStmt)) - if (PropSetters[PropOrImplicitRefExpr] == S) { - S = newStmt; - newStmt = 0; - } - if (newStmt) - *CI = newStmt; + *CI = newStmt; } - // If dealing with an assignment with LHS being a property reference - // expression, the entire assignment tree is rewritten into a property - // setter messaging. This involvs the RHS too. Do not attempt to rewrite - // RHS again. - if (Expr *Exp = dyn_cast<Expr>(ChildStmt)) - if (isa<ObjCPropertyRefExpr>(Exp)) { - if (PropSetters[Exp]) { - ++CI; - continue; - } - } } if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { @@ -5661,7 +5594,6 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { // Rewrite the block body in place. Stmt *SaveCurrentBody = CurrentBody; CurrentBody = BE->getBody(); - CollectPropertySetters(CurrentBody); PropParentMap = 0; // block literal on rhs of a property-dot-sytax assignment // must be replaced by its synthesize ast so getRewrittenText @@ -5689,67 +5621,6 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) return RewriteAtEncode(AtEncode); - if (isa<ObjCPropertyRefExpr>(S)) { - Expr *PropOrImplicitRefExpr = dyn_cast<Expr>(S); - assert(PropOrImplicitRefExpr && "Property or implicit setter/getter is null"); - - BinaryOperator *BinOp = PropSetters[PropOrImplicitRefExpr]; - if (BinOp) { - // Because the rewriter doesn't allow us to rewrite rewritten code, - // we need to rewrite the right hand side prior to rewriting the setter. - DisableReplaceStmt = true; - // Save the source range. Even if we disable the replacement, the - // rewritten node will have been inserted into the tree. If the synthesized - // node is at the 'end', the rewriter will fail. Consider this: - // self.errorHandler = handler ? handler : - // ^(NSURL *errorURL, NSError *error) { return (BOOL)1; }; - SourceRange SrcRange = BinOp->getSourceRange(); - Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(BinOp->getRHS()); - // Need to rewrite the ivar access expression if need be. - if (isa<ObjCIvarRefExpr>(newStmt)) { - bool replaced = false; - newStmt = RewriteObjCNestedIvarRefExpr(newStmt, replaced); - } - - DisableReplaceStmt = false; - // - // Unlike the main iterator, we explicily avoid changing 'BinOp'. If - // we changed the RHS of BinOp, the rewriter would fail (since it needs - // to see the original expression). Consider this example: - // - // Foo *obj1, *obj2; - // - // obj1.i = [obj2 rrrr]; - // - // 'BinOp' for the previous expression looks like: - // - // (BinaryOperator 0x231ccf0 'int' '=' - // (ObjCPropertyRefExpr 0x231cc70 'int' Kind=PropertyRef Property="i" - // (DeclRefExpr 0x231cc50 'Foo *' Var='obj1' 0x231cbb0)) - // (ObjCMessageExpr 0x231ccb0 'int' selector=rrrr - // (DeclRefExpr 0x231cc90 'Foo *' Var='obj2' 0x231cbe0))) - // - // 'newStmt' represents the rewritten message expression. For example: - // - // (CallExpr 0x231d300 'id':'struct objc_object *' - // (ParenExpr 0x231d2e0 'int (*)(id, SEL)' - // (CStyleCastExpr 0x231d2c0 'int (*)(id, SEL)' - // (CStyleCastExpr 0x231d220 'void *' - // (DeclRefExpr 0x231d200 'id (id, SEL, ...)' FunctionDecl='objc_msgSend' 0x231cdc0)))) - // - // Note that 'newStmt' is passed to RewritePropertyOrImplicitSetter so that it - // can be used as the setter argument. ReplaceStmt() will still 'see' - // the original RHS (since we haven't altered BinOp). - // - // This implies the Rewrite* routines can no longer delete the original - // node. As a result, we now leak the original AST nodes. - // - return RewritePropertyOrImplicitSetter(BinOp, dyn_cast<Expr>(newStmt), SrcRange); - } else { - return RewritePropertyOrImplicitGetter(PropOrImplicitRefExpr); - } - } - if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S)) return RewriteAtSelector(AtSelector); @@ -5930,7 +5801,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) { if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { CurFunctionDef = FD; CurFunctionDeclToDeclareForBlock = FD; - CollectPropertySetters(Body); CurrentBody = Body; Body = cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); @@ -5951,7 +5821,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) { if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (CompoundStmt *Body = MD->getCompoundBody()) { CurMethodDef = MD; - CollectPropertySetters(Body); CurrentBody = Body; Body = cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); @@ -5989,7 +5858,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) { } if (VD->getInit()) { GlobalVarDecl = VD; - CollectPropertySetters(VD->getInit()); CurrentBody = VD->getInit(); RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); CurrentBody = 0; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8cae0408f06..8a3324230d0 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4213,6 +4213,26 @@ static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) { continue; } + if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) { + // Only pay attention to pseudo-objects on property references. + ObjCPropertyRefExpr *pre + = dyn_cast<ObjCPropertyRefExpr>(pseudo->getSyntacticForm() + ->IgnoreParens()); + if (!pre) return false; + if (pre->isImplicitProperty()) return false; + ObjCPropertyDecl *property = pre->getExplicitProperty(); + if (!property->isRetaining() && + !(property->getPropertyIvarDecl() && + property->getPropertyIvarDecl()->getType() + .getObjCLifetime() == Qualifiers::OCL_Strong)) + return false; + + owner.Indirect = true; + e = const_cast<Expr*>(cast<OpaqueValueExpr>(pre->getBase()) + ->getSourceExpr()); + continue; + } + // Array ivars? return false; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index febd394eb33..c6a5f1ec6f5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7468,7 +7468,7 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp, // The operand must be either an l-value or a function designator if (!op->getType()->isFunctionType()) { // Use a special diagnostic for loads from property references. - if (isa<ObjCPropertyRefExpr>(op->IgnoreImplicit()->IgnoreParens())) { + if (isa<PseudoObjectExpr>(op)) { AddressOfError = AO_Property_Expansion; } else { // FIXME: emit more specific diag... @@ -7483,9 +7483,6 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp, } else if (op->getObjectKind() == OK_VectorComponent) { // The operand cannot be an element of a vector AddressOfError = AO_Vector_Element; - } else if (op->getObjectKind() == OK_ObjCProperty) { - // cannot take address of a property expression. - AddressOfError = AO_Property_Expansion; } else if (dcl) { // C99 6.5.3.2p1 // We have an lvalue with a decl. Make sure the decl is not declared // with the register storage-class specifier. @@ -8951,8 +8948,15 @@ static void MakeObjCStringLiteralFixItHint(Sema& SemaRef, QualType DstType, return; } - // Strip off any parens and casts. - StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr->IgnoreParenCasts()); + // Ignore any parens, implicit casts (should only be + // array-to-pointer decays), and not-so-opaque values. The last is + // important for making this trigger for property assignments. + SrcExpr = SrcExpr->IgnoreParenImpCasts(); + if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(SrcExpr)) + if (OV->getSourceExpr()) + SrcExpr = OV->getSourceExpr()->IgnoreParenImpCasts(); + + StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr); if (!SL || !SL->isAscii()) return; diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 019dc817643..a9179dcf7fd 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -467,14 +467,13 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, bool Sema::isSelfExpr(Expr *receiver) { // 'self' is objc 'self' in an objc method only. - DeclContext *DC = CurContext; - while (isa<BlockDecl>(DC)) - DC = DC->getParent(); - if (DC && !isa<ObjCMethodDecl>(DC)) - return false; + ObjCMethodDecl *method = + dyn_cast<ObjCMethodDecl>(CurContext->getNonClosureAncestor()); + if (!method) return false; + receiver = receiver->IgnoreParenLValueCasts(); if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver)) - if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self")) + if (DRE->getDecl() == method->getSelfDecl()) return true; return false; } @@ -1725,6 +1724,12 @@ namespace { return merge(left, Visit(e->getFalseExpr())); } + /// Look through pseudo-objects. + ACCResult VisitPseudoObjectExpr(PseudoObjectExpr *e) { + // If we're getting here, we should always have a result. + return Visit(e->getResultExpr()); + } + /// Statement expressions are okay if their result expression is okay. ACCResult VisitStmtExpr(StmtExpr *e) { return Visit(e->getSubStmt()->body_back()); diff --git a/clang/lib/Sema/SemaPseudoObject.cpp b/clang/lib/Sema/SemaPseudoObject.cpp index 97fbe48aa23..e0a62712159 100644 --- a/clang/lib/Sema/SemaPseudoObject.cpp +++ b/clang/lib/Sema/SemaPseudoObject.cpp @@ -38,11 +38,364 @@ using namespace clang; using namespace sema; +namespace { + // Basically just a very focused copy of TreeTransform. + template <class T> struct Rebuilder { + Sema &S; + Rebuilder(Sema &S) : S(S) {} + + T &getDerived() { return static_cast<T&>(*this); } + + Expr *rebuild(Expr *e) { + // Fast path: nothing to look through. + if (typename T::specific_type *specific + = dyn_cast<typename T::specific_type>(e)) + return getDerived().rebuildSpecific(specific); + + // Otherwise, we should look through and rebuild anything that + // IgnoreParens would. + + if (ParenExpr *parens = dyn_cast<ParenExpr>(e)) { + e = rebuild(parens->getSubExpr()); + return new (S.Context) ParenExpr(parens->getLParen(), + parens->getRParen(), + e); + } + + if (UnaryOperator *uop = dyn_cast<UnaryOperator>(e)) { + assert(uop->getOpcode() == UO_Extension); + e = rebuild(uop->getSubExpr()); + return new (S.Context) UnaryOperator(e, uop->getOpcode(), + uop->getType(), + uop->getValueKind(), + uop->getObjectKind(), + uop->getOperatorLoc()); + } + + if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) { + assert(!gse->isResultDependent()); + unsigned resultIndex = gse->getResultIndex(); + unsigned numAssocs = gse->getNumAssocs(); + + SmallVector<Expr*, 8> assocs(numAssocs); + SmallVector<TypeSourceInfo*, 8> assocTypes(numAssocs); + + for (unsigned i = 0; i != numAssocs; ++i) { + Expr *assoc = gse->getAssocExpr(i); + if (i == resultIndex) assoc = rebuild(assoc); + assocs[i] = assoc; + assocTypes[i] = gse->getAssocTypeSourceInfo(i); + } + + return new (S.Context) GenericSelectionExpr(S.Context, + gse->getGenericLoc(), + gse->getControllingExpr(), + assocTypes.data(), + assocs.data(), + numAssocs, + gse->getDefaultLoc(), + gse->getRParenLoc(), + gse->containsUnexpandedParameterPack(), + resultIndex); + } + + llvm_unreachable("bad expression to rebuild!"); + } + }; + + struct ObjCPropertyRefRebuilder : Rebuilder<ObjCPropertyRefRebuilder> { + Expr *NewBase; + ObjCPropertyRefRebuilder(Sema &S, Expr *newBase) + : Rebuilder(S), NewBase(newBase) {} + + typedef ObjCPropertyRefExpr specific_type; + Expr *rebuildSpecific(ObjCPropertyRefExpr *refExpr) { + // Fortunately, the constraint that we're rebuilding something + // with a base limits the number of cases here. + assert(refExpr->getBase()); + + if (refExpr->isExplicitProperty()) { + return new (S.Context) + ObjCPropertyRefExpr(refExpr->getExplicitProperty(), + refExpr->getType(), refExpr->getValueKind(), + refExpr->getObjectKind(), refExpr->getLocation(), + NewBase); + } + return new (S.Context) + ObjCPropertyRefExpr(refExpr->getImplicitPropertyGetter(), + refExpr->getImplicitPropertySetter(), + refExpr->getType(), refExpr->getValueKind(), + refExpr->getObjectKind(),refExpr->getLocation(), + NewBase); + } + }; + + class PseudoOpBuilder { + public: + Sema &S; + unsigned ResultIndex; + SourceLocation GenericLoc; + SmallVector<Expr *, 4> Semantics; + + PseudoOpBuilder(Sema &S, SourceLocation genericLoc) + : S(S), ResultIndex(PseudoObjectExpr::NoResult), + GenericLoc(genericLoc) {} + + /// Add a normal semantic expression. + void addSemanticExpr(Expr *semantic) { + Semantics.push_back(semantic); + } + + /// Add the 'result' semantic expression. + void addResultSemanticExpr(Expr *resultExpr) { + assert(ResultIndex == PseudoObjectExpr::NoResult); + ResultIndex = Semantics.size(); + Semantics.push_back(resultExpr); + } + + ExprResult buildRValueOperation(Expr *op); + ExprResult buildAssignmentOperation(Scope *Sc, + SourceLocation opLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS); + ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc, + UnaryOperatorKind opcode, + Expr *op); + + ExprResult complete(Expr *syntacticForm); + + OpaqueValueExpr *capture(Expr *op); + OpaqueValueExpr *captureValueAsResult(Expr *op); + + void setResultToLastSemantic() { + assert(ResultIndex == PseudoObjectExpr::NoResult); + ResultIndex = Semantics.size() - 1; + } + + /// Return true if assignments have a non-void result. + virtual bool assignmentsHaveResult() { return true; } + + virtual Expr *rebuildAndCaptureObject(Expr *) = 0; + virtual ExprResult buildGet() = 0; + virtual ExprResult buildSet(Expr *, SourceLocation, + bool captureSetValueAsResult) = 0; + }; + + /// A PseudoOpBuilder for Objective-C @properties. + class ObjCPropertyOpBuilder : public PseudoOpBuilder { + ObjCPropertyRefExpr *RefExpr; + OpaqueValueExpr *InstanceReceiver; + ObjCMethodDecl *Getter; + + ObjCMethodDecl *Setter; + Selector SetterSelector; + + public: + ObjCPropertyOpBuilder(Sema &S, ObjCPropertyRefExpr *refExpr) : + PseudoOpBuilder(S, refExpr->getLocation()), RefExpr(refExpr), + InstanceReceiver(0), Getter(0), Setter(0) { + } + + ExprResult buildRValueOperation(Expr *op); + ExprResult buildAssignmentOperation(Scope *Sc, + SourceLocation opLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS); + ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc, + UnaryOperatorKind opcode, + Expr *op); + + bool tryBuildGetOfReference(Expr *op, ExprResult &result); + bool findSetter(); + bool findGetter(); + + Expr *rebuildAndCaptureObject(Expr *syntacticBase); + ExprResult buildGet(); + ExprResult buildSet(Expr *op, SourceLocation, bool); + }; +} + +/// Capture the given expression in an OpaqueValueExpr. +OpaqueValueExpr *PseudoOpBuilder::capture(Expr *e) { + // Make a new OVE whose source is the given expression. + OpaqueValueExpr *captured = + new (S.Context) OpaqueValueExpr(GenericLoc, e->getType(), + e->getValueKind()); + captured->setSourceExpr(e); + + // Make sure we bind that in the semantics. + addSemanticExpr(captured); + return captured; +} + +/// Capture the given expression as the result of this pseudo-object +/// operation. This routine is safe against expressions which may +/// already be captured. +/// +/// \param Returns the captured expression, which will be the +/// same as the input if the input was already captured +OpaqueValueExpr *PseudoOpBuilder::captureValueAsResult(Expr *e) { + assert(ResultIndex == PseudoObjectExpr::NoResult); + + // If the expression hasn't already been captured, just capture it + // and set the new semantic + if (!isa<OpaqueValueExpr>(e)) { + OpaqueValueExpr *cap = capture(e); + setResultToLastSemantic(); + return cap; + } + + // Otherwise, it must already be one of our semantic expressions; + // set ResultIndex to its index. + unsigned index = 0; + for (;; ++index) { + assert(index < Semantics.size() && + "captured expression not found in semantics!"); + if (e == Semantics[index]) break; + } + ResultIndex = index; + return cast<OpaqueValueExpr>(e); +} + +/// The routine which creates the final PseudoObjectExpr. +ExprResult PseudoOpBuilder::complete(Expr *syntactic) { + return PseudoObjectExpr::Create(S.Context, syntactic, + Semantics, ResultIndex); +} + +/// The main skeleton for building an r-value operation. +ExprResult PseudoOpBuilder::buildRValueOperation(Expr *op) { + Expr *syntacticBase = rebuildAndCaptureObject(op); + + ExprResult getExpr = buildGet(); + if (getExpr.isInvalid()) return ExprError(); + addResultSemanticExpr(getExpr.take()); + + return complete(syntacticBase); +} + +/// The basic skeleton for building a simple or compound +/// assignment operation. +ExprResult +PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS) { + assert(BinaryOperator::isAssignmentOp(opcode)); + + Expr *syntacticLHS = rebuildAndCaptureObject(LHS); + OpaqueValueExpr *capturedRHS = capture(RHS); + + Expr *syntactic; + + ExprResult result; + if (opcode == BO_Assign) { + result = capturedRHS; + syntactic = new (S.Context) BinaryOperator(syntacticLHS, capturedRHS, + opcode, capturedRHS->getType(), + capturedRHS->getValueKind(), + OK_Ordinary, opcLoc); + } else { + ExprResult opLHS = buildGet(); + if (opLHS.isInvalid()) return ExprError(); + + // Build an ordinary, non-compound operation. + BinaryOperatorKind nonCompound = + BinaryOperator::getOpForCompoundAssignment(opcode); + result = S.BuildBinOp(Sc, opcLoc, nonCompound, + opLHS.take(), capturedRHS); + if (result.isInvalid()) return ExprError(); + + syntactic = + new (S.Context) CompoundAssignOperator(syntacticLHS, capturedRHS, opcode, + result.get()->getType(), + result.get()->getValueKind(), + OK_Ordinary, + opLHS.get()->getType(), + result.get()->getType(), + opcLoc); + } + + // The result of the assignment, if not void, is the value set into + // the l-value. + result = buildSet(result.take(), opcLoc, assignmentsHaveResult()); + if (result.isInvalid()) return ExprError(); + addSemanticExpr(result.take()); + + return complete(syntactic); +} + +/// The basic skeleton for building an increment or decrement +/// operation. +ExprResult +PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, + UnaryOperatorKind opcode, + Expr *op) { + assert(UnaryOperator::isIncrementDecrementOp(opcode)); + + Expr *syntacticOp = rebuildAndCaptureObject(op); + + // Load the value. + ExprResult result = buildGet(); + if (result.isInvalid()) return ExprError(); + + QualType resultType = result.get()->getType(); + + // That's the postfix result. + if (UnaryOperator::isPostfix(opcode) && assignmentsHaveResult()) { + result = capture(result.take()); + setResultToLastSemantic(); + } + + // Add or subtract a literal 1. + llvm::APInt oneV(S.Context.getTypeSize(S.Context.IntTy), 1); + Expr *one = IntegerLiteral::Create(S.Context, oneV, S.Context.IntTy, + GenericLoc); + + if (UnaryOperator::isIncrementOp(opcode)) { + result = S.BuildBinOp(Sc, opcLoc, BO_Add, result.take(), one); + } else { + result = S.BuildBinOp(Sc, opcLoc, BO_Sub, result.take(), one); + } + if (result.isInvalid()) return ExprError(); + + // Store that back into the result. The value stored is the result + // of a prefix operation. + result = buildSet(result.take(), opcLoc, + UnaryOperator::isPrefix(opcode) && assignmentsHaveResult()); + if (result.isInvalid()) return ExprError(); + addSemanticExpr(result.take()); + + UnaryOperator *syntactic = + new (S.Context) UnaryOperator(syntacticOp, opcode, resultType, + VK_LValue, OK_Ordinary, opcLoc); + return complete(syntactic); +} + + +//===----------------------------------------------------------------------===// +// Objective-C @property and implicit property references +//===----------------------------------------------------------------------===// + +/// Look up a method in the receiver type of an Objective-C property +/// reference. static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel, const ObjCPropertyRefExpr *PRE) { if (PRE->isObjectReceiver()) { const ObjCObjectPointerType *PT = PRE->getBase()->getType()->castAs<ObjCObjectPointerType>(); + + // Special case for 'self' in class method implementations. + if (PT->isObjCClassType() && + S.isSelfExpr(const_cast<Expr*>(PRE->getBase()))) { + // This cast is safe because isSelfExpr is only true within + // methods. + ObjCMethodDecl *method = + cast<ObjCMethodDecl>(S.CurContext->getNonClosureAncestor()); + return S.LookupMethodInObjectType(sel, + S.Context.getObjCInterfaceType(method->getClassInterface()), + /*instance*/ false); + } + return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true); } @@ -59,281 +412,374 @@ static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel, return S.LookupMethodInObjectType(sel, IT, false); } -ExprResult Sema::checkPseudoObjectRValue(Expr *E) { - assert(E->getValueKind() == VK_LValue && - E->getObjectKind() == OK_ObjCProperty); - const ObjCPropertyRefExpr *PRE = E->getObjCProperty(); - - QualType ReceiverType; - if (PRE->isObjectReceiver()) - ReceiverType = PRE->getBase()->getType(); - else if (PRE->isSuperReceiver()) - ReceiverType = PRE->getSuperReceiverType(); - else - ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver()); - - ExprValueKind VK = VK_RValue; - QualType T; - if (PRE->isImplicitProperty()) { - if (ObjCMethodDecl *GetterMethod = - PRE->getImplicitPropertyGetter()) { - T = getMessageSendResultType(ReceiverType, GetterMethod, - PRE->isClassReceiver(), - PRE->isSuperReceiver()); - VK = Expr::getValueKindForType(GetterMethod->getResultType()); - } else { - Diag(PRE->getLocation(), diag::err_getter_not_found) - << PRE->getBase()->getType(); - return ExprError(); - } - } else { - ObjCPropertyDecl *prop = PRE->getExplicitProperty(); - - ObjCMethodDecl *getter = - LookupMethodInReceiverType(*this, prop->getGetterName(), PRE); - if (getter && !getter->hasRelatedResultType()) - DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation()); - if (!getter) getter = prop->getGetterMethodDecl(); - - // Figure out the type of the expression. Mostly this is the - // result type of the getter, if possible. - if (getter) { - T = getMessageSendResultType(ReceiverType, getter, - PRE->isClassReceiver(), - PRE->isSuperReceiver()); - VK = Expr::getValueKindForType(getter->getResultType()); - - // As a special case, if the method returns 'id', try to get a - // better type from the property. - if (VK == VK_RValue && T->isObjCIdType() && - prop->getType()->isObjCRetainableType()) - T = prop->getType(); +bool ObjCPropertyOpBuilder::findGetter() { + if (Getter) return true; + + Getter = LookupMethodInReceiverType(S, RefExpr->getGetterSelector(), RefExpr); + return (Getter != 0); +} + +/// Try to find the most accurate setter declaration for the property +/// reference. +/// +/// \return true if a setter was found, in which case Setter +bool ObjCPropertyOpBuilder::findSetter() { + // For implicit properties, just trust the lookup we already did. + if (RefExpr->isImplicitProperty()) { + if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) { + Setter = setter; + SetterSelector = setter->getSelector(); + return true; } else { - T = prop->getType(); - VK = Expr::getValueKindForType(T); - T = T.getNonLValueExprType(Context); + IdentifierInfo *getterName = + RefExpr->getImplicitPropertyGetter()->getSelector() + .getIdentifierInfoForSlot(0); + SetterSelector = + SelectorTable::constructSetterName(S.PP.getIdentifierTable(), + S.PP.getSelectorTable(), + getterName); + return false; } } - E->setType(T); - E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK); + // For explicit properties, this is more involved. + ObjCPropertyDecl *prop = RefExpr->getExplicitProperty(); + SetterSelector = prop->getSetterName(); + + // Do a normal method lookup first. + if (ObjCMethodDecl *setter = + LookupMethodInReceiverType(S, SetterSelector, RefExpr)) { + Setter = setter; + return true; + } + + // That can fail in the somewhat crazy situation that we're + // type-checking a message send within the @interface declaration + // that declared the @property. But it's not clear that that's + // valuable to support. + + return false; +} + +/// Capture the base object of an Objective-C property expression. +Expr *ObjCPropertyOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) { + assert(InstanceReceiver == 0); + + // If we have a base, capture it in an OVE and rebuild the syntactic + // form to use the OVE as its base. + if (RefExpr->isObjectReceiver()) { + InstanceReceiver = capture(RefExpr->getBase()); + + syntacticBase = + ObjCPropertyRefRebuilder(S, InstanceReceiver).rebuild(syntacticBase); + } + + return syntacticBase; +} + +/// Load from an Objective-C property reference. +ExprResult ObjCPropertyOpBuilder::buildGet() { + findGetter(); + assert(Getter); - ExprResult Result = MaybeBindToTemporary(E); - if (!Result.isInvalid()) - E = Result.take(); + QualType receiverType; + SourceLocation superLoc; + if (RefExpr->isClassReceiver()) { + receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver()); + } else if (RefExpr->isSuperReceiver()) { + superLoc = RefExpr->getReceiverLocation(); + receiverType = RefExpr->getSuperReceiverType(); + } else { + assert(InstanceReceiver); + receiverType = InstanceReceiver->getType(); + } - return Owned(E); + // Build a message-send. + ExprResult msg; + if (Getter->isInstanceMethod() || RefExpr->isObjectReceiver()) { + assert(InstanceReceiver || RefExpr->isSuperReceiver()); + msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc, + Getter->getSelector(), Getter, + GenericLoc, GenericLoc, GenericLoc, + MultiExprArg()); + } else { + TypeSourceInfo *receiverTypeInfo = 0; + if (!RefExpr->isSuperReceiver()) + receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType); + + msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc, + Getter->getSelector(), Getter, + GenericLoc, GenericLoc, GenericLoc, + MultiExprArg()); + } + return msg; } -namespace { - struct PseudoObjectInfo { - const ObjCPropertyRefExpr *RefExpr; - bool HasSetter; - Selector SetterSelector; - ParmVarDecl *SetterParam; - QualType SetterParamType; +/// Store to an Objective-C property reference. +/// +/// \param bindSetValueAsResult - If true, capture the actual +/// value being set as the value of the property operation. +ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc, + bool captureSetValueAsResult) { + bool hasSetter = findSetter(); + assert(hasSetter); (void) hasSetter; + + QualType receiverType; + SourceLocation superLoc; + if (RefExpr->isClassReceiver()) { + receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver()); + } else if (RefExpr->isSuperReceiver()) { + superLoc = RefExpr->getReceiverLocation(); + receiverType = RefExpr->getSuperReceiverType(); + } else { + assert(InstanceReceiver); + receiverType = InstanceReceiver->getType(); + } - void setSetter(ObjCMethodDecl *setter) { - HasSetter = true; - SetterParam = *setter->param_begin(); - SetterParamType = SetterParam->getType().getUnqualifiedType(); + // Use assignment constraints when possible; they give us better + // diagnostics. "When possible" basically means anything except a + // C++ class type. + if (!S.getLangOptions().CPlusPlus || !op->getType()->isRecordType()) { + QualType paramType = (*Setter->param_begin())->getType(); + if (!S.getLangOptions().CPlusPlus || !paramType->isRecordType()) { + ExprResult opResult = op; + Sema::AssignConvertType assignResult + = S.CheckSingleAssignmentConstraints(paramType, opResult); + if (S.DiagnoseAssignmentResult(assignResult, opcLoc, paramType, + op->getType(), opResult.get(), + Sema::AA_Assigning)) + return ExprError(); + + op = opResult.take(); + assert(op && "successful assignment left argument invalid?"); } + } - PseudoObjectInfo(Sema &S, Expr *E) - : RefExpr(E->getObjCProperty()), HasSetter(false), SetterParam(0) { - - assert(E->getValueKind() == VK_LValue && - E->getObjectKind() == OK_ObjCProperty); - - // Try to find a setter. - - // For implicit properties, just trust the lookup we already did. - if (RefExpr->isImplicitProperty()) { - if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) { - setSetter(setter); - SetterSelector = setter->getSelector(); - } else { - IdentifierInfo *getterName = - RefExpr->getImplicitPropertyGetter()->getSelector() - .getIdentifierInfoForSlot(0); - SetterSelector = - SelectorTable::constructSetterName(S.PP.getIdentifierTable(), - S.PP.getSelectorTable(), - getterName); - } - return; - } + // Arguments. + Expr *args[] = { op }; - // For explicit properties, this is more involved. - ObjCPropertyDecl *prop = RefExpr->getExplicitProperty(); - SetterSelector = prop->getSetterName(); + // Build a message-send. + ExprResult msg; + if (Setter->isInstanceMethod() || RefExpr->isObjectReceiver()) { + msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc, + SetterSelector, Setter, + GenericLoc, GenericLoc, GenericLoc, + MultiExprArg(args, 1)); + } else { + TypeSourceInfo *receiverTypeInfo = 0; + if (!RefExpr->isSuperReceiver()) + receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType); + + msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc, + SetterSelector, Setter, + GenericLoc, GenericLoc, GenericLoc, + MultiExprArg(args, 1)); + } - // Do a normal method lookup first. - if (ObjCMethodDecl *setter = - LookupMethodInReceiverType(S, SetterSelector, RefExpr)) { - setSetter(setter); - return; - } + if (!msg.isInvalid() && captureSetValueAsResult) { + ObjCMessageExpr *msgExpr = + cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit()); + Expr *arg = msgExpr->getArg(0); + msgExpr->setArg(0, captureValueAsResult(arg)); + } - // If that failed, trust the type on the @property declaration. - if (!prop->isReadOnly()) { - HasSetter = true; - SetterParamType = prop->getType().getUnqualifiedType(); - } + return msg; +} + +/// @property-specific behavior for doing lvalue-to-rvalue conversion. +ExprResult ObjCPropertyOpBuilder::buildRValueOperation(Expr *op) { + // Explicit properties always have getters, but implicit ones don't. + // Check that before proceeding. + if (RefExpr->isImplicitProperty() && + !RefExpr->getImplicitPropertyGetter()) { + S.Diag(RefExpr->getLocation(), diag::err_getter_not_found) + << RefExpr->getBase()->getType(); + return ExprError(); + } + + ExprResult result = PseudoOpBuilder::buildRValueOperation(op); + if (result.isInvalid()) return ExprError(); + + if (RefExpr->isExplicitProperty() && !Getter->hasRelatedResultType()) + S.DiagnosePropertyAccessorMismatch(RefExpr->getExplicitProperty(), + Getter, RefExpr->getLocation()); + + // As a special case, if the method returns 'id', try to get + // a better type from the property. + if (RefExpr->isExplicitProperty() && result.get()->isRValue() && + result.get()->getType()->isObjCIdType()) { + QualType propType = RefExpr->getExplicitProperty()->getType(); + if (const ObjCObjectPointerType *ptr + = propType->getAs<ObjCObjectPointerType>()) { + if (!ptr->isObjCIdType()) + result = S.ImpCastExprToType(result.get(), propType, CK_BitCast); } - }; + } + + return result; } -/// Check an increment or decrement of a pseudo-object expression. -ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc, - UnaryOperatorKind opcode, Expr *op) { - assert(UnaryOperator::isIncrementDecrementOp(opcode)); - PseudoObjectInfo info(*this, op); +/// Try to build this as a call to a getter that returns a reference. +/// +/// \return true if it was possible, whether or not it actually +/// succeeded +bool ObjCPropertyOpBuilder::tryBuildGetOfReference(Expr *op, + ExprResult &result) { + if (!S.getLangOptions().CPlusPlus) return false; + + findGetter(); + assert(Getter && "property has no setter and no getter!"); + + // Only do this if the getter returns an l-value reference type. + QualType resultType = Getter->getResultType(); + if (!resultType->isLValueReferenceType()) return false; + + result = buildRValueOperation(op); + return true; +} + +/// @property-specific behavior for doing assignments. +ExprResult +ObjCPropertyOpBuilder::buildAssignmentOperation(Scope *Sc, + SourceLocation opcLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS) { + assert(BinaryOperator::isAssignmentOp(opcode)); // If there's no setter, we have no choice but to try to assign to // the result of the getter. - if (!info.HasSetter) { - QualType resultType = info.RefExpr->getGetterResultType(); - assert(!resultType.isNull() && "property has no setter and no getter!"); - - // Only do this if the getter returns an l-value reference type. - if (const LValueReferenceType *refType - = resultType->getAs<LValueReferenceType>()) { - op = ImplicitCastExpr::Create(Context, refType->getPointeeType(), - CK_GetObjCProperty, op, 0, VK_LValue); - return BuildUnaryOp(S, opcLoc, opcode, op); + if (!findSetter()) { + ExprResult result; + if (tryBuildGetOfReference(LHS, result)) { + if (result.isInvalid()) return ExprError(); + return S.BuildBinOp(Sc, opcLoc, opcode, result.take(), RHS); } // Otherwise, it's an error. - Diag(opcLoc, diag::err_nosetter_property_incdec) - << unsigned(info.RefExpr->isImplicitProperty()) - << unsigned(UnaryOperator::isDecrementOp(opcode)) - << info.SetterSelector - << op->getSourceRange(); + S.Diag(opcLoc, diag::err_nosetter_property_assignment) + << unsigned(RefExpr->isImplicitProperty()) + << SetterSelector + << LHS->getSourceRange() << RHS->getSourceRange(); return ExprError(); } - // ++/-- behave like compound assignments, i.e. they need a getter. - QualType getterResultType = info.RefExpr->getGetterResultType(); - if (getterResultType.isNull()) { - assert(info.RefExpr->isImplicitProperty()); - Diag(opcLoc, diag::err_nogetter_property_incdec) - << unsigned(UnaryOperator::isDecrementOp(opcode)) - << info.RefExpr->getImplicitPropertyGetter()->getSelector() - << op->getSourceRange(); + // If there is a setter, we definitely want to use it. + + // Verify that we can do a compound assignment. + if (opcode != BO_Assign && !findGetter()) { + S.Diag(opcLoc, diag::err_nogetter_property_compound_assignment) + << LHS->getSourceRange() << RHS->getSourceRange(); return ExprError(); } - // HACK: change the type of the operand to prevent further placeholder - // transformation. - op->setType(getterResultType.getNonLValueExprType(Context)); - op->setObjectKind(OK_Ordinary); - - ExprResult result = CreateBuiltinUnaryOp(opcLoc, opcode, op); + ExprResult result = + PseudoOpBuilder::buildAssignmentOperation(Sc, opcLoc, opcode, LHS, RHS); if (result.isInvalid()) return ExprError(); - // Change the object kind back. - op->setObjectKind(OK_ObjCProperty); + // Various warnings about property assignments in ARC. + if (S.getLangOptions().ObjCAutoRefCount && InstanceReceiver) { + S.checkRetainCycles(InstanceReceiver->getSourceExpr(), RHS); + S.checkUnsafeExprAssigns(opcLoc, LHS, RHS); + } + return result; } -ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, - BinaryOperatorKind opcode, - Expr *LHS, Expr *RHS) { - assert(BinaryOperator::isAssignmentOp(opcode)); - PseudoObjectInfo info(*this, LHS); - +/// @property-specific behavior for doing increments and decrements. +ExprResult +ObjCPropertyOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc, + UnaryOperatorKind opcode, + Expr *op) { // If there's no setter, we have no choice but to try to assign to // the result of the getter. - if (!info.HasSetter) { - QualType resultType = info.RefExpr->getGetterResultType(); - assert(!resultType.isNull() && "property has no setter and no getter!"); - - // Only do this if the getter returns an l-value reference type. - if (const LValueReferenceType *refType - = resultType->getAs<LValueReferenceType>()) { - LHS = ImplicitCastExpr::Create(Context, refType->getPointeeType(), - CK_GetObjCProperty, LHS, 0, VK_LValue); - return BuildBinOp(S, opcLoc, opcode, LHS, RHS); + if (!findSetter()) { + ExprResult result; + if (tryBuildGetOfReference(op, result)) { + if (result.isInvalid()) return ExprError(); + return S.BuildUnaryOp(Sc, opcLoc, opcode, result.take()); } // Otherwise, it's an error. - Diag(opcLoc, diag::err_nosetter_property_assignment) - << unsigned(info.RefExpr->isImplicitProperty()) - << info.SetterSelector - << LHS->getSourceRange() << RHS->getSourceRange(); + S.Diag(opcLoc, diag::err_nosetter_property_incdec) + << unsigned(RefExpr->isImplicitProperty()) + << unsigned(UnaryOperator::isDecrementOp(opcode)) + << SetterSelector + << op->getSourceRange(); return ExprError(); } // If there is a setter, we definitely want to use it. - // If this is a simple assignment, just initialize the parameter - // with the RHS. - if (opcode == BO_Assign) { - LHS->setType(info.SetterParamType.getNonLValueExprType(Context)); - - // Under certain circumstances, we need to type-check the RHS as a - // straight-up parameter initialization. This gives somewhat - // inferior diagnostics, so we try to avoid it. - - if (RHS->isTypeDependent()) { - // Just build the expression. - - } else if ((getLangOptions().CPlusPlus && LHS->getType()->isRecordType()) || - (getLangOptions().ObjCAutoRefCount && - info.SetterParam && - info.SetterParam->hasAttr<NSConsumedAttr>())) { - InitializedEntity param = (info.SetterParam - ? InitializedEntity::InitializeParameter(Context, info.SetterParam) - : InitializedEntity::InitializeParameter(Context, info.SetterParamType, - /*consumed*/ false)); - ExprResult arg = PerformCopyInitialization(param, opcLoc, RHS); - if (arg.isInvalid()) return ExprError(); - RHS = arg.take(); - - // Warn about assignments of +1 objects to unsafe pointers in ARC. - // CheckAssignmentOperands does this on the other path. - if (getLangOptions().ObjCAutoRefCount) - checkUnsafeExprAssigns(opcLoc, LHS, RHS); - } else { - ExprResult RHSResult = Owned(RHS); - - LHS->setObjectKind(OK_Ordinary); - QualType resultType = CheckAssignmentOperands(LHS, RHSResult, opcLoc, - /*compound*/ QualType()); - LHS->setObjectKind(OK_ObjCProperty); + // We also need a getter. + if (!findGetter()) { + assert(RefExpr->isImplicitProperty()); + S.Diag(opcLoc, diag::err_nogetter_property_incdec) + << unsigned(UnaryOperator::isDecrementOp(opcode)) + << RefExpr->getImplicitPropertyGetter()->getSelector() // FIXME! + << op->getSourceRange(); + return ExprError(); + } - if (!RHSResult.isInvalid()) RHS = RHSResult.take(); - if (resultType.isNull()) return ExprError(); - } + return PseudoOpBuilder::buildIncDecOperation(Sc, opcLoc, opcode, op); +} - // Warn about property sets in ARC that might cause retain cycles. - if (getLangOptions().ObjCAutoRefCount && !info.RefExpr->isSuperReceiver()) - checkRetainCycles(const_cast<Expr*>(info.RefExpr->getBase()), RHS); +//===----------------------------------------------------------------------===// +// General Sema routines. +//===----------------------------------------------------------------------===// - return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(), - RHS->getValueKind(), - RHS->getObjectKind(), - opcLoc); +ExprResult Sema::checkPseudoObjectRValue(Expr *E) { + Expr *opaqueRef = E->IgnoreParens(); + if (ObjCPropertyRefExpr *refExpr + = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { + ObjCPropertyOpBuilder builder(*this, refExpr); + return builder.buildRValueOperation(E); + } else { + llvm_unreachable("unknown pseudo-object kind!"); } +} - // If this is a compound assignment, we need to use the getter, too. - QualType getterResultType = info.RefExpr->getGetterResultType(); - if (getterResultType.isNull()) { - Diag(opcLoc, diag::err_nogetter_property_compound_assignment) - << LHS->getSourceRange() << RHS->getSourceRange(); - return ExprError(); +/// Check an increment or decrement of a pseudo-object expression. +ExprResult Sema::checkPseudoObjectIncDec(Scope *Sc, SourceLocation opcLoc, + UnaryOperatorKind opcode, Expr *op) { + // Do nothing if the operand is dependent. + if (op->isTypeDependent()) + return new (Context) UnaryOperator(op, opcode, Context.DependentTy, + VK_RValue, OK_Ordinary, opcLoc); + + assert(UnaryOperator::isIncrementDecrementOp(opcode)); + Expr *opaqueRef = op->IgnoreParens(); + if (ObjCPropertyRefExpr *refExpr + = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { + ObjCPropertyOpBuilder builder(*this, refExpr); + return builder.buildIncDecOperation(Sc, opcLoc, opcode, op); + } else { + llvm_unreachable("unknown pseudo-object kind!"); } +} - // HACK: change the type of the LHS to prevent further placeholder - // transformation. - LHS->setType(getterResultType.getNonLValueExprType(Context)); - LHS->setObjectKind(OK_Ordinary); - - ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS); - if (result.isInvalid()) return ExprError(); +ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, + BinaryOperatorKind opcode, + Expr *LHS, Expr *RHS) { + // Do nothing if either argument is dependent. + if (LHS->isTypeDependent() || RHS->isTypeDependent()) + return new (Context) BinaryOperator(LHS, RHS, opcode, Context.DependentTy, + VK_RValue, OK_Ordinary, opcLoc); + + // Filter out non-overload placeholder types in the RHS. + if (const BuiltinType *PTy = RHS->getType()->getAsPlaceholderType()) { + if (PTy->getKind() != BuiltinType::Overload) { + ExprResult result = CheckPlaceholderExpr(RHS); + if (result.isInvalid()) return ExprError(); + RHS = result.take(); + } + } - // Change the object kind back. - LHS->setObjectKind(OK_ObjCProperty); - return result; + Expr *opaqueRef = LHS->IgnoreParens(); + if (ObjCPropertyRefExpr *refExpr + = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) { + ObjCPropertyOpBuilder builder(*this, refExpr); + return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); + } else { + llvm_unreachable("unknown pseudo-object kind!"); + } } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 8c99e8a4de4..a34d83812f6 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -198,7 +198,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { Diag(Loc, diag::warn_unused_result) << R1 << R2; return; } - } else if (isa<ObjCPropertyRefExpr>(E)) { + } else if (isa<PseudoObjectExpr>(E)) { DiagID = diag::warn_unused_property_expr; } else if (const CXXFunctionalCastExpr *FC = dyn_cast<CXXFunctionalCastExpr>(E)) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 5596a9a6caa..f55e2a706bb 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -6103,6 +6103,22 @@ TreeTransform<Derived>::TransformOpaqueValueExpr(OpaqueValueExpr *E) { template<typename Derived> ExprResult +TreeTransform<Derived>::TransformPseudoObjectExpr(PseudoObjectExpr *E) { + // Rebuild the syntactic form. + ExprResult result = getDerived().TransformExpr(E->getSyntacticForm()); + if (result.isInvalid()) return ExprError(); + + // If that gives us a pseudo-object result back, the pseudo-object + // expression must have been an lvalue-to-rvalue conversion which we + // should reapply. + if (result.get()->hasPlaceholderType(BuiltinType::PseudoObject)) + result = SemaRef.checkPseudoObjectRValue(result.take()); + + return result; +} + +template<typename Derived> +ExprResult TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr( UnaryExprOrTypeTraitExpr *E) { if (E->isArgumentType()) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index e57ab1937c5..ff306b06c9e 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -775,6 +775,24 @@ void ASTStmtReader::VisitGenericSelectionExpr(GenericSelectionExpr *E) { E->RParenLoc = ReadSourceLocation(Record, Idx); } +void ASTStmtReader::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + VisitExpr(E); + unsigned numSemanticExprs = Record[Idx++]; + assert(numSemanticExprs + 1 == E->PseudoObjectExprBits.NumSubExprs); + E->PseudoObjectExprBits.ResultIndex = Record[Idx++]; + + // Read the syntactic expression. + E->getSubExprsBuffer()[0] = Reader.ReadSubExpr(); + + // Read all the semantic expressions. + for (unsigned i = 0; i != numSemanticExprs; ++i) { + Expr *subExpr = Reader.ReadSubExpr(); + if (isa<OpaqueValueExpr>(subExpr)) + cast<OpaqueValueExpr>(subExpr)->setSourceExpr(Reader.ReadSubExpr()); + E->getSubExprsBuffer()[i+1] = subExpr; + } +} + void ASTStmtReader::VisitAtomicExpr(AtomicExpr *E) { VisitExpr(E); E->setOp(AtomicExpr::AtomicOp(Record[Idx++])); @@ -2059,6 +2077,12 @@ Stmt *ASTReader::ReadStmtFromStream(Module &F) { S = new (Context) AsTypeExpr(Empty); break; + case EXPR_PSEUDO_OBJECT: { + unsigned numSemanticExprs = Record[ASTStmtReader::NumExprFields]; + S = PseudoObjectExpr::Create(Context, Empty, numSemanticExprs); + break; + } + case EXPR_ATOMIC: S = new (Context) AtomicExpr(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 61570a880a0..a8c76b51143 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -736,6 +736,25 @@ void ASTStmtWriter::VisitGenericSelectionExpr(GenericSelectionExpr *E) { Code = serialization::EXPR_GENERIC_SELECTION; } +void ASTStmtWriter::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSemanticExprs()); + + // Push the result index. Currently, this needs to exactly match + // the encoding used internally for ResultIndex. + unsigned result = E->getResultExprIndex(); + result = (result == PseudoObjectExpr::NoResult ? 0 : result + 1); + Record.push_back(result); + + Writer.AddStmt(E->getSyntacticForm()); + for (PseudoObjectExpr::semantics_iterator + i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) { + Writer.AddStmt(*i); + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(*i)) + Writer.AddStmt(OVE->getSourceExpr()); + } +} + void ASTStmtWriter::VisitAtomicExpr(AtomicExpr *E) { VisitExpr(E); Record.push_back(E->getOp()); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 471936c84ad..32cfa847e9e 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -857,6 +857,21 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, Bldr.addNodes(Dst); break; } + + case Stmt::PseudoObjectExprClass: { + Bldr.takeNodes(Pred); + const ProgramState *state = Pred->getState(); + const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S); + if (const Expr *Result = PE->getResultExpr()) { + SVal V = state->getSVal(Result); + Bldr.generateNode(S, Pred, state->BindExpr(S, V)); + } + else + Bldr.generateNode(S, Pred, state->BindExpr(S, UnknownVal())); + + Bldr.addNodes(Dst); + break; + } } } diff --git a/clang/test/Analysis/casts.m b/clang/test/Analysis/casts.m index d9700fe5c75..6f19211976b 100644 --- a/clang/test/Analysis/casts.m +++ b/clang/test/Analysis/casts.m @@ -33,9 +33,10 @@ typedef enum { RDR10087620Enum elem; } @property (readwrite, nonatomic) RDR10087620Enum elem; +@end + static void adium_media_ready_cb(RDR10087620 *InObj) { InObj.elem |= EEOne; } -@end
\ No newline at end of file diff --git a/clang/test/Analysis/retain-release-path-notes.m b/clang/test/Analysis/retain-release-path-notes.m index e34942a8968..dfe44748e65 100644 --- a/clang/test/Analysis/retain-release-path-notes.m +++ b/clang/test/Analysis/retain-release-path-notes.m @@ -1,5 +1,10 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease,osx.cocoa.RetainCount -analyzer-store=region -analyzer-output=text -verify %s +// This actually still works after the pseudo-object refactor, it just +// uses messages that say 'method' instead of 'property'. Ted wanted +// this xfailed and filed as a bug. rdar://problem/10402993 +// XFAIL: * + /*** This file is for testing the path-sensitive notes for retain/release errors. Its goal is to have simple branch coverage of any path-based diagnostics, diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index bf5607b5300..0f93931034d 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1714,6 +1714,8 @@ public: void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U); void VisitVAArgExpr(VAArgExpr *E); void VisitSizeOfPackExpr(SizeOfPackExpr *E); + void VisitPseudoObjectExpr(PseudoObjectExpr *E); + void VisitOpaqueValueExpr(OpaqueValueExpr *E); private: void AddDeclarationNameInfo(Stmt *S); @@ -2022,6 +2024,17 @@ void EnqueueVisitor::VisitVAArgExpr(VAArgExpr *E) { void EnqueueVisitor::VisitSizeOfPackExpr(SizeOfPackExpr *E) { WL.push_back(SizeOfPackExprParts(E, Parent)); } +void EnqueueVisitor::VisitOpaqueValueExpr(OpaqueValueExpr *E) { + // If the opaque value has a source expression, just transparently + // visit that. This is useful for (e.g.) pseudo-object expressions. + if (Expr *SourceExpr = E->getSourceExpr()) + return Visit(SourceExpr); + AddStmt(E); +} +void EnqueueVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + // Treat the expression like its syntactic form. + Visit(E->getSyntacticForm()); +} void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, Stmt *S) { EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); @@ -2702,6 +2715,11 @@ static Decl *getDeclFromExpr(Stmt *E) { return RE->getDecl(); if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) return PRE->isExplicitProperty() ? PRE->getExplicitProperty() : 0; + if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + return getDeclFromExpr(POE->getSyntacticForm()); + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + if (Expr *Src = OVE->getSourceExpr()) + return getDeclFromExpr(Src); if (CallExpr *CE = dyn_cast<CallExpr>(E)) return getDeclFromExpr(CE->getCallee()); diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index ae3aeea06c7..202a19254e9 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -219,7 +219,6 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, case Stmt::MaterializeTemporaryExprClass: case Stmt::ObjCIndirectCopyRestoreExprClass: case Stmt::OffsetOfExprClass: - case Stmt::OpaqueValueExprClass: case Stmt::ParenListExprClass: case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: @@ -229,6 +228,16 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU, K = CXCursor_UnexposedExpr; break; + case Stmt::OpaqueValueExprClass: + if (Expr *Src = cast<OpaqueValueExpr>(S)->getSourceExpr()) + return MakeCXCursor(Src, Parent, TU, RegionOfInterest); + K = CXCursor_UnexposedExpr; + break; + + case Stmt::PseudoObjectExprClass: + return MakeCXCursor(cast<PseudoObjectExpr>(S)->getSyntacticForm(), + Parent, TU, RegionOfInterest); + case Stmt::CompoundStmtClass: K = CXCursor_CompoundStmt; break; |

