diff options
author | Douglas Gregor <dgregor@apple.com> | 2008-12-20 23:49:58 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2008-12-20 23:49:58 +0000 |
commit | 2eedc3aa1ce4af390e5f1efd49087fd23f191565 (patch) | |
tree | 00fa35dbcbb3156de9c1e06278cf87da294a7399 | |
parent | a0befc0a6f064c3cff0d2c5386eabb763e991bf0 (diff) | |
download | bcm5719-llvm-2eedc3aa1ce4af390e5f1efd49087fd23f191565.tar.gz bcm5719-llvm-2eedc3aa1ce4af390e5f1efd49087fd23f191565.zip |
Add support for member references (E1.E2, E1->E2) with C++ semantics,
which can refer to static data members, enumerators, and member
functions as well as to non-static data members.
Implement correct lvalue computation for member references in C++.
Compute the result type of non-static data members of reference type properly.
llvm-svn: 61294
-rw-r--r-- | clang/include/clang/AST/Expr.h | 12 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticKinds.def | 4 | ||||
-rw-r--r-- | clang/lib/AST/Expr.cpp | 69 | ||||
-rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 5 | ||||
-rw-r--r-- | clang/lib/AST/StmtPrinter.cpp | 5 | ||||
-rw-r--r-- | clang/lib/AST/StmtSerialization.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Analysis/GRExprEngine.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExpr.cpp | 4 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprConstant.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 64 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 39 | ||||
-rw-r--r-- | clang/test/SemaCXX/member-expr.cpp | 22 |
12 files changed, 168 insertions, 71 deletions
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2f2ca04947c..48267bafa64 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -126,7 +126,8 @@ public: LV_NotObjectType, LV_IncompleteVoidType, LV_DuplicateVectorComponents, - LV_InvalidExpression + LV_InvalidExpression, + LV_MemberFunction }; isLvalueResult isLvalue(ASTContext &Ctx) const; @@ -147,7 +148,8 @@ public: MLV_ArrayType, MLV_NotBlockQualified, MLV_ReadonlyProperty, - MLV_NoSetterProperty + MLV_NoSetterProperty, + MLV_MemberFunction }; isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const; @@ -830,17 +832,17 @@ public: /// class MemberExpr : public Expr { Stmt *Base; - FieldDecl *MemberDecl; + NamedDecl *MemberDecl; SourceLocation MemberLoc; bool IsArrow; // True if this is "X->F", false if this is "X.F". public: - MemberExpr(Expr *base, bool isarrow, FieldDecl *memberdecl, SourceLocation l, + MemberExpr(Expr *base, bool isarrow, NamedDecl *memberdecl, SourceLocation l, QualType ty) : Expr(MemberExprClass, ty), Base(base), MemberDecl(memberdecl), MemberLoc(l), IsArrow(isarrow) {} Expr *getBase() const { return cast<Expr>(Base); } - FieldDecl *getMemberDecl() const { return MemberDecl; } + NamedDecl *getMemberDecl() const { return MemberDecl; } bool isArrow() const { return IsArrow; } virtual SourceRange getSourceRange() const { diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index b1c5cbdaa8d..df21d8d30e0 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -1171,6 +1171,10 @@ DIAG(err_typecheck_member_reference_ivar, ERROR, "%0 does not have a member named %1") DIAG(err_typecheck_member_reference_arrow, ERROR, "member reference type %0 is not a pointer") +DIAG(err_typecheck_member_reference_type, ERROR, + "cannot refer to type member %0 with '%select{.|->}1'") +DIAG(err_typecheck_member_reference_unknown, ERROR, + "cannot refer to member %0 with '%select{.|->}1'") DIAG(err_typecheck_incomplete_tag, ERROR, "incomplete definition of type %0") DIAG(err_typecheck_no_member, ERROR, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8aaea7ac9f3..adad2a472ac 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -407,8 +407,43 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { return LV_Valid; break; } - case MemberExprClass: { // C99 6.5.2.3p4 + case MemberExprClass: { const MemberExpr *m = cast<MemberExpr>(this); + if (Ctx.getLangOptions().CPlusPlus) { // C++ [expr.ref]p4: + NamedDecl *Member = m->getMemberDecl(); + // C++ [expr.ref]p4: + // If E2 is declared to have type "reference to T", then E1.E2 + // is an lvalue. + if (ValueDecl *Value = dyn_cast<ValueDecl>(Member)) + if (Value->getType()->isReferenceType()) + return LV_Valid; + + // -- If E2 is a static data member [...] then E1.E2 is an lvalue. + if (isa<CXXClassVarDecl>(Member)) + return LV_Valid; + + // -- If E2 is a non-static data member [...]. If E1 is an + // lvalue, then E1.E2 is an lvalue. + if (isa<FieldDecl>(Member)) + return m->isArrow() ? LV_Valid : m->getBase()->isLvalue(Ctx); + + // -- If it refers to a static member function [...], then + // E1.E2 is an lvalue. + // -- Otherwise, if E1.E2 refers to a non-static member + // function [...], then E1.E2 is not an lvalue. + if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member)) + return Method->isStatic()? LV_Valid : LV_MemberFunction; + + // -- If E2 is a member enumerator [...], the expression E1.E2 + // is not an lvalue. + if (isa<EnumConstantDecl>(Member)) + return LV_InvalidExpression; + + // Not an lvalue. + return LV_InvalidExpression; + } + + // C99 6.5.2.3p4 return m->isArrow() ? LV_Valid : m->getBase()->isLvalue(Ctx); } case UnaryOperatorClass: @@ -542,6 +577,7 @@ Expr::isModifiableLvalueResult Expr::isModifiableLvalue(ASTContext &Ctx) const { if (CE->getSubExpr()->isLvalue(Ctx) == LV_Valid) return MLV_LValueCast; return MLV_InvalidExpression; + case LV_MemberFunction: return MLV_MemberFunction; } QualType CT = Ctx.getCanonicalType(getType()); @@ -1113,7 +1149,8 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx) const bool Expr::isBitField() { Expr *E = this->IgnoreParenCasts(); if (MemberExpr *MemRef = dyn_cast<MemberExpr>(E)) - return MemRef->getMemberDecl()->isBitField(); + if (FieldDecl *Field = dyn_cast<FieldDecl>(MemRef->getMemberDecl())) + return Field->isBitField(); return false; } @@ -1245,21 +1282,21 @@ static int64_t evaluateOffsetOf(ASTContext& C, const Expr *E) { RecordDecl *RD = Ty->getAsRecordType()->getDecl(); const ASTRecordLayout &RL = C.getASTRecordLayout(RD); - FieldDecl *FD = ME->getMemberDecl(); - - // FIXME: This is linear time. And the fact that we're indexing - // into the layout by position in the record means that we're - // either stuck numbering the fields in the AST or we have to keep - // the linear search (yuck and yuck). - unsigned i = 0; - for (RecordDecl::field_iterator Field = RD->field_begin(), - FieldEnd = RD->field_end(); - Field != FieldEnd; (void)++Field, ++i) { - if (*Field == FD) - break; + if (FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) { + // FIXME: This is linear time. And the fact that we're indexing + // into the layout by position in the record means that we're + // either stuck numbering the fields in the AST or we have to keep + // the linear search (yuck and yuck). + unsigned i = 0; + for (RecordDecl::field_iterator Field = RD->field_begin(), + FieldEnd = RD->field_end(); + Field != FieldEnd; (void)++Field, ++i) { + if (*Field == FD) + break; + } + + return RL.getFieldOffset(i) + evaluateOffsetOf(C, ME->getBase()); } - - return RL.getFieldOffset(i) + evaluateOffsetOf(C, ME->getBase()); } else if (const ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(E)) { const Expr *Base = ASE->getBase(); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 681016793f9..11f07fb1166 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -153,7 +153,10 @@ APValue LValueExprEvaluator::VisitMemberExpr(MemberExpr *E) { RecordDecl *RD = Ty->getAsRecordType()->getDecl(); const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); - FieldDecl *FD = E->getMemberDecl(); + + FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl()); + if (!FD) // FIXME: deal with other kinds of member expressions + return APValue(); // FIXME: This is linear time. unsigned i = 0; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 94bca638663..4061aa14979 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -702,10 +702,7 @@ void StmtPrinter::VisitCallExpr(CallExpr *Call) { void StmtPrinter::VisitMemberExpr(MemberExpr *Node) { PrintExpr(Node->getBase()); OS << (Node->isArrow() ? "->" : "."); - - FieldDecl *Field = Node->getMemberDecl(); - assert(Field && "MemberExpr should alway reference a field!"); - OS << Field->getNameAsString(); + OS << Node->getMemberDecl()->getNameAsString(); } void StmtPrinter::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { PrintExpr(Node->getBase()); diff --git a/clang/lib/AST/StmtSerialization.cpp b/clang/lib/AST/StmtSerialization.cpp index 686ee68b201..d12ecd0d93a 100644 --- a/clang/lib/AST/StmtSerialization.cpp +++ b/clang/lib/AST/StmtSerialization.cpp @@ -761,7 +761,7 @@ void MemberExpr::EmitImpl(Serializer& S) const { MemberExpr* MemberExpr::CreateImpl(Deserializer& D, ASTContext& C) { SourceLocation L = SourceLocation::ReadVal(D); - FieldDecl* MemberDecl = cast<FieldDecl>(D.ReadPtr<Decl>()); + NamedDecl* MemberDecl = cast<NamedDecl>(D.ReadPtr<Decl>()); bool IsArrow = D.ReadBool(); QualType T = QualType::ReadVal(D); Expr* base = D.ReadOwnedPtr<Expr>(C); diff --git a/clang/lib/Analysis/GRExprEngine.cpp b/clang/lib/Analysis/GRExprEngine.cpp index 0dd6b5fa689..424229019ec 100644 --- a/clang/lib/Analysis/GRExprEngine.cpp +++ b/clang/lib/Analysis/GRExprEngine.cpp @@ -930,12 +930,16 @@ void GRExprEngine::VisitMemberExpr(MemberExpr* M, NodeTy* Pred, else VisitLValue(Base, Pred, Tmp); // x.f = ... or ... = x.f + FieldDecl *Field = dyn_cast<FieldDecl>(M->getMemberDecl()); + if (!Field) // FIXME: skipping member expressions for non-fields + return; + for (NodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { const GRState* St = GetState(*I); // FIXME: Should we insert some assumption logic in here to determine // if "Base" is a valid piece of memory? Before we put this assumption - // later when using FieldOffset lvals (which we no longer have). - SVal L = StateMgr.GetLValue(St, GetSVal(St, Base), M->getMemberDecl()); + // later when using FieldOffset lvals (which we no longer have). + SVal L = StateMgr.GetLValue(St, GetSVal(St, Base), Field); if (asLValue) MakeNode(Dst, M, *I, BindExpr(St, M, L)); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 59d7108e19f..b085de304aa 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -782,7 +782,9 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { CVRQualifiers = BaseExpr->getType().getCVRQualifiers(); } - FieldDecl *Field = E->getMemberDecl(); + FieldDecl *Field = dyn_cast<FieldDecl>(E->getMemberDecl()); + // FIXME: Handle non-field member expressions + assert(Field && "No code generation for non-field member references"); LValue MemExpLV = EmitLValueForField(BaseValue, Field, isUnion, CVRQualifiers); LValue::SetObjCIvar(MemExpLV, isIvar); return MemExpLV; diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 2166a5c8989..a82efca6148 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -564,7 +564,10 @@ public: else Base = EmitLValue(ME->getBase()); - unsigned FieldNumber = CGM.getTypes().getLLVMFieldNo(ME->getMemberDecl()); + FieldDecl *Field = dyn_cast<FieldDecl>(ME->getMemberDecl()); + // FIXME: Handle other kinds of member expressions. + assert(Field && "No code generation for non-field member expressions"); + unsigned FieldNumber = CGM.getTypes().getLLVMFieldNo(Field); llvm::Constant *Zero = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0); llvm::Constant *Idx = llvm::ConstantInt::get(llvm::Type::Int32Ty, FieldNumber); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 51e4e05eec8..7fafb145916 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1244,24 +1244,44 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc, << &Member << BaseExpr->getSourceRange(); } - FieldDecl *MemberDecl = dyn_cast<FieldDecl>(*Lookup.first); - if (!MemberDecl) { - unsigned DiagID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error, - "Clang only supports references to members"); - return Diag(MemberLoc, DiagID); - } - - // Figure out the type of the member; see C99 6.5.2.3p3 - // FIXME: Handle address space modifiers - QualType MemberType = MemberDecl->getType(); - unsigned combinedQualifiers = - MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers(); - if (MemberDecl->isMutable()) - combinedQualifiers &= ~QualType::Const; - MemberType = MemberType.getQualifiedType(combinedQualifiers); + if (FieldDecl *MemberDecl = dyn_cast<FieldDecl>(*Lookup.first)) { + // Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref] + // FIXME: Handle address space modifiers + QualType MemberType = MemberDecl->getType(); + if (const ReferenceType *Ref = MemberType->getAsReferenceType()) + MemberType = Ref->getPointeeType(); + else { + unsigned combinedQualifiers = + MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers(); + if (MemberDecl->isMutable()) + combinedQualifiers &= ~QualType::Const; + MemberType = MemberType.getQualifiedType(combinedQualifiers); + } - return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl, - MemberLoc, MemberType); + return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl, + MemberLoc, MemberType); + } else if (CXXClassVarDecl *Var = dyn_cast<CXXClassVarDecl>(*Lookup.first)) + return new MemberExpr(BaseExpr, OpKind == tok::arrow, Var, MemberLoc, + Var->getType().getNonReferenceType()); + else if (FunctionDecl *MemberFn = dyn_cast<FunctionDecl>(*Lookup.first)) + return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberFn, MemberLoc, + MemberFn->getType()); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast<OverloadedFunctionDecl>(*Lookup.first)) + return new MemberExpr(BaseExpr, OpKind == tok::arrow, Ovl, MemberLoc, + Context.OverloadTy); + else if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(*Lookup.first)) + return new MemberExpr(BaseExpr, OpKind == tok::arrow, Enum, MemberLoc, + Enum->getType()); + else if (isa<TypeDecl>(*Lookup.first)) + return Diag(MemberLoc, diag::err_typecheck_member_reference_type) + << DeclarationName(&Member) << int(OpKind == tok::arrow); + + // We found a declaration kind that we didn't expect. This is a + // generic error message that tells the user that she can't refer + // to this member with '.' or '->'. + return Diag(MemberLoc, diag::err_typecheck_member_reference_unknown) + << DeclarationName(&Member) << int(OpKind == tok::arrow); } // Handle access to Objective-C instance variables, such as "Obj->ivar" and @@ -2920,10 +2940,12 @@ QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { return QualType(); } } else if (MemberExpr *MemExpr = dyn_cast<MemberExpr>(op)) { // C99 6.5.3.2p1 - if (MemExpr->getMemberDecl()->isBitField()) { - Diag(OpLoc, diag::err_typecheck_address_of) - << "bit-field" << op->getSourceRange(); - return QualType(); + if (FieldDecl *Field = dyn_cast<FieldDecl>(MemExpr->getMemberDecl())) { + if (Field->isBitField()) { + Diag(OpLoc, diag::err_typecheck_address_of) + << "bit-field" << op->getSourceRange(); + return QualType(); + } } // Check for Apple extension for accessing vector components. } else if (isa<ArraySubscriptExpr>(op) && diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index c719f56c4ee..fe4b763502a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -666,26 +666,27 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) // other value of that type for promotion purposes (C++ 4.5p3). if (MemberExpr *MemRef = dyn_cast<MemberExpr>(From)) { using llvm::APSInt; - FieldDecl *MemberDecl = MemRef->getMemberDecl(); - APSInt BitWidth; - if (MemberDecl->isBitField() && - FromType->isIntegralType() && !FromType->isEnumeralType() && - From->isIntegerConstantExpr(BitWidth, Context)) { - APSInt ToSize(Context.getTypeSize(ToType)); - - // Are we promoting to an int from a bitfield that fits in an int? - if (BitWidth < ToSize || - (FromType->isSignedIntegerType() && BitWidth <= ToSize)) { - return To->getKind() == BuiltinType::Int; - } - - // Are we promoting to an unsigned int from an unsigned bitfield - // that fits into an unsigned int? - if (FromType->isUnsignedIntegerType() && BitWidth <= ToSize) { - return To->getKind() == BuiltinType::UInt; + if (FieldDecl *MemberDecl = dyn_cast<FieldDecl>(MemRef->getMemberDecl())) { + APSInt BitWidth; + if (MemberDecl->isBitField() && + FromType->isIntegralType() && !FromType->isEnumeralType() && + From->isIntegerConstantExpr(BitWidth, Context)) { + APSInt ToSize(Context.getTypeSize(ToType)); + + // Are we promoting to an int from a bitfield that fits in an int? + if (BitWidth < ToSize || + (FromType->isSignedIntegerType() && BitWidth <= ToSize)) { + return To->getKind() == BuiltinType::Int; + } + + // Are we promoting to an unsigned int from an unsigned bitfield + // that fits into an unsigned int? + if (FromType->isUnsignedIntegerType() && BitWidth <= ToSize) { + return To->getKind() == BuiltinType::UInt; + } + + return false; } - - return false; } } diff --git a/clang/test/SemaCXX/member-expr.cpp b/clang/test/SemaCXX/member-expr.cpp new file mode 100644 index 00000000000..0175bd6f138 --- /dev/null +++ b/clang/test/SemaCXX/member-expr.cpp @@ -0,0 +1,22 @@ +// RUN: clang -fsyntax-only -verify %s + +class X{ +public: + enum E {Enumerator}; + int f(); + static int mem; + static float g(); +}; + +void test(X* xp, X x) { + int i1 = x.f(); + int i2 = xp->f(); + x.E; // expected-error{{cannot refer to type member 'E' with '.'}} + xp->E; // expected-error{{cannot refer to type member 'E' with '->'}} + // FIXME: lookup needs to find enumerators int i3 = x.Enumerator; + // FIXME: int i4 = xp->Enumerator; + x.mem = 1; + xp->mem = 2; + float f1 = x.g(); + float f2 = xp->g(); +} |