New AST node to access "implicit" setter/getter using property dor syntax.
Issuing diagnostics when assigning to read-only properties. This is work in progress. llvm-svn: 59874
This commit is contained in:
parent
bff4b37af5
commit
8a1810f06b
|
@ -103,7 +103,8 @@ public:
|
|||
MLV_IncompleteType,
|
||||
MLV_ConstQualified,
|
||||
MLV_ArrayType,
|
||||
MLV_NotBlockQualified
|
||||
MLV_NotBlockQualified,
|
||||
MLV_ReadonlyProperty
|
||||
};
|
||||
isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const;
|
||||
|
||||
|
|
|
@ -195,68 +195,21 @@ public:
|
|||
};
|
||||
|
||||
/// ObjCPropertyRefExpr - A dot-syntax expression to access an ObjC
|
||||
/// property. Note that dot-syntax can also be used to access
|
||||
/// "implicit" properties (i.e. methods following the property naming
|
||||
/// convention). Additionally, sema is not yet smart enough to know if
|
||||
/// a property reference is to a getter or a setter, so the expr must
|
||||
/// have access to both methods.
|
||||
/// property.
|
||||
///
|
||||
// FIXME: Consider splitting these into separate Expr classes.
|
||||
class ObjCPropertyRefExpr : public Expr {
|
||||
public:
|
||||
enum Kind {
|
||||
PropertyRef, // This expressions references a declared property.
|
||||
MethodRef // This expressions references methods.
|
||||
};
|
||||
|
||||
private:
|
||||
// A dot-syntax reference via methods must always have a getter. We
|
||||
// avoid storing the kind explicitly by relying on this invariant
|
||||
// and assuming this is a MethodRef iff Getter is non-null. Setter
|
||||
// can be null in situations which access a read-only property.
|
||||
union {
|
||||
ObjCPropertyDecl *AsProperty;
|
||||
struct {
|
||||
ObjCMethodDecl *Setter;
|
||||
ObjCMethodDecl *Getter;
|
||||
} AsMethod;
|
||||
} Referent;
|
||||
SourceLocation Loc;
|
||||
Stmt *Base;
|
||||
|
||||
public:
|
||||
ObjCPropertyRefExpr(ObjCPropertyDecl *PD, QualType t,
|
||||
SourceLocation l, Expr *base)
|
||||
: Expr(ObjCPropertyRefExprClass, t), Loc(l), Base(base) {
|
||||
Referent.AsMethod.Getter = Referent.AsMethod.Setter = NULL;
|
||||
Referent.AsProperty = PD;
|
||||
: Expr(ObjCPropertyRefExprClass, t), AsProperty(PD), Loc(l), Base(base) {
|
||||
}
|
||||
ObjCPropertyRefExpr(ObjCMethodDecl *Getter, ObjCMethodDecl *Setter,
|
||||
QualType t,
|
||||
SourceLocation l, Expr *base)
|
||||
: Expr(ObjCPropertyRefExprClass, t), Loc(l), Base(base) {
|
||||
Referent.AsMethod.Getter = Getter;
|
||||
Referent.AsMethod.Setter = Setter;
|
||||
}
|
||||
|
||||
Kind getKind() const {
|
||||
return Referent.AsMethod.Getter ? MethodRef : PropertyRef;
|
||||
}
|
||||
|
||||
ObjCPropertyDecl *getProperty() const {
|
||||
assert(getKind() == PropertyRef &&
|
||||
"Cannot get property from an ObjCPropertyRefExpr using methods");
|
||||
return Referent.AsProperty;
|
||||
}
|
||||
ObjCMethodDecl *getGetterMethod() const {
|
||||
assert(getKind() == MethodRef &&
|
||||
"Cannot get method from an ObjCPropertyRefExpr using a property");
|
||||
return Referent.AsMethod.Getter;
|
||||
}
|
||||
ObjCMethodDecl *getSetterMethod() const {
|
||||
assert(getKind() == MethodRef &&
|
||||
"Cannot get method from an ObjCPropertyRefExpr using a property");
|
||||
return Referent.AsMethod.Setter;
|
||||
return AsProperty;
|
||||
}
|
||||
|
||||
virtual SourceRange getSourceRange() const {
|
||||
|
@ -281,6 +234,60 @@ public:
|
|||
static ObjCPropertyRefExpr* CreateImpl(llvm::Deserializer& D, ASTContext& C);
|
||||
};
|
||||
|
||||
/// ObjCKVCRefExpr - A dot-syntax expression to access "implicit" properties
|
||||
/// (i.e. methods following the property naming convention). KVC stands for
|
||||
/// Key Value Encoding, a generic concept for accessing or setting a 'Key'
|
||||
/// value for an object.
|
||||
///
|
||||
|
||||
class ObjCKVCRefExpr : public Expr {
|
||||
private:
|
||||
|
||||
ObjCMethodDecl *Setter;
|
||||
ObjCMethodDecl *Getter;
|
||||
SourceLocation Loc;
|
||||
Stmt *Base;
|
||||
|
||||
public:
|
||||
ObjCKVCRefExpr(ObjCMethodDecl *getter,
|
||||
QualType t,
|
||||
SourceLocation l, Expr *base)
|
||||
: Expr(ObjCKVCRefExprClass, t), Setter(0),
|
||||
Getter(getter), Loc(l), Base(base) {
|
||||
}
|
||||
|
||||
ObjCMethodDecl *getGetterMethod() const {
|
||||
return Getter;
|
||||
}
|
||||
void setSetterMethod(ObjCMethodDecl *setter) {
|
||||
Setter = setter;
|
||||
}
|
||||
ObjCMethodDecl *getSetterMethod() const {
|
||||
return Setter;
|
||||
}
|
||||
|
||||
virtual SourceRange getSourceRange() const {
|
||||
return SourceRange(getBase()->getLocStart(), Loc);
|
||||
}
|
||||
const Expr *getBase() const { return cast<Expr>(Base); }
|
||||
Expr *getBase() { return cast<Expr>(Base); }
|
||||
void setBase(Expr * base) { Base = base; }
|
||||
|
||||
SourceLocation getLocation() const { return Loc; }
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() == ObjCKVCRefExprClass;
|
||||
}
|
||||
static bool classof(const ObjCKVCRefExpr *) { return true; }
|
||||
|
||||
// Iterators
|
||||
virtual child_iterator child_begin();
|
||||
virtual child_iterator child_end();
|
||||
|
||||
virtual void EmitImpl(llvm::Serializer& S) const;
|
||||
static ObjCKVCRefExpr* CreateImpl(llvm::Deserializer& D, ASTContext& C);
|
||||
};
|
||||
|
||||
class ObjCMessageExpr : public Expr {
|
||||
// SubExprs - The receiver and arguments of the message expression.
|
||||
Stmt **SubExprs;
|
||||
|
|
|
@ -115,6 +115,7 @@ STMT(ObjCSelectorExpr , Expr)
|
|||
STMT(ObjCProtocolExpr , Expr)
|
||||
STMT(ObjCIvarRefExpr , Expr)
|
||||
STMT(ObjCPropertyRefExpr , Expr)
|
||||
STMT(ObjCKVCRefExpr , Expr)
|
||||
STMT(ObjCSuperExpr , Expr)
|
||||
|
||||
// Clang Extensions.
|
||||
|
|
|
@ -541,6 +541,8 @@ DIAG(error_synthesize_category_decl, ERROR,
|
|||
"@synthesize not allowed in a category's implementation")
|
||||
DIAG(error_property_ivar_type, ERROR,
|
||||
"type of property '%0' does not match type of ivar '%1'")
|
||||
DIAG(error_readonly_property_assignment, ERROR,
|
||||
"assigning to property with 'readonly' attribute not allowed")
|
||||
DIAG(warn_readonly_property, WARNING,
|
||||
"attribute 'readonly' of property '%0' restricts attribute "
|
||||
"'readwrite' of property inherited from '%1'")
|
||||
|
|
|
@ -463,6 +463,8 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
|
|||
return LV_Valid;
|
||||
case ObjCPropertyRefExprClass: // FIXME: check if read-only property.
|
||||
return LV_Valid;
|
||||
case ObjCKVCRefExprClass: // FIXME: check if read-only property.
|
||||
return LV_Valid;
|
||||
case PredefinedExprClass:
|
||||
return LV_Valid;
|
||||
case VAArgExprClass:
|
||||
|
@ -545,6 +547,16 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue(ASTContext &Ctx) const {
|
|||
if (!BDR->isByRef() && isa<VarDecl>(BDR->getDecl()))
|
||||
return MLV_NotBlockQualified;
|
||||
}
|
||||
// Assigning to a readonly property?
|
||||
if (getStmtClass() == ObjCPropertyRefExprClass) {
|
||||
const ObjCPropertyRefExpr* PropExpr = cast<ObjCPropertyRefExpr>(this);
|
||||
if (ObjCPropertyDecl *PDecl = PropExpr->getProperty()) {
|
||||
ObjCPropertyDecl::PropertyAttributeKind Pkind =
|
||||
PDecl->getPropertyAttributes();
|
||||
if (Pkind == ObjCPropertyDecl::OBJC_PR_readonly)
|
||||
return MLV_ReadonlyProperty;
|
||||
}
|
||||
}
|
||||
return MLV_Valid;
|
||||
}
|
||||
|
||||
|
@ -1353,6 +1365,10 @@ Stmt::child_iterator ObjCIvarRefExpr::child_end() { return &Base+1; }
|
|||
Stmt::child_iterator ObjCPropertyRefExpr::child_begin() { return &Base; }
|
||||
Stmt::child_iterator ObjCPropertyRefExpr::child_end() { return &Base+1; }
|
||||
|
||||
// ObjCKVCRefExpr
|
||||
Stmt::child_iterator ObjCKVCRefExpr::child_begin() { return &Base; }
|
||||
Stmt::child_iterator ObjCKVCRefExpr::child_end() { return &Base+1; }
|
||||
|
||||
// ObjCSuperExpr
|
||||
Stmt::child_iterator ObjCSuperExpr::child_begin() { return child_iterator(); }
|
||||
Stmt::child_iterator ObjCSuperExpr::child_end() { return child_iterator(); }
|
||||
|
|
|
@ -139,6 +139,7 @@ namespace {
|
|||
void VisitObjCSelectorExpr(ObjCSelectorExpr *Node);
|
||||
void VisitObjCProtocolExpr(ObjCProtocolExpr *Node);
|
||||
void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node);
|
||||
void VisitObjCKVCRefExpr(ObjCKVCRefExpr *Node);
|
||||
void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node);
|
||||
void VisitObjCSuperExpr(ObjCSuperExpr *Node);
|
||||
};
|
||||
|
@ -470,16 +471,18 @@ void StmtDumper::VisitObjCProtocolExpr(ObjCProtocolExpr *Node) {
|
|||
void StmtDumper::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) {
|
||||
DumpExpr(Node);
|
||||
|
||||
if (Node->getKind() == ObjCPropertyRefExpr::MethodRef) {
|
||||
fprintf(F, " Kind=PropertyRef Property=\"%s\"",
|
||||
Node->getProperty()->getIdentifierName());
|
||||
}
|
||||
|
||||
void StmtDumper::VisitObjCKVCRefExpr(ObjCKVCRefExpr *Node) {
|
||||
DumpExpr(Node);
|
||||
|
||||
ObjCMethodDecl *Getter = Node->getGetterMethod();
|
||||
ObjCMethodDecl *Setter = Node->getSetterMethod();
|
||||
fprintf(F, " Kind=MethodRef Getter=\"%s\" Setter=\"%s\"",
|
||||
Getter->getSelector().getName().c_str(),
|
||||
Setter ? Setter->getSelector().getName().c_str() : "(null)");
|
||||
} else {
|
||||
fprintf(F, " Kind=PropertyRef Property=\"%s\"",
|
||||
Node->getProperty()->getIdentifierName());
|
||||
}
|
||||
}
|
||||
|
||||
void StmtDumper::VisitObjCSuperExpr(ObjCSuperExpr *Node) {
|
||||
|
|
|
@ -502,6 +502,14 @@ void StmtPrinter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) {
|
|||
// FIXME: OS << Node->getDecl()->getName();
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitObjCKVCRefExpr(ObjCKVCRefExpr *Node) {
|
||||
if (Node->getBase()) {
|
||||
PrintExpr(Node->getBase());
|
||||
OS << ".";
|
||||
}
|
||||
// FIXME: Setter/Getter names
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) {
|
||||
switch (Node->getIdentType()) {
|
||||
default:
|
||||
|
|
|
@ -1150,14 +1150,14 @@ ObjCIvarRefExpr* ObjCIvarRefExpr::CreateImpl(Deserializer& D, ASTContext& C) {
|
|||
void ObjCPropertyRefExpr::EmitImpl(Serializer& S) const {
|
||||
S.Emit(Loc);
|
||||
S.Emit(getType());
|
||||
unsigned Kind = getKind();
|
||||
S.Emit(Kind);
|
||||
if (Kind == PropertyRef) {
|
||||
S.EmitPtr(getProperty());
|
||||
} else {
|
||||
}
|
||||
|
||||
void ObjCKVCRefExpr::EmitImpl(Serializer& S) const {
|
||||
S.Emit(Loc);
|
||||
S.Emit(getType());
|
||||
S.EmitPtr(getGetterMethod());
|
||||
S.EmitPtr(getSetterMethod());
|
||||
}
|
||||
}
|
||||
|
||||
ObjCPropertyRefExpr* ObjCPropertyRefExpr::CreateImpl(Deserializer& D,
|
||||
|
@ -1165,13 +1165,17 @@ ObjCPropertyRefExpr* ObjCPropertyRefExpr::CreateImpl(Deserializer& D,
|
|||
SourceLocation Loc = SourceLocation::ReadVal(D);
|
||||
QualType T = QualType::ReadVal(D);
|
||||
ObjCPropertyRefExpr* dr = new ObjCPropertyRefExpr(NULL,T,Loc,0);
|
||||
unsigned Kind = D.ReadInt();
|
||||
if (Kind == PropertyRef) {
|
||||
D.ReadPtr(dr->Referent.AsProperty,false);
|
||||
} else {
|
||||
D.ReadPtr(dr->Referent.AsMethod.Setter,false);
|
||||
D.ReadPtr(dr->Referent.AsMethod.Getter,false);
|
||||
}
|
||||
D.ReadPtr(dr->AsProperty,false);
|
||||
return dr;
|
||||
}
|
||||
|
||||
ObjCKVCRefExpr* ObjCKVCRefExpr::CreateImpl(Deserializer& D,
|
||||
ASTContext& C) {
|
||||
SourceLocation Loc = SourceLocation::ReadVal(D);
|
||||
QualType T = QualType::ReadVal(D);
|
||||
ObjCKVCRefExpr* dr = new ObjCKVCRefExpr(NULL,T,Loc,0);
|
||||
D.ReadPtr(dr->Setter,false);
|
||||
D.ReadPtr(dr->Getter,false);
|
||||
return dr;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,8 +72,7 @@ static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
|
|||
if (BO->isAssignmentOp())
|
||||
if(ObjCPropertyRefExpr* PRE =
|
||||
dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
|
||||
if(PRE->getKind() == ObjCPropertyRefExpr::PropertyRef &&
|
||||
PRE->getProperty() == PD)
|
||||
if(PRE->getProperty() == PD)
|
||||
if(BO->getRHS()->isNullPointerConstant(Ctx))
|
||||
return true;
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
EmitAggLoadOfLValue(E);
|
||||
}
|
||||
void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E);
|
||||
void VisitObjCKVCRefExpr(ObjCKVCRefExpr *E);
|
||||
|
||||
void VisitConditionalOperator(const ConditionalOperator *CO);
|
||||
void VisitInitListExpr(InitListExpr *E);
|
||||
|
@ -170,6 +171,18 @@ void AggExprEmitter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
|
|||
CGF.EmitAggregateCopy(DestPtr, RV.getAggregateAddr(), E->getType());
|
||||
}
|
||||
|
||||
void AggExprEmitter::VisitObjCKVCRefExpr(ObjCKVCRefExpr *E) {
|
||||
RValue RV = CGF.EmitObjCPropertyGet(E);
|
||||
assert(RV.isAggregate() && "Return value must be aggregate value!");
|
||||
|
||||
// If the result is ignored, don't copy from the value.
|
||||
if (DestPtr == 0)
|
||||
// FIXME: If the source is volatile, we must read from it.
|
||||
return;
|
||||
|
||||
CGF.EmitAggregateCopy(DestPtr, RV.getAggregateAddr(), E->getType());
|
||||
}
|
||||
|
||||
void AggExprEmitter::VisitOverloadExpr(const OverloadExpr *E) {
|
||||
RValue RV = CGF.EmitCallExpr(E->getFn(), E->arg_begin(),
|
||||
E->arg_end(CGF.getContext()));
|
||||
|
|
|
@ -278,40 +278,21 @@ llvm::Value *CodeGenFunction::LoadObjCSelf() {
|
|||
return Builder.CreateLoad(LocalDeclMap[OMD->getSelfDecl()], "self");
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitObjCPropertyGet(const ObjCPropertyRefExpr *E) {
|
||||
// Determine getter selector.
|
||||
Selector S;
|
||||
if (E->getKind() == ObjCPropertyRefExpr::MethodRef) {
|
||||
S = E->getGetterMethod()->getSelector();
|
||||
} else {
|
||||
S = E->getProperty()->getGetterName();
|
||||
}
|
||||
RValue CodeGenFunction::EmitObjCPropertyGet(const Expr *Exp) {
|
||||
if (const ObjCPropertyRefExpr *E = dyn_cast<ObjCPropertyRefExpr>(Exp)) {
|
||||
Selector S = E->getProperty()->getGetterName();
|
||||
|
||||
return CGM.getObjCRuntime().
|
||||
GenerateMessageSend(*this, E->getType(), S,
|
||||
EmitScalarExpr(E->getBase()),
|
||||
false, CallArgList());
|
||||
}
|
||||
assert (0);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitObjCPropertySet(const ObjCPropertyRefExpr *E,
|
||||
RValue Src) {
|
||||
Selector S;
|
||||
if (E->getKind() == ObjCPropertyRefExpr::MethodRef) {
|
||||
ObjCMethodDecl *Setter = E->getSetterMethod();
|
||||
|
||||
if (Setter) {
|
||||
S = Setter->getSelector();
|
||||
} else {
|
||||
// FIXME: This should be diagnosed by sema.
|
||||
CGM.getDiags().Report(getContext().getFullLoc(E->getLocStart()),
|
||||
diag::err_typecheck_assign_const)
|
||||
<< E->getSourceRange();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
S = E->getProperty()->getSetterName();
|
||||
}
|
||||
|
||||
Selector S = E->getProperty()->getSetterName();
|
||||
CallArgList Args;
|
||||
Args.push_back(std::make_pair(Src, E->getType()));
|
||||
CGM.getObjCRuntime().GenerateMessageSend(*this, getContext().VoidTy, S,
|
||||
|
|
|
@ -524,7 +524,7 @@ public:
|
|||
llvm::Value *EmitObjCStringLiteral(const ObjCStringLiteral *E);
|
||||
llvm::Value *EmitObjCSelectorExpr(const ObjCSelectorExpr *E);
|
||||
RValue EmitObjCMessageExpr(const ObjCMessageExpr *E);
|
||||
RValue EmitObjCPropertyGet(const ObjCPropertyRefExpr *E);
|
||||
RValue EmitObjCPropertyGet(const Expr *E);
|
||||
void EmitObjCPropertySet(const ObjCPropertyRefExpr *E, RValue Src);
|
||||
|
||||
|
||||
|
|
|
@ -1090,20 +1090,6 @@ CheckExtVectorComponent(QualType baseType, SourceLocation OpLoc,
|
|||
return VT; // should never get here (a typedef type should always be found).
|
||||
}
|
||||
|
||||
/// constructSetterName - Return the setter name for the given
|
||||
/// identifier, i.e. "set" + Name where the initial character of Name
|
||||
/// has been capitalized.
|
||||
// FIXME: Merge with same routine in Parser. But where should this
|
||||
// live?
|
||||
static IdentifierInfo *constructSetterName(IdentifierTable &Idents,
|
||||
const IdentifierInfo *Name) {
|
||||
llvm::SmallString<100> SelectorName;
|
||||
SelectorName = "set";
|
||||
SelectorName.append(Name->getName(), Name->getName()+Name->getLength());
|
||||
SelectorName[3] = toupper(SelectorName[3]);
|
||||
return &Idents.get(&SelectorName[0], &SelectorName[SelectorName.size()]);
|
||||
}
|
||||
|
||||
Action::ExprResult Sema::
|
||||
ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc,
|
||||
tok::TokenKind OpKind, SourceLocation MemberLoc,
|
||||
|
@ -1212,26 +1198,9 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc,
|
|||
}
|
||||
if (Getter) {
|
||||
// If we found a getter then this may be a valid dot-reference, we
|
||||
// need to also look for the matching setter.
|
||||
IdentifierInfo *SetterName = constructSetterName(PP.getIdentifierTable(),
|
||||
&Member);
|
||||
Selector SetterSel = PP.getSelectorTable().getUnarySelector(SetterName);
|
||||
ObjCMethodDecl *Setter = IFace->lookupInstanceMethod(SetterSel);
|
||||
|
||||
if (!Setter) {
|
||||
if (ObjCMethodDecl *CurMeth = getCurMethodDecl())
|
||||
if (ObjCInterfaceDecl *ClassDecl = CurMeth->getClassInterface())
|
||||
if (ObjCImplementationDecl *ImpDecl =
|
||||
ObjCImplementations[ClassDecl->getIdentifier()])
|
||||
Setter = ImpDecl->getInstanceMethod(SetterSel);
|
||||
}
|
||||
|
||||
// FIXME: There are some issues here. First, we are not
|
||||
// diagnosing accesses to read-only properties because we do not
|
||||
// know if this is a getter or setter yet. Second, we are
|
||||
// checking that the type of the setter matches the type we
|
||||
// expect.
|
||||
return new ObjCPropertyRefExpr(Getter, Setter, Getter->getResultType(),
|
||||
// will look for the matching setter, if it is needed. But we don't
|
||||
// know this yet.
|
||||
return new ObjCKVCRefExpr(Getter, Getter->getResultType(),
|
||||
MemberLoc, BaseExpr);
|
||||
}
|
||||
}
|
||||
|
@ -2550,6 +2519,9 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
|
|||
case Expr::MLV_NotBlockQualified:
|
||||
Diag = diag::err_block_decl_ref_not_modifiable_lvalue;
|
||||
break;
|
||||
case Expr::MLV_ReadonlyProperty:
|
||||
Diag = diag::error_readonly_property_assignment;
|
||||
break;
|
||||
}
|
||||
|
||||
if (NeedType)
|
||||
|
|
Loading…
Reference in New Issue