[cfe-commits] r143234 - in /cfe/trunk: include/clang/AST/Expr.h lib/AST/ExprConstant.cpp test/SemaCXX/constant-expression-cxx11.cpp

Richard Smith richard-llvm at metafoo.co.uk
Fri Oct 28 15:34:42 PDT 2011


Author: rsmith
Date: Fri Oct 28 17:34:42 2011
New Revision: 143234

URL: http://llvm.org/viewvc/llvm-project?rev=143234&view=rev
Log:
Initial support for C++11 constexpr function invocation substitution. Using
constexpr function arguments outside of their function (passing or returning
them by reference) does not work correctly yet.

Calling constexpr function templates does not work yet, since the bodies are not
instantiated until the end of the translation unit.

Modified:
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=143234&r1=143233&r2=143234&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Fri Oct 28 17:34:42 2011
@@ -1959,6 +1959,9 @@
   Expr **getArgs() {
     return reinterpret_cast<Expr **>(SubExprs+getNumPreArgs()+PREARGS_START);
   }
+  const Expr *const *getArgs() const {
+    return const_cast<CallExpr*>(this)->getArgs();
+  }
   
   /// getArg - Return the specified argument.
   Expr *getArg(unsigned Arg) {

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=143234&r1=143233&r2=143234&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Fri Oct 28 17:34:42 2011
@@ -43,12 +43,23 @@
 /// evaluate the expression regardless of what the RHS is, but C only allows
 /// certain things in certain situations.
 namespace {
+  struct CallStackFrame;
+
   struct EvalInfo {
     const ASTContext &Ctx;
 
     /// EvalStatus - Contains information about the evaluation.
     Expr::EvalStatus &EvalStatus;
 
+    /// CurrentCall - The top of the constexpr call stack.
+    const CallStackFrame *CurrentCall;
+
+    /// NumCalls - The number of calls we've evaluated so far.
+    unsigned NumCalls;
+
+    /// CallStackDepth - The number of calls in the call stack right now.
+    unsigned CallStackDepth;
+
     typedef llvm::DenseMap<const OpaqueValueExpr*, APValue> MapTy;
     MapTy OpaqueValues;
     const APValue *getOpaqueValue(const OpaqueValueExpr *e) const {
@@ -58,11 +69,40 @@
     }
 
     EvalInfo(const ASTContext &C, Expr::EvalStatus &S)
-      : Ctx(C), EvalStatus(S) {}
+      : Ctx(C), EvalStatus(S), CurrentCall(0), NumCalls(0), CallStackDepth(0) {}
 
     const LangOptions &getLangOpts() { return Ctx.getLangOptions(); }
   };
 
+  /// A stack frame in the constexpr call stack.
+  struct CallStackFrame {
+    EvalInfo &Info;
+
+    /// Parent - The caller of this stack frame.
+    const CallStackFrame *Caller;
+
+    /// ParmBindings - Parameter bindings for this function call, indexed by
+    /// parameters' function scope indices.
+    const APValue *Arguments;
+
+    /// CallIndex - The index of the current call. This is used to match lvalues
+    /// referring to parameters up with the corresponding stack frame, and to
+    /// detect when the parameter is no longer in scope.
+    unsigned CallIndex;
+
+    CallStackFrame(EvalInfo &Info, const APValue *Arguments)
+        : Info(Info), Caller(Info.CurrentCall), Arguments(Arguments),
+          CallIndex(Info.NumCalls++) {
+      Info.CurrentCall = this;
+      ++Info.CallStackDepth;
+    }
+    ~CallStackFrame() {
+      assert(Info.CurrentCall == this && "calls retired out of order");
+      --Info.CallStackDepth;
+      Info.CurrentCall = Caller;
+    }
+  };
+
   struct ComplexValue {
   private:
     bool IsInt;
@@ -269,11 +309,17 @@
 }
 
 /// Try to evaluate the initializer for a variable declaration.
-static APValue *EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD) {
-  // FIXME: If this is a parameter to an active constexpr function call, perform
-  // substitution now.
-  if (isa<ParmVarDecl>(VD))
+static const APValue *EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD) {
+  // If this is a parameter to an active constexpr function call, perform
+  // argument substitution.
+  if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
+    // FIXME This assumes that all parameters must be parameters of the current
+    // call. Add the CallIndex to the LValue representation and use that to
+    // check.
+    if (Info.CurrentCall)
+      return &Info.CurrentCall->Arguments[PVD->getFunctionScopeIndex()];
     return 0;
+  }
 
   const Expr *Init = VD->getAnyInitializer();
   if (!Init)
@@ -341,22 +387,28 @@
   if (D) {
     // In C++98, const, non-volatile integers initialized with ICEs are ICEs.
     // In C++11, constexpr, non-volatile variables initialized with constant
-    // expressions are constant expressions too.
+    // expressions are constant expressions too. Inside constexpr functions,
+    // parameters are constant expressions even if they're non-const.
     // In C, such things can also be folded, although they are not ICEs.
     //
     // FIXME: Allow folding any const variable of literal type initialized with
     // a constant expression. For now, we only allow variables with integral and
     // floating types to be folded.
+    // FIXME: volatile-qualified ParmVarDecls need special handling. A literal
+    // interpretation of C++11 suggests that volatile parameters are OK if
+    // they're never read (there's no prohibition against constructing volatile
+    // objects in constant expressions), but lvalue-to-rvalue conversions on
+    // them are not permitted.
     const VarDecl *VD = dyn_cast<VarDecl>(D);
-    if (!VD || !IsConstNonVolatile(VD->getType()) ||
-        (!Type->isIntegralOrEnumerationType() && !Type->isRealFloatingType()))
+    if (!VD || !(IsConstNonVolatile(VD->getType()) || isa<ParmVarDecl>(VD)) ||
+        !(Type->isIntegralOrEnumerationType() || Type->isRealFloatingType()))
       return false;
 
-    APValue *V = EvaluateVarDeclInit(Info, VD);
+    const APValue *V = EvaluateVarDeclInit(Info, VD);
     if (!V || V->isUninit())
       return false;
 
-    if (!VD->getAnyInitializer()->isLValue()) {
+    if (isa<ParmVarDecl>(VD) || !VD->getAnyInitializer()->isLValue()) {
       RVal = *V;
       return true;
     }
@@ -386,6 +438,64 @@
 }
 
 namespace {
+enum EvalStmtResult {
+  /// Evaluation failed.
+  ESR_Failed,
+  /// Hit a 'return' statement.
+  ESR_Returned,
+  /// Evaluation succeeded.
+  ESR_Succeeded
+};
+}
+
+// Evaluate a statement.
+static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
+                                   const Stmt *S) {
+  switch (S->getStmtClass()) {
+  default:
+    return ESR_Failed;
+
+  case Stmt::NullStmtClass:
+  case Stmt::DeclStmtClass:
+    return ESR_Succeeded;
+
+  case Stmt::ReturnStmtClass:
+    if (Evaluate(Result, Info, cast<ReturnStmt>(S)->getRetValue()))
+      return ESR_Returned;
+    return ESR_Failed;
+
+  case Stmt::CompoundStmtClass: {
+    const CompoundStmt *CS = cast<CompoundStmt>(S);
+    for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
+           BE = CS->body_end(); BI != BE; ++BI) {
+      EvalStmtResult ESR = EvaluateStmt(Result, Info, *BI);
+      if (ESR != ESR_Succeeded)
+        return ESR;
+    }
+    return ESR_Succeeded;
+  }
+  }
+}
+
+/// Evaluate a function call.
+static bool HandleFunctionCall(ArrayRef<const Expr*> Args, const Stmt *Body,
+                               EvalInfo &Info, APValue &Result) {
+  // FIXME: Implement a proper call limit, along with a command-line flag.
+  if (Info.NumCalls >= 1000000 || Info.CallStackDepth >= 512)
+    return false;
+
+  SmallVector<APValue, 16> ArgValues(Args.size());
+  // FIXME: Deal with default arguments and 'this'.
+  for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
+       I != E; ++I)
+    if (!Evaluate(ArgValues[I - Args.begin()], Info, *I))
+      return false;
+
+  CallStackFrame Frame(Info, ArgValues.data());
+  return EvaluateStmt(Result, Info, Body) == ESR_Returned;
+}
+
+namespace {
 class HasSideEffect
   : public ConstStmtVisitor<HasSideEffect, bool> {
   const ASTContext &Ctx;
@@ -568,6 +678,47 @@
     return DerivedSuccess(*value, E);
   }
 
+  RetTy VisitCallExpr(const CallExpr *E) {
+    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);
+
+    APValue 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()))
+      FD = dyn_cast<FunctionDecl>(ME->getMemberDecl());
+    if (!FD)
+      return DerivedError(Callee);
+
+    // Don't call function pointers which have been cast to some other type.
+    if (!Info.Ctx.hasSameType(CalleeType->getPointeeType(), FD->getType()))
+      return DerivedError(E);
+
+    const FunctionDecl *Definition;
+    Stmt *Body = FD->getBody(Definition);
+    APValue Result;
+    llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
+
+    if (Body && Definition->isConstexpr() && !Definition->isInvalidDecl() &&
+        HandleFunctionCall(Args, Body, Info, Result))
+      return DerivedSuccess(Result, E);
+
+    return DerivedError(E);
+  }
+
   RetTy VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
     return StmtVisitorTy::Visit(E->getInitializer());
   }
