[cfe-commits] r144369 - in /cfe/trunk: lib/AST/ExprConstant.cpp test/SemaCXX/constant-expression-cxx11.cpp test/SemaCXX/constexpr-ackermann.cpp test/SemaCXX/constexpr-factorial.cpp test/SemaCXX/constexpr-nqueens.cpp

Richard Smith richard-llvm at metafoo.co.uk
Thu Nov 10 20:05:33 PST 2011


Author: rsmith
Date: Thu Nov 10 22:05:33 2011
New Revision: 144369

URL: http://llvm.org/viewvc/llvm-project?rev=144369&view=rev
Log:
Constant expression evaluation: support for constexpr member functions. This
reinstates r144273; a combination of r144333's fix for NoOp rvalue-to-lvalue
casts and some corresponding changes here resolve the regression which that
caused.

This patch also adds support for some additional forms of member function call,
along with additional testing.

Added:
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
      - copied, changed from r144295, cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
    cfe/trunk/test/SemaCXX/constexpr-ackermann.cpp
    cfe/trunk/test/SemaCXX/constexpr-factorial.cpp
    cfe/trunk/test/SemaCXX/constexpr-nqueens.cpp
Modified:
    cfe/trunk/lib/AST/ExprConstant.cpp

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=144369&r1=144368&r2=144369&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Thu Nov 10 22:05:33 2011
@@ -810,6 +810,21 @@
   return Quals.hasConst() && !Quals.hasVolatile();
 }
 
+/// Get the base index of the given base class within an APValue representing
+/// the given derived class.
+static unsigned getBaseIndex(const CXXRecordDecl *Derived,
+                             const CXXRecordDecl *Base) {
+  Base = Base->getCanonicalDecl();
+  unsigned Index = 0;
+  for (CXXRecordDecl::base_class_const_iterator I = Derived->bases_begin(),
+         E = Derived->bases_end(); I != E; ++I, ++Index) {
+    if (I->getType()->getAsCXXRecordDecl()->getCanonicalDecl() == Base)
+      return Index;
+  }
+
+  llvm_unreachable("base class missing from derived class's bases list");
+}
+
 /// Extract the designated sub-object of an rvalue.
 static bool ExtractSubobject(EvalInfo &Info, CCValue &Obj, QualType ObjType,
                              const SubobjectDesignator &Sub, QualType SubType) {
@@ -852,22 +867,10 @@
       ObjType = Field->getType();
     } else {
       // Next subobject is a base class.
-      const CXXRecordDecl *RD =
-        cast<CXXRecordDecl>(ObjType->castAs<RecordType>()->getDecl());
-      const CXXRecordDecl *Base =
-        getAsBaseClass(Sub.Entries[I])->getCanonicalDecl();
-      unsigned Index = 0;
-      for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
-             E = RD->bases_end(); I != E; ++I, ++Index) {
-        QualType BT = I->getType();
-        if (BT->castAs<RecordType>()->getDecl()->getCanonicalDecl() == Base) {
-          O = &O->getStructBase(Index);
-          ObjType = BT;
-          break;
-        }
-      }
-      if (Index == RD->getNumBases())
-        return false;
+      const CXXRecordDecl *Derived = ObjType->getAsCXXRecordDecl();
+      const CXXRecordDecl *Base = getAsBaseClass(Sub.Entries[I]);
+      O = &O->getStructBase(getBaseIndex(Derived, Base));
+      ObjType = Info.Ctx.getRecordType(Base);
     }
 
     if (O->isUninit())
@@ -974,6 +977,21 @@
   return ExtractSubobject(Info, RVal, Base->getType(), LVal.Designator, Type);
 }
 
