aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaPseudoObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Sema/SemaPseudoObject.cpp')
-rw-r--r--lib/Sema/SemaPseudoObject.cpp896
1 files changed, 671 insertions, 225 deletions
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp
index 97fbe48aa2..e0a6271215 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 @@ static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel,
return S.LookupMethodInObjectType(sel, IT, false);
}
-ExprResult Sema::checkPseudoObjectRValue(Expr *E) {
- assert(E->getValueKind() == VK_LValue &&
- E->getObjectKind() == OK_ObjCProperty);
- const ObjCPropertyRefExpr *PRE = E->getObjCProperty();
-
- QualType ReceiverType;
- if (PRE->isObjectReceiver())
- ReceiverType = PRE->getBase()->getType();
- else if (PRE->isSuperReceiver())
- ReceiverType = PRE->getSuperReceiverType();
- else
- ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver());
-
- ExprValueKind VK = VK_RValue;
- QualType T;
- if (PRE->isImplicitProperty()) {
- if (ObjCMethodDecl *GetterMethod =
- PRE->getImplicitPropertyGetter()) {
- T = getMessageSendResultType(ReceiverType, GetterMethod,
- PRE->isClassReceiver(),
- PRE->isSuperReceiver());
- VK = Expr::getValueKindForType(GetterMethod->getResultType());
- } else {
- Diag(PRE->getLocation(), diag::err_getter_not_found)
- << PRE->getBase()->getType();
- return ExprError();
- }
- } else {
- ObjCPropertyDecl *prop = PRE->getExplicitProperty();
-
- ObjCMethodDecl *getter =
- LookupMethodInReceiverType(*this, prop->getGetterName(), PRE);
- if (getter && !getter->hasRelatedResultType())
- DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation());
- if (!getter) getter = prop->getGetterMethodDecl();
-
- // Figure out the type of the expression. Mostly this is the
- // result type of the getter, if possible.
- if (getter) {
- T = getMessageSendResultType(ReceiverType, getter,
- PRE->isClassReceiver(),
- PRE->isSuperReceiver());
- VK = Expr::getValueKindForType(getter->getResultType());
-
- // As a special case, if the method returns 'id', try to get a
- // better type from the property.
- if (VK == VK_RValue && T->isObjCIdType() &&
- prop->getType()->isObjCRetainableType())
- T = prop->getType();
+bool ObjCPropertyOpBuilder::findGetter() {
+ if (Getter) return true;
+
+ Getter = LookupMethodInReceiverType(S, RefExpr->getGetterSelector(), RefExpr);
+ return (Getter != 0);
+}
+
+/// Try to find the most accurate setter declaration for the property
+/// reference.
+///
+/// \return true if a setter was found, in which case Setter
+bool ObjCPropertyOpBuilder::findSetter() {
+ // For implicit properties, just trust the lookup we already did.
+ if (RefExpr->isImplicitProperty()) {
+ if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
+ Setter = setter;
+ SetterSelector = setter->getSelector();
+ return true;
} else {
- T = prop->getType();
- VK = Expr::getValueKindForType(T);
- T = T.getNonLValueExprType(Context);
+ IdentifierInfo *getterName =
+ RefExpr->getImplicitPropertyGetter()->getSelector()
+ .getIdentifierInfoForSlot(0);
+ SetterSelector =
+ SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
+ S.PP.getSelectorTable(),
+ getterName);
+ return false;
}
}
- E->setType(T);
- E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK);
+ // For explicit properties, this is more involved.
+ ObjCPropertyDecl *prop = RefExpr->getExplicitProperty();
+ SetterSelector = prop->getSetterName();
+
+ // Do a normal method lookup first.
+ if (ObjCMethodDecl *setter =
+ LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
+ Setter = setter;
+ return true;
+ }
+
+ // That can fail in the somewhat crazy situation that we're
+ // type-checking a message send within the @interface declaration
+ // that declared the @property. But it's not clear that that's
+ // valuable to support.
+
+ return false;
+}
+
+/// Capture the base object of an Objective-C property expression.
+Expr *ObjCPropertyOpBuilder::rebuildAndCaptureObject(Expr *syntacticBase) {
+ assert(InstanceReceiver == 0);
+
+ // If we have a base, capture it in an OVE and rebuild the syntactic
+ // form to use the OVE as its base.
+ if (RefExpr->isObjectReceiver()) {
+ InstanceReceiver = capture(RefExpr->getBase());
+
+ syntacticBase =
+ ObjCPropertyRefRebuilder(S, InstanceReceiver).rebuild(syntacticBase);
+ }
+
+ return syntacticBase;
+}
+
+/// Load from an Objective-C property reference.
+ExprResult ObjCPropertyOpBuilder::buildGet() {
+ findGetter();
+ assert(Getter);
- ExprResult Result = MaybeBindToTemporary(E);
- if (!Result.isInvalid())
- E = Result.take();
+ QualType receiverType;
+ SourceLocation superLoc;
+ if (RefExpr->isClassReceiver()) {
+ receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver());
+ } else if (RefExpr->isSuperReceiver()) {
+ superLoc = RefExpr->getReceiverLocation();
+ receiverType = RefExpr->getSuperReceiverType();
+ } else {
+ assert(InstanceReceiver);
+ receiverType = InstanceReceiver->getType();
+ }
- return Owned(E);
+ // Build a message-send.
+ ExprResult msg;
+ if (Getter->isInstanceMethod() || RefExpr->isObjectReceiver()) {
+ assert(InstanceReceiver || RefExpr->isSuperReceiver());
+ msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc,
+ Getter->getSelector(), Getter,
+ GenericLoc, GenericLoc, GenericLoc,
+ MultiExprArg());
+ } else {
+ TypeSourceInfo *receiverTypeInfo = 0;
+ if (!RefExpr->isSuperReceiver())
+ receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType);
+
+ msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
+ Getter->getSelector(), Getter,
+ GenericLoc, GenericLoc, GenericLoc,
+ MultiExprArg());
+ }
+ return msg;
}
-namespace {
- struct PseudoObjectInfo {
- const ObjCPropertyRefExpr *RefExpr;
- bool HasSetter;
- Selector SetterSelector;
- ParmVarDecl *SetterParam;
- QualType SetterParamType;
+/// Store to an Objective-C property reference.
+///
+/// \param bindSetValueAsResult - If true, capture the actual
+/// value being set as the value of the property operation.
+ExprResult ObjCPropertyOpBuilder::buildSet(Expr *op, SourceLocation opcLoc,
+ bool captureSetValueAsResult) {
+ bool hasSetter = findSetter();
+ assert(hasSetter); (void) hasSetter;
+
+ QualType receiverType;
+ SourceLocation superLoc;
+ if (RefExpr->isClassReceiver()) {
+ receiverType = S.Context.getObjCInterfaceType(RefExpr->getClassReceiver());
+ } else if (RefExpr->isSuperReceiver()) {
+ superLoc = RefExpr->getReceiverLocation();
+ receiverType = RefExpr->getSuperReceiverType();
+ } else {
+ assert(InstanceReceiver);
+ receiverType = InstanceReceiver->getType();
+ }
- void setSetter(ObjCMethodDecl *setter) {
- HasSetter = true;
- SetterParam = *setter->param_begin();
- SetterParamType = SetterParam->getType().getUnqualifiedType();
+ // Use assignment constraints when possible; they give us better
+ // diagnostics. "When possible" basically means anything except a
+ // C++ class type.
+ if (!S.getLangOptions().CPlusPlus || !op->getType()->isRecordType()) {
+ QualType paramType = (*Setter->param_begin())->getType();
+ if (!S.getLangOptions().CPlusPlus || !paramType->isRecordType()) {
+ ExprResult opResult = op;
+ Sema::AssignConvertType assignResult
+ = S.CheckSingleAssignmentConstraints(paramType, opResult);
+ if (S.DiagnoseAssignmentResult(assignResult, opcLoc, paramType,
+ op->getType(), opResult.get(),
+ Sema::AA_Assigning))
+ return ExprError();
+
+ op = opResult.take();
+ assert(op && "successful assignment left argument invalid?");
}
+ }
- PseudoObjectInfo(Sema &S, Expr *E)
- : RefExpr(E->getObjCProperty()), HasSetter(false), SetterParam(0) {
-
- assert(E->getValueKind() == VK_LValue &&
- E->getObjectKind() == OK_ObjCProperty);
-
- // Try to find a setter.
-
- // For implicit properties, just trust the lookup we already did.
- if (RefExpr->isImplicitProperty()) {
- if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
- setSetter(setter);
- SetterSelector = setter->getSelector();
- } else {
- IdentifierInfo *getterName =
- RefExpr->getImplicitPropertyGetter()->getSelector()
- .getIdentifierInfoForSlot(0);
- SetterSelector =
- SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
- S.PP.getSelectorTable(),
- getterName);
- }
- return;
- }
+ // Arguments.
+ Expr *args[] = { op };
- // For explicit properties, this is more involved.
- ObjCPropertyDecl *prop = RefExpr->getExplicitProperty();
- SetterSelector = prop->getSetterName();
+ // Build a message-send.
+ ExprResult msg;
+ if (Setter->isInstanceMethod() || RefExpr->isObjectReceiver()) {
+ msg = S.BuildInstanceMessage(InstanceReceiver, receiverType, superLoc,
+ SetterSelector, Setter,
+ GenericLoc, GenericLoc, GenericLoc,
+ MultiExprArg(args, 1));
+ } else {
+ TypeSourceInfo *receiverTypeInfo = 0;
+ if (!RefExpr->isSuperReceiver())
+ receiverTypeInfo = S.Context.getTrivialTypeSourceInfo(receiverType);
+
+ msg = S.BuildClassMessage(receiverTypeInfo, receiverType, superLoc,
+ SetterSelector, Setter,
+ GenericLoc, GenericLoc, GenericLoc,
+ MultiExprArg(args, 1));
+ }
- // Do a normal method lookup first.
- if (ObjCMethodDecl *setter =
- LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
- setSetter(setter);
- return;
- }
+ if (!msg.isInvalid() && captureSetValueAsResult) {
+ ObjCMessageExpr *msgExpr =
+ cast<ObjCMessageExpr>(msg.get()->IgnoreImplicit());
+ Expr *arg = msgExpr->getArg(0);
+ msgExpr->setArg(0, captureValueAsResult(arg));
+ }
- // If that failed, trust the type on the @property declaration.
- if (!prop->isReadOnly()) {
- HasSetter = true;
- SetterParamType = prop->getType().getUnqualifiedType();
- }
+ return msg;
+}
+
+/// @property-specific behavior for doing lvalue-to-rvalue conversion.
+ExprResult ObjCPropertyOpBuilder::buildRValueOperation(Expr *op) {
+ // Explicit properties always have getters, but implicit ones don't.
+ // Check that before proceeding.
+ if (RefExpr->isImplicitProperty() &&
+ !RefExpr->getImplicitPropertyGetter()) {
+ S.Diag(RefExpr->getLocation(), diag::err_getter_not_found)
+ << RefExpr->getBase()->getType();
+ return ExprError();
+ }
+
+ ExprResult result = PseudoOpBuilder::buildRValueOperation(op);
+ if (result.isInvalid()) return ExprError();
+
+ if (RefExpr->isExplicitProperty() && !Getter->hasRelatedResultType())
+ S.DiagnosePropertyAccessorMismatch(RefExpr->getExplicitProperty(),
+ Getter, RefExpr->getLocation());
+
+ // As a special case, if the method returns 'id', try to get
+ // a better type from the property.
+ if (RefExpr->isExplicitProperty() && result.get()->isRValue() &&
+ result.get()->getType()->isObjCIdType()) {
+ QualType propType = RefExpr->getExplicitProperty()->getType();
+ if (const ObjCObjectPointerType *ptr
+ = propType->getAs<ObjCObjectPointerType>()) {
+ if (!ptr->isObjCIdType())
+ result = S.ImpCastExprToType(result.get(), propType, CK_BitCast);
}
- };
+ }
+
+ return result;
}
-/// Check an increment or decrement of a pseudo-object expression.
-ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc,
- UnaryOperatorKind opcode, Expr *op) {
- assert(UnaryOperator::isIncrementDecrementOp(opcode));
- PseudoObjectInfo info(*this, op);
+/// Try to build this as a call to a getter that returns a reference.
+///
+/// \return true if it was possible, whether or not it actually
+/// succeeded
+bool ObjCPropertyOpBuilder::tryBuildGetOfReference(Expr *op,
+ ExprResult &result) {
+ if (!S.getLangOptions().CPlusPlus) return false;
+
+ findGetter();
+ assert(Getter && "property has no setter and no getter!");
+
+ // Only do this if the getter returns an l-value reference type.
+ QualType resultType = Getter->getResultType();
+ if (!resultType->isLValueReferenceType()) return false;
+
+ result = buildRValueOperation(op);
+ return true;
+}
+
+/// @property-specific behavior for doing assignments.
+ExprResult
+ObjCPropertyOpBuilder::buildAssignmentOperation(Scope *Sc,
+ SourceLocation opcLoc,
+ BinaryOperatorKind opcode,
+ Expr *LHS, Expr *RHS) {
+ assert(BinaryOperator::isAssignmentOp(opcode));
// If there's no setter, we have no choice but to try to assign to
// the result of the getter.
- if (!info.HasSetter) {
- QualType resultType = info.RefExpr->getGetterResultType();
- assert(!resultType.isNull() && "property has no setter and no getter!");
-
- // Only do this if the getter returns an l-value reference type.
- if (const LValueReferenceType *refType
- = resultType->getAs<LValueReferenceType>()) {
- op = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
- CK_GetObjCProperty, op, 0, VK_LValue);
- return BuildUnaryOp(S, opcLoc, opcode, op);
+ if (!findSetter()) {
+ ExprResult result;
+ if (tryBuildGetOfReference(LHS, result)) {
+ if (result.isInvalid()) return ExprError();
+ return S.BuildBinOp(Sc, opcLoc, opcode, result.take(), RHS);
}
// Otherwise, it's an error.
- Diag(opcLoc, diag::err_nosetter_property_incdec)
- << unsigned(info.RefExpr->isImplicitProperty())
- << unsigned(UnaryOperator::isDecrementOp(opcode))
- << info.SetterSelector
- << op->getSourceRange();
+ S.Diag(opcLoc, diag::err_nosetter_property_assignment)
+ << unsigned(RefExpr->isImplicitProperty())
+ << SetterSelector
+ << LHS->getSourceRange() << RHS->getSourceRange();
return ExprError();
}
- // ++/-- behave like compound assignments, i.e. they need a getter.
- QualType getterResultType = info.RefExpr->getGetterResultType();
- if (getterResultType.isNull()) {
- assert(info.RefExpr->isImplicitProperty());
- Diag(opcLoc, diag::err_nogetter_property_incdec)
- << unsigned(UnaryOperator::isDecrementOp(opcode))
- << info.RefExpr->getImplicitPropertyGetter()->getSelector()
- << op->getSourceRange();
+ // If there is a setter, we definitely want to use it.
+
+ // Verify that we can do a compound assignment.
+ if (opcode != BO_Assign && !findGetter()) {
+ S.Diag(opcLoc, diag::err_nogetter_property_compound_assignment)
+ << LHS->getSourceRange() << RHS->getSourceRange();
return ExprError();
}
- // HACK: change the type of the operand to prevent further placeholder
- // transformation.
- op->setType(getterResultType.getNonLValueExprType(Context));
- op->setObjectKind(OK_Ordinary);
-
- ExprResult result = CreateBuiltinUnaryOp(opcLoc, opcode, op);
+ ExprResult result =
+ PseudoOpBuilder::buildAssignmentOperation(Sc, opcLoc, opcode, LHS, RHS);
if (result.isInvalid()) return ExprError();
- // Change the object kind back.
- op->setObjectKind(OK_ObjCProperty);
+ // Various warnings about property assignments in ARC.
+ if (S.getLangOptions().ObjCAutoRefCount && InstanceReceiver) {
+ S.checkRetainCycles(InstanceReceiver->getSourceExpr(), RHS);
+ S.checkUnsafeExprAssigns(opcLoc, LHS, RHS);
+ }
+
return result;
}
-ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc,
- BinaryOperatorKind opcode,
- Expr *LHS, Expr *RHS) {
- assert(BinaryOperator::isAssignmentOp(opcode));
- PseudoObjectInfo info(*this, LHS);
-
+/// @property-specific behavior for doing increments and decrements.
+ExprResult
+ObjCPropertyOpBuilder::buildIncDecOperation(Scope *Sc, SourceLocation opcLoc,
+ UnaryOperatorKind opcode,
+ Expr *op) {
// If there's no setter, we have no choice but to try to assign to
// the result of the getter.
- if (!info.HasSetter) {
- QualType resultType = info.RefExpr->getGetterResultType();
- assert(!resultType.isNull() && "property has no setter and no getter!");
-
- // Only do this if the getter returns an l-value reference type.
- if (const LValueReferenceType *refType
- = resultType->getAs<LValueReferenceType>()) {
- LHS = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
- CK_GetObjCProperty, LHS, 0, VK_LValue);
- return BuildBinOp(S, opcLoc, opcode, LHS, RHS);
+ if (!findSetter()) {
+ ExprResult result;
+ if (tryBuildGetOfReference(op, result)) {
+ if (result.isInvalid()) return ExprError();
+ return S.BuildUnaryOp(Sc, opcLoc, opcode, result.take());
}
// Otherwise, it's an error.
- Diag(opcLoc, diag::err_nosetter_property_assignment)
- << unsigned(info.RefExpr->isImplicitProperty())
- << info.SetterSelector
- << LHS->getSourceRange() << RHS->getSourceRange();
+ S.Diag(opcLoc, diag::err_nosetter_property_incdec)
+ << unsigned(RefExpr->isImplicitProperty())
+ << unsigned(UnaryOperator::isDecrementOp(opcode))
+ << SetterSelector
+ << op->getSourceRange();
return ExprError();
}
// If there is a setter, we definitely want to use it.
- // If this is a simple assignment, just initialize the parameter
- // with the RHS.
- if (opcode == BO_Assign) {
- LHS->setType(info.SetterParamType.getNonLValueExprType(Context));
-
- // Under certain circumstances, we need to type-check the RHS as a
- // straight-up parameter initialization. This gives somewhat
- // inferior diagnostics, so we try to avoid it.
-
- if (RHS->isTypeDependent()) {
- // Just build the expression.
-
- } else if ((getLangOptions().CPlusPlus && LHS->getType()->isRecordType()) ||
- (getLangOptions().ObjCAutoRefCount &&
- info.SetterParam &&
- info.SetterParam->hasAttr<NSConsumedAttr>())) {
- InitializedEntity param = (info.SetterParam
- ? InitializedEntity::InitializeParameter(Context, info.SetterParam)
- : InitializedEntity::InitializeParameter(Context, info.SetterParamType,
- /*consumed*/ false));
- ExprResult arg = PerformCopyInitialization(param, opcLoc, RHS);
- if (arg.isInvalid()) return ExprError();
- RHS = arg.take();
-
- // Warn about assignments of +1 objects to unsafe pointers in ARC.
- // CheckAssignmentOperands does this on the other path.
- if (getLangOptions().ObjCAutoRefCount)
- checkUnsafeExprAssigns(opcLoc, LHS, RHS);
- } else {
- ExprResult RHSResult = Owned(RHS);
-
- LHS->setObjectKind(OK_Ordinary);
- QualType resultType = CheckAssignmentOperands(LHS, RHSResult, opcLoc,
- /*compound*/ QualType());
- LHS->setObjectKind(OK_ObjCProperty);
+ // We also need a getter.
+ if (!findGetter()) {
+ assert(RefExpr->isImplicitProperty());
+ S.Diag(opcLoc, diag::err_nogetter_property_incdec)
+ << unsigned(UnaryOperator::isDecrementOp(opcode))
+ << RefExpr->getImplicitPropertyGetter()->getSelector() // FIXME!
+ << op->getSourceRange();
+ return ExprError();
+ }
- if (!RHSResult.isInvalid()) RHS = RHSResult.take();
- if (resultType.isNull()) return ExprError();
- }
+ return PseudoOpBuilder::buildIncDecOperation(Sc, opcLoc, opcode, op);
+}
- // Warn about property sets in ARC that might cause retain cycles.
- if (getLangOptions().ObjCAutoRefCount && !info.RefExpr->isSuperReceiver())
- checkRetainCycles(const_cast<Expr*>(info.RefExpr->getBase()), RHS);
+//===----------------------------------------------------------------------===//
+// General Sema routines.
+//===----------------------------------------------------------------------===//
- return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(),
- RHS->getValueKind(),
- RHS->getObjectKind(),
- opcLoc);
+ExprResult Sema::checkPseudoObjectRValue(Expr *E) {
+ Expr *opaqueRef = E->IgnoreParens();
+ if (ObjCPropertyRefExpr *refExpr
+ = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) {
+ ObjCPropertyOpBuilder builder(*this, refExpr);
+ return builder.buildRValueOperation(E);
+ } else {
+ llvm_unreachable("unknown pseudo-object kind!");
}
+}
- // If this is a compound assignment, we need to use the getter, too.
- QualType getterResultType = info.RefExpr->getGetterResultType();
- if (getterResultType.isNull()) {
- Diag(opcLoc, diag::err_nogetter_property_compound_assignment)
- << LHS->getSourceRange() << RHS->getSourceRange();
- return ExprError();
+/// Check an increment or decrement of a pseudo-object expression.
+ExprResult Sema::checkPseudoObjectIncDec(Scope *Sc, SourceLocation opcLoc,
+ UnaryOperatorKind opcode, Expr *op) {
+ // Do nothing if the operand is dependent.
+ if (op->isTypeDependent())
+ return new (Context) UnaryOperator(op, opcode, Context.DependentTy,
+ VK_RValue, OK_Ordinary, opcLoc);
+
+ assert(UnaryOperator::isIncrementDecrementOp(opcode));
+ Expr *opaqueRef = op->IgnoreParens();
+ if (ObjCPropertyRefExpr *refExpr
+ = dyn_cast<ObjCPropertyRefExpr>(opaqueRef)) {
+ ObjCPropertyOpBuilder builder(*this, refExpr);
+ return builder.buildIncDecOperation(Sc, opcLoc, opcode, op);
+ } else {
+ llvm_unreachable("unknown pseudo-object kind!");
}
+}
- // HACK: change the type of the LHS to prevent further placeholder
- // transformation.
- LHS->setType(getterResultType.getNonLValueExprType(Context));
- LHS->setObjectKind(OK_Ordinary);
-
- ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS);
- if (result.isInvalid()) return ExprError();
+ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc,
+ BinaryOperatorKind opcode,
+ Expr *LHS, Expr *RHS) {
+ // Do nothing if either argument is dependent.
+ if (LHS->isTypeDependent() || RHS->isTypeDependent())
+ return new (Context) BinaryOperator(LHS, RHS, opcode, Context.DependentTy,
+ VK_RValue, OK_Ordinary, opcLoc);
+
+ // Filter out non-overload placeholder types in the RHS.
+ if (const BuiltinType *PT