Commit a3d3bd21 authored by Richard Smith's avatar Richard Smith
Browse files

C++1y: Add a step limit to constexpr evaluation, to catch runaway loops.

llvm-svn: 181388
parent 1aee70b7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -83,6 +83,8 @@ def note_constexpr_depth_limit_exceeded : Note<
  "constexpr evaluation exceeded maximum depth of %0 calls">;
def note_constexpr_call_limit_exceeded : Note<
  "constexpr evaluation hit maximum call limit">;
def note_constexpr_step_limit_exceeded : Note<
  "constexpr evaluation hit maximum step limit; possible infinite loop?">;
def note_constexpr_lifetime_ended : Note<
  "%select{read of|assignment to|increment of|decrement of}0 "
  "%select{temporary|variable}1 whose lifetime has ended">;
+2 −0
Original line number Diff line number Diff line
@@ -160,6 +160,8 @@ BENIGN_LANGOPT(InstantiationDepth, 32, 256,
               "maximum template instantiation depth")
BENIGN_LANGOPT(ConstexprCallDepth, 32, 512,
               "maximum constexpr call depth")
BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576,
               "maximum constexpr evaluation steps")
BENIGN_LANGOPT(BracketDepth, 32, 256,
               "maximum bracket nesting depth")
BENIGN_LANGOPT(NumLargeByValueCopy, 32, 0, 
+2 −0
Original line number Diff line number Diff line
@@ -434,6 +434,8 @@ def ftemplate_depth : Separate<["-"], "ftemplate-depth">,
  HelpText<"Maximum depth of recursive template instantiation">;
def fconstexpr_depth : Separate<["-"], "fconstexpr-depth">,
  HelpText<"Maximum depth of recursive constexpr function calls">;
def fconstexpr_steps : Separate<["-"], "fconstexpr-steps">,
  HelpText<"Maximum number of steps in constexpr function evaluation">;
def fbracket_depth : Separate<["-"], "fbracket-depth">,
  HelpText<"Maximum nesting level for parentheses, brackets, and braces">;
def fconst_strings : Flag<["-"], "fconst-strings">,
+1 −0
Original line number Diff line number Diff line
@@ -344,6 +344,7 @@ def fcompile_resource_EQ : Joined<["-"], "fcompile-resource=">, Group<f_Group>;
def fconstant_cfstrings : Flag<["-"], "fconstant-cfstrings">, Group<f_Group>;
def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group<f_Group>;
def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group<f_Group>;
def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group<f_Group>;
def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">,
                                    Group<f_Group>;
def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group<f_clang_Group>, Flags<[NoArgumentUnused]>;
+22 −4
Original line number Diff line number Diff line
@@ -380,6 +380,11 @@ namespace {
    /// NextCallIndex - The next call index to assign.
    unsigned NextCallIndex;

    /// StepsLeft - The remaining number of evaluation steps we're permitted
    /// to perform. This is essentially a limit for the number of statements
    /// we will evaluate.
    unsigned StepsLeft;

    /// BottomFrame - The frame in which evaluation started. This must be
    /// initialized after CurrentCall and CallStackDepth.
    CallStackFrame BottomFrame;
@@ -407,6 +412,7 @@ namespace {
             bool OverflowCheckMode = false)
      : Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
        CallStackDepth(0), NextCallIndex(1),
        StepsLeft(getLangOpts().ConstexprStepLimit),
        BottomFrame(*this, SourceLocation(), 0, 0, 0),
        EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false),
        CheckingPotentialConstantExpression(false),
@@ -446,6 +452,15 @@ namespace {
      return (Frame->Index == CallIndex) ? Frame : 0;
    }

    bool nextStep(const Stmt *S) {
      if (!StepsLeft) {
        Diag(S->getLocStart(), diag::note_constexpr_step_limit_exceeded);
        return false;
      }
      --StepsLeft;
      return true;
    }

  private:
    /// Add a diagnostic to the diagnostics list.
    PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) {
@@ -530,9 +545,9 @@ namespace {
    bool keepEvaluatingAfterFailure() {
      // Should return true in IntOverflowCheckMode, so that we check for
      // overflow even if some subexpressions can't be evaluated as constants.
      return IntOverflowCheckMode ||
      return StepsLeft && (IntOverflowCheckMode ||
                           (CheckingPotentialConstantExpression &&
              EvalStatus.Diag && EvalStatus.Diag->empty());
                            EvalStatus.Diag && EvalStatus.Diag->empty()));
    }
  };

@@ -2794,6 +2809,9 @@ static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info,
// Evaluate a statement.
static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
                                   const Stmt *S) {
  if (!Info.nextStep(S))
    return ESR_Failed;

  // FIXME: Mark all temporaries in the current frame as destroyed at
  // the end of each full-expression.
  switch (S->getStmtClass()) {
Loading