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.
bool Encloses(const DeclContext *DC) const;
/// \brief Find the nearest non-closure ancestor of this context,
/// i.e. the innermost semantic parent of this context which is not
/// a closure. A context may be its own non-closure ancestor.
DeclContext *getNonClosureAncestor();
const DeclContext *getNonClosureAncestor() const {
return const_cast<DeclContext*>(this)->getNonClosureAncestor();
}
/// getPrimaryContext - There may be many different
/// declarations of the same entity (including forward declarations
/// of classes, multiple definitions of namespaces, etc.), each with

View File

@ -2758,6 +2758,13 @@ public:
bool isCompoundAssignmentOp() const {
return isCompoundAssignmentOp(getOpcode());
}
static Opcode getOpForCompoundAssignment(Opcode Opc) {
assert(isCompoundAssignmentOp(Opc));
if (Opc >= BO_XorAssign)
return Opcode(unsigned(Opc) - BO_XorAssign + BO_Xor);
else
return Opcode(unsigned(Opc) - BO_MulAssign + BO_Mul);
}
static bool isShiftAssignOp(Opcode Opc) {
return Opc == BO_ShlAssign || Opc == BO_ShrAssign;
@ -4251,6 +4258,140 @@ public:
child_range children() { return child_range(&SrcExpr, &SrcExpr+1); }
};
/// PseudoObjectExpr - An expression which accesses a pseudo-object
/// l-value. A pseudo-object is an abstract object, accesses to which
/// are translated to calls. The pseudo-object expression has a
/// syntactic form, which shows how the expression was actually
/// written in the source code, and a semantic form, which is a series
/// of expressions to be executed in order which detail how the
/// operation is actually evaluated. Optionally, one of the semantic
/// forms may also provide a result value for the expression.
///
/// If any of the semantic-form expressions is an OpaqueValueExpr,
/// that OVE is required to have a source expression, and it is bound
/// to the result of that source expression. Such OVEs may appear
/// only in subsequent semantic-form expressions and as
/// sub-expressions of the syntactic form.
///
/// PseudoObjectExpr should be used only when an operation can be
/// usefully described in terms of fairly simple rewrite rules on
/// objects and functions that are meant to be used by end-developers.
/// For example, under the Itanium ABI, dynamic casts are implemented
/// as a call to a runtime function called __dynamic_cast; using this
/// class to describe that would be inappropriate because that call is
/// not really part of the user-visible semantics, and instead the
/// cast is properly reflected in the AST and IR-generation has been
/// taught to generate the call as necessary. In contrast, an
/// Objective-C property access is semantically defined to be
/// equivalent to a particular message send, and this is very much
/// part of the user model. The name of this class encourages this
/// modelling design.
class PseudoObjectExpr : public Expr {
// PseudoObjectExprBits.NumSubExprs - The number of sub-expressions.
// Always at least two, because the first sub-expression is the
// syntactic form.
// PseudoObjectExprBits.ResultIndex - The index of the
// sub-expression holding the result. 0 means the result is void,
// which is unambiguous because it's the index of the syntactic
// form. Note that this is therefore 1 higher than the value passed
// in to Create, which is an index within the semantic forms.
// Note also that ASTStmtWriter assumes this encoding.
Expr **getSubExprsBuffer() { return reinterpret_cast<Expr**>(this + 1); }
const Expr * const *getSubExprsBuffer() const {
return reinterpret_cast<const Expr * const *>(this + 1);
}
friend class ASTStmtReader;
PseudoObjectExpr(QualType type, ExprValueKind VK,
Expr *syntactic, ArrayRef<Expr*> semantic,
unsigned resultIndex);
PseudoObjectExpr(EmptyShell shell, unsigned numSemanticExprs);
unsigned getNumSubExprs() const {
return PseudoObjectExprBits.NumSubExprs;
}
public:
/// NoResult - A value for the result index indicating that there is
/// no semantic result.
enum { NoResult = ~0U };
static PseudoObjectExpr *Create(ASTContext &Context, Expr *syntactic,
ArrayRef<Expr*> semantic,
unsigned resultIndex);
static PseudoObjectExpr *Create(ASTContext &Context, EmptyShell shell,
unsigned numSemanticExprs);
/// Return the syntactic form of this expression, i.e. the
/// expression it actually looks like. Likely to be expressed in
/// terms of OpaqueValueExprs bound in the semantic form.
Expr *getSyntacticForm() { return getSubExprsBuffer()[0]; }
const Expr *getSyntacticForm() const { return getSubExprsBuffer()[0]; }
/// Return the index of the result-bearing expression into the semantics
/// expressions, or PseudoObjectExpr::NoResult if there is none.
unsigned getResultExprIndex() const {
if (PseudoObjectExprBits.ResultIndex == 0) return NoResult;
return PseudoObjectExprBits.ResultIndex - 1;
}
/// Return the result-bearing expression, or null if there is none.
Expr *getResultExpr() {
if (PseudoObjectExprBits.ResultIndex == 0)
return 0;
return getSubExprsBuffer()[PseudoObjectExprBits.ResultIndex];
}
const Expr *getResultExpr() const {
return const_cast<PseudoObjectExpr*>(this)->getResultExpr();
}
unsigned getNumSemanticExprs() const { return getNumSubExprs() - 1; }
typedef Expr * const *semantics_iterator;
typedef const Expr * const *const_semantics_iterator;
semantics_iterator semantics_begin() {
return getSubExprsBuffer() + 1;
}
const_semantics_iterator semantics_begin() const {
return getSubExprsBuffer() + 1;
}
semantics_iterator semantics_end() {
return getSubExprsBuffer() + getNumSubExprs();
}
const_semantics_iterator semantics_end() const {
return getSubExprsBuffer() + getNumSubExprs();
}
Expr *getSemanticExpr(unsigned index) {
assert(index + 1 < getNumSubExprs());
return getSubExprsBuffer()[index + 1];
}
const Expr *getSemanticExpr(unsigned index) const {
return const_cast<PseudoObjectExpr*>(this)->getSemanticExpr(index);
}
SourceLocation getExprLoc() const {
return getSyntacticForm()->getExprLoc();
}
SourceRange getSourceRange() const {
return getSyntacticForm()->getSourceRange();
}
child_range children() {
Stmt **cs = reinterpret_cast<Stmt**>(getSubExprsBuffer());
return child_range(cs, cs + getNumSubExprs());
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == PseudoObjectExprClass;
}
static bool classof(const PseudoObjectExpr *) { return true; }
};
/// AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*,
/// __atomic_load, __atomic_store, and __atomic_compare_exchange_*, for the
/// similarly-named C++0x instructions. All of these instructions take one

View File

@ -1874,6 +1874,23 @@ TraverseGenericSelectionExpr(GenericSelectionExpr *S) {
return true;
}
// PseudoObjectExpr is a special case because of the wierdness with
// syntactic expressions and opaque values.
template<typename Derived>
bool RecursiveASTVisitor<Derived>::
TraversePseudoObjectExpr(PseudoObjectExpr *S) {
TRY_TO(WalkUpFromPseudoObjectExpr(S));
TRY_TO(TraverseStmt(S->getSyntacticForm()));
for (PseudoObjectExpr::semantics_iterator
i = S->semantics_begin(), e = S->semantics_end(); i != e; ++i) {
Expr *sub = *i;
if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
sub = OVE->getSourceExpr();
TRY_TO(TraverseStmt(sub));
}
return true;
}
DEF_TRAVERSE_STMT(CXXScalarValueInitExpr, {
// This is called for code like 'return T()' where T is a built-in
// (i.e. non-class) type.

View File

@ -146,6 +146,7 @@ protected:
friend class CXXUnresolvedConstructExpr; // ctor
friend class CXXDependentScopeMemberExpr; // ctor
friend class OverloadExpr; // ctor
friend class PseudoObjectExpr; // ctor
friend class AtomicExpr; // ctor
unsigned : NumStmtBits;
@ -184,6 +185,18 @@ protected:
unsigned NumPreArgs : 1;
};
class PseudoObjectExprBitfields {
friend class PseudoObjectExpr;
friend class ASTStmtReader; // deserialization
unsigned : NumExprBits;
// These don't need to be particularly wide, because they're
// strictly limited by the forms of expressions we permit.
unsigned NumSubExprs : 8;
unsigned ResultIndex : 32 - 8 - NumExprBits;
};
class ObjCIndirectCopyRestoreExprBitfields {
friend class ObjCIndirectCopyRestoreExpr;
unsigned : NumExprBits;
@ -201,6 +214,7 @@ protected:
DeclRefExprBitfields DeclRefExprBits;
CastExprBitfields CastExprBits;
CallExprBitfields CallExprBits;
PseudoObjectExprBitfields PseudoObjectExprBits;
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
};

View File

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

View File

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

View File

@ -160,12 +160,14 @@ private:
if (!E) return false;
E = E->IgnoreParenCasts();
// Also look through property-getter sugar.
if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))
E = pseudoOp->getResultExpr()->IgnoreImplicit();
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E))
return propE->getGetterSelector() == DelegateSel;
return false;
}

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

View File

@ -78,6 +78,15 @@ public:
return true;
}
bool VisitPseudoObjectExpr(PseudoObjectExpr *POE) {
if (isZeroingPropIvar(POE) && isRemovable(POE)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(POE);
}
return true;
}
bool VisitBinaryOperator(BinaryOperator *BOE) {
if (isZeroingPropIvar(BOE) && isRemovable(BOE)) {
Transaction Trans(Pass.TA);
@ -142,9 +151,15 @@ private:
}
bool isZeroingPropIvar(Expr *E) {
BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E);
if (!BOE) return false;
E = E->IgnoreParens();
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
return isZeroingPropIvar(BO);
if (PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(E))
return isZeroingPropIvar(PO);
return false;
}
bool isZeroingPropIvar(BinaryOperator *BOE) {
if (BOE->getOpcode() == BO_Comma)
return isZeroingPropIvar(BOE->getLHS()) &&
isZeroingPropIvar(BOE->getRHS());
@ -152,8 +167,6 @@ private:
if (BOE->getOpcode() != BO_Assign)
return false;
ASTContext &Ctx = Pass.Ctx;
Expr *LHS = BOE->getLHS();
if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) {
ObjCIvarDecl *IVDecl = IV->getDecl();
@ -172,25 +185,38 @@ private:
if (!IvarBacksPropertySynthesis)
return false;
}
else if (ObjCPropertyRefExpr *PropRefExp = dyn_cast<ObjCPropertyRefExpr>(LHS)) {
else
return false;
return isZero(BOE->getRHS());
}
bool isZeroingPropIvar(PseudoObjectExpr *PO) {
BinaryOperator *BO = dyn_cast<BinaryOperator>(PO->getSyntacticForm());
if (!BO) return false;
if (BO->getOpcode() != BO_Assign) return false;
ObjCPropertyRefExpr *PropRefExp =
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParens());
if (!PropRefExp) return false;
// TODO: Using implicit property decl.
if (PropRefExp->isImplicitProperty())
return false;
if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) {
if (!SynthesizedProperties.count(PDecl))
return false;
}
}
else
return false;
Expr *RHS = BOE->getRHS();
bool RHSIsNull = RHS->isNullPointerConstant(Ctx,
Expr::NPC_ValueDependentIsNull);
if (RHSIsNull)
return isZero(cast<OpaqueValueExpr>(BO->getRHS())->getSourceExpr());
}
bool isZero(Expr *E) {
if (E->isNullPointerConstant(Pass.Ctx, Expr::NPC_ValueDependentIsNull))
return true;
return isZeroingPropIvar(RHS);
return isZeroingPropIvar(E);
}
};

