aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaPseudoObject.cpp
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2011-10-25 17:37:35 +0000
committerJohn McCall <rjmccall@apple.com>2011-10-25 17:37:35 +0000
commit3c3b7f90a863af43fa63043d396553ecf205351c (patch)
tree0bf73812c898572adb7e3f2e6ebbe2d62f60612d /lib/Sema/SemaPseudoObject.cpp
parentedae1a2fc81982b87207cd0b385ee7a9c8ce426b (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.cpp342
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;
+}