Change the AST representation of operations on Objective-C

property references to use a new PseudoObjectExpr
expression which pairs a syntactic form of the expression
with a set of semantic expressions implementing it.
This should significantly reduce the complexity required
elsewhere in the compiler to deal with these kinds of
expressions (e.g. IR generation's special l-value kind,
the static analyzer's Message abstraction), at the lower
cost of specifically dealing with the odd AST structure
of these expressions.  It should also greatly simplify
efforts to implement similar language features in the
future, most notably Managed C++'s properties and indexed
properties.

Most of the effort here is in dealing with the various
clients of the AST.  I've gone ahead and simplified the
ObjC rewriter's use of properties;  other clients, like
IR-gen and the static analyzer, have all the old
complexity *and* all the new complexity, at least
temporarily.  Many thanks to Ted for writing and advising
on the necessary changes to the static analyzer.

I've xfailed a small diagnostics regression in the static
analyzer at Ted's request.

llvm-svn: 143867
This commit is contained in:
John McCall 2011-11-06 09:01:30 +00:00
parent 5b77c6c618
commit fe96e0b6be
40 changed files with 1684 additions and 662 deletions

View File

@ -974,6 +974,14 @@ public:
/// declaration context DC. /// declaration context DC.
bool Encloses(const DeclContext *DC) const; 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 /// getPrimaryContext - There may be many different
/// declarations of the same entity (including forward declarations /// declarations of the same entity (including forward declarations
/// of classes, multiple definitions of namespaces, etc.), each with /// of classes, multiple definitions of namespaces, etc.), each with

View File

@ -2758,6 +2758,13 @@ public:
bool isCompoundAssignmentOp() const { bool isCompoundAssignmentOp() const {
return isCompoundAssignmentOp(getOpcode()); 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) { static bool isShiftAssignOp(Opcode Opc) {
return Opc == BO_ShlAssign || Opc == BO_ShrAssign; return Opc == BO_ShlAssign || Opc == BO_ShrAssign;
@ -4251,6 +4258,140 @@ public:
child_range children() { return child_range(&SrcExpr, &SrcExpr+1); } 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_*, /// AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*,
/// __atomic_load, __atomic_store, and __atomic_compare_exchange_*, for the /// __atomic_load, __atomic_store, and __atomic_compare_exchange_*, for the
/// similarly-named C++0x instructions. All of these instructions take one /// similarly-named C++0x instructions. All of these instructions take one

View File

@ -1874,6 +1874,23 @@ TraverseGenericSelectionExpr(GenericSelectionExpr *S) {
return true; 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, { DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, {
// This is called for code like 'return T()' where T is a built-in // This is called for code like 'return T()' where T is a built-in
// (i.e. non-class) type. // (i.e. non-class) type.

View File

@ -146,6 +146,7 @@ protected:
friend class CXXUnresolvedConstructExpr; // ctor friend class CXXUnresolvedConstructExpr; // ctor
friend class CXXDependentScopeMemberExpr; // ctor friend class CXXDependentScopeMemberExpr; // ctor
friend class OverloadExpr; // ctor friend class OverloadExpr; // ctor
friend class PseudoObjectExpr; // ctor
friend class AtomicExpr; // ctor friend class AtomicExpr; // ctor
unsigned : NumStmtBits; unsigned : NumStmtBits;
@ -184,6 +185,18 @@ protected:
unsigned NumPreArgs : 1; 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 { class ObjCIndirectCopyRestoreExprBitfields {
friend class ObjCIndirectCopyRestoreExpr; friend class ObjCIndirectCopyRestoreExpr;
unsigned : NumExprBits; unsigned : NumExprBits;
@ -201,6 +214,7 @@ protected:
DeclRefExprBitfields DeclRefExprBits; DeclRefExprBitfields DeclRefExprBits;
CastExprBitfields CastExprBits; CastExprBitfields CastExprBits;
CallExprBitfields CallExprBits; CallExprBitfields CallExprBits;
PseudoObjectExprBitfields PseudoObjectExprBits;
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
}; };

View File

@ -77,6 +77,7 @@ def ImplicitValueInitExpr : DStmt<Expr>;
def ParenListExpr : DStmt<Expr>; def ParenListExpr : DStmt<Expr>;
def VAArgExpr : DStmt<Expr>; def VAArgExpr : DStmt<Expr>;
def GenericSelectionExpr : DStmt<Expr>; def GenericSelectionExpr : DStmt<Expr>;
def PseudoObjectExpr : DStmt<Expr>;
// Atomic expressions // Atomic expressions
def AtomicExpr : DStmt<Expr>; def AtomicExpr : DStmt<Expr>;

View File

@ -1000,6 +1000,8 @@ namespace clang {
EXPR_BLOCK_DECL_REF, EXPR_BLOCK_DECL_REF,
/// \brief A GenericSelectionExpr record. /// \brief A GenericSelectionExpr record.
EXPR_GENERIC_SELECTION, EXPR_GENERIC_SELECTION,
/// \brief A PseudoObjectExpr record.
EXPR_PSEUDO_OBJECT,
/// \brief An AtomicExpr record. /// \brief An AtomicExpr record.
EXPR_ATOMIC, EXPR_ATOMIC,

View File

@ -160,12 +160,14 @@ private:
if (!E) return false; if (!E) return false;
E = E->IgnoreParenCasts(); 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)) if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E))
return propE->getGetterSelector() == DelegateSel;
return false; return false;
} }

View File

@ -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) if (implCE->getCastKind() == CK_ARCConsumeObject)
return rewriteToBridgedCast(E, OBC_BridgeRetained); return rewriteToBridgedCast(E, OBC_BridgeRetained);
if (implCE->getCastKind() == CK_ARCReclaimReturnedObject) if (implCE->getCastKind() == CK_ARCReclaimReturnedObject)

View File

@ -78,6 +78,15 @@ public:
return true; 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) { bool VisitBinaryOperator(BinaryOperator *BOE) {
if (isZeroingPropIvar(BOE) && isRemovable(BOE)) { if (isZeroingPropIvar(BOE) && isRemovable(BOE)) {
Transaction Trans(Pass.TA); Transaction Trans(Pass.TA);
@ -142,17 +151,21 @@ private:
} }
bool isZeroingPropIvar(Expr *E) { bool isZeroingPropIvar(Expr *E) {
BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E); E = E->IgnoreParens();
if (!BOE) return false; 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) if (BOE->getOpcode() == BO_Comma)
return isZeroingPropIvar(BOE->getLHS()) && return isZeroingPropIvar(BOE->getLHS()) &&
isZeroingPropIvar(BOE->getRHS()); isZeroingPropIvar(BOE->getRHS());
if (BOE->getOpcode() != BO_Assign) if (BOE->getOpcode() != BO_Assign)
return false; return false;
ASTContext &Ctx = Pass.Ctx;
Expr *LHS = BOE->getLHS(); Expr *LHS = BOE->getLHS();
if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) { if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
@ -172,25 +185,38 @@ private:
if (!IvarBacksPropertySynthesis) if (!IvarBacksPropertySynthesis)
return false; 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 else
return false; return false;
Expr *RHS = BOE->getRHS(); return isZero(BOE->getRHS());
bool RHSIsNull = RHS->isNullPointerConstant(Ctx, }
Expr::NPC_ValueDependentIsNull);
if (RHSIsNull) 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 true;
return isZeroingPropIvar(RHS); return isZeroingPropIvar(E);
} }
}; };

