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.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@143867 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 8cae040..8a33242 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -4213,6 +4213,26 @@
       continue;
     }
 
+    if (PseudoObjectExpr *pseudo = dyn_cast<PseudoObjectExpr>(e)) {
+      // Only pay attention to pseudo-objects on property references.
+      ObjCPropertyRefExpr *pre
+        = dyn_cast<ObjCPropertyRefExpr>(pseudo->getSyntacticForm()
+                                              ->IgnoreParens());
+      if (!pre) return false;
+      if (pre->isImplicitProperty()) return false;
+      ObjCPropertyDecl *property = pre->getExplicitProperty();
+      if (!property->isRetaining() &&
+          !(property->getPropertyIvarDecl() &&
+            property->getPropertyIvarDecl()->getType()
+              .getObjCLifetime() == Qualifiers::OCL_Strong))
+          return false;
+
+      owner.Indirect = true;
+      e = const_cast<Expr*>(cast<OpaqueValueExpr>(pre->getBase())
+                              ->getSourceExpr());
+      continue;
+    }
+
     // Array ivars?
 
     return false;
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index febd394..c6a5f1e 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -7468,7 +7468,7 @@
     // 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 @@
   } 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 @@
       return;
   }
 
-  // Strip off any parens and casts.
-  StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr->IgnoreParenCasts());
+  // Ignore any parens, implicit casts (should only be
+  // array-to-pointer decays), and not-so-opaque values.  The last is
+  // important for making this trigger for property assignments.
+  SrcExpr = SrcExpr->IgnoreParenImpCasts();
+  if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(SrcExpr))
+    if (OV->getSourceExpr())
+      SrcExpr = OV->getSourceExpr()->IgnoreParenImpCasts();
+
+  StringLiteral *SL = dyn_cast<StringLiteral>(SrcExpr);
   if (!SL || !SL->isAscii())
     return;
 
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index 019dc81..a9179dc 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -467,14 +467,13 @@
 
 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 @@
       return merge(left, Visit(e->getFalseExpr()));
     }
 