View File

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

View File

@ -1694,6 +1694,19 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1,
R1 = getSourceRange();
return true;
case PseudoObjectExprClass: {
const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(this);
// Only complain about things that have the form of a getter.
if (isa<UnaryOperator>(PO->getSyntacticForm()) ||
isa<BinaryOperator>(PO->getSyntacticForm()))
return false;
Loc = getExprLoc();
R1 = getSourceRange();
return true;
}
case StmtExprClass: {
// Statement exprs don't logically have side effects themselves, but are
// sometimes used in macros in ways that give them a type that is unused.
@ -2598,6 +2611,9 @@ Expr::isNullPointerConstant(ASTContext &Ctx,
} else if (const MaterializeTemporaryExpr *M
= dyn_cast<MaterializeTemporaryExpr>(this)) {
return M->GetTemporaryExpr()->isNullPointerConstant(Ctx, NPC);
} else if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(this)) {
if (const Expr *Source = OVE->getSourceExpr())
return Source->isNullPointerConstant(Ctx, NPC);
}
// C++0x nullptr_t is always a null pointer constant.
@ -3306,6 +3322,72 @@ const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) {
return cast<OpaqueValueExpr>(e);
}
PseudoObjectExpr *PseudoObjectExpr::Create(ASTContext &Context, EmptyShell sh,
unsigned numSemanticExprs) {
void *buffer = Context.Allocate(sizeof(PseudoObjectExpr) +
(1 + numSemanticExprs) * sizeof(Expr*),
llvm::alignOf<PseudoObjectExpr>());
return new(buffer) PseudoObjectExpr(sh, numSemanticExprs);
}
PseudoObjectExpr::PseudoObjectExpr(EmptyShell shell, unsigned numSemanticExprs)
: Expr(PseudoObjectExprClass, shell) {
PseudoObjectExprBits.NumSubExprs = numSemanticExprs + 1;
}
PseudoObjectExpr *PseudoObjectExpr::Create(ASTContext &C, Expr *syntax,
ArrayRef<Expr*> semantics,
unsigned resultIndex) {
assert(syntax && "no syntactic expression!");
assert(semantics.size() && "no semantic expressions!");
QualType type;
ExprValueKind VK;
if (resultIndex == NoResult) {
type = C.VoidTy;
VK = VK_RValue;
} else {
assert(resultIndex < semantics.size());
type = semantics[resultIndex]->getType();
VK = semantics[resultIndex]->getValueKind();
assert(semantics[resultIndex]->getObjectKind() == OK_Ordinary);
}
void *buffer = C.Allocate(sizeof(PseudoObjectExpr) +
(1 + semantics.size()) * sizeof(Expr*),
llvm::alignOf<PseudoObjectExpr>());
return new(buffer) PseudoObjectExpr(type, VK, syntax, semantics,
resultIndex);
}
PseudoObjectExpr::PseudoObjectExpr(QualType type, ExprValueKind VK,
Expr *syntax, ArrayRef<Expr*> semantics,
unsigned resultIndex)
: Expr(PseudoObjectExprClass, type, VK, OK_Ordinary,
/*filled in at end of ctor*/ false, false, false, false) {
PseudoObjectExprBits.NumSubExprs = semantics.size() + 1;
PseudoObjectExprBits.ResultIndex = resultIndex + 1;
for (unsigned i = 0, e = semantics.size() + 1; i != e; ++i) {
Expr *E = (i == 0 ? syntax : semantics[i-1]);
getSubExprsBuffer()[i] = E;
if (E->isTypeDependent())
ExprBits.TypeDependent = true;
if (E->isValueDependent())
ExprBits.ValueDependent = true;
if (E->isInstantiationDependent())
ExprBits.InstantiationDependent = true;
if (E->containsUnexpandedParameterPack())
ExprBits.ContainsUnexpandedParameterPack = true;
if (isa<OpaqueValueExpr>(E))
assert(cast<OpaqueValueExpr>(E)->getSourceExpr() != 0 &&
"opaque-value semantic expressions for pseudo-object "
"operations must have sources");
}
}
//===----------------------------------------------------------------------===//
// ExprIterator.
//===----------------------------------------------------------------------===//

View File

@ -232,6 +232,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
return ClassifyExprValueKind(Lang, E,
cast<OpaqueValueExpr>(E)->getValueKind());
// Pseudo-object expressions can produce l-values with reference magic.
case Expr::PseudoObjectExprClass:
return ClassifyExprValueKind(Lang, E,
cast<PseudoObjectExpr>(E)->getValueKind());
// Implicit casts are lvalues if they're lvalue casts. Other than that, we
// only specifically record class temporaries.
case Expr::ImplicitCastExprClass:

View File

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

View File

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

View File

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

View File

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

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) {
VisitExpr(S);
ID.AddInteger(S->getOp());

View File

@ -344,6 +344,7 @@ private:
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
CFGBlock *VisitReturnStmt(ReturnStmt *R);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
AddStmtChoice asc);
CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
@ -981,6 +982,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::OpaqueValueExprClass:
return Block;
case Stmt::PseudoObjectExprClass:
return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S));
case Stmt::ReturnStmtClass:
return VisitReturnStmt(cast<ReturnStmt>(S));
@ -1907,6 +1911,31 @@ CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *S) {
return NYS();
}
CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) {
autoCreateBlock();
// Add the PseudoObject as the last thing.
appendStmt(Block, E);
CFGBlock *lastBlock = Block;
// Before that, evaluate all of the semantics in order. In
// CFG-land, that means appending them in reverse order.
for (unsigned i = E->getNumSemanticExprs(); i != 0; ) {
Expr *Semantic = E->getSemanticExpr(--i);
// If the semantic is an opaque value, we're being asked to bind
// it to its source expression.
if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Semantic))
Semantic = OVE->getSourceExpr();
if (CFGBlock *B = Visit(Semantic))
lastBlock = B;
}
return lastBlock;
}
CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
CFGBlock *LoopSuccessor = NULL;

View File

@ -288,6 +288,18 @@ void TransferFunctions::Visit(Stmt *S) {
}
break;
}
case Stmt::PseudoObjectExprClass: {
// A pseudo-object operation only directly consumes its result
// expression.
Expr *child = cast<PseudoObjectExpr>(S)->getResultExpr();
if (!child) return;
if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child))
child = OV->getSourceExpr();
child = child->IgnoreParens();
val.liveStmts = LV.SSetFact.add(val.liveStmts, child);
return;
}
// FIXME: These cases eventually shouldn't be needed.
case Stmt::ExprWithCleanupsClass: {
S = cast<ExprWithCleanups>(S)->getSubExpr();

View File

@ -672,6 +672,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
return EmitStringLiteralLValue(cast<StringLiteral>(E));
case Expr::ObjCEncodeExprClass:
return EmitObjCEncodeExprLValue(cast<ObjCEncodeExpr>(E));
case Expr::PseudoObjectExprClass:
return EmitPseudoObjectLValue(cast<PseudoObjectExpr>(E));
case Expr::BlockDeclRefExprClass:
return EmitBlockDeclRefLValue(cast<BlockDeclRefExpr>(E));
@ -2768,3 +2770,86 @@ void CodeGenFunction::SetFPAccuracy(llvm::Value *Val, unsigned AccuracyN,
cast<llvm::Instruction>(Val)->setMetadata(llvm::LLVMContext::MD_fpaccuracy,
Node);
}
namespace {
struct LValueOrRValue {
LValue LV;
RValue RV;
};
}
static LValueOrRValue emitPseudoObjectExpr(CodeGenFunction &CGF,
const PseudoObjectExpr *E,
bool forLValue,
AggValueSlot slot) {
llvm::SmallVector<CodeGenFunction::OpaqueValueMappingData, 4> opaques;
// Find the result expression, if any.
const Expr *resultExpr = E->getResultExpr();
LValueOrRValue result;
for (PseudoObjectExpr::const_semantics_iterator
i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) {
const Expr *semantic = *i;
// If this semantic expression is an opaque value, bind it
// to the result of its source expression.
if (const OpaqueValueExpr *ov = dyn_cast<OpaqueValueExpr>(semantic)) {
// If this is the result expression, we may need to evaluate
// directly into the slot.
typedef CodeGenFunction::OpaqueValueMappingData OVMA;
OVMA opaqueData;
if (ov == resultExpr && ov->isRValue() && !forLValue &&
CodeGenFunction::hasAggregateLLVMType(ov->getType()) &&
!ov->getType()->isAnyComplexType()) {
CGF.EmitAggExpr(ov->getSourceExpr(), slot);
LValue LV = CGF.MakeAddrLValue(slot.getAddr(), ov->getType());
opaqueData = OVMA::bind(CGF, ov, LV);
result.RV = slot.asRValue();
// Otherwise, emit as normal.
} else {
opaqueData = OVMA::bind(CGF, ov, ov->getSourceExpr());
// If this is the result, also evaluate the result now.
if (ov == resultExpr) {
if (forLValue)
result.LV = CGF.EmitLValue(ov);
else
result.RV = CGF.EmitAnyExpr(ov, slot);
}
}
opaques.push_back(opaqueData);
// Otherwise, if the expression is the result, evaluate it
// and remember the result.
} else if (semantic == resultExpr) {
if (forLValue)
result.LV = CGF.EmitLValue(semantic);
else
result.RV = CGF.EmitAnyExpr(semantic, slot);
// Otherwise, evaluate the expression in an ignored context.
} else {
CGF.EmitIgnoredExpr(semantic);
}
}
// Unbind all the opaques now.
for (unsigned i = 0, e = opaques.size(); i != e; ++i)
opaques[i].unbind(CGF);
return result;
}
RValue CodeGenFunction::EmitPseudoObjectRValue(const PseudoObjectExpr *E,
AggValueSlot slot) {
return emitPseudoObjectExpr(*this, E, false, slot).RV;
}
LValue CodeGenFunction::EmitPseudoObjectLValue(const PseudoObjectExpr *E) {
return emitPseudoObjectExpr(*this, E, true, AggValueSlot::ignored()).LV;
}

View File

@ -148,6 +148,15 @@ public:
void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E);
void VisitOpaqueValueExpr(OpaqueValueExpr *E);
void VisitPseudoObjectExpr(PseudoObjectExpr *E) {
if (E->isGLValue()) {
LValue LV = CGF.EmitPseudoObjectLValue(E);
return EmitFinalDestCopy(E, LV);
}
CGF.EmitPseudoObjectRValue(E, EnsureSlot(E->getType()));
}
void VisitVAArgExpr(VAArgExpr *E);
void EmitInitializationToLValue(Expr *E, LValue Address);