View File

@ -640,7 +640,11 @@ void Decl::CheckAccessDeclContext() const {
} }
DeclContext *Decl::getNonClosureContext() { DeclContext *Decl::getNonClosureContext() {
DeclContext *DC = getDeclContext(); return getDeclContext()->getNonClosureAncestor();
}
DeclContext *DeclContext::getNonClosureAncestor() {
DeclContext *DC = this;
// This is basically "while (DC->isClosure()) DC = DC->getParent();" // This is basically "while (DC->isClosure()) DC = DC->getParent();"
// except that it's significantly more efficient to cast to a known // except that it's significantly more efficient to cast to a known

View File

@ -1694,6 +1694,19 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
R1 = getSourceRange(); R1 = getSourceRange();
return true; 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: { case StmtExprClass: {
// Statement exprs don't logically have side effects themselves, but are // 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. // 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 } else if (const MaterializeTemporaryExpr *M
= dyn_cast<MaterializeTemporaryExpr>(this)) { = dyn_cast<MaterializeTemporaryExpr>(this)) {
return M->GetTemporaryExpr()->isNullPointerConstant(Ctx, NPC); 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. // 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); 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. // ExprIterator.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -232,6 +232,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
return ClassifyExprValueKind(Lang, E, return ClassifyExprValueKind(Lang, E,
cast<OpaqueValueExpr>(E)->getValueKind()); 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 // Implicit casts are lvalues if they're lvalue casts. Other than that, we
// only specifically record class temporaries. // only specifically record class temporaries.
case Expr::ImplicitCastExprClass: case Expr::ImplicitCastExprClass:

View File

@ -3450,6 +3450,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) {
case Expr::AsTypeExprClass: case Expr::AsTypeExprClass:
case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::ObjCIndirectCopyRestoreExprClass:
case Expr::MaterializeTemporaryExprClass: case Expr::MaterializeTemporaryExprClass:
case Expr::PseudoObjectExprClass:
case Expr::AtomicExprClass: case Expr::AtomicExprClass:
return ICEDiag(2, E->getLocStart()); return ICEDiag(2, E->getLocStart());

View File

@ -2257,6 +2257,7 @@ recurse:
case Expr::CXXNoexceptExprClass: case Expr::CXXNoexceptExprClass:
case Expr::CUDAKernelCallExprClass: case Expr::CUDAKernelCallExprClass:
case Expr::AsTypeExprClass: case Expr::AsTypeExprClass:
case Expr::PseudoObjectExprClass:
case Expr::AtomicExprClass: case Expr::AtomicExprClass:
{ {
// As bad as this diagnostic is, it's better than crashing. // As bad as this diagnostic is, it's better than crashing.

View File

@ -148,6 +148,7 @@ namespace {
void VisitCompoundAssignOperator(CompoundAssignOperator *Node); void VisitCompoundAssignOperator(CompoundAssignOperator *Node);
void VisitAddrLabelExpr(AddrLabelExpr *Node); void VisitAddrLabelExpr(AddrLabelExpr *Node);
void VisitBlockExpr(BlockExpr *Node); void VisitBlockExpr(BlockExpr *Node);
void VisitOpaqueValueExpr(OpaqueValueExpr *Node);
// C++ // C++
void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node); void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node);
@ -524,6 +525,15 @@ void StmtDumper::VisitBlockExpr(BlockExpr *Node) {
DumpSubTree(block->getBody()); DumpSubTree(block->getBody());
} }
void StmtDumper::VisitOpaqueValueExpr(OpaqueValueExpr *Node) {
DumpExpr(Node);
if (Expr *Source = Node->getSourceExpr()) {
OS << '\n';
DumpSubTree(Source);
}
}
// GNU extensions. // GNU extensions.
void StmtDumper::VisitAddrLabelExpr(AddrLabelExpr *Node) { void StmtDumper::VisitAddrLabelExpr(AddrLabelExpr *Node) {

View File

@ -1027,6 +1027,10 @@ void StmtPrinter::VisitVAArgExpr(VAArgExpr *Node) {
OS << ")"; OS << ")";
} }
void StmtPrinter::VisitPseudoObjectExpr(PseudoObjectExpr *Node) {
PrintExpr(Node->getSyntacticForm());
}
void StmtPrinter::VisitAtomicExpr(AtomicExpr *Node) { void StmtPrinter::VisitAtomicExpr(AtomicExpr *Node) {
const char *Name = 0; const char *Name = 0;
switch (Node->getOp()) { switch (Node->getOp()) {

View File

@ -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) { void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) {
VisitExpr(S); VisitExpr(S);
ID.AddInteger(S->getOp()); ID.AddInteger(S->getOp());

View File

@ -344,6 +344,7 @@ private:
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S); CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
CFGBlock *VisitReturnStmt(ReturnStmt *R); CFGBlock *VisitReturnStmt(ReturnStmt *R);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
AddStmtChoice asc); AddStmtChoice asc);
CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc); CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
@ -981,6 +982,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::OpaqueValueExprClass: case Stmt::OpaqueValueExprClass:
return Block; return Block;
case Stmt::PseudoObjectExprClass:
return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S));
case Stmt::ReturnStmtClass: case Stmt::ReturnStmtClass:
return VisitReturnStmt(cast<ReturnStmt>(S)); return VisitReturnStmt(cast<ReturnStmt>(S));
@ -1907,6 +1911,31 @@ CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *S) {
return NYS(); 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 *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
CFGBlock *LoopSuccessor = NULL; CFGBlock *LoopSuccessor = NULL;

View File

@ -288,6 +288,18 @@ void TransferFunctions::Visit(Stmt *S) {
} }
break; 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. // FIXME: These cases eventually shouldn't be needed.
case Stmt::ExprWithCleanupsClass: { case Stmt::ExprWithCleanupsClass: {
S = cast<ExprWithCleanups>(S)->getSubExpr(); S = cast<ExprWithCleanups>(S)->getSubExpr();

View File

@ -672,6 +672,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
return EmitStringLiteralLValue(cast<StringLiteral>(E)); return EmitStringLiteralLValue(cast<StringLiteral>(E));
case Expr::ObjCEncodeExprClass: case Expr::ObjCEncodeExprClass:
return EmitObjCEncodeExprLValue(cast<ObjCEncodeExpr>(E)); return EmitObjCEncodeExprLValue(cast<ObjCEncodeExpr>(E));
case Expr::PseudoObjectExprClass:
return EmitPseudoObjectLValue(cast<PseudoObjectExpr>(E));
case Expr::BlockDeclRefExprClass: case Expr::BlockDeclRefExprClass:
return EmitBlockDeclRefLValue(cast<BlockDeclRefExpr>(E)); 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, cast<llvm::Instruction>(Val)->setMetadata(llvm::LLVMContext::MD_fpaccuracy,
Node); 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;
}

View File

@ -148,6 +148,15 @@ public:
void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E); void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E);
void VisitOpaqueValueExpr(OpaqueValueExpr *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 VisitVAArgExpr(VAArgExpr *E);
void EmitInitializationToLValue(Expr *E, LValue Address); void EmitInitializationToLValue(Expr *E, LValue Address);

View File

@ -137,6 +137,10 @@ public:
return CGF.getOpaqueRValueMapping(E).getComplexVal(); return CGF.getOpaqueRValueMapping(E).getComplexVal();
} }
ComplexPairTy VisitPseudoObjectExpr(PseudoObjectExpr *E) {
return CGF.EmitPseudoObjectRValue(E).getComplexVal();
}
// FIXME: CompoundLiteralExpr // FIXME: CompoundLiteralExpr
ComplexPairTy EmitCast(CastExpr::CastKind CK, Expr *Op, QualType DestTy); ComplexPairTy EmitCast(CastExpr::CastKind CK, Expr *Op, QualType DestTy);

View File

@ -197,6 +197,10 @@ public:
return llvm::ConstantInt::get(ConvertType(E->getType()),E->getPackLength()); return llvm::ConstantInt::get(ConvertType(E->getType()),E->getPackLength());
} }
Value *VisitPseudoObjectExpr(PseudoObjectExpr *E) {
return CGF.EmitPseudoObjectRValue(E).getScalarVal();
}
Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) { Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) {
if (E->isGLValue()) if (E->isGLValue())
return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E)); return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E));

