mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-22 22:22:11 +02:00
Bug 429891 - Limit the total number of computation steps in a constexpr
evaluation Change-Id: I057d94a78e92d26c9ad824b2ea0c6a4ad896e948 Signed-off-by: Nathan Ridge <zeratul976@hotmail.com> Reviewed-on: https://git.eclipse.org/r/24925 Tested-by: Hudson CI Reviewed-by: Sergey Prigogin <eclipse.sprigogin@gmail.com> Tested-by: Sergey Prigogin <eclipse.sprigogin@gmail.com>
This commit is contained in:
parent
ec10b77e67
commit
d66e5c04bf
18 changed files with 110 additions and 46 deletions
|
@ -8490,4 +8490,22 @@ public class AST2TemplateTests extends AST2TestBase {
|
|||
IVariable waldo = helper.assertNonProblem("waldo");
|
||||
assertEquals(5, waldo.getInitialValue().numericalValue().longValue());
|
||||
}
|
||||
|
||||
|
||||
// constexpr int naive_fibonacci(int x) {
|
||||
// return x == 0 ? 0
|
||||
// : x == 1 ? 1
|
||||
// : naive_fibonacci(x - 2) + naive_fibonacci(x - 1);
|
||||
// }
|
||||
//
|
||||
// constexpr int waldo = naive_fibonacci(50);
|
||||
public void testConstexprEvaluationLimit_429891() throws Exception {
|
||||
// Here we're just checking that the computation of the initial
|
||||
// value finishes (with a null result) in a reasonable time.
|
||||
// If we tried to run the computation of naive_fibonacci(50)
|
||||
// to its end, the IDE would appear to hang.
|
||||
BindingAssertionHelper helper = getAssertionHelper();
|
||||
IVariable waldo = helper.assertNonProblem("waldo");
|
||||
assertNull(waldo.getInitialValue().numericalValue());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,16 +85,62 @@ public interface ICPPEvaluation extends ISerializableEvaluation {
|
|||
ICPPEvaluation instantiate(ICPPTemplateParameterMap tpMap, int packOffset,
|
||||
ICPPClassSpecialization within, int maxdepth, IASTNode point);
|
||||
|
||||
/**
|
||||
* Keeps track of state during a constexpr evaluation.
|
||||
*/
|
||||
class ConstexprEvaluationContext {
|
||||
/**
|
||||
* The maximum number of steps allowed in a single constexpr evaluation.
|
||||
* This is used to prevent a buggy constexpr function from causing the
|
||||
* IDE to hang.
|
||||
*/
|
||||
public static final int MAX_CONSTEXPR_EVALUATION_STEPS = 1024;
|
||||
|
||||
private int fStepsPerformed;
|
||||
private IASTNode fPoint;
|
||||
|
||||
/**
|
||||
* Construct a ConstexprEvaluationContext for a new constexpr evaluation.
|
||||
* @param point the point of instantiation, determines the scope for name lookups
|
||||
*/
|
||||
public ConstexprEvaluationContext(IASTNode point) {
|
||||
fStepsPerformed = 0;
|
||||
fPoint = point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a new step being performed in this constexpr evaluation.
|
||||
* @return this constexpr evaluation
|
||||
*/
|
||||
public ConstexprEvaluationContext recordStep() {
|
||||
++fStepsPerformed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of steps performed so far in the constexpr evaluation.
|
||||
*/
|
||||
public int getStepsPerformed() {
|
||||
return fStepsPerformed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the point of instantiation.
|
||||
*/
|
||||
public IASTNode getPoint() {
|
||||
return fPoint;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the evaluation produced by substituting function parameters by their values.
|
||||
*
|
||||
* @param parameterMap maps function parameters to their values
|
||||
* @param maxdepth allowed recursion depth
|
||||
* @param point the point of instantiation, determines the scope for name lookups
|
||||
* @param context the context for the current constexpr evaluation
|
||||
* @return the computed evaluation
|
||||
*/
|
||||
ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
|
||||
IASTNode point);
|
||||
ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
ConstexprEvaluationContext context);
|
||||
|
||||
/**
|
||||
* Searches the evaluation for a usage of a template parameter which is a parameter pack,
|
||||
|
|
|
@ -366,9 +366,9 @@ public class EvalBinary extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ICPPEvaluation arg1 = fArg1.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation arg2 = fArg2.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation arg1 = fArg1.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
ICPPEvaluation arg2 = fArg2.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (arg1 == fArg1 && arg2 == fArg2)
|
||||
return this;
|
||||
return new EvalBinary(fOperator, arg1, arg2, getTemplateDefinition());
|
||||
|
|
|
@ -142,7 +142,7 @@ public class EvalBinaryTypeId extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ public class EvalBinding extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
int pos = getFunctionParameterPosition();
|
||||
if (pos >= 0) {
|
||||
ICPPEvaluation eval = parameterMap.getArgument(pos);
|
||||
|
|
|
@ -204,10 +204,10 @@ public class EvalComma extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation[] args = fArguments;
|
||||
for (int i = 0; i < fArguments.length; i++) {
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (arg != fArguments[i]) {
|
||||
if (args == fArguments) {
|
||||
args = new ICPPEvaluation[fArguments.length];
|
||||
|
|
|
@ -109,8 +109,8 @@ public class EvalCompound extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ICPPEvaluation delegate = fDelegate.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation delegate = fDelegate.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (delegate == fDelegate)
|
||||
return this;
|
||||
return new EvalCompound(delegate, getTemplateDefinition());
|
||||
|
|
|
@ -354,23 +354,23 @@ public class EvalConditional extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ICPPEvaluation condition = fCondition.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation condition = fCondition.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
// If the condition can be evaluated, fold the conditional into
|
||||
// just the branch that is taken. This avoids infinite recursion
|
||||
// when computing a recursive constexpr function where the base
|
||||
// case of the recursion is one of the branches of the conditional.
|
||||
Long conditionValue = condition.getValue(point).numericalValue();
|
||||
Long conditionValue = condition.getValue(context.getPoint()).numericalValue();
|
||||
if (conditionValue != null) {
|
||||
if (conditionValue.longValue() != 0) {
|
||||
return fPositive == null ? null : fPositive.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
return fPositive == null ? null : fPositive.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
} else {
|
||||
return fNegative.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
return fNegative.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
}
|
||||
}
|
||||
ICPPEvaluation positive = fPositive == null ?
|
||||
null : fPositive.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation negative = fNegative.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
null : fPositive.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
ICPPEvaluation negative = fNegative.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (condition == fCondition && positive == fPositive && negative == fNegative)
|
||||
return this;
|
||||
return new EvalConditional(condition, positive, negative, fPositiveThrows, fNegativeThrows, getTemplateDefinition());
|
||||
|
|
|
@ -175,11 +175,11 @@ public class EvalFixed extends CPPEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation eval = fValue.getEvaluation();
|
||||
if (eval == null)
|
||||
return this;
|
||||
eval = eval.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
eval = eval.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (eval == fValue.getEvaluation())
|
||||
return this;
|
||||
return new EvalFixed(fType, fValueCategory, Value.create(eval));
|
||||
|
|
|
@ -145,7 +145,7 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public IValue getValue(IASTNode point) {
|
||||
ICPPEvaluation eval = computeForFunctionCall(Value.MAX_RECURSION_DEPTH, point);
|
||||
ICPPEvaluation eval = computeForFunctionCall(new ConstexprEvaluationContext(point));
|
||||
if (eval == this) {
|
||||
return Value.create(eval);
|
||||
}
|
||||
|
@ -205,13 +205,13 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
if (maxdepth == 0)
|
||||
ConstexprEvaluationContext context) {
|
||||
if (context.getStepsPerformed() >= ConstexprEvaluationContext.MAX_CONSTEXPR_EVALUATION_STEPS)
|
||||
return EvalFixed.INCOMPLETE;
|
||||
|
||||
ICPPEvaluation[] args = fArguments;
|
||||
for (int i = 0; i < fArguments.length; i++) {
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context);
|
||||
if (arg != fArguments[i]) {
|
||||
if (args == fArguments) {
|
||||
args = new ICPPEvaluation[fArguments.length];
|
||||
|
@ -223,17 +223,17 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
|
|||
EvalFunctionCall eval = this;
|
||||
if (args != fArguments)
|
||||
eval = new EvalFunctionCall(args, getTemplateDefinition());
|
||||
return eval.computeForFunctionCall(maxdepth - 1, point);
|
||||
return eval.computeForFunctionCall(context);
|
||||
}
|
||||
|
||||
private ICPPEvaluation computeForFunctionCall(int maxdepth, IASTNode point) {
|
||||
private ICPPEvaluation computeForFunctionCall(ConstexprEvaluationContext context) {
|
||||
if (isValueDependent())
|
||||
return this;
|
||||
// If the arguments are not all constant expressions, there is
|
||||
// no point trying to substitute them into the return expression.
|
||||
if (!areAllConstantExpressions(fArguments, point))
|
||||
if (!areAllConstantExpressions(fArguments, context.getPoint()))
|
||||
return this;
|
||||
ICPPFunction function = getOverload(point);
|
||||
ICPPFunction function = getOverload(context.getPoint());
|
||||
if (function == null) {
|
||||
if (fArguments[0] instanceof EvalBinding) {
|
||||
IBinding binding = ((EvalBinding) fArguments[0]).getBinding();
|
||||
|
@ -247,7 +247,7 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
|
|||
if (eval == null)
|
||||
return EvalFixed.INCOMPLETE;
|
||||
CPPFunctionParameterMap parameterMap = buildParameterMap(function);
|
||||
return eval.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
return eval.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
}
|
||||
|
||||
private CPPFunctionParameterMap buildParameterMap(ICPPFunction function) {
|
||||
|
|
|
@ -266,7 +266,7 @@ public class EvalFunctionSet extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -362,10 +362,10 @@ public class EvalID extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
if (fFieldOwner == null)
|
||||
return this;
|
||||
ICPPEvaluation fieldOwner = fFieldOwner.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation fieldOwner = fFieldOwner.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (fieldOwner == fFieldOwner)
|
||||
return this;
|
||||
return new EvalID(fieldOwner, fNameOwner, fName, fAddressOf, fQualified, fTemplateArgs, getTemplateDefinition());
|
||||
|
|
|
@ -119,10 +119,10 @@ public class EvalInitList extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation[] clauses = fClauses;
|
||||
for (int i = 0; i < fClauses.length; i++) {
|
||||
ICPPEvaluation clause = fClauses[i].computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation clause = fClauses[i].computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (clause != fClauses[i]) {
|
||||
if (clauses == fClauses) {
|
||||
clauses = new ICPPEvaluation[fClauses.length];
|
||||
|
|
|
@ -355,7 +355,7 @@ public class EvalMemberAccess extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,8 +105,8 @@ public class EvalParameterPack extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ICPPEvaluation expansionPattern = fExpansionPattern.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation expansionPattern = fExpansionPattern.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (expansionPattern == fExpansionPattern)
|
||||
return this;
|
||||
return new EvalParameterPack(expansionPattern, getTemplateDefinition());
|
||||
|
|
|
@ -239,11 +239,11 @@ public class EvalTypeId extends CPPDependentEvaluation {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
|
||||
IASTNode point) {
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation[] args = fArguments;
|
||||
for (int i = 0; i < fArguments.length; i++) {
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (arg != fArguments[i]) {
|
||||
if (args == fArguments) {
|
||||
args = new ICPPEvaluation[fArguments.length];
|
||||
|
|
|
@ -345,9 +345,9 @@ public class EvalUnary extends CPPDependentEvaluation {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
|
||||
IASTNode point) {
|
||||
ICPPEvaluation argument = fArgument.computeForFunctionCall(parameterMap, maxdepth, point);
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
ConstexprEvaluationContext context) {
|
||||
ICPPEvaluation argument = fArgument.computeForFunctionCall(parameterMap, context.recordStep());
|
||||
if (argument == fArgument)
|
||||
return this;
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ public class EvalUnaryTypeID extends CPPDependentEvaluation {
|
|||
|
||||
@Override
|
||||
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
|
||||
int maxdepth, IASTNode point) {
|
||||
ConstexprEvaluationContext context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue