]> granicus.if.org Git - clang/commitdiff
Implement dereferencing of pointers-to-member.
authorSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 7 Feb 2009 00:15:38 +0000 (00:15 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Sat, 7 Feb 2009 00:15:38 +0000 (00:15 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63983 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Expr.h
include/clang/AST/StmtVisitor.h
include/clang/Basic/DiagnosticSemaKinds.def
lib/AST/Expr.cpp
lib/Parse/ParseExpr.cpp
lib/Sema/Sema.h
lib/Sema/SemaExpr.cpp
lib/Sema/SemaInherit.h
lib/Sema/SemaLookup.cpp
test/SemaCXX/member-pointer.cpp
www/cxx_status.html

index 3884d7dc2bd96267d4a486548acd83994190685f..8cd27c30da0683009d72b4f99832b805f50b75b2 100644 (file)
@@ -1085,6 +1085,7 @@ public:
   enum Opcode {
     // Operators listed in order of precedence.
     // Note that additions to this should also update the StmtVisitor class.
+    PtrMemD, PtrMemI, // [C++ 5.5] Pointer-to-member operators.
     Mul, Div, Rem,    // [C99 6.5.5] Multiplicative operators.
     Add, Sub,         // [C99 6.5.6] Additive operators.
     Shl, Shr,         // [C99 6.5.7] Bitwise shift operators.
index 4217de32ac131a700bfdb9927489e7d541452642..2b0dd89158b0b28a0c655fc0b8e26deaec767b1b 100644 (file)
@@ -35,6 +35,8 @@ public:
     if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
       switch (BinOp->getOpcode()) {
       default: assert(0 && "Unknown binary operator!");
+      case BinaryOperator::PtrMemD:   DISPATCH(BinPtrMemD,   BinaryOperator);
+      case BinaryOperator::PtrMemI:   DISPATCH(BinPtrMemI,   BinaryOperator);
       case BinaryOperator::Mul:       DISPATCH(BinMul,       BinaryOperator);
       case BinaryOperator::Div:       DISPATCH(BinDiv,       BinaryOperator);
       case BinaryOperator::Rem:       DISPATCH(BinRem,       BinaryOperator);
@@ -95,7 +97,7 @@ public:
       case UnaryOperator::Imag:         DISPATCH(UnaryImag,      UnaryOperator);
       case UnaryOperator::Extension:    DISPATCH(UnaryExtension, UnaryOperator);
       case UnaryOperator::OffsetOf:     DISPATCH(UnaryOffsetOf,  UnaryOperator);
-      }          
+      }
     }
     
     // Top switch stmt: dispatch to VisitFooStmt for each FooStmt.
@@ -119,6 +121,7 @@ public:
   RetTy VisitBin ## NAME(BinaryOperator *S) { \
     DISPATCH(BinaryOperator, BinaryOperator); \
   }
+  BINOP_FALLBACK(PtrMemD)                    BINOP_FALLBACK(PtrMemI)
   BINOP_FALLBACK(Mul)   BINOP_FALLBACK(Div)  BINOP_FALLBACK(Rem)
   BINOP_FALLBACK(Add)   BINOP_FALLBACK(Sub)  BINOP_FALLBACK(Shl)
   BINOP_FALLBACK(Shr)
index f9e07660104e2e6469b213d41f77b4065ae99e43..298e0fe272704214055f5169679ebc1a8164af25 100644 (file)
@@ -863,6 +863,12 @@ DIAG(err_qualified_catch_declarator, ERROR,
      "exception declarator cannot be qualified")
 DIAG(err_early_catch_all, ERROR,
      "catch-all handler must come last")
+DIAG(err_bad_memptr_rhs, ERROR,
+     "right hand operand to %0 must be a pointer to member of a complete class "
+     "but is %1")
+DIAG(err_bad_memptr_lhs, ERROR,
+     "left hand operand to ->* must be a %select{|pointer to }0class "
+     "compatible with the right hand operand, but is %1")
 
 DIAG(err_invalid_use_of_function_type, ERROR,
      "a function type is not allowed here")
index d627ab03b291fd014bff425005eae3abec83217e..89fdbe31f3db4896e4038b4184f955bb44e81312 100644 (file)
@@ -496,6 +496,12 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
         BinOp->getOpcode() == BinaryOperator::Comma)
       return BinOp->getRHS()->isLvalue(Ctx);
 
+    // C++ [expr.mptr.oper]p6
+    if ((BinOp->getOpcode() == BinaryOperator::PtrMemD ||
+         BinOp->getOpcode() == BinaryOperator::PtrMemI) &&
+        !BinOp->getType()->isFunctionType())
+      return BinOp->getLHS()->isLvalue(Ctx);
+
     if (!BinOp->isAssignmentOp())
       return LV_InvalidExpression;
 
