]> granicus.if.org Git - clang/commitdiff
Add support for member references (E1.E2, E1->E2) with C++ semantics,
authorDouglas Gregor <dgregor@apple.com>
Sat, 20 Dec 2008 23:49:58 +0000 (23:49 +0000)
committerDouglas Gregor <dgregor@apple.com>
Sat, 20 Dec 2008 23:49:58 +0000 (23:49 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61294 91177308-0d34-0410-b5e6-96231b3b80d8

12 files changed:
include/clang/AST/Expr.h
include/clang/Basic/DiagnosticKinds.def
lib/AST/Expr.cpp
lib/AST/ExprConstant.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtSerialization.cpp
lib/Analysis/GRExprEngine.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprConstant.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/member-expr.cpp [new file with mode: 0644]

index 2f2ca04947c6e54d47ffd0a6f13cdc23184b8df3..48267bafa64285506b23153960d8e3d4dc609571 100644 (file)
@@ -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 {
index b1c5cbdaa8d81b28db5b13a98e4c61fe950ef653..df21d8d30e0798952d13cb3db4ec222cb390a19a 100644 (file)
@@ -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,
index 8aaea7ac9f3b716755bfdae27e6f42407520d6c1..adad2a472acbf04d5559043386c097511704a94a 100644 (file)
@@ -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();
     
index 681016793f9dc4f8dbc13a38d9e994fa6c39e192..11f07fb1166b7c115270a072b3ef633f60d7b3d7 100644 (file)
@@ -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;
index 94bca6386632080d3d85df4a007f45e249a58e13..4061aa149799f9007b8031f1be24ab42c2ba59f6 100644 (file)
@@ -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());
index 686ee68b201b196111d89722dd6becb71fff483c..d12ecd0d93a17a90fa8a7f5e0ec159b1f9091453 100644 (file)
@@ -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);
index 0dd6b5fa6893c0d42b8d2fda19451d8efaf4eb1f..424229019ec282d0b95a072385d6de32772fae5d 100644 (file)
@@ -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));
index 59d7108e19fdce6daf26073ee1e4292e407b6435..b085de304aac65b75a9caf08ce93886bd6a04df1 100644 (file)
@@ -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;
index 2166a5c8989f20a93f52a519012f576c659c50de..a82efca6148acc3a0355d94f69e1e9d1349ede9d 100644 (file)
@@ -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);
index 51e4e05eec8fdde4b6c617a40a4934be188e8ea0..7fafb1459168d9053259652cd1679acacaa97879 100644 (file)
@@ -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) &&
index c719f56c4eef63886b78ebdfb7209e2551fc1d89..fe4b763502a1e23500bb21f684c781650c45984c 100644 (file)
@@ -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/test/SemaCXX/member-expr.cpp b/test/SemaCXX/member-expr.cpp
new file mode 100644 (file)
index 0000000..0175bd6
--- /dev/null
@@ -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();
+}