+/// Build an lvalue for the object argument of a member function call.
+static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
+                                   LValue &This) {
+  if (Object->getType()->isPointerType())
+    return EvaluatePointer(Object, This, Info);
+
+  if (Object->isGLValue())
+    return EvaluateLValue(Object, This, Info);
+
+  // Implicitly promote a prvalue *this object to a glvalue.
+  This.setExpr(Object, Info.CurrentCall);
+  return EvaluateConstantExpression(Info.CurrentCall->Temporaries[Object], Info,
+                                    This, Object);
+}
+
 namespace {
 enum EvalStmtResult {
   /// Evaluation failed.
@@ -1029,8 +1047,9 @@
 }
 
 /// Evaluate a function call.
-static bool HandleFunctionCall(ArrayRef<const Expr*> Args, const Stmt *Body,
-                               EvalInfo &Info, CCValue &Result) {
+static bool HandleFunctionCall(const LValue *This, ArrayRef<const Expr*> Args,
+                               const Stmt *Body, EvalInfo &Info,
+                               CCValue &Result) {
   // FIXME: Implement a proper call limit, along with a command-line flag.
   if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512)
     return false;
@@ -1039,16 +1058,15 @@
   if (!EvaluateArgs(Args, ArgValues, Info))
     return false;
 
-  // FIXME: Pass in 'this' for member functions.
-  const LValue *This = 0;
   CallStackFrame Frame(Info, This, ArgValues.data());
   return EvaluateStmt(Result, Info, Body) == ESR_Returned;
 }
 
 /// Evaluate a constructor call.
-static bool HandleConstructorCall(ArrayRef<const Expr*> Args,
+static bool HandleConstructorCall(const LValue &This,
+                                  ArrayRef<const Expr*> Args,
                                   const CXXConstructorDecl *Definition,
-                                  EvalInfo &Info, const LValue &This,
+                                  EvalInfo &Info,
                                   APValue &Result) {
   if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512)
     return false;
@@ -1305,39 +1323,61 @@
     const Expr *Callee = E->getCallee();
     QualType CalleeType = Callee->getType();
 
-    // FIXME: Handle the case where Callee is a (parenthesized) MemberExpr for a
-    // non-static member function.
-    if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember))
-      return DerivedError(E);
-
-    if (!CalleeType->isFunctionType() && !CalleeType->isFunctionPointerType())
-      return DerivedError(E);
-
-    CCValue Call;
-    if (!Evaluate(Call, Info, Callee) || !Call.isLValue() ||
-        !Call.getLValueBase() || !Call.getLValueOffset().isZero())
-      return DerivedError(Callee);
-
     const FunctionDecl *FD = 0;
-    if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Call.getLValueBase()))
-      FD = dyn_cast<FunctionDecl>(DRE->getDecl());
-    else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Call.getLValueBase()))
+    LValue *This = 0, ThisVal;
+    llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
+
+    // Extract function decl and 'this' pointer from the callee.
+    if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
+      // Explicit bound member calls, such as x.f() or p->g();
+      // FIXME: Handle a BinaryOperator callee ('.*' or '->*').
+      const MemberExpr *ME = dyn_cast<MemberExpr>(Callee->IgnoreParens());
+      if (!ME)
+        return DerivedError(Callee);
+      if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
+        return DerivedError(ME->getBase());
+      This = &ThisVal;
       FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
-    if (!FD)
-      return DerivedError(Callee);
+      if (!FD)
+        return DerivedError(ME);
+    } else if (CalleeType->isFunctionPointerType()) {
+      CCValue Call;
+      if (!Evaluate(Call, Info, Callee) || !Call.isLValue() ||
+          !Call.getLValueBase() || !Call.getLValueOffset().isZero())
+        return DerivedError(Callee);
+
+      const Expr *Base = Call.getLValueBase();
+
+      if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base))
+        FD = dyn_cast<FunctionDecl>(DRE->getDecl());
+      else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base))
+        FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
+      if (!FD)
+        return DerivedError(Callee);
+
+      // Overloaded operator calls to member functions are represented as normal
+      // calls with '*this' as the first argument.
+      const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
+      if (MD && !MD->isStatic()) {
+        if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
+          return false;
+        This = &ThisVal;
+        Args = Args.slice(1);
+      }
 
-    // Don't call function pointers which have been cast to some other type.
-    if (!Info.Ctx.hasSameType(CalleeType->getPointeeType(), FD->getType()))
+      // Don't call function pointers which have been cast to some other type.
+      if (!Info.Ctx.hasSameType(CalleeType->getPointeeType(), FD->getType()))
+        return DerivedError(E);
+    } else
       return DerivedError(E);
 
     const FunctionDecl *Definition;
     Stmt *Body = FD->getBody(Definition);
     CCValue CCResult;
     APValue Result;
-    llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
 
     if (Body && Definition->isConstexpr() && !Definition->isInvalidDecl() &&
-        HandleFunctionCall(Args, Body, Info, CCResult) &&
+        HandleFunctionCall(This, Args, Body, Info, CCResult) &&
         CheckConstantExpression(CCResult, Result))
       return DerivedSuccess(CCValue(Result, CCValue::GlobalValue()), E);
 