index 597c50c08a6c8287af8c5b5384b6ff72384c6c8b..7c0b43923d60029e2568a1a1905accbfc54d366f 100644 (file)
@@ -33,20 +33,21 @@ using namespace clang;
 /// productions.  Low precedences numbers bind more weakly than high numbers.
 namespace prec {
   enum Level {
-    Unknown        = 0,    // Not binary operator.
-    Comma          = 1,    // ,
-    Assignment     = 2,    // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
-    Conditional    = 3,    // ?
-    LogicalOr      = 4,    // ||
-    LogicalAnd     = 5,    // &&
-    InclusiveOr    = 6,    // |
-    ExclusiveOr    = 7,    // ^
-    And            = 8,    // &
-    Equality       = 9,    // ==, !=
-    Relational     = 10,   //  >=, <=, >, <
-    Shift          = 11,   // <<, >>
-    Additive       = 12,   // -, +
-    Multiplicative = 13    // *, /, %
+    Unknown         = 0,    // Not binary operator.
+    Comma           = 1,    // ,
+    Assignment      = 2,    // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
+    Conditional     = 3,    // ?
+    LogicalOr       = 4,    // ||
+    LogicalAnd      = 5,    // &&
+    InclusiveOr     = 6,    // |
+    ExclusiveOr     = 7,    // ^
+    And             = 8,    // &
+    Equality        = 9,    // ==, !=
+    Relational      = 10,   //  >=, <=, >, <
+    Shift           = 11,   // <<, >>
+    Additive        = 12,   // -, +
+    Multiplicative  = 13,   // *, /, %
+    PointerToMember = 14    // .*, ->*
   };
 }
 
@@ -88,6 +89,8 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind) {
   case tok::percent:
   case tok::slash:
   case tok::star:                 return prec::Multiplicative;
+  case tok::periodstar:
+  case tok::arrowstar:            return prec::PointerToMember;
   }
 }
 
