diff options
author | John McCall <rjmccall@apple.com> | 2011-10-25 17:37:35 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2011-10-25 17:37:35 +0000 |
commit | 3c3b7f90a863af43fa63043d396553ecf205351c (patch) | |
tree | 0bf73812c898572adb7e3f2e6ebbe2d62f60612d /lib/Sema/SemaPseudoObject.cpp | |
parent | edae1a2fc81982b87207cd0b385ee7a9c8ce426b (diff) |
Restore r142914 and r142915, now with missing file and apparent
GCC compiler workaround.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@142931 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaPseudoObject.cpp')
-rw-r--r-- | lib/Sema/SemaPseudoObject.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp new file mode 100644 index 0000000000..92171c5f07 --- /dev/null +++ b/lib/Sema/SemaPseudoObject.cpp @@ -0,0 +1,342 @@ +//===--- 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; +} |