aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/ASTContext.h2
-rw-r--r--include/clang/AST/BuiltinTypes.def18
-rw-r--r--include/clang/AST/Expr.h18
-rw-r--r--include/clang/AST/ExprObjC.h18
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td14
-rw-r--r--include/clang/Sema/Sema.h13
-rw-r--r--include/clang/Serialization/ASTBitCodes.h4
-rw-r--r--lib/AST/ASTContext.cpp3
-rw-r--r--lib/AST/Type.cpp1
-rw-r--r--lib/AST/TypeLoc.cpp1
-rw-r--r--lib/CodeGen/CGExprAgg.cpp3
-rw-r--r--lib/CodeGen/CGExprComplex.cpp2
-rw-r--r--lib/CodeGen/CGExprScalar.cpp4
-rw-r--r--lib/Rewrite/RewriteObjC.cpp4
-rw-r--r--lib/Sema/SemaChecking.cpp3
-rw-r--r--lib/Sema/SemaExpr.cpp567
-rw-r--r--lib/Sema/SemaExprCXX.cpp28
-rw-r--r--lib/Sema/SemaExprMember.cpp48
-rw-r--r--lib/Sema/SemaExprObjC.cpp96
-rw-r--r--lib/Sema/SemaInit.cpp18
-rw-r--r--lib/Sema/SemaObjCProperty.cpp8
-rw-r--r--lib/Sema/SemaOverload.cpp56
-rw-r--r--lib/Sema/SemaStmt.cpp4
-rw-r--r--lib/Sema/TreeTransform.h6
-rw-r--r--lib/Serialization/ASTCommon.cpp1
-rw-r--r--lib/Serialization/ASTReader.cpp1
-rw-r--r--test/SemaObjC/property-category-1.m2
-rw-r--r--test/SemaObjC/property-error-readonly-assign.m6
-rw-r--r--test/SemaObjC/property-in-class-extension.m2
-rw-r--r--test/SemaObjC/property-user-setter.m4
-rw-r--r--test/SemaObjCXX/propert-dot-error.mm2
31 files changed, 593 insertions, 364 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 1a8dbe551b..a8590b2983 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -496,7 +496,7 @@ public:
CanQualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy;
CanQualType VoidPtrTy, NullPtrTy;
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy;
- CanQualType ARCUnbridgedCastTy;
+ CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;
// Types for deductions in C++0x [stmt.ranged]'s desugaring. Built on demand.
diff --git a/include/clang/AST/BuiltinTypes.def b/include/clang/AST/BuiltinTypes.def
index 067862c1c1..bfcd55bedb 100644
--- a/include/clang/AST/BuiltinTypes.def
+++ b/include/clang/AST/BuiltinTypes.def
@@ -179,6 +179,24 @@ PLACEHOLDER_TYPE(Overload, OverloadTy)
// x->foo # if only contains non-static members
PLACEHOLDER_TYPE(BoundMember, BoundMemberTy)
+// The type of an expression which refers to a pseudo-object,
+// such as those introduced by Objective C's @property or
+// VS.NET's __property declarations. A placeholder type. The
+// pseudo-object is actually accessed by emitting a call to
+// some sort of function or method; typically there is a pair
+// of a setter and a getter, with the setter used if the
+// pseudo-object reference is used syntactically as the
+// left-hand-side of an assignment operator.
+//
+// A pseudo-object reference naming an Objective-C @property is
+// always a dot access with a base of object-pointer type,
+// e.g. 'x.foo'.
+//
+// In VS.NET, a __property declaration creates an implicit
+// member with an associated name, which can then be named
+// in any of the normal ways an ordinary member could be.
+PLACEHOLDER_TYPE(PseudoObject, PseudoObjectTy)
+
// __builtin_any_type. A placeholder type. Useful for clients
// like debuggers that don't know what type to give something.
// Only a small number of operations are valid on expressions of
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index e7b1a0593f..e0f5e60157 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -1456,12 +1456,26 @@ public:
bool isPrefix() const { return isPrefix(getOpcode()); }
bool isPostfix() const { return isPostfix(getOpcode()); }
+
+ static bool isIncrementOp(Opcode Op) {
+ return Op == UO_PreInc || Op == UO_PostInc;
+ }
bool isIncrementOp() const {
- return Opc == UO_PreInc || Opc == UO_PostInc;
+ return isIncrementOp(getOpcode());
+ }
+
+ static bool isDecrementOp(Opcode Op) {
+ return Op == UO_PreDec || Op == UO_PostDec;
}
+ bool isDecrementOp() const {
+ return isDecrementOp(getOpcode());
+ }
+
+ static bool isIncrementDecrementOp(Opcode Op) { return Op <= UO_PreDec; }
bool isIncrementDecrementOp() const {
- return Opc <= UO_PreDec;
+ return isIncrementDecrementOp(getOpcode());
}
+
static bool isArithmeticOp(Opcode Op) {
return Op >= UO_Plus && Op <= UO_LNot;
}
diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h
index 55726eb4ae..5e4b5b2310 100644
--- a/include/clang/AST/ExprObjC.h
+++ b/include/clang/AST/ExprObjC.h
@@ -227,7 +227,6 @@ public:
/// ObjCPropertyRefExpr - A dot-syntax expression to access an ObjC
/// property.
-///
class ObjCPropertyRefExpr : public Expr {
private:
/// If the bool is true, this is an implicit property reference; the
@@ -237,6 +236,11 @@ private:
llvm::PointerIntPair<NamedDecl*, 1, bool> PropertyOrGetter;
ObjCMethodDecl *Setter;
+ // FIXME: Maybe we should store the property identifier here,
+ // because it's not rederivable from the other data when there's an
+ // implicit property with no getter (because the 'foo' -> 'setFoo:'
+ // transformation is lossy on the first character).
+
SourceLocation IdLoc;
/// \brief When the receiver in property access is 'super', this is
@@ -255,6 +259,7 @@ public:
base->containsUnexpandedParameterPack()),
PropertyOrGetter(PD, false), Setter(0),
IdLoc(l), ReceiverLoc(), Receiver(base) {
+ assert(t->isSpecificPlaceholderType(BuiltinType::PseudoObject));
}
ObjCPropertyRefExpr(ObjCPropertyDecl *PD, QualType t,
@@ -265,6 +270,7 @@ public:
st->containsUnexpandedParameterPack()),
PropertyOrGetter(PD, false), Setter(0),
IdLoc(l), ReceiverLoc(sl), Receiver(st.getTypePtr()) {
+ assert(t->isSpecificPlaceholderType(BuiltinType::PseudoObject));
}
ObjCPropertyRefExpr(ObjCMethodDecl *Getter, ObjCMethodDecl *Setter,
@@ -275,6 +281,7 @@ public:
Base->containsUnexpandedParameterPack()),
PropertyOrGetter(Getter, true), Setter(Setter),
IdLoc(IdLoc), ReceiverLoc(), Receiver(Base) {
+ assert(T->isSpecificPlaceholderType(BuiltinType::PseudoObject));
}
ObjCPropertyRefExpr(ObjCMethodDecl *Getter, ObjCMethodDecl *Setter,
@@ -284,6 +291,7 @@ public:
: Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false, false),
PropertyOrGetter(Getter, true), Setter(Setter),
IdLoc(IdLoc), ReceiverLoc(SuperLoc), Receiver(SuperTy.getTypePtr()) {
+ assert(T->isSpecificPlaceholderType(BuiltinType::PseudoObject));
}
ObjCPropertyRefExpr(ObjCMethodDecl *Getter, ObjCMethodDecl *Setter,
@@ -293,6 +301,7 @@ public:
: Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false, false),
PropertyOrGetter(Getter, true), Setter(Setter),
IdLoc(IdLoc), ReceiverLoc(ReceiverLoc), Receiver(Receiver) {
+ assert(T->isSpecificPlaceholderType(BuiltinType::PseudoObject));
}
explicit ObjCPropertyRefExpr(EmptyShell Empty)
@@ -348,14 +357,15 @@ public:
if (const ObjCMethodDecl *Getter = PDecl->getGetterMethodDecl())
ResultType = Getter->getResultType();
else
- ResultType = getType();
+ ResultType = PDecl->getType();
} else {
const ObjCMethodDecl *Getter = getImplicitPropertyGetter();
- ResultType = Getter->getResultType(); // with reference!
+ if (Getter)
+ ResultType = Getter->getResultType(); // with reference!
}
return ResultType;
}
-
+
QualType getSetterArgType() const {
QualType ArgType;
if (isImplicitProperty()) {
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 61a182e81b..74272ba523 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3612,16 +3612,22 @@ def ext_gnu_ptr_func_arith : Extension<
"arithmetic on%select{ a|}0 pointer%select{|s}0 to%select{ the|}2 function "
"type%select{|s}2 %1%select{| and %3}2 is a GNU extension">,
InGroup<PointerArith>;
-def error_readonly_property_assignment : Error<
- "assigning to property with 'readonly' attribute not allowed">;
def error_readonly_message_assignment : Error<
"assigning to 'readonly' return result of an objective-c message not allowed">;
def ext_integer_increment_complex : Extension<
"ISO C does not support '++'/'--' on complex integer type %0">;
def ext_integer_complement_complex : Extension<
"ISO C does not support '~' for complex conjugation of %0">;
-def error_nosetter_property_assignment : Error<
- "setter method is needed to assign to object using property" " assignment syntax">;
+def err_nosetter_property_assignment : Error<
+ "%select{assignment to readonly property|"
+ "no setter method %1 for assignment to property}0">;
+def err_nosetter_property_incdec : Error<
+ "%select{%select{increment|decrement}1 of readonly property|"
+ "no setter method %2 for %select{increment|decrement}1 of property}0">;
+def err_nogetter_property_compound_assignment : Error<
+ "a getter method is needed to perform a compound assignment on a property">;
+def err_nogetter_property_incdec : Error<
+ "no getter method %1 for %select{increment|decrement} of property">;
def error_no_subobject_property_setting : Error<
"expression is not assignable">;
def err_qualified_objc_access : Error<
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index b55fdea6b9..2b33392e6b 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -5319,6 +5319,8 @@ public:
ObjCMethodDecl *LookupMethodInQualifiedType(Selector Sel,
const ObjCObjectPointerType *OPT,
bool IsInstance);
+ ObjCMethodDecl *LookupMethodInObjectType(Selector Sel, QualType Ty,
+ bool IsInstance);
bool inferObjCARCLifetime(ValueDecl *decl);
@@ -5777,10 +5779,13 @@ public:
// For compound assignment, pass both expressions and the converted type.
QualType CheckAssignmentOperands( // C99 6.5.16.[1,2]
Expr *LHSExpr, ExprResult &RHS, SourceLocation Loc, QualType CompoundType);
-
- void ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS,
- QualType& LHSTy);
- ExprResult ConvertPropertyForRValue(Expr *E);
+
+ ExprResult checkPseudoObjectIncDec(Scope *S, SourceLocation OpLoc,
+ UnaryOperatorKind Opcode, Expr *Op);
+ ExprResult checkPseudoObjectAssignment(Scope *S, SourceLocation OpLoc,
+ BinaryOperatorKind Opcode,
+ Expr *LHS, Expr *RHS);
+ ExprResult checkPseudoObjectRValue(Expr *E);
QualType CheckConditionalOperands( // C99 6.5.15
ExprResult &Cond, ExprResult &LHS, ExprResult &RHS,
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index d237f1b634..485b8fe3bb 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -556,7 +556,9 @@ namespace clang {
/// \brief The OpenCL 'half' / ARM NEON __fp16 type.
PREDEF_TYPE_HALF_ID = 33,
/// \brief ARC's unbridged-cast placeholder type.
- PREDEF_TYPE_ARC_UNBRIDGED_CAST = 34
+ PREDEF_TYPE_ARC_UNBRIDGED_CAST = 34,
+ /// \brief The pseudo-object placeholder type.
+ PREDEF_TYPE_PSEUDO_OBJECT = 35
};
/// \brief The number of predefined type IDs that are reserved for
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 4fbb5408dc..ae28149407 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -461,6 +461,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) {
// Placeholder type for bound members.
InitBuiltinType(BoundMemberTy, BuiltinType::BoundMember);
+ // Placeholder type for pseudo-objects.
+ InitBuiltinType(PseudoObjectTy, BuiltinType::PseudoObject);
+
// "any" type; useful for debugger-like clients.
InitBuiltinType(UnknownAnyTy, BuiltinType::UnknownAny);
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index 5b7daadd98..0bac5f4f61 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1493,6 +1493,7 @@ const char *BuiltinType::getName(const PrintingPolicy &Policy) const {
case NullPtr: return "nullptr_t";
case Overload: return "<overloaded function type>";
case BoundMember: return "<bound member function type>";
+ case PseudoObject: return "<pseudo-object type>";
case Dependent: return "<dependent type>";
case UnknownAny: return "<unknown type>";
case ARCUnbridgedCast: return "<ARC unbridged cast type>";
diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp
index 2724e8c46a..9bdea433a9 100644
--- a/lib/AST/TypeLoc.cpp
+++ b/lib/AST/TypeLoc.cpp
@@ -238,6 +238,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
case BuiltinType::BoundMember:
case BuiltinType::UnknownAny:
case BuiltinType::ARCUnbridgedCast:
+ case BuiltinType::PseudoObject:
case BuiltinType::ObjCId:
case BuiltinType::ObjCClass:
case BuiltinType::ObjCSel:
diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp
index 97754d5c0b..0fa143391d 100644
--- a/lib/CodeGen/CGExprAgg.cpp
+++ b/lib/CodeGen/CGExprAgg.cpp
@@ -326,7 +326,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
}
case CK_GetObjCProperty: {
- LValue LV = CGF.EmitLValue(E->getSubExpr());
+ LValue LV =
+ CGF.EmitObjCPropertyRefLValue(E->getSubExpr()->getObjCProperty());
assert(LV.isPropertyRef());
RValue RV = CGF.EmitLoadOfPropertyRefLValue(LV, getReturnValueSlot());
EmitMoveFromReturnSlot(E, RV);
diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp
index 4a31bcfbe9..b6c416bc35 100644
--- a/lib/CodeGen/CGExprComplex.cpp
+++ b/lib/CodeGen/CGExprComplex.cpp
@@ -363,7 +363,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op,
case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!");
case CK_GetObjCProperty: {
- LValue LV = CGF.EmitLValue(Op);
+ LValue LV = CGF.EmitObjCPropertyRefLValue(Op->getObjCProperty());
assert(LV.isPropertyRef() && "Unknown LValue type!");
return CGF.EmitLoadOfPropertyRefLValue(LV).getComplexVal();
}
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index b088103aa3..582d1c4572 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -1167,10 +1167,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
break;
case CK_GetObjCProperty: {
- assert(CGF.getContext().hasSameUnqualifiedType(E->getType(), DestTy));
assert(E->isGLValue() && E->getObjectKind() == OK_ObjCProperty &&
"CK_GetObjCProperty for non-lvalue or non-ObjCProperty");
- RValue RV = CGF.EmitLoadOfLValue(CGF.EmitLValue(E));
+ LValue LV = CGF.EmitObjCPropertyRefLValue(E->getObjCProperty());
+ RValue RV = CGF.EmitLoadOfPropertyRefLValue(LV);
return RV.getScalarVal();
}
diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp
index 6a392ea357..1af9a6ce04 100644
--- a/lib/Rewrite/RewriteObjC.cpp
+++ b/lib/Rewrite/RewriteObjC.cpp
@@ -1304,7 +1304,7 @@ Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(BinaryOperator *BinOp, Expr *
} else {
OMD = PropRefExpr->getImplicitPropertySetter();
Sel = OMD->getSelector();
- Ty = PropRefExpr->getType();
+ Ty = (*OMD->param_begin())->getType();
}
Super = PropRefExpr->isSuperReceiver();
if (!Super) {
@@ -1380,7 +1380,7 @@ Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(Expr *PropOrGetterRefExpr) {
} else {
OMD = PropRefExpr->getImplicitPropertyGetter();
Sel = OMD->getSelector();
- Ty = PropRefExpr->getType();
+ Ty = OMD->getResultType();
}
Super = PropRefExpr->isSuperReceiver();
if (!Super)
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index daab1cd487..6e81c3c966 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -4256,7 +4256,8 @@ void Sema::checkUnsafeExprAssigns(SourceLocation Loc,
if (LT != Qualifiers::OCL_None)
return;
- if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(LHS)) {
+ if (ObjCPropertyRefExpr *PRE
+ = dyn_cast<ObjCPropertyRefExpr>(LHS->IgnoreParens())) {
if (PRE->isImplicitProperty())
return;
const ObjCPropertyDecl *PD = PRE->getExplicitProperty();
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 3b4a40b51a..17df1be2fe 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -363,19 +363,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
assert(!T.isNull() && "r-value conversion on typeless expression?");
// We can't do lvalue-to-rvalue on atomics yet.
- if (T->getAs<AtomicType>())
+ if (T->isAtomicType())
return Owned(E);
- // Create a load out of an ObjCProperty l-value, if necessary.
- if (E->getObjectKind() == OK_ObjCProperty) {
- ExprResult Res = ConvertPropertyForRValue(E);
- if (Res.isInvalid())
- return Owned(E);
- E = Res.take();
- if (!E->isGLValue())
- return Owned(E);
- }
-
// We don't want to throw lvalue-to-rvalue casts on top of
// expressions of certain types in C++.
if (getLangOptions().CPlusPlus &&
@@ -3969,6 +3959,23 @@ Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList,
unsigned NumInit = InitArgList.size();
Expr **InitList = InitArgList.release();
+ // Immediately handle non-overload placeholders. Overloads can be
+ // resolved contextually, but everything else here can't.
+ for (unsigned I = 0; I != NumInit; ++I) {
+ if (const BuiltinType *pty
+ = InitList[I]->getType()->getAsPlaceholderType()) {
+ if (pty->getKind() == BuiltinType::Overload) continue;
+
+ ExprResult result = CheckPlaceholderExpr(InitList[I]);
+
+ // Ignore failures; dropping the entire initializer list because
+ // of one failure would be terrible for indexing/etc.
+ if (result.isInvalid()) continue;
+
+ InitList[I] = result.take();
+ }
+ }
+
// Semantic analysis for initializers is done by ActOnDeclarator() and
// CheckInitializer() - it requires knowledge of the object being intialized.
@@ -7085,10 +7092,8 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
Diag = diag::err_block_decl_ref_not_modifiable_lvalue;
break;
case Expr::MLV_ReadonlyProperty:
- Diag = diag::error_readonly_property_assignment;
- break;
case Expr::MLV_NoSetterProperty:
- Diag = diag::error_nosetter_property_assignment;
+ llvm_unreachable("readonly properties should be processed differently");
break;
case Expr::MLV_InvalidMessageExpression:
Diag = diag::error_readonly_message_assignment;
@@ -7114,6 +7119,8 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
SourceLocation Loc,
QualType CompoundType) {
+ assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject));
+
// Verify that LHS is a modifiable lvalue, and emit error if not.
if (CheckForModifiableLvalue(LHSExpr, Loc, *this))
return QualType();
@@ -7124,14 +7131,6 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
AssignConvertType ConvTy;
if (CompoundType.isNull()) {
QualType LHSTy(LHSType);
- // Simple assignment "x = y".
- if (LHSExpr->getObjectKind() == OK_ObjCProperty) {
- ExprResult LHSResult = Owned(LHSExpr);
- ConvertPropertyForLValue(LHSResult, RHS, LHSTy);
- if (LHSResult.isInvalid())
- return QualType();
- LHSExpr = LHSResult.take();
- }
ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS);
if (RHS.isInvalid())
return QualType();
@@ -7293,12 +7292,35 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
}
}
-ExprResult Sema::ConvertPropertyForRValue(Expr *E) {
+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 T = E->getType();
QualType ReceiverType;
if (PRE->isObjectReceiver())
ReceiverType = PRE->getBase()->getType();
@@ -7308,28 +7330,50 @@ ExprResult Sema::ConvertPropertyForRValue(Expr *E) {
ReceiverType = Context.getObjCInterfaceType(PRE->getClassReceiver());
ExprValueKind VK = VK_RValue;
+ QualType T;
if (PRE->isImplicitProperty()) {
if (ObjCMethodDecl *GetterMethod =
PRE->getImplicitPropertyGetter()) {
- T = getMessageSendResultType(ReceiverType, GetterMethod,
+ T = getMessageSendResultType(ReceiverType, GetterMethod,
PRE->isClassReceiver(),
PRE->isSuperReceiver());
VK = Expr::getValueKindForType(GetterMethod->getResultType());
- }
- else {
+ } 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);
}
}
- else {
- // lvalue-ness of an explicit property is determined by
- // getter type.
- QualType ResT = PRE->getGetterResultType();
- VK = Expr::getValueKindForType(ResT);
- }
-
- E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty,
- E, 0, VK);
+
+ E->setType(T);
+ E = ImplicitCastExpr::Create(Context, T, CK_GetObjCProperty, E, 0, VK);
ExprResult Result = MaybeBindToTemporary(E);
if (!Result.isInvalid())
@@ -7338,57 +7382,215 @@ ExprResult Sema::ConvertPropertyForRValue(Expr *E) {
return Owned(E);
}
-void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS,
- QualType &LHSTy) {
- assert(LHS.get()->getValueKind() == VK_LValue &&
- LHS.get()->getObjectKind() == OK_ObjCProperty);
- const ObjCPropertyRefExpr *PropRef = LHS.get()->getObjCProperty();
+namespace {
+ struct PseudoObjectInfo {
+ const ObjCPropertyRefExpr *RefExpr;
+ bool HasSetter;
+ Selector SetterSelector;
+ ParmVarDecl *SetterParam;
+ QualType SetterParamType;
- bool Consumed = false;
+ void setSetter(ObjCMethodDecl *setter) {
+ HasSetter = true;
+ SetterParam = *setter->param_begin();
+ SetterParamType = SetterParam->getType().getUnqualifiedType();
+ }
- if (PropRef->isImplicitProperty()) {
- // If using property-dot syntax notation for assignment, and there is a
- // setter, RHS expression is being passed to the setter argument. So,
- // type conversion (and comparison) is RHS to setter's argument type.
- if (const ObjCMethodDecl *SetterMD = PropRef->getImplicitPropertySetter()) {
- ObjCMethodDecl::param_const_iterator P = SetterMD->param_begin();
- LHSTy = (*P)->getType();
- Consumed = (getLangOptions().ObjCAutoRefCount &&
- (*P)->hasAttr<NSConsumedAttr>());
+ PseudoObjectInfo(Sema &S, Expr *E)
+ : RefExpr(E->getObjCProperty()), HasSetter(false), SetterParam(0) {
- // Otherwise, if the getter returns an l-value, just call that.
- } else {
- QualType Result = PropRef->getImplicitPropertyGetter()->getResultType();
- ExprValueKind VK = Expr::getValueKindForType(Result);
- if (VK == VK_LValue) {
- LHS = ImplicitCastExpr::Create(Context, LHS.get()->getType(),
- CK_GetObjCProperty, LHS.take(), 0, VK);
+ 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))
+ return setSetter(setter);
+
+ // If that failed, trust the type on the @property declaration.
+ if (!prop->isReadOnly()) {
+ HasSetter = true;
+ SetterParamType = prop->getType().getUnqualifiedType();
+ }
}
- } else {
- const ObjCMethodDecl *setter
- = PropRef->getExplicitProperty()->getSetterMethodDecl();
- if (setter) {
- ObjCMethodDecl::param_const_iterator P = setter->param_begin();
- LHSTy = (*P)->getType();
- if (getLangOptions().ObjCAutoRefCount)
- Consumed = (*P)->hasAttr<NSConsumedAttr>();
+ };
+}
+
+/// 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.
+ i