blob: 92171c5f0779dba16f7e60a2c415fb69d9c57ed2 [file] [log] [blame]
John McCall3c3b7f92011-10-25 17:37:35 +00001//===--- 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
38using namespace clang;
39using namespace sema;
40
41static 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
65ExprResult 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
131namespace {
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.
191ExprResult 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
243ExprResult 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}