@@ -1821,11 +1861,43 @@
     }
     bool Error(const Expr *E) { return false; }
 
+    bool VisitCastExpr(const CastExpr *E);
     bool VisitInitListExpr(const InitListExpr *E);
     bool VisitCXXConstructExpr(const CXXConstructExpr *E);
   };
 }
 
+bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) {
+  switch (E->getCastKind()) {
+  default:
+    return ExprEvaluatorBaseTy::VisitCastExpr(E);
+
+  case CK_ConstructorConversion:
+    return Visit(E->getSubExpr());
+
+  case CK_DerivedToBase:
+  case CK_UncheckedDerivedToBase: {
+    CCValue DerivedObject;
+    if (!Evaluate(DerivedObject, Info, E->getSubExpr()) ||
+        !DerivedObject.isStruct())
+      return false;
+
+    // Derived-to-base rvalue conversion: just slice off the derived part.
+    APValue *Value = &DerivedObject;
+    const CXXRecordDecl *RD = E->getSubExpr()->getType()->getAsCXXRecordDecl();
+    for (CastExpr::path_const_iterator PathI = E->path_begin(),
+         PathE = E->path_end(); PathI != PathE; ++PathI) {
+      assert(!(*PathI)->isVirtual() && "record rvalue with virtual base");
+      const CXXRecordDecl *Base = (*PathI)->getType()->getAsCXXRecordDecl();
+      Value = &Value->getStructBase(getBaseIndex(RD, Base));
+      RD = Base;
+    }
+    Result = *Value;
+    return true;
+  }
+  }
+}
+
 bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
   const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
   const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
@@ -1890,8 +1962,8 @@
       return Visit(ME->GetTemporaryExpr());
 
   llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
-  return HandleConstructorCall(Args, cast<CXXConstructorDecl>(Definition),
-                               Info, This, Result);
+  return HandleConstructorCall(This, Args, cast<CXXConstructorDecl>(Definition),
+                               Info, Result);
 }
 
 static bool EvaluateRecord(const Expr *E, const LValue &This,

Copied: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (from r144295, cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?p2=cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp&p1=cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp&r1=144295&r2=144369&rev=144369&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Thu Nov 10 22:05:33 2011
@@ -629,14 +629,43 @@
   friend constexpr bool operator==(const complex &l, const complex &r) {
     return l.re == r.re && l.im == r.im;
   }
+  constexpr bool operator!=(const complex &r) const {
+    return re != r.re || im != r.im;
+  }
   constexpr int real() const { return re; }
   constexpr int imag() const { return im; }
 };
 
 constexpr complex i = complex(0, 1);
 constexpr complex k = (3 + 4*i) * (6 - 4*i);
+static_assert_fold(complex(1,0).real() == 1, "");
+static_assert_fold(complex(1,0).imag() == 0, "");
+static_assert_fold(((complex)1).imag() == 0, "");
 static_assert_fold(k.real() == 34, "");
 static_assert_fold(k.imag() == 12, "");
 static_assert_fold(k - 34 == 12*i, "");
+static_assert_fold((complex)1 == complex(1), "");
+static_assert_fold((complex)1 != complex(0, 1), "");
+static_assert_fold(complex(1) == complex(1), "");
+static_assert_fold(complex(1) != complex(0, 1), "");
+constexpr complex makeComplex(int re, int im) { return complex(re, im); }
+static_assert_fold(makeComplex(1,0) == complex(1), "");
+static_assert_fold(makeComplex(1,0) != complex(0, 1), "");
+
+class complex_wrap : public complex {
+public:
+  constexpr complex_wrap(int re, int im = 0) : complex(re, im) {}
+  constexpr complex_wrap(const complex_wrap &o) : complex(o) {}
+};
+
+static_assert_fold((complex_wrap)1 == complex(1), "");
+static_assert_fold((complex)1 != complex_wrap(0, 1), "");
+static_assert_fold(complex(1) == complex_wrap(1), "");
+static_assert_fold(complex_wrap(1) != complex(0, 1), "");
+constexpr complex_wrap makeComplexWrap(int re, int im) {
+  return complex_wrap(re, im);
+}
+static_assert_fold(makeComplexWrap(1,0) == complex(1), "");
+static_assert_fold(makeComplexWrap(1,0) != complex(0, 1), "");
 
 }