View File

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

View File

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

View File

@ -2231,6 +2231,59 @@ static bool shouldEmitSeparateBlockRetain(const Expr *e) {
return true;
}
/// Try to emit a PseudoObjectExpr at +1.
///
/// This massively duplicates emitPseudoObjectRValue.
static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF,
const PseudoObjectExpr *E) {
llvm::SmallVector<CodeGenFunction::OpaqueValueMappingData, 4> opaques;
// Find the result expression.
const Expr *resultExpr = E->getResultExpr();
assert(resultExpr);
TryEmitResult result;
for (PseudoObjectExpr::const_semantics_iterator
i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) {
const Expr *semantic = *i;
// If this semantic expression is an opaque value, bind it
// to the result of its source expression.
if (const OpaqueValueExpr *ov = dyn_cast<OpaqueValueExpr>(semantic)) {
typedef CodeGenFunction::OpaqueValueMappingData OVMA;
OVMA opaqueData;
// If this semantic is the result of the pseudo-object
// expression, try to evaluate the source as +1.
if (ov == resultExpr) {
assert(!OVMA::shouldBindAsLValue(ov));
result = tryEmitARCRetainScalarExpr(CGF, ov->getSourceExpr());
opaqueData = OVMA::bind(CGF, ov, RValue::get(result.getPointer()));
// Otherwise, just bind it.
} else {
opaqueData = OVMA::bind(CGF, ov, ov->getSourceExpr());
}
opaques.push_back(opaqueData);
// Otherwise, if the expression is the result, evaluate it
// and remember the result.
} else if (semantic == resultExpr) {
result = tryEmitARCRetainScalarExpr(CGF, semantic);
// Otherwise, evaluate the expression in an ignored context.
} else {
CGF.EmitIgnoredExpr(semantic);
}
}
// Unbind all the opaques now.
for (unsigned i = 0, e = opaques.size(); i != e; ++i)
opaques[i].unbind(CGF);
return result;
}
static TryEmitResult
tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
// Look through cleanups.
@ -2356,6 +2409,17 @@ tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) {
llvm::Value *result = emitARCRetainCall(CGF, e);
if (resultType) result = CGF.Builder.CreateBitCast(result, resultType);
return TryEmitResult(result, true);
// Look through pseudo-object expressions.
} else if (const PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) {
TryEmitResult result
= tryEmitARCRetainPseudoObject(CGF, pseudo);
if (resultType) {
llvm::Value *value = result.getPointer();
value = CGF.Builder.CreateBitCast(value, resultType);
result.setPointer(value);
}
return result;
}
// Conservatively halt the search at any other expression kind.

View File