+    /// Look through pseudo-objects.
+    ACCResult VisitPseudoObjectExpr(PseudoObjectExpr *e) {
+      // If we're getting here, we should always have a result.
+      return Visit(e->getResultExpr());
+    }
+
     /// Statement expressions are okay if their result expression is okay.
     ACCResult VisitStmtExpr(StmtExpr *e) {
       return Visit(e->getSubStmt()->body_back());
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp
index 97fbe48..e0a6271 100644
--- a/lib/Sema/SemaPseudoObject.cpp
+++ b/lib/Sema/SemaPseudoObject.cpp
@@ -38,11 +38,364 @@
 using namespace clang;
 using namespace sema;
 
+namespace {
+  // Basically just a very focused copy of TreeTransform.
+  template <class T> struct Rebuilder {
+    Sema &S;
+    Rebuilder(Sema &S) : S(S) {}
+
+    T &getDerived() { return static_cast<T&>(*this); }
+
+    Expr *rebuild(Expr *e) {
+      // Fast path: nothing to look through.
+      if (typename T::specific_type *specific
+            = dyn_cast<typename T::specific_type>(e))
+        return getDerived().rebuildSpecific(specific);
+
+      // Otherwise, we should look through and rebuild anything that
+      // IgnoreParens would.
+
+      if (ParenExpr *parens = dyn_cast<ParenExpr>(e)) {
+        e = rebuild(parens->getSubExpr());
+        return new (S.Context) ParenExpr(parens->getLParen(),
+                                         parens->getRParen(),
+                                         e);
+      }
+
+      if (UnaryOperator *uop = dyn_cast<UnaryOperator>(e)) {
+        assert(uop->getOpcode() == UO_Extension);
+        e = rebuild(uop->getSubExpr());
+        return new (S.Context) UnaryOperator(e, uop->getOpcode(),
+                                             uop->getType(),
+                                             uop->getValueKind(),
+                                             uop->getObjectKind(),
+                                             uop->getOperatorLoc());
+      }
+
+      if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) {
+        assert(!gse->isResultDependent());
+        unsigned resultIndex = gse->getResultIndex();
+        unsigned numAssocs = gse->getNumAssocs();
+
+        SmallVector<Expr*, 8> assocs(numAssocs);
+        SmallVector<TypeSourceInfo*, 8> assocTypes(numAssocs);
+
+        for (unsigned i = 0; i != numAssocs; ++i) {
+          Expr *assoc = gse->getAssocExpr(i);
+          if (i == resultIndex) assoc = rebuild(assoc);
+          assocs[i] = assoc;
+          assocTypes[i] = gse->getAssocTypeSourceInfo(i);
+        }
+
+        return new (S.Context) GenericSelectionExpr(S.Context,
+                                                    gse->getGenericLoc(),
+                                                    gse->getControllingExpr(),
+                                                    assocTypes.data(),
+                                                    assocs.data(),
+                                                    numAssocs,
+                                                    gse->getDefaultLoc(),
+                                                    gse->getRParenLoc(),
+                                      gse->containsUnexpandedParameterPack(),
+                                                    resultIndex);
+      }
+
+      llvm_unreachable("bad expression to rebuild!");
+    }
+  };
+
+  struct ObjCPropertyRefRebuilder : Rebuilder<ObjCPropertyRefRebuilder> {
+    Expr *NewBase;
+    ObjCPropertyRefRebuilder(Sema &S, Expr *newBase)
+      : Rebuilder(S), NewBase(newBase) {}
+
+    typedef ObjCPropertyRefExpr specific_type;
+    Expr *rebuildSpecific(ObjCPropertyRefExpr *refExpr) {
+      // Fortunately, the constraint that we're rebuilding something
+      // with a base limits the number of cases here.
+      assert(refExpr->getBase());
+
+      if (refExpr->isExplicitProperty()) {
+        return new (S.Context)
+          ObjCPropertyRefExpr(refExpr->getExplicitProperty(),
+                              refExpr->getType(), refExpr->getValueKind(),
+                              refExpr->getObjectKind(), refExpr->getLocation(),
+                              NewBase);
+      }
+      return new (S.Context)
+        ObjCPropertyRefExpr(refExpr->getImplicitPropertyGetter(),
+                            refExpr->getImplicitPropertySetter(),
+                            refExpr->getType(), refExpr->getValueKind(),
+                            refExpr->getObjectKind(),refExpr->getLocation(),
+                            NewBase);
+    }
+  };
+
+  class PseudoOpBuilder {
+  public:
+    Sema &S;
+    unsigned ResultIndex;
+    SourceLocation GenericLoc;
+    SmallVector<Expr *, 4> Semantics;
+
+    PseudoOpBuilder(Sema &S, SourceLocation genericLoc)
+      : S(S), ResultIndex(PseudoObjectExpr::NoResult),
+        GenericLoc(genericLoc) {}
+
+    /// Add a normal semantic expression.
+    void addSemanticExpr(Expr *semantic) {
+      Semantics.push_back(semantic);
+    }
+
+    /// Add the 'result' semantic expression.
+    void addResultSemanticExpr(Expr *resultExpr) {
+      assert(ResultIndex == PseudoObjectExpr::NoResult);
+      ResultIndex = Semantics.size();
+      Semantics.push_back(resultExpr);
+    }
+
+    ExprResult buildRValueOperation(Expr *op);
+    ExprResult buildAssignmentOperation(Scope *Sc,
+                                        SourceLocation opLoc,
+                                        BinaryOperatorKind opcode,
+                                        Expr *LHS, Expr *RHS);
+    ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc,
+                                    UnaryOperatorKind opcode,
+                                    Expr *op);
+
+    ExprResult complete(Expr *syntacticForm);
+
+    OpaqueValueExpr *capture(Expr *op);
+    OpaqueValueExpr *captureValueAsResult(Expr *op);
+
+    void setResultToLastSemantic() {
+      assert(ResultIndex == PseudoObjectExpr::NoResult);
+      ResultIndex = Semantics.size() - 1;
+    }
+
+    /// Return true if assignments have a non-void result.
+    virtual bool assignmentsHaveResult() { return true; }
+
+    virtual Expr *rebuildAndCaptureObject(Expr *) = 0;
+    virtual ExprResult buildGet() = 0;
+    virtual ExprResult buildSet(Expr *, SourceLocation,
+                                bool captureSetValueAsResult) = 0;
+  };
+
+  /// A PseudoOpBuilder for Objective-C @properties.
+  class ObjCPropertyOpBuilder : public PseudoOpBuilder {
+    ObjCPropertyRefExpr *RefExpr;
+    OpaqueValueExpr *InstanceReceiver;
+    ObjCMethodDecl *Getter;
+
+    ObjCMethodDecl *Setter;
+    Selector SetterSelector;
+
+  public:
+    ObjCPropertyOpBuilder(Sema &S, ObjCPropertyRefExpr *refExpr) :
+      PseudoOpBuilder(S, refExpr->getLocation()), RefExpr(refExpr),
+      InstanceReceiver(0), Getter(0), Setter(0) {
+    }
+
+    ExprResult buildRValueOperation(Expr *op);
+    ExprResult buildAssignmentOperation(Scope *Sc,
+                                        SourceLocation opLoc,
+                                        BinaryOperatorKind opcode,
+                                        Expr *LHS, Expr *RHS);
+    ExprResult buildIncDecOperation(Scope *Sc, SourceLocation opLoc,
+                                    UnaryOperatorKind opcode,
+                                    Expr *op);
+
+    bool tryBuildGetOfReference(Expr *op, ExprResult &result);
+    bool findSetter();
+    bool findGetter();
+
+    Expr *rebuildAndCaptureObject(Expr *syntacticBase);
+    ExprResult buildGet();
+    ExprResult buildSet(Expr *op, SourceLocation, bool);
+  };
+}
+
+/// Capture the given expression in an OpaqueValueExpr.
+OpaqueValueExpr *PseudoOpBuilder::capture(Expr *e) {
+  // Make a new OVE whose source is the given expression.
+  OpaqueValueExpr *captured = 
+    new (S.Context) OpaqueValueExpr(GenericLoc, e->getType(),
+                                    e->getValueKind());
+  captured->setSourceExpr(e);
+  
+  // Make sure we bind that in the semantics.
+  addSemanticExpr(captured);
+  return captured;
+}
+
+/// Capture the given expression as the result of this pseudo-object
+/// operation.  This routine is safe against expressions which may
+/// already be captured.
+///
+/// \param Returns the captured expression, which will be the
+///   same as the input if the input was already captured
+OpaqueValueExpr *PseudoOpBuilder::captureValueAsResult(Expr *e) {
+  assert(ResultIndex == PseudoObjectExpr::NoResult);
+
+  // If the expression hasn't already been captured, just capture it
+  // and set the new semantic 
+  if (!isa<OpaqueValueExpr>(e)) {
+    OpaqueValueExpr *cap = capture(e);
+    setResultToLastSemantic();
+    return cap;
+  }
+
+  // Otherwise, it must already be one of our semantic expressions;
+  // set ResultIndex to its index.
+  unsigned index = 0;
+  for (;; ++index) {
+    assert(index < Semantics.size() &&
+           "captured expression not found in semantics!");
+    if (e == Semantics[index]) break;
+  }
+  ResultIndex = index;
+  return cast<OpaqueValueExpr>(e);
+}
+
+/// The routine which creates the final PseudoObjectExpr.
+ExprResult PseudoOpBuilder::complete(Expr *syntactic) {
+  return PseudoObjectExpr::Create(S.Context, syntactic,
+                                  Semantics, ResultIndex);
+}
+
+/// The main skeleton for building an r-value operation.
+ExprResult PseudoOpBuilder::buildRValueOperation(Expr *op) {
+  Expr *syntacticBase = rebuildAndCaptureObject(op);
+
+  ExprResult getExpr = buildGet();
+  if (getExpr.isInvalid()) return ExprError();
+  addResultSemanticExpr(getExpr.take());
+
+  return complete(syntacticBase);
+}
+
+/// The basic skeleton for building a simple or compound
+/// assignment operation.
+ExprResult
+PseudoOpBuilder::buildAssignmentOperation(Scope *Sc, SourceLocation opcLoc,
+                                          BinaryOperatorKind opcode,
+                                          Expr *LHS, Expr *RHS) {
+  assert(BinaryOperator::isAssignmentOp(opcode));
+
+  Expr *syntacticLHS = rebuildAndCaptureObject(LHS);
+  OpaqueValueExpr *capturedRHS = capture(RHS);
+
+  Expr *syntactic;
+
+  ExprResult result;
+  if (opcode == BO_Assign) {
+    result = capturedRHS;
+    syntactic = new (S.Context) BinaryOperator(syntacticLHS, capturedRHS,
+                                               opcode, capturedRHS->getType(),
+                                               capturedRHS->getValueKind(),
+                                               OK_Ordinary, opcLoc);
+  } else {
+    ExprResult opLHS = buildGet();
+    if (opLHS.isInvalid()) return ExprError();
+
+    // Build an ordinary, non-compound operation.
+    BinaryOperatorKind nonCompound =
+      BinaryOperator::getOpForCompoundAssignment(opcode);
+    result = S.BuildBinOp(Sc, opcLoc, nonCompound,
+                          opLHS.take(), capturedRHS);
+    if (result.isInvalid()) return ExprError();
+
+    syntactic =
+      new (S.Context) CompoundAssignOperator(syntacticLHS, capturedRHS, opcode,
+                                             result.get()->getType(),
+                                             result.get()->getValueKind(),
+                                             OK_Ordinary,
+                                             opLHS.get()->getType(),
+                                             result.get()->getType(),
+                                             opcLoc);
+  }
+
+  // The result of the assignment, if not void, is the value set into
+  // the l-value.
+  result = buildSet(result.take(), opcLoc, assignmentsHaveResult());
+  if (result.isInvalid()) return ExprError();
+  addSemanticExpr(result.take());
+
+  return complete(syntactic);
+}
+
+/// The basic skeleton for building an increment or decrement
+/// operation.
+ExprResult
+PseudoOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
+                                      UnaryOperatorKind opcode,
+                                      Expr *op) {
+  assert(UnaryOperator::isIncrementDecrementOp(opcode));
+
+  Expr *syntacticOp = rebuildAndCaptureObject(op);
+
+  // Load the value.
+  ExprResult result = buildGet();
+  if (result.isInvalid()) return ExprError();
+
+  QualType resultType = result.get()->getType();
+
+  // That's the postfix result.
+  if (UnaryOperator::isPostfix(opcode) && assignmentsHaveResult()) {
+    result = capture(result.take());
+    setResultToLastSemantic();
+  }
+
+  // Add or subtract a literal 1.
+  llvm::APInt oneV(S.Context.getTypeSize(S.Context.IntTy), 1);
+  Expr *one = IntegerLiteral::Create(S.Context, oneV, S.Context.IntTy,
+                                     GenericLoc);
+
+  if (UnaryOperator::isIncrementOp(opcode)) {
+    result = S.BuildBinOp(Sc, opcLoc, BO_Add, result.take(), one);
+  } else {
+    result = S.BuildBinOp(Sc, opcLoc, BO_Sub, result.take(), one);
+  }
+  if (result.isInvalid()) return ExprError();
+
+  // Store that back into the result.  The value stored is the result
+  // of a prefix operation.
+  result = buildSet(result.take(), opcLoc,
+             UnaryOperator::isPrefix(opcode) && assignmentsHaveResult());
+  if (result.isInvalid()) return ExprError();
+  addSemanticExpr(result.take());
+
+  UnaryOperator *syntactic =
+    new (S.Context) UnaryOperator(syntacticOp, opcode, resultType,
+                                  VK_LValue, OK_Ordinary, opcLoc);
+  return complete(syntactic);
+}
+
+
+//===----------------------------------------------------------------------===//
+//  Objective-C @property and implicit property references
+//===----------------------------------------------------------------------===//
+
+/// Look up a method in the receiver type of an Objective-C property
+/// reference.
 static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
                                             const ObjCPropertyRefExpr *PRE) {
   if (PRE->isObjectReceiver()) {
     const ObjCObjectPointerType *PT =
       PRE->getBase()->getType()->castAs<ObjCObjectPointerType>();
+
+    // Special case for 'self' in class method implementations.
+    if (PT->isObjCClassType() &&
+        S.isSelfExpr(const_cast<Expr*>(PRE->getBase()))) {
+      // This cast is safe because isSelfExpr is only true within
+      // methods.
+      ObjCMethodDecl *method =
+        cast<ObjCMethodDecl>(S.CurContext->getNonClosureAncestor());
+      return S.LookupMethodInObjectType(sel,
+                 S.Context.getObjCInterfaceType(method->getClassInterface()),
+                                        /*instance*/ false);
+    }
+
     return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true);
   }
 