@@ -716,7 +867,7 @@
   if (!VD->getType()->isReferenceType())
     return Success(E);
 
-  APValue *V = EvaluateVarDeclInit(Info, VD);
+  const APValue *V = EvaluateVarDeclInit(Info, VD);
   if (V && !V->isUninit())
     return Success(*V, E);
 
@@ -738,6 +889,14 @@
     return VisitVarDecl(E, VD);
   }
 
+  // Handle static member functions.
+  if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(E->getMemberDecl())) {
+    if (MD->isStatic()) {
+      VisitIgnoredValue(E->getBase());
+      return Success(E);
+    }
+  }
+
   QualType Ty;
   if (E->isArrow()) {
     if (!EvaluatePointer(E->getBase(), Result, Info))

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=143234&r1=143233&r2=143234&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Fri Oct 28 17:34:42 2011
@@ -1,6 +1,17 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
 
-template<typename T> constexpr T id(const T &t) { return t; }
+// FIXME: support const T& parameters here.
+//template<typename T> constexpr T id(const T &t) { return t; }
+template<typename T> constexpr T id(T t) { return t; }
+// FIXME: support templates here.
+//template<typename T> constexpr T min(const T &a, const T &b) {
+//  return a < b ? a : b;
+//}
+//template<typename T> constexpr T max(const T &a, const T &b) {
+//  return a < b ? b : a;
+//}
+constexpr int min(int a, int b) { return a < b ? a : b; }
+constexpr int max(int a, int b) { return a < b ? b : a; }
 
 struct MemberZero {
   constexpr int zero() { return 0; }
@@ -40,3 +51,43 @@
   constexpr bool b = wme.A == 42;
   int n[b];
 }
+
+namespace Recursion {
+  constexpr int fib(int n) { return n > 1 ? fib(n-1) + fib(n-2) : n; }
+  // FIXME: this isn't an ICE yet.
+  using check_fib = int[fib(11)];
+  using check_fib = int[89];
+
+  constexpr int gcd_inner(int a, int b) {
+    return b == 0 ? a : gcd_inner(b, a % b);
+  }
+  constexpr int gcd(int a, int b) {
+    return gcd_inner(max(a, b), min(a, b));
+  }
+
+  // FIXME: this isn't an ICE yet.
+  using check_gcd = int[gcd(1749237, 5628959)];
+  using check_gcd = int[7];
+}
+
+namespace FunctionCast {
+  // When folding, we allow functions to be cast to different types. Such
+  // cast functions cannot be called, even if they're constexpr.
+  constexpr int f() { return 1; }
+  typedef double (*DoubleFn)();
+  typedef int (*IntFn)();
+  int a[(int)DoubleFn(f)()]; // expected-error {{variable length array}}
+  int b[(int)IntFn(f)()];    // ok
+}
+
+namespace StaticMemberFunction {
+  struct S {
+    static constexpr int k = 42;
+    static constexpr int f(int n) { return n * k + 2; }
+  } s;
+  // FIXME: this isn't an ICE yet.
+  using check_static_call = int[S::f(19)];
+  constexpr int n = s.f(19);
+  using check_static_call = int[s.f(19)];
+  using check_static_call = int[800];
+}





More information about the cfe-commits mailing list