blob: 92171c5f0779dba16f7e60a2c415fb69d9c57ed2 [file] [log] [blame]
//===--- 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;
}