@@ -59,281 +412,374 @@
   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());
+  Getter = LookupMethodInReceiverType(S, RefExpr->getGetterSelector(), RefExpr);
+  return (Getter != 0);
+}
+
+/// Try to find the most accurate setter declaration for the property
+/// reference.
+///
+/// \return true if a setter was found, in which case Setter 
+bool ObjCPropertyOpBuilder::findSetter() {
+  // For implicit properties, just trust the lookup we already did.
+  if (RefExpr->isImplicitProperty()) {
+    if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
+      Setter = setter;
+      SetterSelector = setter->getSelector();
+      return true;
     } else {
-      Diag(PRE->getLocation(), diag::err_getter_not_found)
-            << PRE->getBase()->getType();
-      return ExprError();
+      IdentifierInfo *getterName =
+        RefExpr->getImplicitPropertyGetter()->getSelector()
+          .getIdentifierInfoForSlot(0);
+      SetterSelector =
+        SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
+                                           S.PP.getSelectorTable(),
+                                           getterName);
+      return false;
     }
+  }
+
+  // For explicit properties, this is more involved.
+  ObjCPropertyDecl *prop = RefExpr->getExplicitProperty();
+  SetterSelector = prop->getSetterName();
+
+  // Do a normal method lookup first.
+  if (ObjCMethodDecl *setter =
+        LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
+    Setter = setter;
+    return true;
+  }
+
+  // That can fail in the somewhat crazy situation that we're
+  // type-checking a message send within the @interface declaration
+  // that declared the @property.  But it's not clear that that's
+  // valuable to support.
+
+  return false;
+}
+
+/// Capture the base object of an Objective-C property expression.
+Expr *ObjCPropertyOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) {
+  assert(InstanceReceiver == 0);
+
+  // If we have a base, capture it in an OVE and rebuild the syntactic
+  // form to use the OVE as its base.
+  if (RefExpr->isObjectReceiver()) {
+    InstanceReceiver = capture(RefExpr->getBase());
+
+    syntacticBase =
+      ObjCPropertyRefRebuilder(S, InstanceReceiver).rebuild(syntacticBase);
+  }
+
+  return syntacticBase;
+}
+
+/// Load from an Objective-C property reference.
+ExprResult ObjCPropertyOpBuilder::buildGet() {
+  findGetter();
+  assert(Getter);
+  
+  QualType receiverType;
+  SourceLocation superLoc;
+  if (RefExpr->isClassReceiver()) {
+    receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver());
+  } else if (RefExpr->isSuperReceiver()) {
+    superLoc = RefExpr->getReceiverLocation();
+    receiverType = RefExpr->getSuperReceiverType();
   } else {
-    ObjCPropertyDecl *prop = PRE->getExplicitProperty();
+    assert(InstanceReceiver);
+    receiverType = InstanceReceiver->getType();
+  }
 
