John McCall | 3c3b7f9 | 2011-10-25 17:37:35 +0000 | [diff] [blame^] | 1 | //===--- SemaPseudoObject.cpp - Semantic Analysis for Pseudo-Objects ------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file implements semantic analysis for expressions involving |
| 11 | // pseudo-object references. Pseudo-objects are conceptual objects |
| 12 | // whose storage is entirely abstract and all accesses to which are |
| 13 | // translated through some sort of abstraction barrier. |
| 14 | // |
| 15 | // For example, Objective-C objects can have "properties", either |
| 16 | // declared or undeclared. A property may be accessed by writing |
| 17 | // expr.prop |
| 18 | // where 'expr' is an r-value of Objective-C pointer type and 'prop' |
| 19 | // is the name of the property. If this expression is used in a context |
| 20 | // needing an r-value, it is treated as if it were a message-send |
| 21 | // of the associated 'getter' selector, typically: |
| 22 | // [expr prop] |
| 23 | // If it is used as the LHS of a simple assignment, it is treated |
| 24 | // as a message-send of the associated 'setter' selector, typically: |
| 25 | // [expr setProp: RHS] |
| 26 | // If it is used as the LHS of a compound assignment, or the operand |
| 27 | // of a unary increment or decrement, both are required; for example, |
| 28 | // 'expr.prop *= 100' would be translated to: |
| 29 | // [expr setProp: [expr prop] * 100] |
| 30 | // |
| 31 | //===----------------------------------------------------------------------===// |
| 32 | |
| 33 | #include "clang/Sema/SemaInternal.h" |
| 34 | #include "clang/Sema/Initialization.h" |
| 35 | #include "clang/AST/ExprObjC.h" |
| 36 | #include "clang/Lex/Preprocessor.h" |
| 37 | |
| 38 | using namespace clang; |
| 39 | using namespace sema; |
| 40 | |
| 41 | static ObjCMethodDecl *LookupMethodInReceiverType(Sema &S, Selector sel, |
| 42 | const ObjCPropertyRefExpr *PRE) { |
| 43 | bool instanceProperty; |
| 44 | QualType searchType; |
| 45 | if (PRE->isObjectReceiver()) { |
| 46 | searchType = PRE->getBase()->getType() |
| 47 | ->castAs<ObjCObjectPointerType>()->getPointeeType(); |
| 48 | instanceProperty = true; |
| 49 | } else if (PRE->isSuperReceiver()) { |
| 50 | searchType = PRE->getSuperReceiverType(); |
| 51 | instanceProperty = false; |
| 52 | if (const ObjCObjectPointerType *PT |
| 53 | = searchType->getAs<ObjCObjectPointerType>()) { |
| 54 | searchType = PT->getPointeeType(); |
| 55 | instanceProperty = true; |
| 56 | } |
| 57 | } else if (PRE->isClassReceiver()) { |
| 58 | searchType = S.Context.getObjCInterfaceType(PRE->getClassReceiver()); |
| 59 | instanceProperty = false; |
| 60 | } |
| 61 | |
| 62 | return S.LookupMethodInObjectType(sel, searchType, instanceProperty); |
| 63 | } |
| 64 | |
| 65 | ExprResult Sema::checkPseudoObjectRValue(Expr *E) { |
| 66 | assert(E->getValueKind() == VK_LValue && |
| 67 | E->getObjectKind() == OK_ObjCProperty); |
| 68 | const ObjCPropertyRefExpr *PRE = E->getObjCProperty(); |
| 69 | |
| 70 | QualType ReceiverType; |
| 71 | if (PRE->isObjectReceiver()) |
| 72 | ReceiverType = PRE->getBase()->getType(); |
| 73 | else if (PRE->isSuperReceiver()) |
| 74 | ReceiverType = PRE->getSuperReceiverType(); |
| 75 | else |
| 76 | ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver()); |
| 77 | |
| 78 | ExprValueKind VK = VK_RValue; |
| 79 | QualType T; |
| 80 | if (PRE->isImplicitProperty()) { |
| 81 | if (ObjCMethodDecl *GetterMethod = |
| 82 | PRE->getImplicitPropertyGetter()) { |
| 83 | T = getMessageSendResultType(ReceiverType, GetterMethod, |
| 84 | PRE->isClassReceiver(), |
| 85 | PRE->isSuperReceiver()); |
| 86 | VK = Expr::getValueKindForType(GetterMethod->getResultType()); |
| 87 | } else { |
| 88 | Diag(PRE->getLocation(), diag::err_getter_not_found) |
| 89 | << PRE->getBase()->getType(); |
| 90 | return ExprError(); |
| 91 | } |
| 92 | } else { |
| 93 | ObjCPropertyDecl *prop = PRE->getExplicitProperty(); |
| 94 | |
| 95 | ObjCMethodDecl *getter = |
| 96 | LookupMethodInReceiverType(*this, prop->getGetterName(), PRE); |
| 97 | if (getter && !getter->hasRelatedResultType()) |
| 98 | DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation()); |
| 99 | if (!getter) getter = prop->getGetterMethodDecl(); |
| 100 | |
| 101 | // Figure out the type of the expression. Mostly this is the |
| 102 | // result type of the getter, if possible. |
| 103 | if (getter) { |
| 104 | T = getMessageSendResultType(ReceiverType, getter, |
| 105 | PRE->isClassReceiver(), |
| 106 | PRE->isSuperReceiver()); |
| 107 | VK = Expr::getValueKindForType(getter->getResultType()); |
| 108 | |
| 109 | // As a special case, if the method returns 'id', try to get a |
| 110 | // better type from the property. |
| 111 | if (VK == VK_RValue && T->isObjCIdType() && |
| 112 | prop->getType()->isObjCRetainableType()) |
| 113 | T = prop->getType(); |
| 114 | } else { |
| 115 | T = prop->getType(); |
| 116 | VK = Expr::getValueKindForType(T); |
| 117 | T = T.getNonLValueExprType(Context); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | E->setType(T); |
| 122 | E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK); |
| 123 | |
| 124 | ExprResult Result = MaybeBindToTemporary(E); |
| 125 | if (!Result.isInvalid()) |
| 126 | E = Result.take(); |
| 127 | |
| 128 | return Owned(E); |
| 129 | } |
| 130 | |
| 131 | namespace { |
| 132 | struct PseudoObjectInfo { |
| 133 | const ObjCPropertyRefExpr *RefExpr; |
| 134 | bool HasSetter; |
| 135 | Selector SetterSelector; |
| 136 | ParmVarDecl *SetterParam; |
| 137 | QualType SetterParamType; |
| 138 | |
| 139 | void setSetter(ObjCMethodDecl *setter) { |
| 140 | HasSetter = true; |
| 141 | SetterParam = *setter->param_begin(); |
| 142 | SetterParamType = SetterParam->getType().getUnqualifiedType(); |
| 143 | } |
| 144 | |
| 145 | PseudoObjectInfo(Sema &S, Expr *E) |
| 146 | : RefExpr(E->getObjCProperty()), HasSetter(false), SetterParam(0) { |
| 147 | |
| 148 | assert(E->getValueKind() == VK_LValue && |
| 149 | E->getObjectKind() == OK_ObjCProperty); |
| 150 | |
| 151 | // Try to find a setter. |
| 152 | |
| 153 | // For implicit properties, just trust the lookup we already did. |
| 154 | if (RefExpr->isImplicitProperty()) { |
| 155 | if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) { |
| 156 | setSetter(setter); |
| 157 | SetterSelector = setter->getSelector(); |
| 158 | } else { |
| 159 | IdentifierInfo *getterName = |
| 160 | RefExpr->getImplicitPropertyGetter()->getSelector() |
| 161 | .getIdentifierInfoForSlot(0); |
| 162 | SetterSelector = |
| 163 | SelectorTable::constructSetterName(S.PP.getIdentifierTable(), |
| 164 | S.PP.getSelectorTable(), |
| 165 | getterName); |
| 166 | } |
| 167 | return; |
| 168 | } |
| 169 | |
| 170 | // For explicit properties, this is more involved. |
| 171 | ObjCPropertyDecl *prop = RefExpr->getExplicitProperty(); |
| 172 | SetterSelector = prop->getSetterName(); |
| 173 | |
| 174 | // Do a normal method lookup first. |
| 175 | if (ObjCMethodDecl *setter = |
| 176 | LookupMethodInReceiverType(S, SetterSelector, RefExpr)) { |
| 177 | setSetter(setter); |
| 178 | return; |
| 179 | } |
| 180 | |
| 181 | // If that failed, trust the type on the @property declaration. |
| 182 | if (!prop->isReadOnly()) { |
| 183 | HasSetter = true; |
| 184 | SetterParamType = prop->getType().getUnqualifiedType(); |
| 185 | } |
| 186 | } |
| 187 | }; |
| 188 | } |
| 189 | |
| 190 | /// Check an increment or decrement of a pseudo-object expression. |
| 191 | ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc, |
| 192 | UnaryOperatorKind opcode, Expr *op) { |
| 193 | assert(UnaryOperator::isIncrementDecrementOp(opcode)); |
| 194 | PseudoObjectInfo info(*this, op); |
| 195 | |
| 196 | // If there's no setter, we have no choice but to try to assign to |
| 197 | // the result of the getter. |
| 198 | if (!info.HasSetter) { |
| 199 | QualType resultType = info.RefExpr->getGetterResultType(); |
| 200 | assert(!resultType.isNull() && "property has no setter and no getter!"); |
| 201 | |
| 202 | // Only do this if the getter returns an l-value reference type. |
| 203 | if (const LValueReferenceType *refType |
| 204 | = resultType->getAs<LValueReferenceType>()) { |
| 205 | op = ImplicitCastExpr::Create(Context, refType->getPointeeType(), |
| 206 | CK_GetObjCProperty, op, 0, VK_LValue); |
| 207 | return BuildUnaryOp(S, opcLoc, opcode, op); |
| 208 | } |
| 209 | |
| 210 | // Otherwise, it's an error. |
| 211 | Diag(opcLoc, diag::err_nosetter_property_incdec) |
| 212 | << unsigned(info.RefExpr->isImplicitProperty()) |
| 213 | << unsigned(UnaryOperator::isDecrementOp(opcode)) |
| 214 | << info.SetterSelector |
| 215 | << op->getSourceRange(); |
| 216 | return ExprError(); |
| 217 | } |
| 218 | |
| 219 | // ++/-- behave like compound assignments, i.e. they need a getter. |
| 220 | QualType getterResultType = info.RefExpr->getGetterResultType(); |
| 221 | if (getterResultType.isNull()) { |
| 222 | assert(info.RefExpr->isImplicitProperty()); |
| 223 | Diag(opcLoc, diag::err_nogetter_property_incdec) |
| 224 | << unsigned(UnaryOperator::isDecrementOp(opcode)) |
| 225 | << info.RefExpr->getImplicitPropertyGetter()->getSelector() |
| 226 | << op->getSourceRange(); |
| 227 | return ExprError(); |
| 228 | } |
| 229 | |
| 230 | // HACK: change the type of the operand to prevent further placeholder |
| 231 | // transformation. |
| 232 | op->setType(getterResultType.getNonLValueExprType(Context)); |
| 233 | op->setObjectKind(OK_Ordinary); |
| 234 | |
| 235 | ExprResult result = CreateBuiltinUnaryOp(opcLoc, opcode, op); |
| 236 | if (result.isInvalid()) return ExprError(); |
| 237 | |
| 238 | // Change the object kind back. |
| 239 | op->setObjectKind(OK_ObjCProperty); |
| 240 | return result; |
| 241 | } |
| 242 | |
| 243 | ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc, |
| 244 | BinaryOperatorKind opcode, |
| 245 | Expr *LHS, Expr *RHS) { |
| 246 | assert(BinaryOperator::isAssignmentOp(opcode)); |
| 247 | PseudoObjectInfo info(*this, LHS); |
| 248 | |
| 249 | // If there's no setter, we have no choice but to try to assign to |
| 250 | // the result of the getter. |
| 251 | if (!info.HasSetter) { |
| 252 | QualType resultType = info.RefExpr->getGetterResultType(); |
| 253 | assert(!resultType.isNull() && "property has no setter and no getter!"); |
| 254 | |
| 255 | // Only do this if the getter returns an l-value reference type. |
| 256 | if (const LValueReferenceType *refType |
| 257 | = resultType->getAs<LValueReferenceType>()) { |
| 258 | LHS = ImplicitCastExpr::Create(Context, refType->getPointeeType(), |
| 259 | CK_GetObjCProperty, LHS, 0, VK_LValue); |
| 260 | return BuildBinOp(S, opcLoc, opcode, LHS, RHS); |
| 261 | } |
| 262 | |
| 263 | // Otherwise, it's an error. |
| 264 | Diag(opcLoc, diag::err_nosetter_property_assignment) |
| 265 | << unsigned(info.RefExpr->isImplicitProperty()) |
| 266 | << info.SetterSelector |
| 267 | << LHS->getSourceRange() << RHS->getSourceRange(); |
| 268 | return ExprError(); |
| 269 | } |
| 270 | |
| 271 | // If there is a setter, we definitely want to use it. |
| 272 | |
| 273 | // If this is a simple assignment, just initialize the parameter |
| 274 | // with the RHS. |
| 275 | if (opcode == BO_Assign) { |
| 276 | LHS->setType(info.SetterParamType.getNonLValueExprType(Context)); |
| 277 | |
| 278 | // Under certain circumstances, we need to type-check the RHS as a |
| 279 | // straight-up parameter initialization. This gives somewhat |
| 280 | // inferior diagnostics, so we try to avoid it. |
| 281 | |
| 282 | if (RHS->isTypeDependent()) { |
| 283 | // Just build the expression. |
| 284 | |
| 285 | } else if ((getLangOptions().CPlusPlus && LHS->getType()->isRecordType()) || |
| 286 | (getLangOptions().ObjCAutoRefCount && |
| 287 | info.SetterParam && |
| 288 | info.SetterParam->hasAttr<NSConsumedAttr>())) { |
| 289 | InitializedEntity param = (info.SetterParam |
| 290 | ? InitializedEntity::InitializeParameter(Context, info.SetterParam) |
| 291 | : InitializedEntity::InitializeParameter(Context, info.SetterParamType, |
| 292 | /*consumed*/ false)); |
| 293 | ExprResult arg = PerformCopyInitialization(param, opcLoc, RHS); |
| 294 | if (arg.isInvalid()) return ExprError(); |
| 295 | RHS = arg.take(); |
| 296 | |
| 297 | // Warn about assignments of +1 objects to unsafe pointers in ARC. |
| 298 | // CheckAssignmentOperands does this on the other path. |
| 299 | if (getLangOptions().ObjCAutoRefCount) |
| 300 | checkUnsafeExprAssigns(opcLoc, LHS, RHS); |
| 301 | } else { |
| 302 | ExprResult RHSResult = Owned(RHS); |
| 303 | |
| 304 | LHS->setObjectKind(OK_Ordinary); |
| 305 | QualType resultType = CheckAssignmentOperands(LHS, RHSResult, opcLoc, |
| 306 | /*compound*/ QualType()); |
| 307 | LHS->setObjectKind(OK_ObjCProperty); |
| 308 | |
| 309 | if (!RHSResult.isInvalid()) RHS = RHSResult.take(); |
| 310 | if (resultType.isNull()) return ExprError(); |
| 311 | } |
| 312 | |
| 313 | // Warn about property sets in ARC that might cause retain cycles. |
| 314 | if (getLangOptions().ObjCAutoRefCount && !info.RefExpr->isSuperReceiver()) |
| 315 | checkRetainCycles(const_cast<Expr*>(info.RefExpr->getBase()), RHS); |
| 316 | |
| 317 | return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(), |
| 318 | RHS->getValueKind(), |
| 319 | RHS->getObjectKind(), |
| 320 | opcLoc); |
| 321 | } |
| 322 | |
| 323 | // If this is a compound assignment, we need to use the getter, too. |
| 324 | QualType getterResultType = info.RefExpr->getGetterResultType(); |
| 325 | if (getterResultType.isNull()) { |
| 326 | Diag(opcLoc, diag::err_nogetter_property_compound_assignment) |
| 327 | << LHS->getSourceRange() << RHS->getSourceRange(); |
| 328 | return ExprError(); |
| 329 | } |
| 330 | |
| 331 | // HACK: change the type of the LHS to prevent further placeholder |
| 332 | // transformation. |
| 333 | LHS->setType(getterResultType.getNonLValueExprType(Context)); |
| 334 | LHS->setObjectKind(OK_Ordinary); |
| 335 | |
| 336 | ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS); |
| 337 | if (result.isInvalid()) return ExprError(); |
| 338 | |
| 339 | // Change the object kind back. |
| 340 | LHS->setObjectKind(OK_ObjCProperty); |
| 341 | return result; |
| 342 | } |