@@ -104,7 +107,13 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind) {
 /// consistency, we parse the LHS as a conditional-expression, then check for
 /// l-value-ness in semantic analysis stages.
 ///
+///       pm-expression: [C++ 5.5]
+///         cast-expression
+///         pm-expression '.*' cast-expression
+///         pm-expression '->*' cast-expression
+///
 ///       multiplicative-expression: [C99 6.5.5]
+///     Note: in C++, apply pm-expression instead of cast-expression
 ///         cast-expression
 ///         multiplicative-expression '*' cast-expression
 ///         multiplicative-expression '/' cast-expression
@@ -270,7 +279,7 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) {
     // Consume the operator, saving the operator token for error reporting.
     Token OpToken = Tok;
     ConsumeToken();
-    
+
     // Special case handling for the ternary operator.
     OwningExprResult TernaryMiddle(Actions, true);
     if (NextTokPrec == prec::Conditional) {
index 33b3781200946523642206d9778222c994032e87..4878403333a81b14858c0330e8f110da33014f13 100644 (file)
@@ -1788,16 +1788,18 @@ public:
   bool PerformImplicitConversion(Expr *&From, QualType ToType,
                                  const StandardConversionSequence& SCS,
                                  const char *Flavor);
-  
+
   /// the following "Check" methods will return a valid/converted QualType
   /// or a null QualType (indicating an error diagnostic was issued).
-    
-  /// type checking binary operators (subroutines of ActOnBinOp).
+
+  /// type checking binary operators (subroutines of CreateBuiltinBinOp).
   inline QualType InvalidOperands(SourceLocation l, Expr *&lex, Expr *&rex);
+  inline QualType CheckPointerToMemberOperands( // C++ 5.5
+    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isIndirect);
   inline QualType CheckMultiplyDivideOperands( // C99 6.5.5
-    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false); 
+    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
   inline QualType CheckRemainderOperands( // C99 6.5.5
-    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false); 
+    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
   inline QualType CheckAdditionOperands( // C99 6.5.6
     Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
   inline QualType CheckSubtractionOperands( // C99 6.5.6
@@ -1807,7 +1809,7 @@ public:
   inline QualType CheckCompareOperands( // C99 6.5.8/9
     Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isRelational);
   inline QualType CheckBitwiseOperands( // C99 6.5.[10...12]
-    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false); 
+    Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
   inline QualType CheckLogicalOperands( // C99 6.5.[13,14]
     Expr *&lex, Expr *&rex, SourceLocation OpLoc);
   // CheckAssignmentOperands is used for both simple and compound assignment.
index 8c8207a39d65534b23b2e9a68f60641989063df3..3de916fe3cd2f40b93cb7663d96572ba7fabc853 100644 (file)
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SemaInherit.h"
 #include "Sema.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclObjC.h"
@@ -2701,7 +2702,73 @@ inline QualType Sema::CheckVectorOperands(SourceLocation Loc, Expr *&lex,
     << lex->getType() << rex->getType()
     << lex->getSourceRange() << rex->getSourceRange();
   return QualType();
-}    
+}
+
+inline QualType Sema::CheckPointerToMemberOperands(
+  Expr *&lex, Expr *&rex, SourceLocation Loc, bool isIndirect)
+{
+  const char *OpSpelling = isIndirect ? "->*" : ".*";
+  // C++ 5.5p2
+  //   The binary operator .* [p3: ->*] binds its second operand, which shall
+  //   be of type "pointer to member of T" (where T is a completely-defined
+  //   class type) [...]
+  QualType RType = rex->getType();
+  const MemberPointerType *MemPtr = RType->getAsMemberPointerType();
+  if (!MemPtr || MemPtr->getClass()->isIncompleteType()) {
+    Diag(Loc, diag::err_bad_memptr_rhs)
+      << OpSpelling << RType << rex->getSourceRange();
+    return QualType();
+  }
+  QualType Class(MemPtr->getClass(), 0);
+
+  // C++ 5.5p2
+  //   [...] to its first operand, which shall be of class T or of a class of
+  //   which T is an unambiguous and accessible base class. [p3: a pointer to
+  //   such a class]
+  QualType LType = lex->getType();
+  if (isIndirect) {
+    if (const PointerType *Ptr = LType->getAsPointerType())
+      LType = Ptr->getPointeeType().getNonReferenceType();
+    else {
+      Diag(Loc, diag::err_bad_memptr_lhs)
+        << 1 << LType << lex->getSourceRange();
+      return QualType();
+    }
+  }
+
+  if (Context.getCanonicalType(Class).getUnqualifiedType() !=
+      Context.getCanonicalType(LType).getUnqualifiedType()) {
+    BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
+                    /*DetectVirtual=*/false);
+    // FIXME: Would it be useful to print full ambiguity paths,
+    // or is that overkill?
+    if (!IsDerivedFrom(LType, Class, Paths) ||
+        Paths.isAmbiguous(Context.getCanonicalType(Class))) {
+      Diag(Loc, diag::err_bad_memptr_lhs)
+        << (int)isIndirect << lex->getType() << lex->getSourceRange();
+      return QualType();
+    }
+  }
+
+  // C++ 5.5p2
+  //   The result is an object or a function of the type specified by the
+  //   second operand.
+  // The cv qualifiers are the union of those in the pointer and the left side,
+  // in accordance with 5.5p5 and 5.2.5.
+  // FIXME: This returns a dereferenced member function pointer as a normal
+  // function type. However, the only operation valid on such functions is
+  // calling them. There's also a GCC extension to get a function pointer to
+  // the thing, which is another complication, because this type - unlike the
+  // type that is the result of this expression - takes the class as the first
+  // argument.
+  // We probably need a "MemberFunctionClosureType" or something like that.
+  QualType Result = MemPtr->getPointeeType();
+  if (LType.isConstQualified())
+    Result.addConst();
+  if (LType.isVolatileQualified())
+    Result.addVolatile();
+  return Result;
+}
 
 inline QualType Sema::CheckMultiplyDivideOperands(
   Expr *&lex, Expr *&rex, SourceLocation Loc, bool isCompAssign) 
@@ -3535,6 +3602,8 @@ static inline BinaryOperator::Opcode ConvertTokenKindToBinaryOpcode(
   BinaryOperator::Opcode Opc;
   switch (Kind) {
   default: assert(0 && "Unknown binop!");
+  case tok::periodstar:           Opc = BinaryOperator::PtrMemD; break;
+  case tok::arrowstar:            Opc = BinaryOperator::PtrMemI; break;
   case tok::star:                 Opc = BinaryOperator::Mul; break;
   case tok::slash:                Opc = BinaryOperator::Div; break;
   case tok::percent:              Opc = BinaryOperator::Rem; break;
@@ -3605,7 +3674,12 @@ Action::OwningExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
   case BinaryOperator::Assign:
     ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, QualType());
     break;
-  case BinaryOperator::Mul: 
+  case BinaryOperator::PtrMemD:
+  case BinaryOperator::PtrMemI:
+    ResultTy = CheckPointerToMemberOperands(lhs, rhs, OpLoc,
+                                            Opc == BinaryOperator::PtrMemI);
+    break;
+  case BinaryOperator::Mul:
   case BinaryOperator::Div:
     ResultTy = CheckMultiplyDivideOperands(lhs, rhs, OpLoc);
     break;
@@ -3618,7 +3692,7 @@ Action::OwningExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
   case BinaryOperator::Sub:
     ResultTy = CheckSubtractionOperands(lhs, rhs, OpLoc);
     break;
-  case BinaryOperator::Shl: 
+  case BinaryOperator::Shl:
   case BinaryOperator::Shr:
     ResultTy = CheckShiftOperands(lhs, rhs, OpLoc);
     break;
@@ -3707,11 +3781,11 @@ Action::OwningExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
                                               Context.DependentTy,
                                               Context.DependentTy, TokLoc));
     else
-      return Owned(new (Context) BinaryOperator(lhs, rhs, Opc, Context.DependentTy,
-                                                TokLoc));
+      return Owned(new (Context) BinaryOperator(lhs, rhs, Opc,
+                                                Context.DependentTy, TokLoc));
   }
 