-    ObjCMethodDecl *getter =
-      LookupMethodInReceiverType(*this, prop->getGetterName(), PRE);
-    if (getter && !getter->hasRelatedResultType())
-      DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation());
-    if (!getter) getter = prop->getGetterMethodDecl();
+  // 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);
 
-    // 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());
+    msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
+                              Getter->getSelector(), Getter,
+                              GenericLoc, GenericLoc, GenericLoc,
+                              MultiExprArg());
+  }
+  return msg;
+}
 
-      // 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);
+/// 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?");
     }
   }
 
-  E->setType(T);
-  E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK);
-  
-  ExprResult Result = MaybeBindToTemporary(E);
-  if (!Result.isInvalid())
-    E = Result.take();
+  // Arguments.
+  Expr *args[] = { op };
 
-  return Owned(E);
+  // Build a message-send.
+  ExprResult msg;
+  if (Setter->isInstanceMethod() || RefExpr->isObjectReceiver()) {
+    msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc,
+                                 SetterSelector, Setter,
+                                 GenericLoc, GenericLoc, GenericLoc,
+                                 MultiExprArg(args, 1));
+  } else {
+    TypeSourceInfo *receiverTypeInfo = 0;
+    if (!RefExpr->isSuperReceiver())
+      receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType);
+
+    msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
+                              SetterSelector, Setter,
+                              GenericLoc, GenericLoc, GenericLoc,
+                              MultiExprArg(args, 1));
+  }
+
+  if (!msg.isInvalid() && captureSetValueAsResult) {
+    ObjCMessageExpr *msgExpr =
+      cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit());
+    Expr *arg = msgExpr->getArg(0);
+    msgExpr->setArg(0, captureValueAsResult(arg));
+  }
+
+  return msg;
 }
 