@ -27,6 +27,7 @@ namespace clang {
class ObjCPropertyRefExpr;
namespace CodeGen {
class AggValueSlot;
class CGBitFieldInfo;
/// RValue - This trivial value class is used to represent the result of an

View File

@ -952,18 +952,84 @@ public:
PeepholeProtection() : Inst(0) {}
};
/// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
class OpaqueValueMapping {
CodeGenFunction &CGF;
/// A non-RAII class containing all the information about a bound
/// opaque value. OpaqueValueMapping, below, is a RAII wrapper for
/// this which makes individual mappings very simple; using this
/// class directly is useful when you have a variable number of
/// opaque values or don't want the RAII functionality for some
/// reason.
class OpaqueValueMappingData {
const OpaqueValueExpr *OpaqueValue;
bool BoundLValue;
CodeGenFunction::PeepholeProtection Protection;
OpaqueValueMappingData(const OpaqueValueExpr *ov,
bool boundLValue)
: OpaqueValue(ov), BoundLValue(boundLValue) {}
public:
OpaqueValueMappingData() : OpaqueValue(0) {}
static bool shouldBindAsLValue(const Expr *expr) {
return expr->isGLValue() || expr->getType()->isRecordType();
}
static OpaqueValueMappingData bind(CodeGenFunction &CGF,
const OpaqueValueExpr *ov,
const Expr *e) {
if (shouldBindAsLValue(ov))
return bind(CGF, ov, CGF.EmitLValue(e));
return bind(CGF, ov, CGF.EmitAnyExpr(e));
}
static OpaqueValueMappingData bind(CodeGenFunction &CGF,
const OpaqueValueExpr *ov,
const LValue &lv) {
assert(shouldBindAsLValue(ov));
CGF.OpaqueLValues.insert(std::make_pair(ov, lv));
return OpaqueValueMappingData(ov, true);
}
static OpaqueValueMappingData bind(CodeGenFunction &CGF,
const OpaqueValueExpr *ov,
const RValue &rv) {
assert(!shouldBindAsLValue(ov));
CGF.OpaqueRValues.insert(std::make_pair(ov, rv));
OpaqueValueMappingData data(ov, false);
// Work around an extremely aggressive peephole optimization in
// EmitScalarConversion which assumes that all other uses of a
// value are extant.
data.Protection = CGF.protectFromPeepholes(rv);
return data;
}
bool isValid() const { return OpaqueValue != 0; }
void clear() { OpaqueValue = 0; }
void unbind(CodeGenFunction &CGF) {
assert(OpaqueValue && "no data to unbind!");
if (BoundLValue) {
CGF.OpaqueLValues.erase(OpaqueValue);
} else {
CGF.OpaqueRValues.erase(OpaqueValue);
CGF.unprotectFromPeepholes(Protection);
}
}
};
/// An RAII object to set (and then clear) a mapping for an OpaqueValueExpr.
class OpaqueValueMapping {
CodeGenFunction &CGF;
OpaqueValueMappingData Data;
public:
static bool shouldBindAsLValue(const Expr *expr) {
return OpaqueValueMappingData::shouldBindAsLValue(expr);
}
/// Build the opaque value mapping for the given conditional
/// operator if it's the GNU ?: extension. This is a common
/// enough pattern that the convenience operator is really
@ -971,75 +1037,34 @@ public:
///
OpaqueValueMapping(CodeGenFunction &CGF,
const AbstractConditionalOperator *op) : CGF(CGF) {
if (isa<ConditionalOperator>(op)) {
OpaqueValue = 0;
BoundLValue = false;
if (isa<ConditionalOperator>(op))
// Leave Data empty.
return;
}
const BinaryConditionalOperator *e = cast<BinaryConditionalOperator>(op);
init(e->getOpaqueValue(), e->getCommon());
Data = OpaqueValueMappingData::bind(CGF, e->getOpaqueValue(),
e->getCommon());
}
OpaqueValueMapping(CodeGenFunction &CGF,
const OpaqueValueExpr *opaqueValue,
LValue lvalue)
: CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(true) {
assert(opaqueValue && "no opaque value expression!");
assert(shouldBindAsLValue(opaqueValue));
initLValue(lvalue);
: CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, lvalue)) {
}
OpaqueValueMapping(CodeGenFunction &CGF,
const OpaqueValueExpr *opaqueValue,
RValue rvalue)
: CGF(CGF), OpaqueValue(opaqueValue), BoundLValue(false) {
assert(opaqueValue && "no opaque value expression!");
assert(!shouldBindAsLValue(opaqueValue));
initRValue(rvalue);
: CGF(CGF), Data(OpaqueValueMappingData::bind(CGF, opaqueValue, rvalue)) {
}
void pop() {
assert(OpaqueValue && "mapping already popped!");
popImpl();
OpaqueValue = 0;
Data.unbind(CGF);
Data.clear();
}
~OpaqueValueMapping() {
if (OpaqueValue) popImpl();
}
private:
void popImpl() {
if (BoundLValue)
CGF.OpaqueLValues.erase(OpaqueValue);
else {
CGF.OpaqueRValues.erase(OpaqueValue);
CGF.unprotectFromPeepholes(Protection);
}
}
void init(const OpaqueValueExpr *ov, const Expr *e) {
OpaqueValue = ov;
BoundLValue = shouldBindAsLValue(ov);
assert(BoundLValue == shouldBindAsLValue(e)
&& "inconsistent expression value kinds!");
if (BoundLValue)
initLValue(CGF.EmitLValue(e));
else
initRValue(CGF.EmitAnyExpr(e));
}
void initLValue(const LValue &lv) {
CGF.OpaqueLValues.insert(std::make_pair(OpaqueValue, lv));
}
void initRValue(const RValue &rv) {
// Work around an extremely aggressive peephole optimization in
// EmitScalarConversion which assumes that all other uses of a
// value are extant.
Protection = CGF.protectFromPeepholes(rv);
CGF.OpaqueRValues.insert(std::make_pair(OpaqueValue, rv));
if (Data.isValid()) Data.unbind(CGF);
}
};
@ -2015,6 +2040,10 @@ public:
LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
LValue EmitOpaqueValueLValue(const OpaqueValueExpr *e);
RValue EmitPseudoObjectRValue(const PseudoObjectExpr *e,
AggValueSlot slot = AggValueSlot::ignored());
LValue EmitPseudoObjectLValue(const PseudoObjectExpr *e);
llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface,
const ObjCIvarDecl *Ivar);
LValue EmitLValueForAnonRecordField(llvm::Value* Base,

View File

@ -138,12 +138,6 @@ namespace {
llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs;
// This maps a property to it's assignment statement.
llvm::DenseMap<Expr *, BinaryOperator *> PropSetters;
// This maps a property to it's synthesied message expression.
// This allows us to rewrite chained getters (e.g. o.a.b.c).
llvm::DenseMap<Expr *, Stmt *> PropGetters;
// This maps an original source AST to it's rewritten form. This allows
// us to avoid rewriting the same node twice (which is very uncommon).
// This is needed to support some of the exotic property rewriting.
@ -154,6 +148,19 @@ namespace {
VarDecl *GlobalVarDecl;
bool DisableReplaceStmt;
class DisableReplaceStmtScope {
RewriteObjC &R;
bool SavedValue;
public:
DisableReplaceStmtScope(RewriteObjC &R)
: R(R), SavedValue(R.DisableReplaceStmt) {
R.DisableReplaceStmt = true;
}
~DisableReplaceStmtScope() {
R.DisableReplaceStmt = SavedValue;
}
};
static const int OBJC_ABI_VERSION = 7;
public:
@ -186,7 +193,7 @@ namespace {
return; // We can't rewrite the same node twice.
if (DisableReplaceStmt)
return; // Used when rewriting the assignment of a property setter.
return;
// If replacement succeeded or warning disabled return with no warning.
if (!Rewrite.ReplaceStmt(Old, New)) {
@ -200,6 +207,9 @@ namespace {
}
void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) {
if (DisableReplaceStmt)
return;
// Measure the old text.
int Size = Rewrite.getRangeSize(SrcRange);
if (Size == -1) {
@ -282,18 +292,14 @@ namespace {
// Expression Rewriting.
Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S);
void CollectPropertySetters(Stmt *S);
Stmt *CurrentBody;
ParentMap *PropParentMap; // created lazily.
Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp);
Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV, SourceLocation OrigStart,
bool &replaced);
Stmt *RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced);
Stmt *RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr);
Stmt *RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt,
SourceRange SrcRange);
Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV);
Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo);
Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo);
Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp);
Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp);
Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp);
@ -1281,184 +1287,173 @@ void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) {
"/* @end */");
}
Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *newStmt,
SourceRange SrcRange) {
ObjCMethodDecl *OMD = 0;
QualType Ty;
Selector Sel;
Stmt *Receiver = 0;
bool Super = false;
QualType SuperTy;
SourceLocation SuperLocation;
SourceLocation SelectorLoc;
// Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr or ObjCImplicitSetterGetterRefExpr.
// This allows us to reuse all the fun and games in SynthMessageExpr().
if (ObjCPropertyRefExpr *PropRefExpr =
dyn_cast<ObjCPropertyRefExpr>(BinOp->getLHS())) {
SelectorLoc = PropRefExpr->getLocation();
if (PropRefExpr->isExplicitProperty()) {
ObjCPropertyDecl *PDecl = PropRefExpr->getExplicitProperty();
OMD = PDecl->getSetterMethodDecl();
Ty = PDecl->getType();
Sel = PDecl->getSetterName();
} else {
OMD = PropRefExpr->getImplicitPropertySetter();
Sel = OMD->getSelector();
Ty = (*OMD->param_begin())->getType();
Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) {
SourceRange OldRange = PseudoOp->getSourceRange();
// We just magically know some things about the structure of this
// expression.
ObjCMessageExpr *OldMsg =
cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr(
PseudoOp->getNumSemanticExprs() - 1));
// Because the rewriter doesn't allow us to rewrite rewritten code,
// we need to suppress rewriting the sub-statements.
Expr *Base, *RHS;
{
DisableReplaceStmtScope S(*this);
// Rebuild the base expression if we have one.
Base = 0;
if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) {
Base = OldMsg->getInstanceReceiver();
Base = cast<OpaqueValueExpr>(Base)->getSourceExpr();
Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base));
}
Super = PropRefExpr->isSuperReceiver();
if (!Super) {
Receiver = PropRefExpr->getBase();
} else {
SuperTy = PropRefExpr->getSuperReceiverType();
SuperLocation = PropRefExpr->getReceiverLocation();
// 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(PseudoObjectExpr *PseudoOp) {
SourceRange OldRange = PseudoOp->getSourceRange();
// We just magically know some things about the structure of this
// expression.
ObjCMessageExpr *OldMsg =
cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit());
// Because the rewriter doesn't allow us to rewrite rewritten code,
// we need to suppress rewriting the sub-statements.
Expr *Base = 0;
{
DisableReplaceStmtScope S(*this);
// Rebuild the base expression if we have one.
if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) {
Base = OldMsg->getInstanceReceiver();
Base = cast<OpaqueValueExpr>(Base)->getSourceExpr();
Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base));
}
}
assert(OMD && "RewritePropertyOrImplicitSetter - null OMD");
// Intentionally empty.
SmallVector<SourceLocation, 1> SelLocs;
SmallVector<Expr*, 1> Args;
ObjCMessageExpr *MsgExpr;
if (Super)
MsgExpr = ObjCMessageExpr::Create(*Context,
Ty.getNonReferenceType(),
Expr::getValueKindForType(Ty),
/*FIXME?*/SourceLocation(),
SuperLocation,
/*IsInstanceSuper=*/true,
SuperTy,
Sel, SelectorLoc, OMD,
newStmt,
/*FIXME:*/SourceLocation());
else {
// FIXME. Refactor this into common code with that in
// RewritePropertyOrImplicitGetter
assert(Receiver && "RewritePropertyOrImplicitSetter - null Receiver");
if (Expr *Exp = dyn_cast<Expr>(Receiver))
if (PropGetters[Exp])
// This allows us to handle chain/nested property/implicit getters.
Receiver = PropGetters[Exp];
ObjCMessageExpr *NewMsg;
switch (OldMsg->getReceiverKind()) {
case ObjCMessageExpr::Class:
NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(),
OldMsg->getValueKind(),
OldMsg->getLeftLoc(),
OldMsg->getClassReceiverTypeInfo(),
OldMsg->getSelector(),
SelLocs,
OldMsg->getMethodDecl(),
Args,
OldMsg->getRightLoc());
break;
MsgExpr = ObjCMessageExpr::Create(*Context,
Ty.getNonReferenceType(),
Expr::getValueKindForType(Ty),
/*FIXME: */SourceLocation(),
cast<Expr>(Receiver),
Sel, SelectorLoc, OMD,
newStmt,
/*FIXME:*/SourceLocation());
}
Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr);
case ObjCMessageExpr::Instance:
NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(),
OldMsg->getValueKind(),
OldMsg->getLeftLoc(),
Base,
OldMsg->getSelector(),
SelLocs,
OldMsg->getMethodDecl(),
Args,
OldMsg->getRightLoc());
break;
// Now do the actual rewrite.
ReplaceStmtWithRange(BinOp, ReplacingStmt, SrcRange);
//delete BinOp;
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references
// to things that stay around.
Context->Deallocate(MsgExpr);
return ReplacingStmt;
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 *RewriteObjC::RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr) {
// Synthesize a ObjCMessageExpr from a ObjCPropertyRefExpr or ImplicitGetter.
// This allows us to reuse all the fun and games in SynthMessageExpr().
Stmt *Receiver = 0;
ObjCMethodDecl *OMD = 0;
QualType Ty;
Selector Sel;
bool Super = false;
QualType SuperTy;
SourceLocation SuperLocation;
SourceLocation SelectorLoc;
if (ObjCPropertyRefExpr *PropRefExpr =
dyn_cast<ObjCPropertyRefExpr>(PropOrGetterRefExpr)) {
SelectorLoc = PropRefExpr->getLocation();
if (PropRefExpr->isExplicitProperty()) {
ObjCPropertyDecl *PDecl = PropRefExpr->getExplicitProperty();
OMD = PDecl->getGetterMethodDecl();
Ty = PDecl->getType();
Sel = PDecl->getGetterName();
} else {
OMD = PropRefExpr->getImplicitPropertyGetter();
Sel = OMD->getSelector();
Ty = OMD->getResultType();
}
Super = PropRefExpr->isSuperReceiver();
if (!Super)
Receiver = PropRefExpr->getBase();
else {
SuperTy = PropRefExpr->getSuperReceiverType();
SuperLocation = PropRefExpr->getReceiverLocation();
}
Stmt *Replacement = SynthMessageExpr(NewMsg);
ReplaceStmtWithRange(PseudoOp, Replacement, OldRange);
return Replacement;
}
assert (OMD && "RewritePropertyOrImplicitGetter - OMD is null");
Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
SourceRange OldRange = IV->getSourceRange();
Expr *BaseExpr = IV->getBase();
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());
// Rewrite the base, but without actually doing replaces.
{
DisableReplaceStmtScope S(*this);
BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr));
IV->setBase(BaseExpr);
}
Stmt *ReplacingStmt = SynthMessageExpr(MsgExpr, MsgExpr->getLocStart(),
MsgExpr->getLocEnd());
if (!PropParentMap)
PropParentMap = new ParentMap(CurrentBody);
bool NestedPropertyRef = false;
Stmt *Parent = PropParentMap->getParent(PropOrGetterRefExpr);
ImplicitCastExpr*ICE=0;
if (Parent)
if ((ICE = dyn_cast<ImplicitCastExpr>(Parent))) {
assert((ICE->getCastKind() == CK_GetObjCProperty)
&& "RewritePropertyOrImplicitGetter");
Parent = PropParentMap->getParent(Parent);
NestedPropertyRef = (Parent && isa<ObjCPropertyRefExpr>(Parent));
}
if (NestedPropertyRef) {
// We stash away the ReplacingStmt since actually doing the
// replacement/rewrite won't work for nested getters (e.g. obj.p.i)
PropGetters[ICE] = ReplacingStmt;
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references
// to things that stay around.
Context->Deallocate(MsgExpr);
return PropOrGetterRefExpr; // return the original...
} else {
ReplaceStmt(PropOrGetterRefExpr, ReplacingStmt);
// delete PropRefExpr; elsewhere...
// NOTE: We don't want to call MsgExpr->Destroy(), as it holds references
// to things that stay around.
Context->Deallocate(MsgExpr);
return ReplacingStmt;
}
}
Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV,
SourceLocation OrigStart,
bool &replaced) {
ObjCIvarDecl *D = IV->getDecl();
const Expr *BaseExpr = IV->getBase();
Expr *Replacement = IV;
if (CurMethodDef) {
if (BaseExpr->getType()->isObjCObjectPointerType()) {
const ObjCInterfaceType *iFaceDecl =
@ -1483,25 +1478,19 @@ Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV,
CK_BitCast,
IV->getBase());
// Don't forget the parens to enforce the proper binding.
ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(),
IV->getBase()->getLocEnd(),
ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(),
OldRange.getEnd(),
castExpr);
replaced = true;
if (IV->isFreeIvar() &&
CurMethodDef->getClassInterface() == iFaceDecl->getDecl()) {
MemberExpr *ME = new (Context) MemberExpr(PE, true, D,
IV->getLocation(),
D->getType(),
VK_LValue, OK_Ordinary);
// delete IV; leak for now, see RewritePropertyOrImplicitSetter() usage for more info.
return ME;
}
// 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].
Replacement = ME;
} else {
IV->setBase(PE);
return IV;
}
}
} else { // we are outside a method.
assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method");
@ -1532,36 +1521,15 @@ Stmt *RewriteObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV,
// Don't forget the parens to enforce the proper binding.
ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(),
IV->getBase()->getLocEnd(), castExpr);
replaced = true;
// Cannot delete IV->getBase(), since PE points to it.
// Replace the old base with the cast. This is important when doing
// embedded rewrites. For example, [newInv->_container addObject:0].
IV->setBase(PE);
return IV;
}
}
return IV;
}
Stmt *RewriteObjC::RewriteObjCNestedIvarRefExpr(Stmt *S, bool &replaced) {
for (Stmt::child_range CI = S->children(); CI; ++CI) {
if (*CI) {
Stmt *newStmt = RewriteObjCNestedIvarRefExpr(*CI, replaced);
if (newStmt)
*CI = newStmt;
}
}
if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) {
SourceRange OrigStmtRange = S->getSourceRange();
Stmt *newStmt = RewriteObjCIvarRefExpr(IvarRefExpr, OrigStmtRange.getBegin(),
replaced);
return newStmt;
}
if (ObjCMessageExpr *MsgRefExpr = dyn_cast<ObjCMessageExpr>(S)) {
Stmt *newStmt = SynthMessageExpr(MsgRefExpr);
return newStmt;
}
return S;
ReplaceStmtWithRange(IV, Replacement, OldRange);
return Replacement;
}
/// SynthCountByEnumWithState - To print:
@ -4753,6 +4721,9 @@ Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) {
return CondExpr;
} else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) {
CPT = IRE->getType()->getAs<BlockPointerType>();
} else if (const PseudoObjectExpr *POE
= dyn_cast<PseudoObjectExpr>(BlockExp)) {
CPT = POE->getType()->castAs<BlockPointerType>();
} else {
assert(1 && "RewriteBlockClass: Bad type");
}
@ -5580,26 +5551,6 @@ bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) {
// Function Body / Expression rewriting
//===----------------------------------------------------------------------===//
// This is run as a first "pass" prior to RewriteFunctionBodyOrGlobalInitializer().
// The allows the main rewrite loop to associate all ObjCPropertyRefExprs with
// their respective BinaryOperator. Without this knowledge, we'd need to rewrite
// the ObjCPropertyRefExpr twice (once as a getter, and later as a setter).
// Since the rewriter isn't capable of rewriting rewritten code, it's important
// we get this right.
void RewriteObjC::CollectPropertySetters(Stmt *S) {
// Perform a bottom up traversal of all children.
for (Stmt::child_range CI = S->children(); CI; ++CI)
if (*CI)
CollectPropertySetters(*CI);
if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
if (BinOp->isAssignmentOp()) {
if (isa<ObjCPropertyRefExpr>(BinOp->getLHS()))
PropSetters[BinOp->getLHS()] = BinOp;
}
}
}
Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) ||
isa<DoStmt>(S) || isa<ForStmt>(S))
@ -5609,46 +5560,28 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
ObjCBcLabelNo.push_back(++BcLabelCount);
}
// Pseudo-object operations and ivar references need special
// treatment because we're going to recursively rewrite them.
if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) {
if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) {
return RewritePropertyOrImplicitSetter(PseudoOp);
} else {
return RewritePropertyOrImplicitGetter(PseudoOp);
}
} else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) {
return RewriteObjCIvarRefExpr(IvarRefExpr);
}
SourceRange OrigStmtRange = S->getSourceRange();
// Perform a bottom up rewrite of all children.
for (Stmt::child_range CI = S->children(); CI; ++CI)
if (*CI) {
Stmt *newStmt;
Stmt *ChildStmt = (*CI);
if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(ChildStmt)) {
Expr *OldBase = IvarRefExpr->getBase();
bool replaced = false;
newStmt = RewriteObjCNestedIvarRefExpr(ChildStmt, replaced);
if (replaced) {
if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(newStmt))
ReplaceStmt(OldBase, IRE->getBase());
else
ReplaceStmt(ChildStmt, newStmt);
}
}
else
newStmt = RewriteFunctionBodyOrGlobalInitializer(ChildStmt);
Stmt *childStmt = (*CI);
Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt);
if (newStmt) {
if (Expr *PropOrImplicitRefExpr = dyn_cast<Expr>(ChildStmt))
if (PropSetters[PropOrImplicitRefExpr] == S) {
S = newStmt;
newStmt = 0;
}
if (newStmt)
*CI = newStmt;
}
// If dealing with an assignment with LHS being a property reference
// expression, the entire assignment tree is rewritten into a property
// setter messaging. This involvs the RHS too. Do not attempt to rewrite
// RHS again.
if (Expr *Exp = dyn_cast<Expr>(ChildStmt))
if (isa<ObjCPropertyRefExpr>(Exp)) {
if (PropSetters[Exp]) {
++CI;
continue;
}
}
}
if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
@ -5661,7 +5594,6 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
// Rewrite the block body in place.
Stmt *SaveCurrentBody = CurrentBody;
CurrentBody = BE->getBody();
CollectPropertySetters(CurrentBody);
PropParentMap = 0;
// block literal on rhs of a property-dot-sytax assignment
// must be replaced by its synthesize ast so getRewrittenText
@ -5689,67 +5621,6 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) {
if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S))
return RewriteAtEncode(AtEncode);
if (isa<ObjCPropertyRefExpr>(S)) {
Expr *PropOrImplicitRefExpr = dyn_cast<Expr>(S);
assert(PropOrImplicitRefExpr && "Property or implicit setter/getter is null");
BinaryOperator *BinOp = PropSetters[PropOrImplicitRefExpr];
if (BinOp) {
// Because the rewriter doesn't allow us to rewrite rewritten code,
// we need to rewrite the right hand side prior to rewriting the setter.
DisableReplaceStmt = true;
// Save the source range. Even if we disable the replacement, the
// rewritten node will have been inserted into the tree. If the synthesized
// node is at the 'end', the rewriter will fail. Consider this:
// self.errorHandler = handler ? handler :
// ^(NSURL *errorURL, NSError *error) { return (BOOL)1; };
SourceRange SrcRange = BinOp->getSourceRange();
Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(BinOp->getRHS());
// Need to rewrite the ivar access expression if need be.
if (isa<ObjCIvarRefExpr>(newStmt)) {
bool replaced = false;
newStmt = RewriteObjCNestedIvarRefExpr(newStmt, replaced);
}
DisableReplaceStmt = false;
//
// Unlike the main iterator, we explicily avoid changing 'BinOp'. If
// we changed the RHS of BinOp, the rewriter would fail (since it needs
// to see the original expression). Consider this example:
//
// Foo *obj1, *obj2;
//
// obj1.i = [obj2 rrrr];
//
// 'BinOp' for the previous expression looks like:
//
// (BinaryOperator 0x231ccf0 'int' '='
// (ObjCPropertyRefExpr 0x231cc70 'int' Kind=PropertyRef Property="i"
// (DeclRefExpr 0x231cc50 'Foo *' Var='obj1' 0x231cbb0))
// (ObjCMessageExpr 0x231ccb0 'int' selector=rrrr
// (DeclRefExpr 0x231cc90 'Foo *' Var='obj2' 0x231cbe0)))
//
// 'newStmt' represents the rewritten message expression. For example:
//
// (CallExpr 0x231d300 'id':'struct objc_object *'
// (ParenExpr 0x231d2e0 'int (*)(id, SEL)'
// (CStyleCastExpr 0x231d2c0 'int (*)(id, SEL)'
// (CStyleCastExpr 0x231d220 'void *'
// (DeclRefExpr 0x231d200 'id (id, SEL, ...)' FunctionDecl='objc_msgSend' 0x231cdc0))))
//
// Note that 'newStmt' is passed to RewritePropertyOrImplicitSetter so that it
// can be used as the setter argument. ReplaceStmt() will still 'see'
// the original RHS (since we haven't altered BinOp).
//
// This implies the Rewrite* routines can no longer delete the original
// node. As a result, we now leak the original AST nodes.
//
return RewritePropertyOrImplicitSetter(BinOp, dyn_cast<Expr>(newStmt), SrcRange);
} else {
return RewritePropertyOrImplicitGetter(PropOrImplicitRefExpr);
}
}
if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S))
return RewriteAtSelector(AtSelector);
@ -5930,7 +5801,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) {
CurFunctionDef = FD;
CurFunctionDeclToDeclareForBlock = FD;
CollectPropertySetters(Body);
CurrentBody = Body;
Body =
cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body));
@ -5951,7 +5821,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
if (CompoundStmt *Body = MD->getCompoundBody()) {
CurMethodDef = MD;
CollectPropertySetters(Body);
CurrentBody = Body;
Body =
cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body));
@ -5989,7 +5858,6 @@ void RewriteObjC::HandleDeclInMainFile(Decl *D) {
}
if (VD->getInit()) {
GlobalVarDecl = VD;
CollectPropertySetters(VD->getInit());
CurrentBody = VD->getInit();
RewriteFunctionBodyOrGlobalInitializer(VD->getInit());
CurrentBody = 0;

View File

@ -4213,6 +4213,26 @@ static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) {
continue;
}
if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) {
// Only pay attention to pseudo-objects on property references.
ObjCPropertyRefExpr *pre
= dyn_cast<ObjCPropertyRefExpr>(pseudo->getSyntacticForm()
->IgnoreParens());
if (!pre) return false;
if (pre->isImplicitProperty()) return false;
ObjCPropertyDecl *property = pre->getExplicitProperty();
if (!property->isRetaining() &&
!(property->getPropertyIvarDecl() &&
property->getPropertyIvarDecl()->getType()
.getObjCLifetime() == Qualifiers::OCL_Strong))
return false;
owner.Indirect = true;
e = const_cast<Expr*>(cast<OpaqueValueExpr>(pre->getBase())
->getSourceExpr());
continue;
}
// Array ivars?
return false;

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
if (!op->getType()->isFunctionType()) {
// Use a special diagnostic for loads from property references.
if (isa<ObjCPropertyRefExpr>(op->IgnoreImplicit()->IgnoreParens())) {
if (isa<PseudoObjectExpr>(op)) {
AddressOfError = AO_Property_Expansion;
} else {
// FIXME: emit more specific diag...
@ -7483,9 +7483,6 @@ static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp,
} else if (op->getObjectKind() == OK_VectorComponent) {
// The operand cannot be an element of a vector
AddressOfError = AO_Vector_Element;
} else if (op->getObjectKind() == OK_ObjCProperty) {
// cannot take address of a property expression.
AddressOfError = AO_Property_Expansion;
} else if (dcl) { // C99 6.5.3.2p1
// We have an lvalue with a decl. Make sure the decl is not declared
// with the register storage-class specifier.
@ -8951,8 +8948,15 @@ static void MakeObjCStringLiteralFixItHint(Sema& SemaRef, QualType DstType,
return;
}
// Strip off any parens and casts.
StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr->IgnoreParenCasts());
// Ignore any parens, implicit casts (should only be
// array-to-pointer decays), and not-so-opaque values. The last is
// important for making this trigger for property assignments.
SrcExpr = SrcExpr->IgnoreParenImpCasts();
if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(SrcExpr))
if (OV->getSourceExpr())
SrcExpr = OV->getSourceExpr()->IgnoreParenImpCasts();
StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr);
if (!SL || !SL->isAscii())
return;