-  if (getLangOptions().CPlusPlus &&
+  if (getLangOptions().CPlusPlus && Opc != BinaryOperator::PtrMemD &&
       (lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() ||
        rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) {
     // If this is one of the assignment operators, we only perform
@@ -3724,6 +3798,8 @@ Action::OwningExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
 
     // Determine which overloaded operator we're dealing with.
     static const OverloadedOperatorKind OverOps[] = {
+      // Overloading .* is not possible.
+      static_cast<OverloadedOperatorKind>(0), OO_ArrowStar,
       OO_Star, OO_Slash, OO_Percent,
       OO_Plus, OO_Minus,
       OO_LessLess, OO_GreaterGreater,
index eb169a2982bb366c87aeb9c756f306a096447993..311c136a0fff9f4f04544dbe05bcae15345a87a4 100644 (file)
@@ -16,6 +16,7 @@
 #ifndef LLVM_CLANG_SEMA_INHERIT_H
 #define LLVM_CLANG_SEMA_INHERIT_H
 
+#include "Sema.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/Type.h"
@@ -25,7 +26,6 @@
 #include <map>
 
 namespace clang {
-  class Sema;
   class CXXBaseSpecifier;
   class CXXRecordType;
 
index 78b26264162f2715057f8ba3948f4c73e02a25bd..ee4c14d0ca49d509e96fb7b8c73499024ea96111 100644 (file)
@@ -610,8 +610,8 @@ Sema::CppLookupName(Scope *S, DeclarationName Name,
         // using-directives later.
         for (OutOfLineCtx = Ctx; OutOfLineCtx && !OutOfLineCtx->isFileContext();
              OutOfLineCtx = OutOfLineCtx->getParent()) {
-          if (R = LookupQualifiedName(OutOfLineCtx, Name, NameKind,
-                                      RedeclarationOnly))
+          if ((R = LookupQualifiedName(OutOfLineCtx, Name, NameKind,
+                                      RedeclarationOnly)))
             return std::make_pair(true, R);
         }
       }
@@ -638,7 +638,7 @@ Sema::CppLookupName(Scope *S, DeclarationName Name,
   // context as well as walking through the scopes.
 
   LookupResultsTy LookupResults;
-  assert(!OutOfLineCtx || OutOfLineCtx->isFileContext() &&
+  assert((!OutOfLineCtx || OutOfLineCtx->isFileContext()) &&
          "We should have been looking only at file context here already.");
   bool LookedInCtx = false;
   LookupResult Result;
index 64cfc68c0126e21cdcd4a9265eca1cf9a35c4eac..7d71b49fca9175815a84bf41273bea6f29bc3908 100644 (file)
@@ -79,3 +79,28 @@ void g() {
 
   void (HasMembers::*pmd)() = &HasMembers::d;
 }
+
+void h() {
+  HasMembers hm, *phm = &hm;
+
+  int HasMembers::*pi = &HasMembers::i;
+  hm.*pi = 0;
+  int i = phm->*pi;
+  (void)&(hm.*pi);
+  (void)&(phm->*pi);
+  (void)&((&hm)->*pi); // expected-error {{address expression must be an lvalue or a function designator}}
+
+  void (HasMembers::*pf)() = &HasMembers::f;
+  (hm.*pf)();
+  (phm->*pf)();
+}
+
+struct OverloadsPtrMem
+{
+  int operator ->*(const char *);
+};
+
+void i() {
+  OverloadsPtrMem m;
+  int foo = m->*"Awesome!";
+}
index 432e9643af0357a6b66b65d3aa16f9bf30199c50..71bbada5085af9afa3b68e09a14623c61ff81fa4 100644 (file)
@@ -698,11 +698,11 @@ welcome!</p>
 </tr>\r
 <tr>\r
   <td>&nbsp;&nbsp;5.5 [expr.mptr.oper]</td>\r
-  <td class="broken"></td>\r
-  <td class="broken"></td>\r
-  <td class="broken"></td>\r
-  <td></td>\r
+  <td class="complete" align="center">&#x2713;</td>\r
+  <td class="advanced"></td>\r
+  <td class="advanced"></td>\r
   <td></td>\r
+  <td>Dereferenced member function pointers have the wrong type.</td>\r
 </tr>\r
 <tr>\r
   <td>&nbsp;&nbsp;5.6 [expr.mul]</td>\r