-namespace {
-  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.
-
-      // For implicit properties, just trust the lookup we already did.
-      if (RefExpr->isImplicitProperty()) {
-        if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
-          setSetter(setter);
-          SetterSelector = setter->getSelector();
-        } else {
-          IdentifierInfo *getterName =
-            RefExpr->getImplicitPropertyGetter()->getSelector()
-              .getIdentifierInfoForSlot(0);
-          SetterSelector = 
-            SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
-                                               S.PP.getSelectorTable(),
-                                               getterName);
-        }
-        return;
-      }
-
-      // For explicit properties, this is more involved.
-      ObjCPropertyDecl *prop = RefExpr->getExplicitProperty();
-      SetterSelector = prop->getSetterName();
-
-      // Do a normal method lookup first.
-      if (ObjCMethodDecl *setter =
-            LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
-        setSetter(setter);
-        return;
-      }
-
-      // If that failed, trust the type on the @property declaration.
-      if (!prop->isReadOnly()) {
-        HasSetter = true;
-        SetterParamType = prop->getType().getUnqualifiedType();
-      }
-    }
-  };
-}
-
-/// Check an increment or decrement of a pseudo-object expression.
-ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc,
-                                         UnaryOperatorKind opcode, Expr *op) {
-  assert(UnaryOperator::isIncrementDecrementOp(opcode));
-  PseudoObjectInfo info(*this, op);
-
-  // If there's no setter, we have no choice but to try to assign to
-  // the result of the getter.
-  if (!info.HasSetter) {
-    QualType resultType = info.RefExpr->getGetterResultType();
-    assert(!resultType.isNull() && "property has no setter and no getter!");
-
-    // Only do this if the getter returns an l-value reference type.
-    if (const LValueReferenceType *refType
-          = resultType->getAs<LValueReferenceType>()) {
-      op = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
-                                    CK_GetObjCProperty, op, 0, VK_LValue);
-      return BuildUnaryOp(S, opcLoc, opcode, op);
-    }
-
-    // Otherwise, it's an error.
-    Diag(opcLoc, diag::err_nosetter_property_incdec)
-      << unsigned(info.RefExpr->isImplicitProperty())
-      << unsigned(UnaryOperator::isDecrementOp(opcode))
-      << info.SetterSelector
-      << op->getSourceRange();
+/// @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,
-                                             BinaryOperatorKind opcode,
-                                             Expr *LHS, Expr *RHS) {
+/// 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!");
+  }
+}
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 8c99e8a..a34d838 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -198,7 +198,7 @@
       Diag(Loc, diag::warn_unused_result) << R1 << R2;
       return;
     }
