From: Steve Naroff Date: Fri, 24 Jul 2009 17:54:45 +0000 (+0000) Subject: Allow front-end 'isa' access on object's of type 'id'. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f242b1b0c4e998911cb96b2ba7e27ab4a5abaed3;p=clang Allow front-end 'isa' access on object's of type 'id'. Enhance test case to cover 'isa' access on interface types (clang produces an error, GCC produces a warning). Still need back-end CodeGen for ObjCIsaExpr. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@76979 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index e00833b582..d610b3c01e 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -496,6 +496,51 @@ public: virtual child_iterator child_end(); }; +/// ObjCIsaExpr - Represent X->isa and X.isa (similiar in spirit to MemberExpr). +class ObjCIsaExpr : public Expr { + /// Base - the expression for the base object pointer. + Stmt *Base; + + /// IsaMemberLoc - This is the location of the 'isa'. + SourceLocation IsaMemberLoc; + + /// IsArrow - True if this is "X->F", false if this is "X.F". + bool IsArrow; +public: + ObjCIsaExpr(Expr *base, bool isarrow, SourceLocation l, QualType ty) + : Expr(ObjCIsaExprClass, ty), + Base(base), IsaMemberLoc(l), IsArrow(isarrow) {} + + /// \brief Build an empty expression. + explicit ObjCIsaExpr(EmptyShell Empty) : Expr(ObjCIsaExprClass, Empty) { } + + void setBase(Expr *E) { Base = E; } + Expr *getBase() const { return cast(Base); } + + bool isArrow() const { return IsArrow; } + void setArrow(bool A) { IsArrow = A; } + + /// getMemberLoc - Return the location of the "member", in X->F, it is the + /// location of 'F'. + SourceLocation getIsaMemberLoc() const { return IsaMemberLoc; } + void setIsaMemberLoc(SourceLocation L) { IsaMemberLoc = L; } + + virtual SourceRange getSourceRange() const { + return SourceRange(getBase()->getLocStart(), IsaMemberLoc); + } + + virtual SourceLocation getExprLoc() const { return IsaMemberLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCIsaExprClass; + } + static bool classof(const ObjCIsaExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); +}; + } // end namespace clang #endif diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index a95a627311..914ea2798b 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -141,6 +141,7 @@ EXPR(ObjCIvarRefExpr , Expr) EXPR(ObjCPropertyRefExpr , Expr) EXPR(ObjCKVCRefExpr , Expr) EXPR(ObjCSuperExpr , Expr) +EXPR(ObjCIsaExpr , Expr) // Clang Extensions. EXPR(ShuffleVectorExpr , Expr) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 28f1c9d9d0..5ef3539e69 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2189,7 +2189,6 @@ inline bool Type::isObjCClassType() const { inline bool Type::isObjCBuiltinType() const { return isObjCIdType() || isObjCClassType(); } - inline bool Type::isTemplateTypeParmType() const { return isa(CanonicalType.getUnqualifiedType()); } diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h index 85d0c56bbb..926ba224f4 100644 --- a/include/clang/Frontend/PCHBitCodes.h +++ b/include/clang/Frontend/PCHBitCodes.h @@ -638,7 +638,9 @@ namespace clang { EXPR_OBJC_MESSAGE_EXPR, /// \brief An ObjCSuperExpr record. EXPR_OBJC_SUPER_EXPR, - + /// \brief An ObjCIsa Expr record. + EXPR_OBJC_ISA, + /// \brief An ObjCForCollectionStmt record. STMT_OBJC_FOR_COLLECTION, /// \brief An ObjCAtCatchStmt record. diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index efc37f768c..b7b0a041e3 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1878,6 +1878,10 @@ Stmt::child_iterator ObjCKVCRefExpr::child_end() { return &Base+1; } Stmt::child_iterator ObjCSuperExpr::child_begin() { return child_iterator(); } Stmt::child_iterator ObjCSuperExpr::child_end() { return child_iterator(); } +// ObjCIsaExpr +Stmt::child_iterator ObjCIsaExpr::child_begin() { return &Base; } +Stmt::child_iterator ObjCIsaExpr::child_end() { return &Base+1; } + // PredefinedExpr Stmt::child_iterator PredefinedExpr::child_begin() { return child_iterator(); } Stmt::child_iterator PredefinedExpr::child_end() { return child_iterator(); } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 26732d4d29..a5b0df8e45 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -739,6 +739,11 @@ void StmtPrinter::VisitMemberExpr(MemberExpr *Node) { // representing anonymous unions/structs OS << Node->getMemberDecl()->getNameAsString(); } +void StmtPrinter::VisitObjCIsaExpr(ObjCIsaExpr *Node) { + PrintExpr(Node->getBase()); + OS << (Node->isArrow() ? "->isa" : ".isa"); +} + void StmtPrinter::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { PrintExpr(Node->getBase()); OS << "."; diff --git a/lib/Frontend/PCHReaderStmt.cpp b/lib/Frontend/PCHReaderStmt.cpp index 5681b675a2..16c0e79f4e 100644 --- a/lib/Frontend/PCHReaderStmt.cpp +++ b/lib/Frontend/PCHReaderStmt.cpp @@ -104,6 +104,7 @@ namespace { unsigned VisitObjCKVCRefExpr(ObjCKVCRefExpr *E); unsigned VisitObjCMessageExpr(ObjCMessageExpr *E); unsigned VisitObjCSuperExpr(ObjCSuperExpr *E); + unsigned VisitObjCIsaExpr(ObjCIsaExpr *E); unsigned VisitObjCForCollectionStmt(ObjCForCollectionStmt *); unsigned VisitObjCAtCatchStmt(ObjCAtCatchStmt *); @@ -448,6 +449,14 @@ unsigned PCHStmtReader::VisitMemberExpr(MemberExpr *E) { return 1; } +unsigned PCHStmtReader::VisitObjCIsaExpr(ObjCIsaExpr *E) { + VisitExpr(E); + E->setBase(cast(StmtStack.back())); + E->setIsaMemberLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); + E->setArrow(Record[Idx++]); + return 1; +} + unsigned PCHStmtReader::VisitCastExpr(CastExpr *E) { VisitExpr(E); E->setSubExpr(cast(StmtStack.back())); @@ -1106,6 +1115,9 @@ Stmt *PCHReader::ReadStmt(llvm::BitstreamCursor &Cursor) { case pch::EXPR_OBJC_SUPER_EXPR: S = new (Context) ObjCSuperExpr(Empty); break; + case pch::EXPR_OBJC_ISA: + S = new (Context) ObjCIsaExpr(Empty); + break; case pch::STMT_OBJC_FOR_COLLECTION: S = new (Context) ObjCForCollectionStmt(Empty); break; diff --git a/lib/Frontend/PCHWriterStmt.cpp b/lib/Frontend/PCHWriterStmt.cpp index ae606b6b41..3e4c6f5fed 100644 --- a/lib/Frontend/PCHWriterStmt.cpp +++ b/lib/Frontend/PCHWriterStmt.cpp @@ -97,6 +97,7 @@ namespace { void VisitObjCKVCRefExpr(ObjCKVCRefExpr *E); void VisitObjCMessageExpr(ObjCMessageExpr *E); void VisitObjCSuperExpr(ObjCSuperExpr *E); + void VisitObjCIsaExpr(ObjCIsaExpr *E); // Objective-C Statements void VisitObjCForCollectionStmt(ObjCForCollectionStmt *); @@ -416,6 +417,14 @@ void PCHStmtWriter::VisitMemberExpr(MemberExpr *E) { Code = pch::EXPR_MEMBER; } +void PCHStmtWriter::VisitObjCIsaExpr(ObjCIsaExpr *E) { + VisitExpr(E); + Writer.WriteSubStmt(E->getBase()); + Writer.AddSourceLocation(E->getIsaMemberLoc(), Record); + Record.push_back(E->isArrow()); + Code = pch::EXPR_OBJC_ISA; +} + void PCHStmtWriter::VisitCastExpr(CastExpr *E) { VisitExpr(E); Writer.WriteSubStmt(E->getSubExpr()); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index bfde991555..66e73f99e9 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2283,7 +2283,6 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, const ObjCObjectPointerType *OPT = BaseType->getAsObjCObjectPointerType(); const ObjCInterfaceType *IFaceT = OPT ? OPT->getInterfaceType() : BaseType->getAsObjCInterfaceType(); - if (IFaceT) { ObjCInterfaceDecl *IDecl = IFaceT->getDecl(); ObjCInterfaceDecl *ClassDeclared; @@ -2340,7 +2339,11 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, << IDecl->getDeclName() << &Member << BaseExpr->getSourceRange()); } - // We don't have an interface. FIXME: deal with ObjC builtin 'id' type. + // We have an 'id' type. Rather than fall through, we check if this + // is a reference to 'isa'. + if (&Member == &Context.Idents.get("isa")) + return Owned(new (Context) ObjCIsaExpr(BaseExpr, true, MemberLoc, + Context.getObjCIdType())); } // Handle properties on 'id' and qualified "id". if (OpKind == tok::period && (BaseType->isObjCIdType() || @@ -2472,6 +2475,13 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, << &Member << BaseType); } + // Handle the following exceptional case (*Obj).isa. + if (OpKind == tok::period && + BaseType->isSpecificBuiltinType(BuiltinType::ObjCId) && + &Member == &Context.Idents.get("isa")) + return Owned(new (Context) ObjCIsaExpr(BaseExpr, false, MemberLoc, + Context.getObjCIdType())); + // Handle 'field access' to vectors, such as 'V.xx'. if (BaseType->isExtVectorType()) { QualType ret = CheckExtVectorComponent(BaseType, OpLoc, Member, MemberLoc); diff --git a/lib/Sema/SemaTemplateInstantiateExpr.cpp b/lib/Sema/SemaTemplateInstantiateExpr.cpp index 5e664add5c..dcf2ebcd2e 100644 --- a/lib/Sema/SemaTemplateInstantiateExpr.cpp +++ b/lib/Sema/SemaTemplateInstantiateExpr.cpp @@ -1340,6 +1340,12 @@ TemplateExprInstantiator::VisitObjCSuperExpr(ObjCSuperExpr *E) { return SemaRef.ExprError(); } +Sema::OwningExprResult +TemplateExprInstantiator::VisitObjCIsaExpr(ObjCIsaExpr *E) { + assert(false && "FIXME: Template instantiations for ObjC expressions"); + return SemaRef.ExprError(); +} + Sema::OwningExprResult Sema::InstantiateExpr(Expr *E, const TemplateArgumentList &TemplateArgs) { if (!E) diff --git a/test/SemaObjC/id-isa-ref.m b/test/SemaObjC/id-isa-ref.m index 1480a52e4a..dbb6b2f53d 100644 --- a/test/SemaObjC/id-isa-ref.m +++ b/test/SemaObjC/id-isa-ref.m @@ -4,7 +4,11 @@ typedef struct objc_object { struct objc_class *isa; } *id; -@interface Whatever +@interface NSObject { + struct objc_class *isa; +} +@end +@interface Whatever : NSObject +self; @end @@ -12,6 +16,19 @@ static void func() { id x; - // FIXME: The following needs to compile without error. I will fix this tomorrow (7/15/09). Until I do, we will produce an error. - [x->isa self]; // expected-error {{member reference base type 'id' is not a structure or union}} + [(*x).isa self]; + [x->isa self]; + + Whatever *y; + + // GCC allows this, with the following warning: + // instance variable ‘isa’ is @protected; this will be a hard error in the future + // + // FIXME: see if we can avoid the 2 warnings that follow the error. + [(*y).isa self]; // expected-error {{instance variable 'isa' is protected}} \ + expected-warning{{receiver type 'struct objc_class *' is not 'id' or interface pointer, consider casting it to 'id'}} \ + expected-warning{{method '-self' not found (return type defaults to 'id')}} + [y->isa self]; // expected-error {{instance variable 'isa' is protected}} \ + expected-warning{{receiver type 'struct objc_class *' is not 'id' or interface pointer, consider casting it to 'id'}} \ + expected-warning{{method '-self' not found (return type defaults to 'id')}} }