blob: 97fbe48aa23b4221708bf7130df5f6e608fa12b9 [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) {
John McCall3c3b7f92011-10-25 17:37:35 +000043 if (PRE->isObjectReceiver()) {
Benjamin Krameraa9807a2011-10-28 13:21:18 +000044 const ObjCObjectPointerType *PT =
45 PRE->getBase()->getType()->castAs<ObjCObjectPointerType>();
46 return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true);
John McCall3c3b7f92011-10-25 17:37:35 +000047 }
48
Benjamin Krameraa9807a2011-10-28 13:21:18 +000049 if (PRE->isSuperReceiver()) {
50 if (const ObjCObjectPointerType *PT =
51 PRE->getSuperReceiverType()->getAs<ObjCObjectPointerType>())
52 return S.LookupMethodInObjectType(sel, PT->getPointeeType(), true);
53
54 return S.LookupMethodInObjectType(sel, PRE->getSuperReceiverType(), false);
55 }
56
57 assert(PRE->isClassReceiver() && "Invalid expression");
58 QualType IT = S.Context.getObjCInterfaceType(PRE->getClassReceiver());
59 return S.LookupMethodInObjectType(sel, IT, false);
John McCall3c3b7f92011-10-25 17:37:35 +000060}
61
62ExprResult Sema::checkPseudoObjectRValue(Expr *E) {
63 assert(E->getValueKind() == VK_LValue &&
64 E->getObjectKind() == OK_ObjCProperty);
65 const ObjCPropertyRefExpr *PRE = E->getObjCProperty();
66
67 QualType ReceiverType;
68 if (PRE->isObjectReceiver())
69 ReceiverType = PRE->getBase()->getType();
70 else if (PRE->isSuperReceiver())
71 ReceiverType = PRE->getSuperReceiverType();
72 else
73 ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver());
74
75 ExprValueKind VK = VK_RValue;
76 QualType T;
77 if (PRE->isImplicitProperty()) {
78 if (ObjCMethodDecl *GetterMethod =
79 PRE->getImplicitPropertyGetter()) {
80 T = getMessageSendResultType(ReceiverType, GetterMethod,
81 PRE->isClassReceiver(),
82 PRE->isSuperReceiver());
83 VK = Expr::getValueKindForType(GetterMethod->getResultType());
84 } else {
85 Diag(PRE->getLocation(), diag::err_getter_not_found)
86 << PRE->getBase()->getType();
87 return ExprError();
88 }
89 } else {
90 ObjCPropertyDecl *prop = PRE->getExplicitProperty();
91
92 ObjCMethodDecl *getter =
93 LookupMethodInReceiverType(*this, prop->getGetterName(), PRE);
94 if (getter && !getter->hasRelatedResultType())
95 DiagnosePropertyAccessorMismatch(prop, getter, PRE->getLocation());
96 if (!getter) getter = prop->getGetterMethodDecl();
97
98 // Figure out the type of the expression. Mostly this is the
99 // result type of the getter, if possible.
100 if (getter) {
101 T = getMessageSendResultType(ReceiverType, getter,
102 PRE->isClassReceiver(),
103 PRE->isSuperReceiver());
104 VK = Expr::getValueKindForType(getter->getResultType());
105
106 // As a special case, if the method returns 'id', try to get a
107 // better type from the property.
108 if (VK == VK_RValue && T->isObjCIdType() &&
109 prop->getType()->isObjCRetainableType())
110 T = prop->getType();
111 } else {
112 T = prop->getType();
113 VK = Expr::getValueKindForType(T);
114 T = T.getNonLValueExprType(Context);
115 }
116 }
117
118 E->setType(T);
119 E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK);
120
121 ExprResult Result = MaybeBindToTemporary(E);
122 if (!Result.isInvalid())
123 E = Result.take();
124
125 return Owned(E);
126}
127
128namespace {
129 struct PseudoObjectInfo {
130 const ObjCPropertyRefExpr *RefExpr;
131 bool HasSetter;
132 Selector SetterSelector;
133 ParmVarDecl *SetterParam;
134 QualType SetterParamType;
135
136 void setSetter(ObjCMethodDecl *setter) {
137 HasSetter = true;
138 SetterParam = *setter->param_begin();
139 SetterParamType = SetterParam->getType().getUnqualifiedType();
140 }
141
142 PseudoObjectInfo(Sema &S, Expr *E)
143 : RefExpr(E->getObjCProperty()), HasSetter(false), SetterParam(0) {
144
145 assert(E->getValueKind() == VK_LValue &&
146 E->getObjectKind() == OK_ObjCProperty);
147
148 // Try to find a setter.
149
150 // For implicit properties, just trust the lookup we already did.
151 if (RefExpr->isImplicitProperty()) {
152 if (ObjCMethodDecl *setter = RefExpr->getImplicitPropertySetter()) {
153 setSetter(setter);
154 SetterSelector = setter->getSelector();
155 } else {
156 IdentifierInfo *getterName =
157 RefExpr->getImplicitPropertyGetter()->getSelector()
158 .getIdentifierInfoForSlot(0);
159 SetterSelector =
160 SelectorTable::constructSetterName(S.PP.getIdentifierTable(),
161 S.PP.getSelectorTable(),
162 getterName);
163 }
164 return;
165 }
166
167 // For explicit properties, this is more involved.
168 ObjCPropertyDecl *prop = RefExpr->getExplicitProperty();
169 SetterSelector = prop->getSetterName();
170
171 // Do a normal method lookup first.
172 if (ObjCMethodDecl *setter =
173 LookupMethodInReceiverType(S, SetterSelector, RefExpr)) {
174 setSetter(setter);
175 return;
176 }
177
178 // If that failed, trust the type on the @property declaration.
179 if (!prop->isReadOnly()) {
180 HasSetter = true;
181 SetterParamType = prop->getType().getUnqualifiedType();
182 }
183 }
184 };
185}
186
187/// Check an increment or decrement of a pseudo-object expression.
188ExprResult Sema::checkPseudoObjectIncDec(Scope *S, SourceLocation opcLoc,
189 UnaryOperatorKind opcode, Expr *op) {
190 assert(UnaryOperator::isIncrementDecrementOp(opcode));
191 PseudoObjectInfo info(*this, op);
192
193 // If there's no setter, we have no choice but to try to assign to
194 // the result of the getter.
195 if (!info.HasSetter) {
196 QualType resultType = info.RefExpr->getGetterResultType();
197 assert(!resultType.isNull() && "property has no setter and no getter!");
198
199 // Only do this if the getter returns an l-value reference type.
200 if (const LValueReferenceType *refType
201 = resultType->getAs<LValueReferenceType>()) {
202 op = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
203 CK_GetObjCProperty, op, 0, VK_LValue);
204 return BuildUnaryOp(S, opcLoc, opcode, op);
205 }
206
207 // Otherwise, it's an error.
208 Diag(opcLoc, diag::err_nosetter_property_incdec)
209 << unsigned(info.RefExpr->isImplicitProperty())
210 << unsigned(UnaryOperator::isDecrementOp(opcode))
211 << info.SetterSelector
212 << op->getSourceRange();
213 return ExprError();
214 }
215
216 // ++/-- behave like compound assignments, i.e. they need a getter.
217 QualType getterResultType = info.RefExpr->getGetterResultType();
218 if (getterResultType.isNull()) {
219 assert(info.RefExpr->isImplicitProperty());
220 Diag(opcLoc, diag::err_nogetter_property_incdec)
221 << unsigned(UnaryOperator::isDecrementOp(opcode))
222 << info.RefExpr->getImplicitPropertyGetter()->getSelector()
223 << op->getSourceRange();
224 return ExprError();
225 }
226
227 // HACK: change the type of the operand to prevent further placeholder
228 // transformation.
229 op->setType(getterResultType.getNonLValueExprType(Context));
230 op->setObjectKind(OK_Ordinary);
231
232 ExprResult result = CreateBuiltinUnaryOp(opcLoc, opcode, op);
233 if (result.isInvalid()) return ExprError();
234
235 // Change the object kind back.
236 op->setObjectKind(OK_ObjCProperty);
237 return result;
238}
239
240ExprResult Sema::checkPseudoObjectAssignment(Scope *S, SourceLocation opcLoc,
241 BinaryOperatorKind opcode,
242 Expr *LHS, Expr *RHS) {
243 assert(BinaryOperator::isAssignmentOp(opcode));
244 PseudoObjectInfo info(*this, LHS);
245
246 // If there's no setter, we have no choice but to try to assign to
247 // the result of the getter.
248 if (!info.HasSetter) {
249 QualType resultType = info.RefExpr->getGetterResultType();
250 assert(!resultType.isNull() && "property has no setter and no getter!");
251
252 // Only do this if the getter returns an l-value reference type.
253 if (const LValueReferenceType *refType
254 = resultType->getAs<LValueReferenceType>()) {
255 LHS = ImplicitCastExpr::Create(Context, refType->getPointeeType(),
256 CK_GetObjCProperty, LHS, 0, VK_LValue);
257 return BuildBinOp(S, opcLoc, opcode, LHS, RHS);
258 }
259
260 // Otherwise, it's an error.
261 Diag(opcLoc, diag::err_nosetter_property_assignment)
262 << unsigned(info.RefExpr->isImplicitProperty())
263 << info.SetterSelector
264 << LHS->getSourceRange() << RHS->getSourceRange();
265 return ExprError();
266 }
267
268 // If there is a setter, we definitely want to use it.
269
270 // If this is a simple assignment, just initialize the parameter
271 // with the RHS.
272 if (opcode == BO_Assign) {
273 LHS->setType(info.SetterParamType.getNonLValueExprType(Context));
274
275 // Under certain circumstances, we need to type-check the RHS as a
276 // straight-up parameter initialization. This gives somewhat
277 // inferior diagnostics, so we try to avoid it.
278
279 if (RHS->isTypeDependent()) {
280 // Just build the expression.
281
282 } else if ((getLangOptions().CPlusPlus && LHS->getType()->isRecordType()) ||
283 (getLangOptions().ObjCAutoRefCount &&
284 info.SetterParam &&
285 info.SetterParam->hasAttr<NSConsumedAttr>())) {
286 InitializedEntity param = (info.SetterParam
287 ? InitializedEntity::InitializeParameter(Context, info.SetterParam)
288 : InitializedEntity::InitializeParameter(Context, info.SetterParamType,
289 /*consumed*/ false));
290 ExprResult arg = PerformCopyInitialization(param, opcLoc, RHS);
291 if (arg.isInvalid()) return ExprError();
292 RHS = arg.take();
293
294 // Warn about assignments of +1 objects to unsafe pointers in ARC.
295 // CheckAssignmentOperands does this on the other path.
296 if (getLangOptions().ObjCAutoRefCount)
297 checkUnsafeExprAssigns(opcLoc, LHS, RHS);
298 } else {
299 ExprResult RHSResult = Owned(RHS);
300
301 LHS->setObjectKind(OK_Ordinary);
302 QualType resultType = CheckAssignmentOperands(LHS, RHSResult, opcLoc,
303 /*compound*/ QualType());
304 LHS->setObjectKind(OK_ObjCProperty);
305
306 if (!RHSResult.isInvalid()) RHS = RHSResult.take();
307 if (resultType.isNull()) return ExprError();
308 }
309
310 // Warn about property sets in ARC that might cause retain cycles.
311 if (getLangOptions().ObjCAutoRefCount && !info.RefExpr->isSuperReceiver())
312 checkRetainCycles(const_cast<Expr*>(info.RefExpr->getBase()), RHS);
313
314 return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(),
315 RHS->getValueKind(),
316 RHS->getObjectKind(),
317 opcLoc);
318 }
319
320 // If this is a compound assignment, we need to use the getter, too.
321 QualType getterResultType = info.RefExpr->getGetterResultType();
322 if (getterResultType.isNull()) {
323 Diag(opcLoc, diag::err_nogetter_property_compound_assignment)
324 << LHS->getSourceRange() << RHS->getSourceRange();
325 return ExprError();
326 }
327
328 // HACK: change the type of the LHS to prevent further placeholder
329 // transformation.
330 LHS->setType(getterResultType.getNonLValueExprType(Context));
331 LHS->setObjectKind(OK_Ordinary);
332
333 ExprResult result = CreateBuiltinBinOp(opcLoc, opcode, LHS, RHS);
334 if (result.isInvalid()) return ExprError();
335
336 // Change the object kind back.
337 LHS->setObjectKind(OK_ObjCProperty);
338 return result;
339}