From: John McCall Date: Tue, 25 Oct 2011 07:27:56 +0000 (+0000) Subject: Introduce a placeholder type for "pseudo object" X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a1b852f8e1bee5ed3604ee483803cef39ce57a20;p=clang Introduce a placeholder type for "pseudo object" expressions: expressions which refer to a logical rather than a physical l-value, where the logical object is actually accessed via custom getter/setter code. A subsequent patch will generalize the AST for these so that arbitrary "implementing" sub-expressions can be provided. Right now the only client is ObjC properties, but this should be generalizable to similar language features, e.g. Managed C++'s __property methods. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@142914 91177308-0d34-0410-b5e6-96231b3b80d8 --- 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 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; -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 ""; case BoundMember: return ""; + case PseudoObject: return ""; case Dependent: return ""; case UnknownAny: return ""; case ARCUnbridgedCast: return ""; 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(LHS)) { + if (ObjCPropertyRefExpr *PRE + = dyn_cast(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()) + 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()->getPointeeType(); + instanceProperty = true; + } else if (PRE->isSuperReceiver()) { + searchType = PRE->getSuperReceiverType(); + instanceProperty = false; + if (const ObjCObjectPointerType *PT + = searchType->getAs()) { + 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()); + 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(); + }; +} + +/// 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()) { + 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()) { + 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 ((getLangOptions().CPlusPlus && LHSTy->isRecordType()) || - getLangOptions().ObjCAutoRefCount) { - InitializedEntity Entity = - InitializedEntity::InitializeParameter(Context, LHSTy, Consumed); - ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), RHS); - if (!ArgE.isInvalid()) { - RHS = ArgE; - if (getLangOptions().ObjCAutoRefCount && !PropRef->isSuperReceiver()) - checkRetainCycles(const_cast(PropRef->getBase()), RHS.get()); + // 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())) { + 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(info.RefExpr->getBase()), RHS); + + return new (Context) BinaryOperator(LHS, RHS, opcode, RHS->getType(), + RHS->getValueKind(), + RHS->getObjectKind(), + opcLoc); } - LHSTy = LHSTy.getNonReferenceType(); + + // 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; } @@ -7473,31 +7675,39 @@ static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, /// operator (C99 6.3.2.1p[2-4]), and its result is never an lvalue. /// In C++, the operand might be an overloaded function name, in which case /// we allow the '&' but retain the overloaded-function type. -static QualType CheckAddressOfOperand(Sema &S, Expr *OrigOp, +static QualType CheckAddressOfOperand(Sema &S, ExprResult &OrigOp, SourceLocation OpLoc) { - if (OrigOp->isTypeDependent()) - return S.Context.DependentTy; - if (OrigOp->getType() == S.Context.OverloadTy) { - if (!isa(OrigOp->IgnoreParens())) { - S.Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) - << OrigOp->getSourceRange(); + if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){ + if (PTy->getKind() == BuiltinType::Overload) { + if (!isa(OrigOp.get()->IgnoreParens())) { + S.Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + return S.Context.OverloadTy; + } + + if (PTy->getKind() == BuiltinType::UnknownAny) + return S.Context.UnknownAnyTy; + + if (PTy->getKind() == BuiltinType::BoundMember) { + S.Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); return QualType(); } - - return S.Context.OverloadTy; - } - if (OrigOp->getType() == S.Context.UnknownAnyTy) - return S.Context.UnknownAnyTy; - if (OrigOp->getType() == S.Context.BoundMemberTy) { - S.Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp->getSourceRange(); - return QualType(); + + OrigOp = S.CheckPlaceholderExpr(OrigOp.take()); + if (OrigOp.isInvalid()) return QualType(); } - assert(!OrigOp->getType()->isPlaceholderType()); + if (OrigOp.get()->isTypeDependent()) + return S.Context.DependentTy; + + assert(!OrigOp.get()->getType()->isPlaceholderType()); // Make sure to ignore parentheses in subsequent checks - Expr *op = OrigOp->IgnoreParens(); + Expr *op = OrigOp.get()->IgnoreParens(); if (S.getLangOptions().C99) { // Implement C99-only parts of addressof rules. @@ -7530,16 +7740,16 @@ static QualType CheckAddressOfOperand(Sema &S, Expr *OrigOp, // If the underlying expression isn't a decl ref, give up. if (!isa(op)) { S.Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp->getSourceRange(); + << OrigOp.get()->getSourceRange(); return QualType(); } DeclRefExpr *DRE = cast(op); CXXMethodDecl *MD = cast(DRE->getDecl()); // The id-expression was parenthesized. - if (OrigOp != DRE) { + if (OrigOp.get() != DRE) { S.Diag(OpLoc, diag::err_parens_pointer_member_function) - << OrigOp->getSourceRange(); + << OrigOp.get()->getSourceRange(); // The method was named without a qualifier. } else if (!DRE->getQualifier()) { @@ -7553,10 +7763,15 @@ static QualType CheckAddressOfOperand(Sema &S, Expr *OrigOp, // C99 6.5.3.2p1 // The operand must be either an l-value or a function designator if (!op->getType()->isFunctionType()) { - // FIXME: emit more specific diag... - S.Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) - << op->getSourceRange(); - return QualType(); + // Use a special diagnostic for loads from property references. + if (isa(op->IgnoreImplicit()->IgnoreParens())) { + AddressOfError = AO_Property_Expansion; + } else { + // FIXME: emit more specific diag... + S.Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) + << op->getSourceRange(); + return QualType(); + } } } else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1 // The operand cannot be a bit-field @@ -7781,23 +7996,6 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, ExprValueKind VK = VK_RValue; ExprObjectKind OK = OK_Ordinary; - // Check if a 'foo' involved in a binary op, identifies a single - // function unambiguously (i.e. an lvalue ala 13.4) - // But since an assignment can trigger target based overload, exclude it in - // our blind search. i.e: - // template void f(); template void f(U); - // f == 0; // resolve f blindly - // void (*p)(int); p = f; // resolve f using target - if (Opc != BO_Assign) { - ExprResult resolvedLHS = CheckPlaceholderExpr(LHS.get()); - if (!resolvedLHS.isUsable()) return ExprError(); - LHS = move(resolvedLHS); - - ExprResult resolvedRHS = CheckPlaceholderExpr(RHS.get()); - if (!resolvedRHS.isUsable()) return ExprError(); - RHS = move(resolvedRHS); - } - switch (Opc) { case BO_Assign: ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType()); @@ -8093,38 +8291,83 @@ ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); } +/// Build an overloaded binary operator expression in the given scope. +static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS) { + // Find all of the overloaded operators visible from this + // point. We perform both an operator-name lookup from the local + // scope and an argument-dependent lookup based on the types of + // the arguments. + UnresolvedSet<16> Functions; + OverloadedOperatorKind OverOp + = BinaryOperator::getOverloadedOperator(Opc); + if (Sc && OverOp != OO_None) + S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(), + RHS->getType(), Functions); + + // Build the (potentially-overloaded, potentially-dependent) + // binary operation. + return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); +} + ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr) { + // Handle pseudo-objects in the LHS. + if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { + // Assignments with a pseudo-object l-value need special analysis. + if (pty->getKind() == BuiltinType::PseudoObject && + BinaryOperator::isAssignmentOp(Opc)) + return checkPseudoObjectAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); + + // Don't resolve overloads if the other type is overloadable. + if (pty->getKind() == BuiltinType::Overload) { + // We can't actually test that if we still have a placeholder, + // though. Fortunately, none of the exceptions we see in that + // code below are valid when the LHS is an overload set. + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (resolvedRHS.isInvalid()) return ExprError(); + RHSExpr = resolvedRHS.take(); + + if (RHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + } + + ExprResult LHS = CheckPlaceholderExpr(LHSExpr); + if (LHS.isInvalid()) return ExprError(); + LHSExpr = LHS.take(); + } + + // Handle pseudo-objects in the RHS. + if (const BuiltinType *pty = RHSExpr->getType()->getAsPlaceholderType()) { + // An overload in the RHS can potentially be resolved by the type + // being assigned to. + if (Opc == BO_Assign && pty->getKind() == BuiltinType::Overload) + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + + // Don't resolve overloads if the other type is overloadable. + if (pty->getKind() == BuiltinType::Overload && + LHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (!resolvedRHS.isUsable()) return ExprError(); + RHSExpr = resolvedRHS.take(); + } + if (getLangOptions().CPlusPlus) { bool UseBuiltinOperator; if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent()) { UseBuiltinOperator = false; - } else if (Opc == BO_Assign && - LHSExpr->getObjectKind() == OK_ObjCProperty) { - UseBuiltinOperator = true; } else { UseBuiltinOperator = !LHSExpr->getType()->isOverloadableType() && !RHSExpr->getType()->isOverloadableType(); } - if (!UseBuiltinOperator) { - // Find all of the overloaded operators visible from this - // point. We perform both an operator-name lookup from the local - // scope and an argument-dependent lookup based on the types of - // the arguments. - UnresolvedSet<16> Functions; - OverloadedOperatorKind OverOp - = BinaryOperator::getOverloadedOperator(Opc); - if (S && OverOp != OO_None) - LookupOverloadedOperatorName(OverOp, S, LHSExpr->getType(), - RHSExpr->getType(), Functions); - - // Build the (potentially-overloaded, potentially-dependent) - // binary operation. - return CreateOverloadedBinOp(OpLoc, Opc, Functions, LHSExpr, RHSExpr); - } + if (!UseBuiltinOperator) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } // Build a built-in binary operation. @@ -8150,12 +8393,9 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, Opc == UO_PreDec); break; case UO_AddrOf: - resultType = CheckAddressOfOperand(*this, Input.get(), OpLoc); + resultType = CheckAddressOfOperand(*this, Input, OpLoc); break; case UO_Deref: { - ExprResult resolved = CheckPlaceholderExpr(Input.get()); - if (!resolved.isUsable()) return ExprError(); - Input = move(resolved); Input = DefaultFunctionArrayLvalueConversion(Input.take()); resultType = CheckIndirectionOperand(*this, Input.get(), VK, OpLoc); break; @@ -8177,11 +8417,6 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, Opc == UO_Plus && resultType->isPointerType()) break; - else if (resultType->isPlaceholderType()) { - Input = CheckPlaceholderExpr(Input.take()); - if (Input.isInvalid()) return ExprError(); - return CreateBuiltinUnaryOp(OpLoc, Opc, Input.take()); - } return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); @@ -8199,11 +8434,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, << resultType << Input.get()->getSourceRange(); else if (resultType->hasIntegerRepresentation()) break; - else if (resultType->isPlaceholderType()) { - Input = CheckPlaceholderExpr(Input.take()); - if (Input.isInvalid()) return ExprError(); - return CreateBuiltinUnaryOp(OpLoc, Opc, Input.take()); - } else { + else { return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); } @@ -8231,10 +8462,6 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, Input = ImpCastExprToType(Input.take(), Context.BoolTy, ScalarTypeToBooleanCastKind(resultType)); } - } else if (resultType->isPlaceholderType()) { - Input = CheckPlaceholderExpr(Input.take()); - if (Input.isInvalid()) return ExprError(); - return CreateBuiltinUnaryOp(OpLoc, Opc, Input.take()); } else { return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); @@ -8275,6 +8502,32 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc, UnaryOperatorKind Opc, Expr *Input) { + // First things first: handle placeholders so that the + // overloaded-operator check considers the right type. + if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { + // Increment and decrement of pseudo-object references. + if (pty->getKind() == BuiltinType::PseudoObject && + UnaryOperator::isIncrementDecrementOp(Opc)) + return checkPseudoObjectIncDec(S, OpLoc, Opc, Input); + + // extension is always a builtin operator. + if (Opc == UO_Extension) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + + // & gets special logic for several kinds of placeholder. + // The builtin code knows what to do. + if (Opc == UO_AddrOf && + (pty->getKind() == BuiltinType::Overload || + pty->getKind() == BuiltinType::UnknownAny || + pty->getKind() == BuiltinType::BoundMember)) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + + // Anything else needs to be handled now. + ExprResult Result = CheckPlaceholderExpr(Input); + if (Result.isInvalid()) return ExprError(); + Input = Result.take(); + } + if (getLangOptions().CPlusPlus && Input->getType()->isOverloadableType() && UnaryOperator::getOverloadedOperator(Opc) != OO_None) { // Find all of the overloaded operators visible from this @@ -10151,6 +10404,10 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) { case BuiltinType::UnknownAny: return diagnoseUnknownAnyExpr(*this, E); + // Pseudo-objects. + case BuiltinType::PseudoObject: + return checkPseudoObjectRValue(E); + // Everything else should be impossible. #define BUILTIN_TYPE(Id, SingletonId) \ case BuiltinType::Id: diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 9267860d15..c96ab42b88 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2260,15 +2260,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, break; case ICK_Lvalue_To_Rvalue: - // Should this get its own ICK? - if (From->getObjectKind() == OK_ObjCProperty) { - ExprResult FromRes = ConvertPropertyForRValue(From); - if (FromRes.isInvalid()) - return ExprError(); - From = FromRes.take(); - if (!From->isGLValue()) break; - } - + assert(From->getObjectKind() != OK_ObjCProperty); FromType = FromType.getUnqualifiedType(); From = ImplicitCastExpr::Create(Context, FromType, CK_LValueToRValue, From, 0, VK_RValue); @@ -4194,6 +4186,10 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, if (Result.isInvalid()) return ExprError(); Base = Result.get(); + Result = CheckPlaceholderExpr(Base); + if (Result.isInvalid()) return ExprError(); + Base = Result.take(); + QualType BaseType = Base->getType(); MayBePseudoDestructor = false; if (BaseType->isDependentType()) { @@ -4592,6 +4588,12 @@ ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation, /// Perform the conversions required for an expression used in a /// context that ignores the result. ExprResult Sema::IgnoredValueConversions(Expr *E) { + if (E->hasPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(E); + if (result.isInvalid()) return Owned(E); + E = result.take(); + } + // C99 6.3.2.1: // [Except in specific positions,] an lvalue that does not have // array type is converted to the value stored in the @@ -4607,14 +4609,6 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { return Owned(E); } - // We always want to do this on ObjC property references. - if (E->getObjectKind() == OK_ObjCProperty) { - ExprResult Res = ConvertPropertyForRValue(E); - if (Res.isInvalid()) return Owned(E); - E = Res.take(); - if (E->isRValue()) return Owned(E); - } - // Otherwise, this rule does not apply in C++, at least not for the moment. if (getLangOptions().CPlusPlus) return Owned(E); diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 16c17fb890..25d3c2a425 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -777,7 +777,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); - BaseType = BaseType->getAs()->getPointeeType(); + BaseType = BaseType->castAs()->getPointeeType(); } R.setBaseObjectType(BaseType); @@ -814,15 +814,6 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R)) return ExprError(); - // Perform a property load on the base regardless of whether we - // actually need it for the declaration. - if (BaseExpr && BaseExpr->getObjectKind() == OK_ObjCProperty) { - ExprResult Result = ConvertPropertyForRValue(BaseExpr); - if (Result.isInvalid()) - return ExprError(); - BaseExpr = Result.take(); - } - // Construct an unresolved result if we in fact got an unresolved // result. if (R.isOverloadedResult() || R.isUnresolvableResult()) { @@ -1209,11 +1200,8 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - QualType T = PD->getType(); - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - T = getMessageSendResultType(BaseType, Getter, false, false); - - return Owned(new (Context) ObjCPropertyRefExpr(PD, T, + return Owned(new (Context) ObjCPropertyRefExpr(PD, + Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, @@ -1231,16 +1219,10 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, if (Decl *SDecl = FindGetterSetterNameDecl(OPT, /*Property id*/0, SetterSel, Context)) SMD = dyn_cast(SDecl); - QualType PType = getMessageSendResultType(BaseType, OMD, false, - false); - ExprValueKind VK = VK_LValue; - if (!getLangOptions().CPlusPlus && PType.isCForbiddenLValueType()) - VK = VK_RValue; - ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); - - return Owned(new (Context) ObjCPropertyRefExpr(OMD, SMD, PType, - VK, OK, + return Owned(new (Context) ObjCPropertyRefExpr(OMD, SMD, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr.take())); } } @@ -1295,23 +1277,9 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, return ExprError(); if (Getter || Setter) { - QualType PType; - - ExprValueKind VK = VK_LValue; - if (Getter) { - PType = getMessageSendResultType(QualType(OT, 0), Getter, true, - false); - if (!getLangOptions().CPlusPlus && PType.isCForbiddenLValueType()) - VK = VK_RValue; - } else { - // Get the expression type from Setter's incoming parameter. - PType = (*(Setter->param_end() -1))->getType(); - } - ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); - - // FIXME: we must check that the setter has property type. return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr.take())); } diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 527983805c..ceff1de3d4 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -527,6 +527,35 @@ ObjCMethodDecl *Sema::LookupPrivateInstanceMethod(Selector Sel, return Method; } +/// LookupMethodInType - Look up a method in an ObjCObjectType. +ObjCMethodDecl *Sema::LookupMethodInObjectType(Selector sel, QualType type, + bool isInstance) { + const ObjCObjectType *objType = type->castAs(); + if (ObjCInterfaceDecl *iface = objType->getInterface()) { + // Look it up in the main interface (and categories, etc.) + if (ObjCMethodDecl *method = iface->lookupMethod(sel, isInstance)) + return method; + + // Okay, look for "private" methods declared in any + // @implementations we've seen. + if (isInstance) { + if (ObjCMethodDecl *method = LookupPrivateInstanceMethod(sel, iface)) + return method; + } else { + if (ObjCMethodDecl *method = LookupPrivateClassMethod(sel, iface)) + return method; + } + } + + // Check qualifiers. + for (ObjCObjectType::qual_iterator + i = objType->qual_begin(), e = objType->qual_end(); i != e; ++i) + if (ObjCMethodDecl *method = (*i)->lookupMethod(sel, isInstance)) + return method; + + return 0; +} + /// LookupMethodInQualifiedType - Lookups up a method in protocol qualifier /// list of a qualified objective pointer type. ObjCMethodDecl *Sema::LookupMethodInQualifiedType(Selector Sel, @@ -575,23 +604,14 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - QualType ResTy = PD->getType(); - ResTy = ResTy.getNonLValueExprType(Context); - Selector Sel = PP.getSelectorTable().getNullarySelector(Member); - ObjCMethodDecl *Getter = IFace->lookupInstanceMethod(Sel); - if (Getter && - (Getter->hasRelatedResultType() - || DiagnosePropertyAccessorMismatch(PD, Getter, MemberLoc))) - ResTy = getMessageSendResultType(QualType(OPT, 0), Getter, false, - Super); if (Super) - return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, + return Owned(new (Context) ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else - return Owned(new (Context) ObjCPropertyRefExpr(PD, ResTy, + return Owned(new (Context) ObjCPropertyRefExpr(PD, Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr)); } @@ -603,17 +623,16 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); - QualType T = PD->getType(); - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - T = getMessageSendResultType(QualType(OPT, 0), Getter, false, Super); if (Super) - return Owned(new (Context) ObjCPropertyRefExpr(PD, T, + return Owned(new (Context) ObjCPropertyRefExpr(PD, + Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else - return Owned(new (Context) ObjCPropertyRefExpr(PD, T, + return Owned(new (Context) ObjCPropertyRefExpr(PD, + Context.PseudoObjectTy, VK_LValue, OK_ObjCProperty, MemberLoc, @@ -668,28 +687,16 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, return ExprError(); if (Getter || Setter) { - QualType PType; - if (Getter) - PType = getMessageSendResultType(QualType(OPT, 0), Getter, false, Super); - else { - ParmVarDecl *ArgDecl = *Setter->param_begin(); - PType = ArgDecl->getType().getUnqualifiedType(); // can't be an array - } - - ExprValueKind VK = VK_LValue; - ExprObjectKind OK = OK_ObjCProperty; - if (!getLangOptions().CPlusPlus && !PType.hasQualifiers() && - PType->isVoidType()) - VK = VK_RValue, OK = OK_Ordinary; - if (Super) return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, MemberLoc, SuperLoc, SuperType)); else return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, MemberLoc, BaseExpr)); } @@ -825,34 +832,17 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, return ExprError(); if (Getter || Setter) { - QualType PType; - - ExprValueKind VK = VK_LValue; - if (Getter) { - PType = getMessageSendResultType(Context.getObjCInterfaceType(IFace), - Getter, true, - receiverNamePtr->isStr("super")); - if (!getLangOptions().CPlusPlus && - !PType.hasQualifiers() && PType->isVoidType()) - VK = VK_RValue; - } else { - for (ObjCMethodDecl::param_iterator PI = Setter->param_begin(), - E = Setter->param_end(); PI != E; ++PI) - PType = (*PI)->getType(); - VK = VK_LValue; - } - - ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); - if (IsSuper) return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, propertyNameLoc, receiverNameLoc, Context.getObjCInterfaceType(IFace))); return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, + Context.PseudoObjectTy, + VK_LValue, OK_ObjCProperty, propertyNameLoc, receiverNameLoc, IFace)); } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 59901cb11b..77cfe6c2de 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -3795,15 +3795,8 @@ InitializationSequence::InitializationSequence(Sema &S, setSequenceKind(NormalSequence); for (unsigned I = 0; I != NumArgs; ++I) - if (Args[I]->getObjectKind() == OK_ObjCProperty) { - ExprResult Result = S.ConvertPropertyForRValue(Args[I]); - if (Result.isInvalid()) { - SetFailed(FK_ConversionFromPropertyFailed); - return; - } - Args[I] = Result.take(); - } else if (const BuiltinType *PlaceholderTy - = Args[I]->getType()->getAsPlaceholderType()) { + if (const BuiltinType *PlaceholderTy + = Args[I]->getType()->getAsPlaceholderType()) { // FIXME: should we be doing this here? if (PlaceholderTy->getKind() != BuiltinType::Overload) { ExprResult result = S.CheckPlaceholderExpr(Args[I]); @@ -4493,13 +4486,6 @@ InitializationSequence::Perform(Sema &S, assert(Args.size() == 1); CurInit = Args.get()[0]; if (!CurInit.get()) return ExprError(); - - // Read from a property when initializing something with it. - if (CurInit.get()->getObjectKind() == OK_ObjCProperty) { - CurInit = S.ConvertPropertyForRValue(CurInit.take()); - if (CurInit.isInvalid()) - return ExprError(); - } break; } diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 7e9bb46cd6..617dbefada 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -800,9 +800,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, SelfExpr, true, true); ObjCMethodDecl::param_iterator P = setterMethod->param_begin(); ParmVarDecl *Param = (*P); - QualType T = Param->getType(); - if (T->isReferenceType()) - T = T->getAs()->getPointeeType(); + QualType T = Param->getType().getNonReferenceType(); Expr *rhs = new (Context) DeclRefExpr(Param, T, VK_LValue, SourceLocation()); ExprResult Res = BuildBinOp(S, lhs->getLocEnd(), @@ -954,8 +952,8 @@ bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, ObjCMethodDecl *GetterMethod, SourceLocation Loc) { if (GetterMethod && - GetterMethod->getResultType().getNonReferenceType() - != property->getType().getNonReferenceType()) { + !Context.hasSameType(GetterMethod->getResultType().getNonReferenceType(), + property->getType().getNonReferenceType())) { AssignConvertType result = Incompatible; if (property->getType()->isObjCObjectPointerType()) result = CheckAssignmentConstraints(Loc, GetterMethod->getResultType(), diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 44395c59df..f5494dee17 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -575,17 +575,6 @@ namespace { /// Return true on unrecoverable error. static bool checkPlaceholderForOverload(Sema &S, Expr *&E, UnbridgedCastsSet *unbridgedCasts = 0) { - // ObjCProperty l-values are placeholder-like. - if (E->getObjectKind() == OK_ObjCProperty) { - ExprResult result = S.ConvertPropertyForRValue(E); - if (result.isInvalid()) - return true; - - E = result.take(); - return false; - } - - // Handle true placeholders. if (const BuiltinType *placeholder = E->getType()->getAsPlaceholderType()) { // We can't handle overloaded expressions here because overload // resolution might reasonably tweak them. @@ -1003,6 +992,9 @@ ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, AssignmentAction Action, bool AllowExplicit, ImplicitConversionSequence& ICS) { + if (checkPlaceholderForOverload(*this, From)) + return ExprError(); + // Objective-C ARC: Determine whether we will allow the writeback conversion. bool AllowObjCWritebackConversion = getLangOptions().ObjCAutoRefCount && @@ -4086,6 +4078,9 @@ TryContextuallyConvertToBool(Sema &S, Expr *From) { /// PerformContextuallyConvertToBool - Perform a contextual conversion /// of the expression From to bool (C++0x [conv]p3). ExprResult Sema::PerformContextuallyConvertToBool(Expr *From) { + if (checkPlaceholderForOverload(*this, From)) + return ExprError(); + ImplicitConversionSequence ICS = TryContextuallyConvertToBool(*this, From); if (!ICS.isBad()) return PerformImplicitConversion(From, Context.BoolTy, ICS, AA_Converting); @@ -4145,6 +4140,9 @@ TryContextuallyConvertToObjCPointer(Sema &S, Expr *From) { /// PerformContextuallyConvertToObjCPointer - Perform a contextual /// conversion of the expression From to an Objective-C pointer type. ExprResult Sema::PerformContextuallyConvertToObjCPointer(Expr *From) { + if (checkPlaceholderForOverload(*this, From)) + return ExprError(); + QualType Ty = Context.getObjCIdType(); ImplicitConversionSequence ICS = TryContextuallyConvertToObjCPointer(*this, From); @@ -9009,39 +9007,9 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, if (checkPlaceholderForOverload(*this, Args[1])) return ExprError(); - // The LHS is more complicated. - if (Args[0]->getObjectKind() == OK_ObjCProperty) { - - // There's a tension for assignment operators between primitive - // property assignment and the overloaded operators. - if (BinaryOperator::isAssignmentOp(Opc)) { - const ObjCPropertyRefExpr *PRE = LHS->getObjCProperty(); - - // Is the property "logically" settable? - bool Settable = (PRE->isExplicitProperty() || - PRE->getImplicitPropertySetter()); - - // To avoid gratuitously inventing semantics, use the primitive - // unless it isn't. Thoughts in case we ever really care: - // - If the property isn't logically settable, we have to - // load and hope. - // - If the property is settable and this is simple assignment, - // we really should use the primitive. - // - If the property is settable, then we could try overloading - // on a generic lvalue of the appropriate type; if it works - // out to a builtin candidate, we would do that same operation - // on the property, and otherwise just error. - if (Settable) - return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]); - } - - ExprResult Result = ConvertPropertyForRValue(Args[0]); - if (Result.isInvalid()) - return ExprError(); - Args[0] = Result.take(); - } - - // Handle all the other placeholders. + // Do placeholder-like conversion on the LHS; note that we should + // not get here with a PseudoObject LHS. + assert(Args[0]->getObjectKind() != OK_ObjCProperty); if (checkPlaceholderForOverload(*this, Args[0])) return ExprError(); diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 3297c0641f..887dd39894 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -492,6 +492,10 @@ Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, Expr *Cond, if (!Cond) return StmtError(); + CondResult = CheckPlaceholderExpr(Cond); + if (CondResult.isInvalid()) + return StmtError(); + CondResult = ConvertToIntegralOrEnumerationType(SwitchLoc, Cond, PDiag(diag::err_typecheck_statement_requires_integer), diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 3c118cb423..59c132cacc 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2249,8 +2249,8 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildObjCPropertyRefExpr(Expr *BaseArg, - ObjCPropertyDecl *Property, - SourceLocation PropertyLoc) { + ObjCPropertyDecl *Property, + SourceLocation PropertyLoc) { CXXScopeSpec SS; ExprResult Base = getSema().Owned(BaseArg); LookupResult R(getSema(), Property->getDeclName(), PropertyLoc, @@ -8049,7 +8049,7 @@ TreeTransform::TransformObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { E->getLocation()); return getDerived().RebuildObjCPropertyRefExpr(Base.get(), - E->getType(), + SemaRef.Context.PseudoObjectTy, E->getImplicitPropertyGetter(), E->getImplicitPropertySetter(), E->getLocation()); diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index 1103dc0aff..67f74f7d7a 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -52,6 +52,7 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::Char32: ID = PREDEF_TYPE_CHAR32_ID; break; case BuiltinType::Overload: ID = PREDEF_TYPE_OVERLOAD_ID; break; case BuiltinType::BoundMember:ID = PREDEF_TYPE_BOUND_MEMBER; break; + case BuiltinType::PseudoObject:ID = PREDEF_TYPE_PSEUDO_OBJECT;break; case BuiltinType::Dependent: ID = PREDEF_TYPE_DEPENDENT_ID; break; case BuiltinType::UnknownAny: ID = PREDEF_TYPE_UNKNOWN_ANY; break; case BuiltinType::ARCUnbridgedCast: diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 7afb3e43de..d47bb3701d 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3857,6 +3857,7 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_LONGDOUBLE_ID: T = Context.LongDoubleTy; break; case PREDEF_TYPE_OVERLOAD_ID: T = Context.OverloadTy; break; case PREDEF_TYPE_BOUND_MEMBER: T = Context.BoundMemberTy; break; + case PREDEF_TYPE_PSEUDO_OBJECT: T = Context.PseudoObjectTy; break; case PREDEF_TYPE_DEPENDENT_ID: T = Context.DependentTy; break; case PREDEF_TYPE_UNKNOWN_ANY: T = Context.UnknownAnyTy; break; case PREDEF_TYPE_NULLPTR_ID: T = Context.NullPtrTy; break; diff --git a/test/SemaObjC/property-category-1.m b/test/SemaObjC/property-category-1.m index 6382826080..fd2ae1165b 100644 --- a/test/SemaObjC/property-category-1.m +++ b/test/SemaObjC/property-category-1.m @@ -48,6 +48,6 @@ int main(int argc, char **argv) { @implementation I0 // expected-warning {{property 'p0' requires method 'p0' to be define}} - (void) foo { - self.p0 = 0; // expected-error {{assigning to property with 'readonly' attribute not allowed}} + self.p0 = 0; // expected-error {{assignment to readonly property}} } @end diff --git a/test/SemaObjC/property-error-readonly-assign.m b/test/SemaObjC/property-error-readonly-assign.m index fc8c48c4f6..3484172844 100644 --- a/test/SemaObjC/property-error-readonly-assign.m +++ b/test/SemaObjC/property-error-readonly-assign.m @@ -13,9 +13,9 @@ @end void f0(A *a, B* b) { - a.x = 10; // expected-error {{assigning to property with 'readonly' attribute not allowed}} + a.x = 10; // expected-error {{assignment to readonly property}} a.ok = 20; - b.x = 10; // expected-error {{setter method is needed to assign to object using property assignment syntax}} + b.x = 10; // expected-error {{no setter method 'setX:' for assignment to property}} b.ok = 20; } @@ -39,6 +39,6 @@ NSRect NSMakeRect(); @implementation NSWindow (Category) -(void)methodToMakeClangCrash { - self.frame = NSMakeRect(); // expected-error {{setter method is needed to assign to object using property assignment syntax}} + self.frame = NSMakeRect(); // expected-error {{no setter method 'setFrame:' for assignment to property}} } @end diff --git a/test/SemaObjC/property-in-class-extension.m b/test/SemaObjC/property-in-class-extension.m index 6ae0b8148a..53210fc34b 100644 --- a/test/SemaObjC/property-in-class-extension.m +++ b/test/SemaObjC/property-in-class-extension.m @@ -9,7 +9,7 @@ void FUNC () { Foo *foo; - foo.bar = 0; // expected-error {{assigning to property with 'readonly' attribute not allowed}} + foo.bar = 0; // expected-error {{assignment to readonly property}} } // rdar://8747333 diff --git a/test/SemaObjC/property-user-setter.m b/test/SemaObjC/property-user-setter.m index 2f1e19727a..3dd19c5f3a 100644 --- a/test/SemaObjC/property-user-setter.m +++ b/test/SemaObjC/property-user-setter.m @@ -20,7 +20,7 @@ -(void) im0 { self.x = 0; self.y = 2; - self.z = 2; // expected-error {{assigning to property with 'readonly' attribute not allowed}} + self.z = 2; // expected-error {{assignment to readonly property}} } @end @@ -85,7 +85,7 @@ static int g_val; - (void)setFoo:(int)value; @end -void g(int); +void g(int); // expected-note {{passing argument to parameter here}} void f(C *c) { c.Foo = 17; // OK diff --git a/test/SemaObjCXX/propert-dot-error.mm b/test/SemaObjCXX/propert-dot-error.mm index 747efeb536..5f7efdd90a 100644 --- a/test/SemaObjCXX/propert-dot-error.mm +++ b/test/SemaObjCXX/propert-dot-error.mm @@ -20,7 +20,7 @@ struct X { @end void f(A* a) { - a.x = X(); // expected-error {{setter method is needed to assign to object using property assignment syntax}} + a.x = X(); // expected-error {{no setter method 'setX:' for assignment to property}} } struct Y : X { };