Added: cfe/trunk/test/SemaCXX/constexpr-ackermann.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-ackermann.cpp?rev=144369&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-ackermann.cpp (added)
+++ cfe/trunk/test/SemaCXX/constexpr-ackermann.cpp Thu Nov 10 22:05:33 2011
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s
+
+constexpr unsigned long long A(unsigned long long m, unsigned long long n) {
+  return m == 0 ? n + 1 : n == 0 ? A(m-1, 1) : A(m - 1, A(m, n - 1));
+}
+
+using X = int[A(3,4)];
+using X = int[125];

Added: cfe/trunk/test/SemaCXX/constexpr-factorial.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-factorial.cpp?rev=144369&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-factorial.cpp (added)
+++ cfe/trunk/test/SemaCXX/constexpr-factorial.cpp Thu Nov 10 22:05:33 2011
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s
+
+constexpr unsigned oddfac(unsigned n) {
+  return n == 1 ? 1 : n * oddfac(n-2);
+}
+constexpr unsigned k = oddfac(999);
+
+using A = int[k % 256];
+using A = int[73];

Added: cfe/trunk/test/SemaCXX/constexpr-nqueens.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-nqueens.cpp?rev=144369&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-nqueens.cpp (added)
+++ cfe/trunk/test/SemaCXX/constexpr-nqueens.cpp Thu Nov 10 22:05:33 2011
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s
+
+typedef unsigned long uint64_t;
+
+struct Board {
+  uint64_t State;
+  bool Failed;
+
+  constexpr Board() : State(0), Failed(false) {}
+  constexpr Board(const Board &O) : State(O.State), Failed(O.Failed) {}
+  constexpr Board(uint64_t State, bool Failed = false) :
+    State(State), Failed(Failed) {}
+  constexpr Board addQueen(int Row, int Col) {
+    return Board(State | ((uint64_t)Row << (Col * 4)));
+  }
+  constexpr int getQueenRow(int Col) {
+    return (State >> (Col * 4)) & 0xf;
+  }
+  constexpr bool ok(int Row, int Col) {
+    return okRecurse(Row, Col, 0);
+  }
+  constexpr bool okRecurse(int Row, int Col, int CheckCol) {
+    return Col == CheckCol ? true :
+           getQueenRow(CheckCol) == Row ? false :
+           getQueenRow(CheckCol) == Row + (Col - CheckCol) ? false :
+           getQueenRow(CheckCol) == Row + (CheckCol - Col) ? false :
+           okRecurse(Row, Col, CheckCol + 1);
+  }
+  constexpr bool at(int Row, int Col) {
+    return getQueenRow(Col) == Row;
+  }
+  constexpr bool check(const char *, int=0, int=0);
+};
+
+constexpr Board buildBoardRecurse(int N, int Col, const Board &B);
+constexpr Board buildBoardScan(int N, int Col, int Row, const Board &B);
+constexpr Board tryBoard(const Board &Try,
+                         int N, int Col, int Row, const Board &B) {
+  return Try.Failed ? buildBoardScan(N, Col, Row, B) : Try;
+}
+constexpr Board buildBoardScan(int N, int Col, int Row, const Board &B) {
+  return Row == N ? Board(0, true) :
+         B.ok(Row, Col) ?
+         tryBoard(buildBoardRecurse(N, Col + 1, B.addQueen(Row, Col)),
+                  N, Col, Row+1, B) :
+         buildBoardScan(N, Col, Row + 1, B);
+}
+constexpr Board buildBoardRecurse(int N, int Col, const Board &B) {
+  return Col == N ? B : buildBoardScan(N, Col, 0, B);
+}
+constexpr Board buildBoard(int N) {
+  return buildBoardRecurse(N, 0, Board());
+}
+
+constexpr Board q8 = buildBoard(8);
+
+constexpr bool Board::check(const char *p, int Row, int Col) {
+  return
+    *p == '\n' ? check(p+1, Row+1, 0) :
+    *p == 'o' ? at(Row, Col) && check(p+1, Row, Col+1) :
+    *p == '-' ? !at(Row, Col) && check(p+1, Row, Col+1) :
+    *p == 0 ? true :
+    false;
+}
+constexpr bool check = q8.check(
+    "o-------\n"
+    "------o-\n"
+    "----o---\n"
+    "-------o\n"
+    "-o------\n"
+    "---o----\n"
+    "-----o--\n"
+    "--o-----\n");
+
+typedef int check_it[1];
+typedef int check_it[check];





More information about the cfe-commits mailing list