| //===--- SemaPseudoObject.cpp - Semantic Analysis for Pseudo-Objects ------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements semantic analysis for expressions involving |
| // pseudo-object references. Pseudo-objects are conceptual objects |
| // whose storage is entirely abstract and all accesses to which are |
| // translated through some sort of abstraction barrier. |
| // |
| // For example, Objective-C objects can have "properties", either |
| // declared or undeclared. A property may be accessed by writing |
| // expr.prop |
| // where 'expr' is an r-value of Objective-C pointer type and 'prop' |
| // is the name of the property. If this expression is used in a context |
| // needing an r-value, it is treated as if it were a message-send |
| // of the associated 'getter' selector, typically: |
| // [expr prop] |
| // If it is used as the LHS of a simple assignment, it is treated |
| // as a message-send of the associated 'setter' selector, typically: |
| // [expr setProp: RHS] |
| // If it is used as the LHS of a compound assignment, or the operand |
| // of a unary increment or decrement, both are required; for example, |
| // 'expr.prop *= 100' would be translated to: |
| // [expr setProp: [expr prop] * 100] |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Sema/SemaInternal.h" |
| #include "clang/Sema/Initialization.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/Lex/Preprocessor.h" |
| |
| using namespace clang; |
| using namespace sema; |
| |
| static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel, |
| const ObjCPropertyRefExpr *PRE) { |
| bool instanceProperty; |
| QualType searchType; |
| if (PRE->isObjectReceiver()) { |
| searchType = PRE->getBase()->getType() |
| ->castAs<ObjCObjectPointerType>()->getPointeeType(); |
| instanceProperty = true; |
| } else if (PRE->isSuperReceiver()) { |
| searchType = PRE->getSuperReceiverType(); |
| instanceProperty = false; |
| if (const ObjCObjectPointerType *PT |
| = searchType->getAs<ObjCObjectPointerType>()) { |
| searchType = PT->getPointeeType(); |
| instanceProperty = true; |
| } |
| } else if (PRE->isClassReceiver()) { |
| searchType = S.Context.getObjCInterfaceType(PRE->getClassReceiver()); |
| instanceProperty = false; |
| } |
| |
| return S.LookupMethodInObjectType(sel, searchType, instanceProperty); |
| } |
| |
| 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(); |
| } else { |
| T = prop->getType(); |
| VK = Expr::getValueKindForType(T); |
| T = T.getNonLValueExprType(Context); |
| } |
| } |
| |
| 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. |
| |
| // For implicit properties, just trust the lookup we already did. |
| if (RefExpr->isImplicitProperty()) { |
| if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) { |
| setSetter(setter); |
| SetterSelector = setter->getSelector(); |
| } else { |
| IdentifierInfo *getterName = |
| RefExpr->getImplicitPropertyGetter()->getSelector() |
| .getIdentifierInfoForSlot(0); |
| SetterSelector = |
| SelectorTable::constructSetterName(S.PP.getIdentifierTable(), |
| S.PP.getSelectorTable(), |
| getterName); |
| } |
| return; |
| } |
| |
| // For explicit properties, this is more involved. |
| ObjCPropertyDecl *prop = RefExpr->getExplicitProperty(); |
| SetterSelector = prop->getSetterName(); |
| |
| // Do a normal method lookup first. |
| if (ObjCMethodDecl *setter = |
| LookupMethodInReceiverType(S, SetterSelector, RefExpr)) { |
| setSetter(setter); |
| return; |
| } |
| |
| // If that failed, trust the type on the @property declaration. |
| if (!prop->isReadOnly()) { |
| HasSetter = true; |
| SetterParamType = prop->getType().getUnqualifiedType(); |
| } |
| } |
| }; |
| } |
| |
| /// Check an increment or decrement of a pseudo-object expression. |
| ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc, |
| UnaryOperatorKind opcode, Expr *op) { |
| assert(UnaryOperator::isIncrementDecrementOp(opcode)); |
| PseudoObjectInfo info(*this, op); |
| |
| // If there's no setter, we have no choice but to try to assign to |
| // the result of the getter. |
| if (!info.HasSetter) { |
| QualType resultType = info.RefExpr->getGetterResultType(); |
| assert(!resultType.isNull() && "property has no setter and no getter!"); |
| |
| // Only do this if the getter returns an l-value reference type. |
| if (const LValueReferenceType *refType |
| = resultType->getAs<LValueReferenceType>()) { |
| op = ImplicitCastExpr::Create(Context, refType->getPointeeType(), |
| CK_GetObjCProperty, op, 0, VK_LValue); |
| return BuildUnaryOp(S, opcLoc, opcode, op); |
| } |
| |
| // Otherwise, it's an error. |
| Diag(opcLoc, diag::err_nosetter_property_incdec) |
| << unsigned(info.RefExpr->isImplicitProperty()) |
| << unsigned(UnaryOperator::isDecrementOp(opcode)) |
| << info.SetterSelector |
| << op->getSourceRange(); |
| return ExprError(); |
| } |
| |
| // ++/-- 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); |
| if (result.isInvalid()) return ExprError(); |
| |
| // Change the object kind back. |
| op->setObjectKind(OK_ObjCProperty); |
| return result; |
| } |
| |
| ExprResult Sema::checkPseudoObjectAssignment(Scope *S, 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); |
| } |
| |
| // Otherwise, it's an error. |
| Diag(opcLoc, diag::err_nosetter_property_assignment) |
| << unsigned(info.RefExpr->isImplicitProperty()) |
| << info.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) |
| << 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); |
| if (result.isInvalid()) return ExprError(); |
| |
| // Change the object kind back. |
| LHS->setObjectKind(OK_ObjCProperty); |
| return result; |
| } |