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:
Fariborz Jahanian 2008-11-22 18:39:36 +00:00
parent bff4b37af5
commit 8a1810f06b
13 changed files with 148 additions and 141 deletions

View File

@ -103,7 +103,8 @@ public:
MLV_IncompleteType,
MLV_ConstQualified,
MLV_ArrayType,
MLV_NotBlockQualified
MLV_NotBlockQualified,
MLV_ReadonlyProperty
};
isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const;

View File

@ -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;

View File

@ -115,6 +115,7 @@ STMT(ObjCSelectorExpr , Expr)
STMT(ObjCProtocolExpr , Expr)
STMT(ObjCIvarRefExpr , Expr)
STMT(ObjCPropertyRefExpr , Expr)
STMT(ObjCKVCRefExpr , Expr)
STMT(ObjCSuperExpr , Expr)
// Clang Extensions.

View File

@ -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'")

View File

@ -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(); }

View File

@ -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) {

View File

@ -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:

View File

@ -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;
}

View File

@ -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;

View File

@ -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()));

View File

@ -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,

View File

@ -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);

View File

@ -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)