diff options
-rw-r--r-- | include/clang/AST/Expr.h | 3 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 181 | ||||
-rw-r--r-- | test/SemaCXX/constant-expression-cxx11.cpp | 53 |
3 files changed, 225 insertions, 12 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index ad686187c4..b484a42b04 100644 --- a/include/clang/AST/Expr.h +++ b/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/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index d59d79ec69..89bcacf2bb 100644 --- a/lib/AST/ExprConstant.cpp +++ b/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/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index fdab283893..4ecc53c61b 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/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]; +} |