diff options
| -rw-r--r-- | clang/include/clang/AST/Expr.h | 3 | ||||
| -rw-r--r-- | clang/lib/AST/ExprConstant.cpp | 181 | ||||
| -rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx11.cpp | 53 | 
3 files changed, 225 insertions, 12 deletions
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index ad686187c49..b484a42b042 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1959,6 +1959,9 @@ public:    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) { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d59d79ec69b..89bcacf2bba 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -43,12 +43,23 @@ using llvm::APFloat;  /// 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 @@ namespace {      }      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 @@ static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType,  }  /// 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 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,    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 @@ bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,  }  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 @@ public:      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 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {    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 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {      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)) diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index fdab2838937..4ecc53c61b8 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -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 @@ namespace MemberEnum {    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]; +}  | 