View File

@ -467,14 +467,13 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType,
bool Sema::isSelfExpr(Expr *receiver) {
// 'self' is objc 'self' in an objc method only.
DeclContext *DC = CurContext;
while (isa<BlockDecl>(DC))
DC = DC->getParent();
if (DC && !isa<ObjCMethodDecl>(DC))
return false;
ObjCMethodDecl *method =
dyn_cast<ObjCMethodDecl>(CurContext->getNonClosureAncestor());
if (!method) return false;
receiver = receiver->IgnoreParenLValueCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver))
if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self"))
if (DRE->getDecl() == method->getSelfDecl())
return true;
return false;
}
@ -1725,6 +1724,12 @@ namespace {
return merge(left, Visit(e->getFalseExpr()));
}
/// Look through pseudo-objects.
ACCResult VisitPseudoObjectExpr(PseudoObjectExpr *e) {
// If we're getting here, we should always have a result.
return Visit(e->getResultExpr());
}
/// Statement expressions are okay if their result expression is okay.
ACCResult VisitStmtExpr(StmtExpr *e) {
return Visit(e->getSubStmt()->body_back());

View File

@ -38,11 +38,364 @@
using namespace clang;
using namespace sema;
namespace {
// Basically just a very focused copy of TreeTransform.
template <class T> struct Rebuilder {
Sema &S;
Rebuilder(Sema &S) : S(S) {}
T &getDerived() { return static_cast<T&>(*this); }
Expr *rebuild(Expr *e) {
// Fast path: nothing to look through.
if (typename T::specific_type *specific
= dyn_cast<typename T::specific_type>(e))
return getDerived().rebuildSpecific(specific);
// Otherwise, we should look through and rebuild anything that
// IgnoreParens would.
if (ParenExpr *parens = dyn_cast<ParenExpr>(e)) {
e = rebuild(parens->getSubExpr());
return new (S.Context) ParenExpr(parens->getLParen(),
parens->getRParen(),
e);
}
if (UnaryOperator *uop = dyn_cast<UnaryOperator>(e)) {
assert(uop->getOpcode() == UO_Extension);
e = rebuild(uop->getSubExpr());
return new (S.Context) UnaryOperator(e, uop->getOpcode(),
uop->getType(),
uop->getValueKind(),
uop->getObjectKind(),
uop->getOperatorLoc());
}
if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) {
assert(!gse->isResultDependent());
unsigned resultIndex = gse->getResultIndex();
unsigned numAssocs = gse->getNumAssocs();
SmallVector<Expr*, 8> assocs(numAssocs);
SmallVector<TypeSourceInfo*, 8> assocTypes(numAssocs);
for (unsigned i = 0; i != numAssocs; ++i) {
Expr *assoc = gse->getAssocExpr(i);
if (i == resultIndex) assoc = rebuild(assoc);
assocs[i] = assoc;
assocTypes[i] = gse->getAssocTypeSourceInfo(i);
}
return new (S.Context) GenericSelectionExpr(S.Context,
gse->getGenericLoc(),
gse->getControllingExpr(),
assocTypes.data(),
assocs.data(),
numAssocs,
gse->getDefaultLoc(),
gse->getRParenLoc(),
gse->containsUnexpandedParameterPack(),
resultIndex);
}
llvm_unreachable("bad expression to rebuild!");
}
};
struct ObjCPropertyRefRebuilder : Rebuilder<ObjCPropertyRefRebuilder> {
Expr *NewBase;
ObjCPropertyRefRebuilder(Sema &S, Expr *newBase)
: Rebuilder(S), NewBase(newBase) {}
typedef ObjCPropertyRefExpr specific_type;
Expr *rebuildSpecific(ObjCPropertyRefExpr *refExpr) {
// Fortunately, the constraint that we're rebuilding something
// with a base limits the number of cases here.
assert(refExpr->getBase());
if (refExpr->isExplicitProperty()) {
return new (S.Context)
ObjCPropertyRefExpr(refExpr->getExplicitProperty(),
refExpr->getType(), refExpr->getValueKind(),
refExpr->getObjectKind(), refExpr->getLocation(),
NewBase);
}
return new (S.Context)
ObjCPropertyRefExpr(refExpr->getImplicitPropertyGetter(),
refExpr->getImplicitPropertySetter(),
refExpr->getType(), refExpr->getValueKind(),
refExpr->getObjectKind(),refExpr->getLocation(),
NewBase);
}
};
class PseudoOpBuilder {
public:
Sema &S;
unsigned ResultIndex;
SourceLocation GenericLoc;
SmallVector<Expr *, 4> Semantics;
PseudoOpBuilder(Sema &S, SourceLocation genericLoc)
: S(S), ResultIndex(PseudoObjectExpr::NoResult),
GenericLoc(genericLoc) {}
/// Add a normal semantic expression.
void addSemanticExpr(Expr *semantic) {
Semantics.push_back(semantic);
}
/// Add the 'result' semantic expression.
void addResultSemanticExpr(Expr *resultExpr) {
assert(ResultIndex == PseudoObjectExpr::NoResult);
ResultIndex = Semantics.size();
Semantics.push_back(resultExpr);
}
ExprResult buildRValueOperation(Expr *op);
ExprResult buildAssignmentOperation(Scope *Sc,
SourceLocation opLoc,
BinaryOperatorKind opcode,
Expr *LHS, Expr *RHS);
ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc,
UnaryOperatorKind opcode,
Expr *op);
ExprResult complete(Expr *syntacticForm);
OpaqueValueExpr *capture(Expr *op);
OpaqueValueExpr *captureValueAsResult(Expr *op);
void setResultToLastSemantic() {
assert(ResultIndex == PseudoObjectExpr::NoResult);
ResultIndex = Semantics.size() - 1;
}
/// Return true if assignments have a non-void result.
virtual bool assignmentsHaveResult() { return true; }
virtual Expr *rebuildAndCaptureObject(Expr *) = 0;
virtual ExprResult buildGet() = 0;
virtual ExprResult buildSet(Expr *, SourceLocation,
bool captureSetValueAsResult) = 0;
};
/// A PseudoOpBuilder for Objective-C @properties.
class ObjCPropertyOpBuilder : public PseudoOpBuilder {
ObjCPropertyRefExpr *RefExpr;
OpaqueValueExpr *InstanceReceiver;
ObjCMethodDecl *Getter;
ObjCMethodDecl *Setter;
Selector SetterSelector;
public:
ObjCPropertyOpBuilder(Sema &S, ObjCPropertyRefExpr *refExpr) :
PseudoOpBuilder(S, refExpr->getLocation()), RefExpr(refExpr),
InstanceReceiver(0), Getter(0), Setter(0) {
}
ExprResult buildRValueOperation(Expr *op);
ExprResult buildAssignmentOperation(Scope *Sc,
SourceLocation opLoc,
BinaryOperatorKind opcode,
Expr *LHS, Expr *RHS);
ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc,
UnaryOperatorKind opcode,
Expr *op);
bool tryBuildGetOfReference(Expr *op, ExprResult &result);
bool findSetter();
bool findGetter();
Expr *rebuildAndCaptureObject(Expr *syntacticBase);
ExprResult buildGet();
ExprResult buildSet(Expr *op, SourceLocation, bool);
};
}
/// Capture the given expression in an OpaqueValueExpr.
OpaqueValueExpr *PseudoOpBuilder::capture(Expr *e) {
// Make a new OVE whose source is the given expression.
OpaqueValueExpr *captured =
new (S.Context) OpaqueValueExpr(GenericLoc, e->getType(),
e->getValueKind());
captured->setSourceExpr(e);
// Make sure we bind that in the semantics.
addSemanticExpr(captured);
return captured;
}
/// Capture the given expression as the result of this pseudo-object
/// operation. This routine is safe against expressions which may
/// already be captured.
///
/// \param Returns the captured expression, which will be the
/// same as the input if the input was already captured
OpaqueValueExpr *PseudoOpBuilder::captureValueAsResult(Expr *e) {
assert(ResultIndex == PseudoObjectExpr::NoResult);
// If the expression hasn't already been captured, just capture it
// and set the new semantic
if (!isa<OpaqueValueExpr>(e)) {
OpaqueValueExpr *cap = capture(e);
setResultToLastSemantic();
return cap;
}
// Otherwise, it must already be one of our semantic expressions;
// set ResultIndex to its index.
unsigned index = 0;
for (;; ++index) {
assert(index < Semantics.size() &&
"captured expression not found in semantics!");
if (e == Semantics[index]) break;
}
ResultIndex = index;
return cast<OpaqueValueExpr>(e);
}
/// The routine which creates the final PseudoObjectExpr.
ExprResult PseudoOpBuilder::complete(Expr *syntactic) {
return PseudoObjectExpr::Create(S.Context, syntactic,
Semantics, ResultIndex);
}
/// The main skeleton for building an r-value operation.
ExprResult PseudoOpBuilder::buildRValueOperation(Expr *op) {
Expr *syntacticBase = rebuildAndCaptureObject(op);
ExprResult getExpr = buildGet();
if (getExpr.isInvalid()) return ExprError();
addResultSemanticExpr(getExpr.take());
return complete(syntacticBase);
}
/// The basic skeleton for building a simple or compound
/// assignment operation.
ExprResult
PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc,
BinaryOperatorKind opcode,
Expr *LHS, Expr *RHS) {
assert(BinaryOperator::isAssignmentOp(opcode));
Expr *syntacticLHS = rebuildAndCaptureObject(LHS);
OpaqueValueExpr *capturedRHS = capture(RHS);
Expr *syntactic;
ExprResult result;
if (opcode == BO_Assign) {
result = capturedRHS;
syntactic = new (S.Context) BinaryOperator(syntacticLHS, capturedRHS,
opcode, capturedRHS->getType(),
capturedRHS->getValueKind(),
OK_Ordinary, opcLoc);
} else {
ExprResult opLHS = buildGet();
if (opLHS.isInvalid()) return ExprError();
// Build an ordinary, non-compound operation.
BinaryOperatorKind nonCompound =
BinaryOperator::getOpForCompoundAssignment(opcode);
result = S.BuildBinOp(Sc, opcLoc, nonCompound,
opLHS.take(), capturedRHS);
if (result.isInvalid()) return ExprError();
syntactic =
new (S.Context) CompoundAssignOperator(syntacticLHS, capturedRHS, opcode,
result.get()->getType(),
result.get()->getValueKind(),
OK_Ordinary,
opLHS.get()->getType(),
result.get()->getType(),
opcLoc);
}
// The result of the assignment, if not void, is the value set into
// the l-value.
result = buildSet(result.take(), opcLoc, assignmentsHaveResult());
if (result.isInvalid()) return ExprError();
addSemanticExpr(result.take());
return complete(syntactic);
}
/// The basic skeleton for building an increment or decrement
/// operation.
ExprResult
PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
UnaryOperatorKind opcode,
Expr *op) {
assert(UnaryOperator::isIncrementDecrementOp(opcode));
Expr *syntacticOp = rebuildAndCaptureObject(op);
// Load the value.
ExprResult result = buildGet();
if (result.isInvalid()) return ExprError();
QualType resultType = result.get()->getType();
// That's the postfix result.
if (UnaryOperator::isPostfix(opcode) && assignmentsHaveResult()) {
result = capture(result.take());
setResultToLastSemantic();
}
// Add or subtract a literal 1.
llvm::APInt oneV(S.Context.getTypeSize(S.Context.IntTy), 1);
Expr *one = IntegerLiteral::Create(S.Context, oneV, S.Context.IntTy,
GenericLoc);
if (UnaryOperator::isIncrementOp(opcode)) {
result = S.BuildBinOp(Sc, opcLoc, BO_Add, result.take(), one);
} else {
result = S.BuildBinOp(Sc, opcLoc, BO_Sub, result.take(), one);
}
if (result.isInvalid()) return ExprError();
// Store that back into the result. The value stored is the result
// of a prefix operation.
result = buildSet(result.take(), opcLoc,
UnaryOperator::isPrefix(opcode) && assignmentsHaveResult());
if (result.isInvalid()) return ExprError();
addSemanticExpr(result.take());
UnaryOperator *syntactic =
new (S.Context) UnaryOperator(syntacticOp, opcode, resultType,
VK_LValue, OK_Ordinary, opcLoc);
return complete(syntactic);
}
//===----------------------------------------------------------------------===//
// Objective-C @property and implicit property references
//===----------------------------------------------------------------------===//
/// Look up a method in the receiver type of an Objective-C property
/// reference.
static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
const ObjCPropertyRefExpr *PRE) {
if (PRE->isObjectReceiver()) {
const ObjCObjectPointerType *PT =
PRE->getBase()->getType()->castAs<ObjCObjectPointerType>();
// Special case for 'self' in class method implementations.
if (PT->isObjCClassType() &&
S.isSelfExpr(const_cast<Expr*>(PRE->getBase()))) {
// This cast is safe because isSelfExpr is only true within
// methods.
ObjCMethodDecl *method =
cast<ObjCMethodDecl>(S.CurContext->getNonClosureAncestor());
return S.LookupMethodInObjectType(sel,
S.Context.getObjCInterfaceType(method->getClassInterface()),
/*instance*/ false);
}
return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true);
}
@ -59,99 +412,24 @@ static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
return S.LookupMethodInObjectType(sel, IT, false);
}
ExprResult Sema::checkPseudoObjectRValue(Expr *E) {
assert(E->getValueKind() == VK_LValue &&
E->getObjectKind() == OK_ObjCProperty);
const ObjCPropertyRefExpr *PRE = E->getObjCProperty();
bool ObjCPropertyOpBuilder::findGetter() {
if (Getter) return true;
QualType ReceiverType;
if (PRE->isObjectReceiver())
ReceiverType = PRE->getBase()->getType();
else if (PRE->isSuperReceiver())
ReceiverType = PRE->getSuperReceiverType();
else
ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver());
ExprValueKind VK = VK_RValue;
QualType T;
if (PRE->isImplicitProperty()) {
if (ObjCMethodDecl *GetterMethod =
PRE->getImplicitPropertyGetter()) {
T = getMessageSendResultType(ReceiverType, GetterMethod,
PRE->isClassReceiver(),
PRE->isSuperReceiver());
VK = Expr::getValueKindForType(GetterMethod->getResultType());
} else {
Diag(PRE->getLocation(), diag::err_getter_not_found)
<< PRE->getBase()->getType();
return ExprError();
}
} else {
ObjCPropertyDecl *prop = PRE->getExplicitProperty();
ObjCMethodDecl *getter =
LookupMethodInReceiverType(*this, prop->getGetterName(), PRE);
if (getter && !getter->hasRelatedResultType())
DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation());
if (!getter) getter = prop->getGetterMethodDecl();
// Figure out the type of the expression. Mostly this is the
// result type of the getter, if possible.
if (getter) {
T = getMessageSendResultType(ReceiverType, getter,
PRE->isClassReceiver(),
PRE->isSuperReceiver());
VK = Expr::getValueKindForType(getter->getResultType());
// As a special case, if the method returns 'id', try to get a
// better type from the property.
if (VK == VK_RValue && T->isObjCIdType() &&
prop->getType()->isObjCRetainableType())
T = prop->getType();
} else {
T = prop->getType();
VK = Expr::getValueKindForType(T);
T = T.getNonLValueExprType(Context);
}
Getter = LookupMethodInReceiverType(S, RefExpr->getGetterSelector(), RefExpr);
return (Getter != 0);
}
E->setType(T);
E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK);
ExprResult Result = MaybeBindToTemporary(E);
if (!Result.isInvalid())
E = Result.take();
return Owned(E);
}
namespace {
struct PseudoObjectInfo {
const ObjCPropertyRefExpr *RefExpr;
bool HasSetter;
Selector SetterSelector;
ParmVarDecl *SetterParam;
QualType SetterParamType;
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.
/// Try to find the most accurate setter declaration for the property
/// reference.
///
/// \return true if a setter was found, in which case Setter
bool ObjCPropertyOpBuilder::findSetter() {
// For implicit properties, just trust the lookup we already did.
if (RefExpr->isImplicitProperty()) {
if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
setSetter(setter);
Setter = setter;
SetterSelector = setter->getSelector();
return true;
} else {
IdentifierInfo *getterName =
RefExpr->getImplicitPropertyGetter()->getSelector()
@ -160,8 +438,8 @@ namespace {
SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
S.PP.getSelectorTable(),
getterName);
return false;
}
return;
}
// For explicit properties, this is more involved.
@ -171,169 +449,337 @@ namespace {
// Do a normal method lookup first.
if (ObjCMethodDecl *setter =
LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
setSetter(setter);
return;
Setter = setter;
return true;
}
// If that failed, trust the type on the @property declaration.
if (!prop->isReadOnly()) {
HasSetter = true;
SetterParamType = prop->getType().getUnqualifiedType();
}
}
};
// 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;
}
/// 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);
/// Capture the base object of an Objective-C property expression.
Expr *ObjCPropertyOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) {
assert(InstanceReceiver == 0);
// 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!");
// 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());
// 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);
syntacticBase =
ObjCPropertyRefRebuilder(S, InstanceReceiver).rebuild(syntacticBase);
}
// 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 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 {
assert(InstanceReceiver);
receiverType = InstanceReceiver->getType();
}
// Build a message-send.
ExprResult msg;
if (Getter->isInstanceMethod() || RefExpr->isObjectReceiver()) {
assert(InstanceReceiver || RefExpr->isSuperReceiver());
msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc,
Getter->getSelector(), Getter,
GenericLoc, GenericLoc, GenericLoc,
MultiExprArg());
} else {
TypeSourceInfo *receiverTypeInfo = 0;
if (!RefExpr->isSuperReceiver())
receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType);
msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
Getter->getSelector(), Getter,
GenericLoc, GenericLoc, GenericLoc,
MultiExprArg());
}
return msg;
}
/// Store to an Objective-C property reference.
///
/// \param bindSetValueAsResult - If true, capture the actual
/// value being set as the value of the property operation.
ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
bool captureSetValueAsResult) {
bool hasSetter = findSetter();
assert(hasSetter); (void) hasSetter;
QualType receiverType;
SourceLocation superLoc;
if (RefExpr->isClassReceiver()) {
receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver());
} else if (RefExpr->isSuperReceiver()) {
superLoc = RefExpr->getReceiverLocation();
receiverType = RefExpr->getSuperReceiverType();
} else {
assert(InstanceReceiver);
receiverType = InstanceReceiver->getType();
}
// 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?");
}
}
// Arguments.
Expr *args[] = { op };
// 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;
}
/// @property-specific behavior for doing lvalue-to-rvalue conversion.
ExprResult ObjCPropertyOpBuilder::buildRValueOperation(Expr *op) {
// Explicit properties always have getters, but implicit ones don't.
// Check that before proceeding.
if (RefExpr->isImplicitProperty() &&
!RefExpr->getImplicitPropertyGetter()) {
S.Diag(RefExpr->getLocation(), diag::err_getter_not_found)
<< RefExpr->getBase()->getType();
return ExprError();
}
// ++/-- behave like compound assignments, i.e. they need a getter.
QualType getterResultType = info.RefExpr->getGetterResultType();
if (getterResultType.isNull()) {
assert(info.RefExpr->isImplicitProperty());
Diag(opcLoc, diag::err_nogetter_property_incdec)
<< unsigned(UnaryOperator::isDecrementOp(opcode))
<< info.RefExpr->getImplicitPropertyGetter()->getSelector()
<< op->getSourceRange();
return ExprError();
}
// HACK: change the type of the operand to prevent further placeholder
// transformation.
op->setType(getterResultType.getNonLValueExprType(Context));
op->setObjectKind(OK_Ordinary);
ExprResult result = CreateBuiltinUnaryOp(opcLoc, opcode, op);
ExprResult result = PseudoOpBuilder::buildRValueOperation(op);
if (result.isInvalid()) return ExprError();
// Change the object kind back.
op->setObjectKind(OK_ObjCProperty);
if (RefExpr->isExplicitProperty() && !Getter->hasRelatedResultType())
S.DiagnosePropertyAccessorMismatch(RefExpr->getExplicitProperty(),
Getter, RefExpr->getLocation());
// As a special case, if the method returns 'id', try to get
// a better type from the property.
if (RefExpr->isExplicitProperty() && result.get()->isRValue() &&
result.get()->getType()->isObjCIdType()) {
QualType propType = RefExpr->getExplicitProperty()->getType();
if (const ObjCObjectPointerType *ptr
= propType->getAs<ObjCObjectPointerType>()) {
if (!ptr->isObjCIdType())
result = S.ImpCastExprToType(result.get(), propType, CK_BitCast);
}
}
return result;
}
ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc,
/// Try to build this as a call to a getter that returns a reference.
///
/// \return true if it was possible, whether or not it actually
/// succeeded
bool ObjCPropertyOpBuilder::tryBuildGetOfReference(Expr *op,
ExprResult &result) {
if (!S.getLangOptions().CPlusPlus) return false;
findGetter();
assert(Getter && "property has no setter and no getter!");
// Only do this if the getter returns an l-value reference type.
QualType resultType = Getter->getResultType();
if (!resultType->isLValueReferenceType()) return false;
result = buildRValueOperation(op);
return true;
}
/// @property-specific behavior for doing assignments.
ExprResult
ObjCPropertyOpBuilder::buildAssignmentOperation(Scope *Sc,
SourceLocation opcLoc,
BinaryOperatorKind opcode,
Expr *LHS, Expr *RHS) {
assert(BinaryOperator::isAssignmentOp(opcode));
PseudoObjectInfo info(*this, LHS);
// If there's no setter, we have no choice but to try to assign to
// the result of the getter.
if (!info.HasSetter) {
QualType resultType = info.RefExpr->getGetterResultType();
assert(!resultType.isNull() && "property has no setter and no getter!");
// Only do this if the getter returns an l-value reference type.
if (const LValueReferenceType *refType
= resultType->getAs<LValueReferenceType>()) {
LHS = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
CK_GetObjCProperty, LHS, 0, VK_LValue);
return BuildBinOp(S, opcLoc, opcode, LHS, RHS);
if (!findSetter()) {
ExprResult result;
if (tryBuildGetOfReference(LHS, result)) {
if (result.isInvalid()) return ExprError();
return S.BuildBinOp(Sc, opcLoc, opcode, result.take(), RHS);
}
// Otherwise, it's an error.
Diag(opcLoc, diag::err_nosetter_property_assignment)
<< unsigned(info.RefExpr->isImplicitProperty())
<< info.SetterSelector
S.Diag(opcLoc, diag::err_nosetter_property_assignment)
<< unsigned(RefExpr->isImplicitProperty())
<< SetterSelector
<< LHS->getSourceRange() << RHS->getSourceRange();
return ExprError();
}
// If there is a setter, we definitely want to use it.
// If this is a simple assignment, just initialize the parameter
// with the RHS.
if (opcode == BO_Assign) {
LHS->setType(info.SetterParamType.getNonLValueExprType(Context));
// Under certain circumstances, we need to type-check the RHS as a
// straight-up parameter initialization. This gives somewhat
// inferior diagnostics, so we try to avoid it.
if (RHS->isTypeDependent()) {
// Just build the expression.
} else if ((getLangOptions().CPlusPlus && LHS->getType()->isRecordType()) ||
(getLangOptions().ObjCAutoRefCount &&
info.SetterParam &&
info.SetterParam->hasAttr<NSConsumedAttr>())) {
InitializedEntity param = (info.SetterParam
? InitializedEntity::InitializeParameter(Context, info.SetterParam)
: InitializedEntity::InitializeParameter(Context, info.SetterParamType,
/*consumed*/ false));
ExprResult arg = PerformCopyInitialization(param, opcLoc, RHS);
if (arg.isInvalid()) return ExprError();
RHS = arg.take();
// Warn about assignments of +1 objects to unsafe pointers in ARC.
// CheckAssignmentOperands does this on the other path.
if (getLangOptions().ObjCAutoRefCount)
checkUnsafeExprAssigns(opcLoc, LHS, RHS);
} else {
ExprResult RHSResult = Owned(RHS);
LHS->setObjectKind(OK_Ordinary);
QualType resultType = CheckAssignmentOperands(LHS, RHSResult, opcLoc,
/*compound*/ QualType());
LHS->setObjectKind(OK_ObjCProperty);
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)
// Verify that we can do a compound assignment.
if (opcode != BO_Assign && !findGetter()) {
S.Diag(opcLoc, diag::err_nogetter_property_compound_assignment)
<< LHS->getSourceRange() << RHS->getSourceRange();
return ExprError();
}
// HACK: change the type of the LHS to prevent further placeholder
// transformation.
LHS->setType(getterResultType.getNonLValueExprType(Context));
LHS->setObjectKind(OK_Ordinary);
ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS);
ExprResult result =
PseudoOpBuilder::buildAssignmentOperation(Sc, opcLoc, opcode, LHS, RHS);
if (result.isInvalid()) return ExprError();
// Change the object kind back.
LHS->setObjectKind(OK_ObjCProperty);
// Various warnings about property assignments in ARC.
if (S.getLangOptions().ObjCAutoRefCount && InstanceReceiver) {
S.checkRetainCycles(InstanceReceiver->getSourceExpr(), RHS);
S.checkUnsafeExprAssigns(opcLoc, LHS, RHS);
}
return result;
}
/// @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;
return;
}
} else if (isa<ObjCPropertyRefExpr>(E)) {
} else if (isa<PseudoObjectExpr>(E)) {
DiagID = diag::warn_unused_property_expr;
} else if (const CXXFunctionalCastExpr *FC
= dyn_cast<CXXFunctionalCastExpr>(E)) {

View File

@ -6101,6 +6101,22 @@ TreeTransform<Derived>::TransformOpaqueValueExpr(OpaqueValueExpr *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>
ExprResult
TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr(

View File

@ -775,6 +775,24 @@ void ASTStmtReader::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
E->RParenLoc = ReadSourceLocation(Record, Idx);
}
void ASTStmtReader::VisitPseudoObjectExpr(PseudoObjectExpr *E) {
VisitExpr(E);
unsigned numSemanticExprs = Record[Idx++];
assert(numSemanticExprs + 1 == E->PseudoObjectExprBits.NumSubExprs);
E->PseudoObjectExprBits.ResultIndex = Record[Idx++];
// Read the syntactic expression.
E->getSubExprsBuffer()[0] = Reader.ReadSubExpr();
// Read all the semantic expressions.
for (unsigned i = 0; i != numSemanticExprs; ++i) {
Expr *subExpr = Reader.ReadSubExpr();
if (isa<OpaqueValueExpr>(subExpr))
cast<OpaqueValueExpr>(subExpr)->setSourceExpr(Reader.ReadSubExpr());
E->getSubExprsBuffer()[i+1] = subExpr;
}
}
void ASTStmtReader::VisitAtomicExpr(AtomicExpr *E) {
VisitExpr(E);
E->setOp(AtomicExpr::AtomicOp(Record[Idx++]));
@ -2059,6 +2077,12 @@ Stmt *ASTReader::ReadStmtFromStream(Module &F) {
S = new (Context) AsTypeExpr(Empty);
break;
case EXPR_PSEUDO_OBJECT: {
unsigned numSemanticExprs = Record[ASTStmtReader::NumExprFields];
S = PseudoObjectExpr::Create(Context, Empty, numSemanticExprs);
break;
}
case EXPR_ATOMIC:
S = new (Context) AtomicExpr(Empty);
break;

View File

@ -736,6 +736,25 @@ void ASTStmtWriter::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
Code = serialization::EXPR_GENERIC_SELECTION;
}
void ASTStmtWriter::VisitPseudoObjectExpr(PseudoObjectExpr *E) {
VisitExpr(E);
Record.push_back(E->getNumSemanticExprs());
// Push the result index. Currently, this needs to exactly match
// the encoding used internally for ResultIndex.
unsigned result = E->getResultExprIndex();
result = (result == PseudoObjectExpr::NoResult ? 0 : result + 1);
Record.push_back(result);
Writer.AddStmt(E->getSyntacticForm());
for (PseudoObjectExpr::semantics_iterator
i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) {
Writer.AddStmt(*i);
if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(*i))
Writer.AddStmt(OVE->getSourceExpr());
}
}
void ASTStmtWriter::VisitAtomicExpr(AtomicExpr *E) {
VisitExpr(E);
Record.push_back(E->getOp());

View File

@ -857,6 +857,21 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
}
case Stmt::PseudoObjectExprClass: {
Bldr.takeNodes(Pred);
const ProgramState *state = Pred->getState();
const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S);
if (const Expr *Result = PE->getResultExpr()) {
SVal V = state->getSVal(Result);
Bldr.generateNode(S, Pred, state->BindExpr(S, V));
}
else
Bldr.generateNode(S, Pred, state->BindExpr(S, UnknownVal()));
Bldr.addNodes(Dst);
break;
}
}
}