-  } else if (isa<ObjCPropertyRefExpr>(E)) {
+  } else if (isa<PseudoObjectExpr>(E)) {
     DiagID = diag::warn_unused_property_expr;
   } else if (const CXXFunctionalCastExpr *FC
                                        = dyn_cast<CXXFunctionalCastExpr>(E)) {
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 5596a9a..f55e2a7 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -6103,6 +6103,22 @@
 
 template<typename Derived>
 ExprResult
+TreeTransform<Derived>::TransformPseudoObjectExpr(PseudoObjectExpr *E) {
+  // Rebuild the syntactic form.
+  ExprResult result = getDerived().TransformExpr(E->getSyntacticForm());
+  if (result.isInvalid()) return ExprError();
+
+  // If that gives us a pseudo-object result back, the pseudo-object
+  // expression must have been an lvalue-to-rvalue conversion which we
+  // should reapply.
+  if (result.get()->hasPlaceholderType(BuiltinType::PseudoObject))
+    result = SemaRef.checkPseudoObjectRValue(result.take());
+
+  return result;
+}
+
+template<typename Derived>
+ExprResult
 TreeTransform<Derived>::TransformUnaryExprOrTypeTraitExpr(
                                                 UnaryExprOrTypeTraitExpr *E) {
   if (E->isArgumentType()) {