View File

@ -2231,6 +2231,59 @@ static bool shouldEmitSeparateBlockRetain(const Expr *e) {
return true; 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 static TryEmitResult
tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
// Look through cleanups. // Look through cleanups.
@ -2356,6 +2409,17 @@ tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
llvm::Value *result = emitARCRetainCall(CGF, e); llvm::Value *result = emitARCRetainCall(CGF, e);
if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); if (resultType) result = CGF.Builder.CreateBitCast(result, resultType);
return TryEmitResult(result, true); 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. // Conservatively halt the search at any other expression kind.

View File

@ -27,6 +27,7 @@ namespace clang {
class ObjCPropertyRefExpr; class ObjCPropertyRefExpr;
namespace CodeGen { namespace CodeGen {
class AggValueSlot;
class CGBitFieldInfo; class CGBitFieldInfo;
/// RValue - This trivial value class is used to represent the result of an /// RValue - This trivial value class is used to represent the result of an
@ -452,7 +453,7 @@ public:
RValue asRValue() const { RValue asRValue() const {
return RValue::getAggregate(getAddr(), isVolatile()); return RValue::getAggregate(getAddr(), isVolatile());
} }
void setZeroed(bool V = true) { ZeroedFlag = V; } void setZeroed(bool V = true) { ZeroedFlag = V; }
IsZeroed_t isZeroed() const { IsZeroed_t isZeroed() const {
return IsZeroed_t(ZeroedFlag); return IsZeroed_t(ZeroedFlag);

View File

@ -950,20 +950,86 @@ public:
public: public:
PeepholeProtection() : Inst(0) {} PeepholeProtection() : Inst(0) {}
}; };
/// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr. /// A non-RAII class containing all the information about a bound
class OpaqueValueMapping { /// opaque value. OpaqueValueMapping, below, is a RAII wrapper for
CodeGenFunction &CGF; /// 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; const OpaqueValueExpr *OpaqueValue;
bool BoundLValue; bool BoundLValue;
CodeGenFunction::PeepholeProtection Protection; CodeGenFunction::PeepholeProtection Protection;
OpaqueValueMappingData(const OpaqueValueExpr *ov,
bool boundLValue)
: OpaqueValue(ov), BoundLValue(boundLValue) {}
public: public:
OpaqueValueMappingData() : OpaqueValue(0) {}
static bool shouldBindAsLValue(const Expr *expr) { static bool shouldBindAsLValue(const Expr *expr) {
return expr->isGLValue() || expr->getType()->isRecordType(); 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 /// Build the opaque value mapping for the given conditional
/// operator if it's the GNU ?: extension. This is a common /// operator if it's the GNU ?: extension. This is a common
/// enough pattern that the convenience operator is really /// enough pattern that the convenience operator is really
@ -971,75 +1037,34 @@ public:
/// ///
OpaqueValueMapping(CodeGenFunction &CGF, OpaqueValueMapping(CodeGenFunction &CGF,
const AbstractConditionalOperator *op) : CGF(CGF) { const AbstractConditionalOperator *op) : CGF(CGF) {
if (isa<ConditionalOperator>(op)) { if (isa<ConditionalOperator>(op))
OpaqueValue = 0; // Leave Data empty.
BoundLValue = false;
return; return;
}
const BinaryConditionalOperator *e = cast<BinaryConditionalOperator>(op); const BinaryConditionalOperator *e = cast<BinaryConditionalOperator>(op);
init(e->getOpaqueValue(), e->getCommon()); Data = OpaqueValueMappingData::bind(CGF, e->getOpaqueValue(),
e->getCommon());
} }
OpaqueValueMapping(CodeGenFunction &CGF, OpaqueValueMapping(CodeGenFunction &CGF,
const OpaqueValueExpr *opaqueValue, const OpaqueValueExpr *opaqueValue,
LValue lvalue) LValue lvalue)
: CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(true) { : CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, lvalue)) {
assert(opaqueValue && "no opaque value expression!");
assert(shouldBindAsLValue(opaqueValue));
initLValue(lvalue);
} }
OpaqueValueMapping(CodeGenFunction &CGF, OpaqueValueMapping(CodeGenFunction &CGF,
const OpaqueValueExpr *opaqueValue, const OpaqueValueExpr *opaqueValue,
RValue rvalue) RValue rvalue)
: CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(false) { : CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, rvalue)) {
assert(opaqueValue && "no opaque value expression!");
assert(!shouldBindAsLValue(opaqueValue));
initRValue(rvalue);
} }
void pop() { void pop() {
assert(OpaqueValue && "mapping already popped!"); Data.unbind(CGF);
popImpl(); Data.clear();
OpaqueValue = 0;
} }
~OpaqueValueMapping() { ~OpaqueValueMapping() {
if (OpaqueValue) popImpl(); if (Data.isValid()) Data.unbind(CGF);
}
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));
} }
}; };
@ -2015,6 +2040,10 @@ public:
LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
LValue EmitOpaqueValueLValue(const OpaqueValueExpr *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, llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar); const ObjCIvarDecl *Ivar);
LValue EmitLValueForAnonRecordField(llvm::Value* Base, LValue EmitLValueForAnonRecordField(llvm::Value* Base,

View File

@ -138,12 +138,6 @@ namespace {
llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; 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 // 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). // us to avoid rewriting the same node twice (which is very uncommon).
// This is needed to support some of the exotic property rewriting. // This is needed to support some of the exotic property rewriting.
@ -154,6 +148,19 @@ namespace {
VarDecl *GlobalVarDecl; VarDecl *GlobalVarDecl;
bool DisableReplaceStmt; 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; static const int OBJC_ABI_VERSION = 7;
public: public:
@ -186,7 +193,7 @@ namespace {
return; // We can't rewrite the same node twice. return; // We can't rewrite the same node twice.
if (DisableReplaceStmt) if (DisableReplaceStmt)
return; // Used when rewriting the assignment of a property setter. return;
// If replacement succeeded or warning disabled return with no warning. // If replacement succeeded or warning disabled return with no warning.
if (!Rewrite.ReplaceStmt(Old, New)) { if (!Rewrite.ReplaceStmt(Old, New)) {
@ -200,6 +207,9 @@ namespace {
} }
void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) {
if (DisableReplaceStmt)
return;
// Measure the old text. // Measure the old text.
int Size = Rewrite.getRangeSize(SrcRange); int Size = Rewrite.getRangeSize(SrcRange);
if (Size == -1) { if (Size == -1) {
@ -282,18 +292,14 @@ namespace {
// Expression Rewriting. // Expression Rewriting.
Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S);
void CollectPropertySetters(Stmt *S);
Stmt *CurrentBody; Stmt *CurrentBody;
ParentMap *PropParentMap; // created lazily. ParentMap *PropParentMap; // created lazily.
Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp);
Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, SourceLocation OrigStart, Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV);
bool &replaced); Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo);
Stmt *RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced); Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo);
Stmt *RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr);
Stmt *RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt,
SourceRange SrcRange);
Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp);
Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp);
Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp);
@ -1281,184 +1287,173 @@ void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) {
"/* @end */"); "/* @end */");
} }
Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt, Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) {
SourceRange SrcRange) { SourceRange OldRange = PseudoOp->getSourceRange();
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();
}
}
assert(OMD && "RewritePropertyOrImplicitSetter - null OMD");
ObjCMessageExpr *MsgExpr; // We just magically know some things about the structure of this
if (Super) // expression.
MsgExpr = ObjCMessageExpr::Create(*Context, ObjCMessageExpr *OldMsg =
Ty.getNonReferenceType(), cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr(
Expr::getValueKindForType(Ty), PseudoOp->getNumSemanticExprs() - 1));
/*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. // Because the rewriter doesn't allow us to rewrite rewritten code,
ReplaceStmtWithRange(BinOp, ReplacingStmt, SrcRange); // we need to suppress rewriting the sub-statements.
//delete BinOp; Expr *Base, *RHS;
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references {
// to things that stay around. DisableReplaceStmtScope S(*this);
Context->Deallocate(MsgExpr);
return ReplacingStmt; // 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;
}
Stmt *Replacement = SynthMessageExpr(NewMsg);
ReplaceStmtWithRange(PseudoOp, Replacement, OldRange);
return Replacement;
} }
Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr) { Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) {
// Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr or ImplicitGetter. SourceRange OldRange = PseudoOp->getSourceRange();
// This allows us to reuse all the fun and games in SynthMessageExpr().
Stmt *Receiver = 0; // We just magically know some things about the structure of this
ObjCMethodDecl *OMD = 0; // expression.
QualType Ty; ObjCMessageExpr *OldMsg =
Selector Sel; cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit());
bool Super = false;
QualType SuperTy; // Because the rewriter doesn't allow us to rewrite rewritten code,
SourceLocation SuperLocation; // we need to suppress rewriting the sub-statements.
SourceLocation SelectorLoc; Expr *Base = 0;
if (ObjCPropertyRefExpr *PropRefExpr = {
dyn_cast<ObjCPropertyRefExpr>(PropOrGetterRefExpr)) { DisableReplaceStmtScope S(*this);
SelectorLoc = PropRefExpr->getLocation();
if (PropRefExpr->isExplicitProperty()) { // Rebuild the base expression if we have one.
ObjCPropertyDecl *PDecl = PropRefExpr->getExplicitProperty(); if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) {
OMD = PDecl->getGetterMethodDecl(); Base = OldMsg->getInstanceReceiver();
Ty = PDecl->getType(); Base = cast<OpaqueValueExpr>(Base)->getSourceExpr();
Sel = PDecl->getGetterName(); Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base));
} 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();
}
}
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(), // Intentionally empty.
MsgExpr->getLocEnd()); SmallVector<SourceLocation, 1> SelLocs;
SmallVector<Expr*, 1> Args;
if (!PropParentMap) ObjCMessageExpr *NewMsg;
PropParentMap = new ParentMap(CurrentBody); switch (OldMsg->getReceiverKind()) {
bool NestedPropertyRef = false; case ObjCMessageExpr::Class:
Stmt *Parent = PropParentMap->getParent(PropOrGetterRefExpr); NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(),
ImplicitCastExpr*ICE=0; OldMsg->getValueKind(),
if (Parent) OldMsg->getLeftLoc(),
if ((ICE = dyn_cast<ImplicitCastExpr>(Parent))) { OldMsg->getClassReceiverTypeInfo(),
assert((ICE->getCastKind() == CK_GetObjCProperty) OldMsg->getSelector(),
&& "RewritePropertyOrImplicitGetter"); SelLocs,
Parent = PropParentMap->getParent(Parent); OldMsg->getMethodDecl(),
NestedPropertyRef = (Parent && isa<ObjCPropertyRefExpr>(Parent)); Args,
} OldMsg->getRightLoc());
if (NestedPropertyRef) { break;
// We stash away the ReplacingStmt since actually doing the
// replacement/rewrite won't work for nested getters (e.g. obj.p.i) case ObjCMessageExpr::Instance:
PropGetters[ICE] = ReplacingStmt; NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(),
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references OldMsg->getValueKind(),
// to things that stay around. OldMsg->getLeftLoc(),
Context->Deallocate(MsgExpr); Base,
return PropOrGetterRefExpr; // return the original... OldMsg->getSelector(),
} else { SelLocs,
ReplaceStmt(PropOrGetterRefExpr, ReplacingStmt); OldMsg->getMethodDecl(),
// delete PropRefExpr; elsewhere... Args,
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references OldMsg->getRightLoc());
// to things that stay around. break;
Context->Deallocate(MsgExpr);
return ReplacingStmt; 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, Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
SourceLocation OrigStart, SourceRange OldRange = IV->getSourceRange();
bool &replaced) { 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(); ObjCIvarDecl *D = IV->getDecl();
const Expr *BaseExpr = IV->getBase();
Expr *Replacement = IV;
if (CurMethodDef) { if (CurMethodDef) {
if (BaseExpr->getType()->isObjCObjectPointerType()) { if (BaseExpr->getType()->isObjCObjectPointerType()) {
const ObjCInterfaceType *iFaceDecl = const ObjCInterfaceType *iFaceDecl =
@ -1483,25 +1478,19 @@ Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV,
CK_BitCast, CK_BitCast,
IV->getBase()); IV->getBase());
// Don't forget the parens to enforce the proper binding. // Don't forget the parens to enforce the proper binding.
ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(),
IV->getBase()->getLocEnd(), OldRange.getEnd(),
castExpr); castExpr);
replaced = true;
if (IV->isFreeIvar() && if (IV->isFreeIvar() &&
CurMethodDef->getClassInterface() == iFaceDecl->getDecl()) { CurMethodDef->getClassInterface() == iFaceDecl->getDecl()) {
MemberExpr *ME = new (Context) MemberExpr(PE, true, D, MemberExpr *ME = new (Context) MemberExpr(PE, true, D,
IV->getLocation(), IV->getLocation(),
D->getType(), D->getType(),
VK_LValue, OK_Ordinary); VK_LValue, OK_Ordinary);
// delete IV; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. Replacement = ME;
return 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. } else { // we are outside a method.
assert(!IV->isFreeIvar() && "Cannot have a free standing ivar 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. // Don't forget the parens to enforce the proper binding.
ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(),
IV->getBase()->getLocEnd(), castExpr); IV->getBase()->getLocEnd(), castExpr);
replaced = true;
// Cannot delete IV->getBase(), since PE points to it. // Cannot delete IV->getBase(), since PE points to it.
// Replace the old base with the cast. This is important when doing // Replace the old base with the cast. This is important when doing
// embedded rewrites. For example, [newInv->_container addObject:0]. // embedded rewrites. For example, [newInv->_container addObject:0].
IV->setBase(PE); IV->setBase(PE);
return IV;
} }
} }
return IV;
}
Stmt *RewriteObjC::RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced) { ReplaceStmtWithRange(IV, Replacement, OldRange);
for (Stmt::child_range CI = S->children(); CI; ++CI) { return Replacement;
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;
} }
/// SynthCountByEnumWithState - To print: /// SynthCountByEnumWithState - To print:
@ -4753,6 +4721,9 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) {
return CondExpr; return CondExpr;
} else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) {
CPT = IRE->getType()->getAs<BlockPointerType>(); CPT = IRE->getType()->getAs<BlockPointerType>();
} else if (const PseudoObjectExpr *POE
= dyn_cast<PseudoObjectExpr>(BlockExp)) {
CPT = POE->getType()->castAs<BlockPointerType>();
} else { } else {
assert(1 && "RewriteBlockClass: Bad type"); assert(1 && "RewriteBlockClass: Bad type");
} }
@ -5580,26 +5551,6 @@ bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) {
// Function Body / Expression rewriting // 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) { Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) ||
isa<DoStmt>(S) || isa<ForStmt>(S)) isa<DoStmt>(S) || isa<ForStmt>(S))
@ -5609,46 +5560,28 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
ObjCBcLabelNo.push_back(++BcLabelCount); 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(); SourceRange OrigStmtRange = S->getSourceRange();
// Perform a bottom up rewrite of all children. // Perform a bottom up rewrite of all children.
for (Stmt::child_range CI = S->children(); CI; ++CI) for (Stmt::child_range CI = S->children(); CI; ++CI)
if (*CI) { if (*CI) {
Stmt *newStmt; Stmt *childStmt = (*CI);
Stmt *ChildStmt = (*CI); Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt);
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);
if (newStmt) { if (newStmt) {
if (Expr *PropOrImplicitRefExpr = dyn_cast<Expr>(ChildStmt)) *CI = newStmt;
if (PropSetters[PropOrImplicitRefExpr] == S) {
S = newStmt;
newStmt = 0;
}
if (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)) { if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
@ -5661,7 +5594,6 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
// Rewrite the block body in place. // Rewrite the block body in place.
Stmt *SaveCurrentBody = CurrentBody; Stmt *SaveCurrentBody = CurrentBody;
CurrentBody = BE->getBody(); CurrentBody = BE->getBody();
CollectPropertySetters(CurrentBody);
PropParentMap = 0; PropParentMap = 0;
// block literal on rhs of a property-dot-sytax assignment // block literal on rhs of a property-dot-sytax assignment
// must be replaced by its synthesize ast so getRewrittenText // 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)) if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S))
return RewriteAtEncode(AtEncode); 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)) if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S))
return RewriteAtSelector(AtSelector); return RewriteAtSelector(AtSelector);
@ -5930,7 +5801,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) {
CurFunctionDef = FD; CurFunctionDef = FD;
CurFunctionDeclToDeclareForBlock = FD; CurFunctionDeclToDeclareForBlock = FD;
CollectPropertySetters(Body);
CurrentBody = Body; CurrentBody = Body;
Body = Body =
cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body));
@ -5951,7 +5821,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (CompoundStmt *Body = MD->getCompoundBody()) { if (CompoundStmt *Body = MD->getCompoundBody()) {
CurMethodDef = MD; CurMethodDef = MD;
CollectPropertySetters(Body);
CurrentBody = Body; CurrentBody = Body;
Body = Body =
cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body));
@ -5989,7 +5858,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
} }
if (VD->getInit()) { if (VD->getInit()) {
GlobalVarDecl = VD; GlobalVarDecl = VD;
CollectPropertySetters(VD->getInit());
CurrentBody = VD->getInit(); CurrentBody = VD->getInit();
RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); RewriteFunctionBodyOrGlobalInitializer(VD->getInit());
CurrentBody = 0; CurrentBody = 0;

View File

@ -4213,6 +4213,26 @@ static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) {
continue; 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? // Array ivars?
return false; return false;

View File

@ -7468,7 +7468,7 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp,
// The operand must be either an l-value or a function designator // The operand must be either an l-value or a function designator
if (!op->getType()->isFunctionType()) { if (!op->getType()->isFunctionType()) {
// Use a special diagnostic for loads from property references. // Use a special diagnostic for loads from property references.
if (isa<ObjCPropertyRefExpr>(op->IgnoreImplicit()->IgnoreParens())) { if (isa<PseudoObjectExpr>(op)) {
AddressOfError = AO_Property_Expansion; AddressOfError = AO_Property_Expansion;
} else { } else {
// FIXME: emit more specific diag... // FIXME: emit more specific diag...
@ -7483,9 +7483,6 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp,
} else if (op->getObjectKind() == OK_VectorComponent) { } else if (op->getObjectKind() == OK_VectorComponent) {
// The operand cannot be an element of a vector // The operand cannot be an element of a vector
AddressOfError = AO_Vector_Element; 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 } else if (dcl) { // C99 6.5.3.2p1
// We have an lvalue with a decl. Make sure the decl is not declared // We have an lvalue with a decl. Make sure the decl is not declared
// with the register storage-class specifier. // with the register storage-class specifier.
@ -8951,8 +8948,15 @@ static void MakeObjCStringLiteralFixItHint(Sema& SemaRef, QualType DstType,
return; return;
} }
// Strip off any parens and casts. // Ignore any parens, implicit casts (should only be
StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr->IgnoreParenCasts()); // 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()) if (!SL || !SL->isAscii())
return; return;

View File

@ -467,14 +467,13 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType,
bool Sema::isSelfExpr(Expr *receiver) { bool Sema::isSelfExpr(Expr *receiver) {
// 'self' is objc 'self' in an objc method only. // 'self' is objc 'self' in an objc method only.
DeclContext *DC = CurContext; ObjCMethodDecl *method =
while (isa<BlockDecl>(DC)) dyn_cast<ObjCMethodDecl>(CurContext->getNonClosureAncestor());
DC = DC->getParent(); if (!method) return false;
if (DC && !isa<ObjCMethodDecl>(DC))
return false;
receiver = receiver->IgnoreParenLValueCasts(); receiver = receiver->IgnoreParenLValueCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver)) if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver))
if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self")) if (DRE->getDecl() == method->getSelfDecl())
return true; return true;
return false; return false;
} }
@ -1725,6 +1724,12 @@ namespace {
return merge(left, Visit(e->getFalseExpr())); 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. /// Statement expressions are okay if their result expression is okay.
ACCResult VisitStmtExpr(StmtExpr *e) { ACCResult VisitStmtExpr(StmtExpr *e) {
return Visit(e->getSubStmt()->body_back()); return Visit(e->getSubStmt()->body_back());

View File

@ -38,11 +38,364 @@
using namespace clang; using namespace clang;
using namespace sema; 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, static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
const ObjCPropertyRefExpr *PRE) { const ObjCPropertyRefExpr *PRE) {
if (PRE->isObjectReceiver()) { if (PRE->isObjectReceiver()) {
const ObjCObjectPointerType *PT = const ObjCObjectPointerType *PT =
PRE->getBase()->getType()->castAs<ObjCObjectPointerType>(); 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); return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true);
} }
@ -59,281 +412,374 @@ static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
return S.LookupMethodInObjectType(sel, IT, false); return S.LookupMethodInObjectType(sel, IT, false);
} }
ExprResult Sema::checkPseudoObjectRValue(Expr *E) { bool ObjCPropertyOpBuilder::findGetter() {
assert(E->getValueKind() == VK_LValue && if (Getter) return true;
E->getObjectKind() == OK_ObjCProperty);
const ObjCPropertyRefExpr *PRE = E->getObjCProperty();
QualType ReceiverType; Getter = LookupMethodInReceiverType(S, RefExpr->getGetterSelector(), RefExpr);
if (PRE->isObjectReceiver()) return (Getter != 0);
ReceiverType = PRE->getBase()->getType(); }
else if (PRE->isSuperReceiver())
ReceiverType = PRE->getSuperReceiverType(); /// Try to find the most accurate setter declaration for the property
else /// reference.
ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver()); ///
/// \return true if a setter was found, in which case Setter
ExprValueKind VK = VK_RValue; bool ObjCPropertyOpBuilder::findSetter() {
QualType T; // For implicit properties, just trust the lookup we already did.
if (PRE->isImplicitProperty()) { if (RefExpr->isImplicitProperty()) {
if (ObjCMethodDecl *GetterMethod = if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
PRE->getImplicitPropertyGetter()) { Setter = setter;
T = getMessageSendResultType(ReceiverType, GetterMethod, SetterSelector = setter->getSelector();
PRE->isClassReceiver(), return true;
PRE->isSuperReceiver());
VK = Expr::getValueKindForType(GetterMethod->getResultType());
} else { } else {
Diag(PRE->getLocation(), diag::err_getter_not_found) IdentifierInfo *getterName =
<< PRE->getBase()->getType(); RefExpr->getImplicitPropertyGetter()->getSelector()
return ExprError(); .getIdentifierInfoForSlot(0);
SetterSelector =
SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
S.PP.getSelectorTable(),
getterName);
return false;
} }
}
// 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);
QualType receiverType;
SourceLocation superLoc;
if (RefExpr->isClassReceiver()) {
receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver());
} else if (RefExpr->isSuperReceiver()) {
superLoc = RefExpr->getReceiverLocation();
receiverType = RefExpr->getSuperReceiverType();
} else { } else {
ObjCPropertyDecl *prop = PRE->getExplicitProperty(); assert(InstanceReceiver);
receiverType = InstanceReceiver->getType();
}
ObjCMethodDecl *getter = // Build a message-send.
LookupMethodInReceiverType(*this, prop->getGetterName(), PRE); ExprResult msg;
if (getter && !getter->hasRelatedResultType()) if (Getter->isInstanceMethod() || RefExpr->isObjectReceiver()) {
DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation()); assert(InstanceReceiver || RefExpr->isSuperReceiver());
if (!getter) getter = prop->getGetterMethodDecl(); 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);
// Figure out the type of the expression. Mostly this is the msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
// result type of the getter, if possible. Getter->getSelector(), Getter,
if (getter) { GenericLoc, GenericLoc, GenericLoc,
T = getMessageSendResultType(ReceiverType, getter, MultiExprArg());
PRE->isClassReceiver(), }
PRE->isSuperReceiver()); return msg;
VK = Expr::getValueKindForType(getter->getResultType()); }
// As a special case, if the method returns 'id', try to get a /// Store to an Objective-C property reference.
// better type from the property. ///
if (VK == VK_RValue && T->isObjCIdType() && /// \param bindSetValueAsResult - If true, capture the actual
prop->getType()->isObjCRetainableType()) /// value being set as the value of the property operation.
T = prop->getType(); ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
} else { bool captureSetValueAsResult) {
T = prop->getType(); bool hasSetter = findSetter();
VK = Expr::getValueKindForType(T); assert(hasSetter); (void) hasSetter;
T = T.getNonLValueExprType(Context);
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();
}
// 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?");
} }
} }
E->setType(T); // Arguments.
E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK); Expr *args[] = { op };
ExprResult Result = MaybeBindToTemporary(E);
if (!Result.isInvalid())
E = Result.take();
return Owned(E); // 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));
}
if (!msg.isInvalid() && captureSetValueAsResult) {
ObjCMessageExpr *msgExpr =
cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit());
Expr *arg = msgExpr->getArg(0);
msgExpr->setArg(0, captureValueAsResult(arg));
}
return msg;
} }
namespace { /// @property-specific behavior for doing lvalue-to-rvalue conversion.
struct PseudoObjectInfo { ExprResult ObjCPropertyOpBuilder::buildRValueOperation(Expr *op) {
const ObjCPropertyRefExpr *RefExpr; // Explicit properties always have getters, but implicit ones don't.
bool HasSetter; // Check that before proceeding.
Selector SetterSelector; if (RefExpr->isImplicitProperty() &&
ParmVarDecl *SetterParam; !RefExpr->getImplicitPropertyGetter()) {
QualType SetterParamType; S.Diag(RefExpr->getLocation(), diag::err_getter_not_found)
<< RefExpr->getBase()->getType();
void setSetter(ObjCMethodDecl *setter) {
HasSetter = true;
SetterParam = *setter->param_begin();
SetterParamType = SetterParam->getType().getUnqualifiedType();
}
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;
}
// 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)) {
setSetter(setter);
return;
}
// If that failed, trust the type on the @property declaration.
if (!prop->isReadOnly()) {
HasSetter = true;
SetterParamType = prop->getType().getUnqualifiedType();
}
}
};
}
/// 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);
// 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);
}
// Otherwise, it's an error.
Diag(opcLoc, diag::err_nosetter_property_incdec)
<< unsigned(info.RefExpr->isImplicitProperty())
<< unsigned(UnaryOperator::isDecrementOp(opcode))
<< info.SetterSelector
<< op->getSourceRange();
return ExprError(); return ExprError();
} }
// ++/-- behave like compound assignments, i.e. they need a getter. ExprResult result = PseudoOpBuilder::buildRValueOperation(op);
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();
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);
if (result.isInvalid()) return ExprError(); if (result.isInvalid()) return ExprError();
// Change the object kind back. if (RefExpr->isExplicitProperty() && !Getter->hasRelatedResultType())
op->setObjectKind(OK_ObjCProperty); 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; return result;
} }
ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, /// Try to build this as a call to a getter that returns a reference.
BinaryOperatorKind opcode, ///
Expr *LHS, Expr *RHS) { /// \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)); assert(BinaryOperator::isAssignmentOp(opcode));
PseudoObjectInfo info(*this, LHS);
// If there's no setter, we have no choice but to try to assign to // If there's no setter, we have no choice but to try to assign to
// the result of the getter. // the result of the getter.
if (!info.HasSetter) { if (!findSetter()) {
QualType resultType = info.RefExpr->getGetterResultType(); ExprResult result;
assert(!resultType.isNull() && "property has no setter and no getter!"); if (tryBuildGetOfReference(LHS, result)) {
if (result.isInvalid()) return ExprError();
// Only do this if the getter returns an l-value reference type. return S.BuildBinOp(Sc, opcLoc, opcode, result.take(), RHS);
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);
} }
// Otherwise, it's an error. // Otherwise, it's an error.
Diag(opcLoc, diag::err_nosetter_property_assignment) S.Diag(opcLoc, diag::err_nosetter_property_assignment)
<< unsigned(info.RefExpr->isImplicitProperty()) << unsigned(RefExpr->isImplicitProperty())
<< info.SetterSelector << SetterSelector
<< LHS->getSourceRange() << RHS->getSourceRange(); << LHS->getSourceRange() << RHS->getSourceRange();
return ExprError(); return ExprError();
} }
// If there is a setter, we definitely want to use it. // If there is a setter, we definitely want to use it.
// If this is a simple assignment, just initialize the parameter // Verify that we can do a compound assignment.
// with the RHS. if (opcode != BO_Assign && !findGetter()) {
if (opcode == BO_Assign) { S.Diag(opcLoc, diag::err_nogetter_property_compound_assignment)
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);
if (!RHSResult.isInvalid()) RHS = RHSResult.take();
if (resultType.isNull()) return ExprError();
}
// 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);
return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(),
RHS->getValueKind(),
RHS->getObjectKind(),
opcLoc);
}
// 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(); << LHS->getSourceRange() << RHS->getSourceRange();
return ExprError(); return ExprError();
} }
// HACK: change the type of the LHS to prevent further placeholder ExprResult result =
// transformation. PseudoOpBuilder::buildAssignmentOperation(Sc, opcLoc, opcode, LHS, RHS);
LHS->setType(getterResultType.getNonLValueExprType(Context));
LHS->setObjectKind(OK_Ordinary);
ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS);
if (result.isInvalid()) return ExprError(); if (result.isInvalid()) return ExprError();
// Change the object kind back. // Various warnings about property assignments in ARC.
LHS->setObjectKind(OK_ObjCProperty); if (S.getLangOptions().ObjCAutoRefCount && InstanceReceiver) {
S.checkRetainCycles(InstanceReceiver->getSourceExpr(), RHS);
S.checkUnsafeExprAssigns(opcLoc, LHS, RHS);
}
return result; return result;
} }
/// @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 (!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.
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.
// 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();
}
return PseudoOpBuilder::buildIncDecOperation(Sc, opcLoc, opcode, op);
}
//===----------------------------------------------------------------------===//
// General Sema routines.
//===----------------------------------------------------------------------===//
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!");
}
}
/// 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!");
}
}
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();
}
}
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!");
}
}

View File

@ -198,7 +198,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
Diag(Loc, diag::warn_unused_result) << R1 << R2; Diag(Loc, diag::warn_unused_result) << R1 << R2;
return; return;
} }
} else if (isa<ObjCPropertyRefExpr>(E)) { } else if (isa<PseudoObjectExpr>(E)) {
DiagID = diag::warn_unused_property_expr; DiagID = diag::warn_unused_property_expr;
} else if (const CXXFunctionalCastExpr *FC } else if (const CXXFunctionalCastExpr *FC
= dyn_cast<CXXFunctionalCastExpr>(E)) { = dyn_cast<CXXFunctionalCastExpr>(E)) {

View File

@ -6101,6 +6101,22 @@ TreeTransform<Derived>::TransformOpaqueValueExpr(OpaqueValueExpr *E) {
return SemaRef.Owned(E); return SemaRef.Owned(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> template<typename Derived>
ExprResult ExprResult
TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr( TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr(

View File

@ -775,6 +775,24 @@ void ASTStmtReader::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
E->RParenLoc = ReadSourceLocation(Record, Idx); 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) { void ASTStmtReader::VisitAtomicExpr(AtomicExpr *E) {
VisitExpr(E); VisitExpr(E);
E->setOp(AtomicExpr::AtomicOp(Record[Idx++])); E->setOp(AtomicExpr::AtomicOp(Record[Idx++]));
@ -2059,6 +2077,12 @@ Stmt *ASTReader::ReadStmtFromStream(Module &F) {
S = new (Context) AsTypeExpr(Empty); S = new (Context) AsTypeExpr(Empty);
break; break;
case EXPR_PSEUDO_OBJECT: {
unsigned numSemanticExprs = Record[ASTStmtReader::NumExprFields];
S = PseudoObjectExpr::Create(Context, Empty, numSemanticExprs);
break;
}
case EXPR_ATOMIC: case EXPR_ATOMIC:
S = new (Context) AtomicExpr(Empty); S = new (Context) AtomicExpr(Empty);
break; break;

View File

@ -736,6 +736,25 @@ void ASTStmtWriter::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
Code = serialization::EXPR_GENERIC_SELECTION; 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) { void ASTStmtWriter::VisitAtomicExpr(AtomicExpr *E) {
VisitExpr(E); VisitExpr(E);
Record.push_back(E->getOp()); Record.push_back(E->getOp());

View File

@ -857,6 +857,21 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst); Bldr.addNodes(Dst);
break; 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;
}
} }
} }

View File

@ -33,9 +33,10 @@ typedef enum {
RDR10087620Enum elem; RDR10087620Enum elem;
} }
@property (readwrite, nonatomic) RDR10087620Enum elem; @property (readwrite, nonatomic) RDR10087620Enum elem;
@end
static void static void
adium_media_ready_cb(RDR10087620 *InObj) adium_media_ready_cb(RDR10087620 *InObj)
{ {
InObj.elem |= EEOne; InObj.elem |= EEOne;
} }
@end

View File

@ -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 // 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. 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, Its goal is to have simple branch coverage of any path-based diagnostics,

View File

@ -1714,6 +1714,8 @@ public:
void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U); void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U);
void VisitVAArgExpr(VAArgExpr *E); void VisitVAArgExpr(VAArgExpr *E);
void VisitSizeOfPackExpr(SizeOfPackExpr *E); void VisitSizeOfPackExpr(SizeOfPackExpr *E);
void VisitPseudoObjectExpr(PseudoObjectExpr *E);
void VisitOpaqueValueExpr(OpaqueValueExpr *E);
private: private:
void AddDeclarationNameInfo(Stmt *S); void AddDeclarationNameInfo(Stmt *S);
@ -2022,6 +2024,17 @@ void EnqueueVisitor::VisitVAArgExpr(VAArgExpr *E) {
void EnqueueVisitor::VisitSizeOfPackExpr(SizeOfPackExpr *E) { void EnqueueVisitor::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
WL.push_back(SizeOfPackExprParts(E, Parent)); 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) { void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, Stmt *S) {
EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S);
@ -2702,6 +2715,11 @@ static Decl *getDeclFromExpr(Stmt *E) {
return RE->getDecl(); return RE->getDecl();
if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E))
return PRE->isExplicitProperty() ? PRE->getExplicitProperty() : 0; 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)) if (CallExpr *CE = dyn_cast<CallExpr>(E))
return getDeclFromExpr(CE->getCallee()); return getDeclFromExpr(CE->getCallee());

View File

@ -219,7 +219,6 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
case Stmt::MaterializeTemporaryExprClass: case Stmt::MaterializeTemporaryExprClass:
case Stmt::ObjCIndirectCopyRestoreExprClass: case Stmt::ObjCIndirectCopyRestoreExprClass:
case Stmt::OffsetOfExprClass: case Stmt::OffsetOfExprClass:
case Stmt::OpaqueValueExprClass:
case Stmt::ParenListExprClass: case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass: case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass: case Stmt::ShuffleVectorExprClass:
@ -229,6 +228,16 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
K = CXCursor_UnexposedExpr; K = CXCursor_UnexposedExpr;
break; 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: case Stmt::CompoundStmtClass:
K = CXCursor_CompoundStmt; K = CXCursor_CompoundStmt;
break; break;