View File

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

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
// This actually still works after the pseudo-object refactor, it just
// uses messages that say 'method' instead of 'property'. Ted wanted
// this xfailed and filed as a bug. rdar://problem/10402993
// XFAIL: *
/***
This file is for testing the path-sensitive notes for retain/release errors.
Its goal is to have simple branch coverage of any path-based diagnostics,

View File

@ -1714,6 +1714,8 @@ public:
void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *U);
void VisitVAArgExpr(VAArgExpr *E);
void VisitSizeOfPackExpr(SizeOfPackExpr *E);
void VisitPseudoObjectExpr(PseudoObjectExpr *E);
void VisitOpaqueValueExpr(OpaqueValueExpr *E);
private:
void AddDeclarationNameInfo(Stmt *S);
@ -2022,6 +2024,17 @@ void EnqueueVisitor::VisitVAArgExpr(VAArgExpr *E) {
void EnqueueVisitor::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
WL.push_back(SizeOfPackExprParts(E, Parent));
}
void EnqueueVisitor::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
// If the opaque value has a source expression, just transparently
// visit that. This is useful for (e.g.) pseudo-object expressions.
if (Expr *SourceExpr = E->getSourceExpr())
return Visit(SourceExpr);
AddStmt(E);
}
void EnqueueVisitor::VisitPseudoObjectExpr(PseudoObjectExpr *E) {
// Treat the expression like its syntactic form.
Visit(E->getSyntacticForm());
}
void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, Stmt *S) {
EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S);
@ -2702,6 +2715,11 @@ static Decl *getDeclFromExpr(Stmt *E) {
return RE->getDecl();
if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E))
return PRE->isExplicitProperty() ? PRE->getExplicitProperty() : 0;
if (PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
return getDeclFromExpr(POE->getSyntacticForm());
if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
if (Expr *Src = OVE->getSourceExpr())
return getDeclFromExpr(Src);
if (CallExpr *CE = dyn_cast<CallExpr>(E))
return getDeclFromExpr(CE->getCallee());

View File

@ -219,7 +219,6 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
case Stmt::MaterializeTemporaryExprClass:
case Stmt::ObjCIndirectCopyRestoreExprClass:
case Stmt::OffsetOfExprClass:
case Stmt::OpaqueValueExprClass:
case Stmt::ParenListExprClass:
case Stmt::PredefinedExprClass:
case Stmt::ShuffleVectorExprClass:
@ -229,6 +228,16 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, CXTranslationUnit TU,
K = CXCursor_UnexposedExpr;
break;
case Stmt::OpaqueValueExprClass:
if (Expr *Src = cast<OpaqueValueExpr>(S)->getSourceExpr())
return MakeCXCursor(Src, Parent, TU, RegionOfInterest);
K = CXCursor_UnexposedExpr;
break;
case Stmt::PseudoObjectExprClass:
return MakeCXCursor(cast<PseudoObjectExpr>(S)->getSyntacticForm(),
Parent, TU, RegionOfInterest);
case Stmt::CompoundStmtClass:
K = CXCursor_CompoundStmt;
break;