[Yul] leave statement.

This commit is contained in:
chriseth 2019-10-28 15:25:02 +01:00
parent edf1e83fda
commit ceb8ee9124
41 changed files with 214 additions and 27 deletions

View File

@ -20,6 +20,7 @@ Language Features:
* Introduce syntax for array slices and implement them for dynamic calldata arrays.
* Introduce ``push()`` for dynamic storage arrays. It returns a reference to the newly allocated element, if applicable.
* Modify ``push(element)`` for dynamic storage arrays such that it does not return the new length anymore.
* Yul: Introduce ``leave`` statement that exits the current function.
Compiler Features:

View File

@ -54,6 +54,7 @@ New Features
============
* The :ref:`try/catch statement <try-catch>` allows you to react on failed external calls.
* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function.
Deprecated Elements

View File

@ -530,6 +530,9 @@ the other two are blocks. If the initializing part
declares any variables, the scope of these variables is extended into the
body (including the condition and the post-iteration part).
The ``break`` and ``continue`` statements can be used to exit the loop
or skip to the post-part, respectively.
The following example computes the sum of an area in memory.
.. code::
@ -571,6 +574,11 @@ statement.
If you call a function that returns multiple values, you have to assign
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
The ``leave`` statement can be used to exit the current function. It
works like the ``return`` statement in other languages just that it does
not take a value to return, it just exits the functions and the function
will return whatever values are currently assigned to the return variable(s).
The following example implements the power function by square-and-multiply.
.. code::
@ -769,6 +777,7 @@ Grammar::
AssemblyFor |
'break' |
'continue' |
'leave' |
SubAssembly
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral

View File

@ -100,7 +100,8 @@ Grammar::
Expression |
Switch |
ForLoop |
BreakContinue
BreakContinue |
Leave
FunctionDefinition =
'function' Identifier '(' TypedIdentifierList? ')'
( '->' TypedIdentifierList )? Block
@ -122,6 +123,7 @@ Grammar::
'for' Block Expression Block Block
BreakContinue =
'break' | 'continue'
Leave = 'leave'
FunctionCall =
Identifier '(' ( Expression ( ',' Expression )* )? ')'
Identifier = [a-zA-Z_$] [a-zA-Z_$0-9.]*
@ -167,6 +169,7 @@ In all other situations, expressions have to evaluate to exactly one value.
The ``continue`` and ``break`` statements can only be used inside loop bodies
and have to be in the same function as the loop (or both have to be at the
top level).
The ``leave`` statement can only be used inside a function.
The condition part of the for-loop has to evaluate to exactly one value.
Functions cannot be defined inside for loop init blocks.
@ -214,7 +217,7 @@ The two state objects are the global state object
blockchain) and the local state object (the state of local variables, i.e. a
segment of the stack in the EVM).
If the AST node is a statement, E returns the two state objects and a "mode",
which is used for the ``break`` and ``continue`` statements.
which is used for the ``break``, ``continue`` and ``leave`` statements.
If the AST node is an expression, E returns the two state objects and
as many values as the expression evaluates to.
@ -255,12 +258,13 @@ We will use a destructuring notation for the AST nodes.
G, L2, regular
E(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
if n >= 1:
let G1, L1, mode = E(G, L, i1, ..., in)
// mode has to be regular due to the syntactic restrictions
let G2, L2, mode = E(G1, L1, for {} condition post body)
// mode has to be regular due to the syntactic restrictions
let L3 be the restriction of L2 to only variables of L
G2, L3, regular
let G1, L, mode = E(G, L, i1, ..., in)
// mode has to be regular or leave due to the syntactic restrictions
if mode is leave then
G1, L1 restricted to variables of L, leave
otherwise
let G2, L2, mode = E(G1, L1, for {} condition post body)
G2, L2 restricted to variables of L, mode
else:
let G1, L1, v = E(G, L, condition)
if v is false:
@ -269,13 +273,20 @@ We will use a destructuring notation for the AST nodes.
let G2, L2, mode = E(G1, L, body)
if mode is break:
G2, L2, regular
otherwise if mode is leave:
G2, L2, leave
else:
G3, L3, mode = E(G2, L2, post)
E(G3, L3, for {} condition post body)
if mode is leave:
G2, L3, leave
otherwise
E(G3, L3, for {} condition post body)
E(G, L, break: BreakContinue) =
G, L, break
E(G, L, continue: BreakContinue) =
G, L, continue
E(G, L, leave: Leave) =
G, L, leave
E(G, L, <if condition body>: If) =
let G0, L0, v = E(G, L, condition)
if v is true:

View File

@ -108,6 +108,9 @@ public:
void operator()(yul::Continue const&)
{
}
void operator()(yul::Leave const&)
{
}
void operator()(yul::Block const& _block)
{
for (auto const& s: _block.statements)

View File

@ -519,6 +519,12 @@ bool AsmAnalyzer::operator()(Continue const& _continue)
return true;
}
bool AsmAnalyzer::operator()(Leave const& _leave)
{
m_info.stackHeightInfo[&_leave] = m_stackHeight;
return true;
}
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;

View File

@ -93,6 +93,7 @@ public:
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&);
bool operator()(Continue const&);
bool operator()(Leave const&);
bool operator()(Block const& _block);
private:

View File

@ -78,6 +78,8 @@ struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<E
struct Break { langutil::SourceLocation location; };
/// Continue statement (valid within for loop)
struct Continue { langutil::SourceLocation location; };
/// Leave statement (valid within function)
struct Leave { langutil::SourceLocation location; };
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
{

View File

@ -42,12 +42,13 @@ struct Case;
struct ForLoop;
struct Break;
struct Continue;
struct Leave;
struct ExpressionStatement;
struct Block;
struct TypedName;
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Leave, Block>;
}

View File

@ -144,6 +144,16 @@ Statement Parser::parseStatement()
m_scanner->next();
return stmt;
}
case Token::Identifier:
if (currentLiteral() == "leave")
{
Statement stmt{createWithLocation<Leave>()};
if (!m_insideFunction)
m_errorReporter.syntaxError(location(), "Keyword \"leave\" can only be used inside a function.");
m_scanner->next();
return stmt;
}
break;
default:
break;
}
@ -439,7 +449,10 @@ FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::Comma);
}
}
bool preInsideFunction = m_insideFunction;
m_insideFunction = true;
funDef.body = parseBlock();
m_insideFunction = preInsideFunction;
funDef.location.end = funDef.body.location.end;
m_currentForLoopComponent = outerForLoopComponent;

View File

@ -98,6 +98,7 @@ protected:
private:
Dialect const& m_dialect;
ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None;
bool m_insideFunction = false;
};
}

View File

@ -227,6 +227,11 @@ string AsmPrinter::operator()(Continue const&) const
return "continue";
}
string AsmPrinter::operator()(Leave const&) const
{
return "leave";
}
string AsmPrinter::operator()(Block const& _block) const
{
if (_block.statements.empty())

View File

@ -50,6 +50,7 @@ public:
std::string operator()(ForLoop const& _forLoop) const;
std::string operator()(Break const& _break) const;
std::string operator()(Continue const& _continue) const;
std::string operator()(Leave const& _continue) const;
std::string operator()(Block const& _block) const;
private:

View File

@ -63,6 +63,7 @@ public:
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&) { return true; }
bool operator()(Continue const&) { return true; }
bool operator()(Leave const&) { return true; }
bool operator()(Block const& _block);
private:

View File

@ -490,6 +490,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
m_assembly.appendConstant(u256(0));
}
m_context->functionExitPoints.push(
CodeTransformContext::JumpInfo{m_assembly.newLabelId(), m_assembly.stackHeight()}
);
try
{
CodeTransform(
@ -518,6 +521,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
stackError(std::move(error), height);
}
m_assembly.appendLabel(m_context->functionExitPoints.top().label);
m_context->functionExitPoints.pop();
{
// The stack layout here is:
// <return label>? <arguments...> <return values...>
@ -643,6 +649,17 @@ void CodeTransform::operator()(Continue const& _continue)
checkStackHeight(&_continue);
}
void CodeTransform::operator()(Leave const& _leave)
{
yulAssert(!m_context->functionExitPoints.empty(), "Invalid leave-statement. Requires surrounding function in code generation.");
m_assembly.setSourceLocation(_leave.location);
Context::JumpInfo const& jump = m_context->functionExitPoints.top();
m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight));
checkStackHeight(&_leave);
}
void CodeTransform::operator()(Block const& _block)
{
Scope* originalScope = m_scope;

View File

@ -73,6 +73,7 @@ struct CodeTransformContext
};
std::stack<ForLoopLabels> forLoopStack;
std::stack<JumpInfo> functionExitPoints;
};
/**
@ -185,6 +186,7 @@ public:
void operator()(ForLoop const&);
void operator()(Break const&);
void operator()(Continue const&);
void operator()(Leave const&);
void operator()(Block const& _block);
private:

View File

@ -42,10 +42,11 @@ struct If;
struct Loop;
struct Break;
struct BreakIf;
struct Return;
using Expression = boost::variant<
Literal, StringLiteral, LocalVariable, GlobalVariable,
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
Block, If, Loop, Break, BreakIf
Block, If, Loop, Break, BreakIf, Return
>;
struct Literal { uint64_t value; };
@ -65,6 +66,7 @@ struct If {
};
struct Loop { std::string labelName; std::vector<Expression> statements; };
struct Break { Label label; };
struct Return {};
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
struct VariableDeclaration { std::string variableName; };

View File

@ -257,6 +257,11 @@ wasm::Expression EWasmCodeTransform::operator()(Continue const&)
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}};
}
wasm::Expression EWasmCodeTransform::operator()(Leave const&)
{
return wasm::Return{};
}
wasm::Expression EWasmCodeTransform::operator()(Block const& _block)
{
return wasm::Block{{}, visit(_block.statements)};

View File

@ -52,6 +52,7 @@ public:
wasm::Expression operator()(yul::ForLoop const&);
wasm::Expression operator()(yul::Break const&);
wasm::Expression operator()(yul::Continue const&);
wasm::Expression operator()(yul::Leave const&);
wasm::Expression operator()(yul::Block const& _block);
private:

View File

@ -128,6 +128,11 @@ string EWasmToText::operator()(wasm::BreakIf const& _break)
return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n";
}
string EWasmToText::operator()(wasm::Return const&)
{
return "(return)\n";
}
string EWasmToText::operator()(wasm::Block const& _block)
{
string label = _block.labelName.empty() ? "" : " $" + _block.labelName;

View File

@ -49,6 +49,7 @@ public:
std::string operator()(wasm::If const& _if);
std::string operator()(wasm::Loop const& _loop);
std::string operator()(wasm::Break const& _break);
std::string operator()(wasm::Return const& _return);
std::string operator()(wasm::BreakIf const& _break);
std::string operator()(wasm::Block const& _block);

View File

@ -136,6 +136,11 @@ Statement ASTCopier::operator()(Continue const& _continue)
return Continue{ _continue };
}
Statement ASTCopier::operator()(Leave const& _leave)
{
return Leave{_leave};
}
Statement ASTCopier::operator ()(Block const& _block)
{
return translate(_block);

View File

@ -58,6 +58,7 @@ public:
virtual Statement operator()(ForLoop const&) = 0;
virtual Statement operator()(Break const&) = 0;
virtual Statement operator()(Continue const&) = 0;
virtual Statement operator()(Leave const&) = 0;
virtual Statement operator()(Block const& _block) = 0;
};
@ -83,6 +84,7 @@ public:
Statement operator()(ForLoop const&) override;
Statement operator()(Break const&) override;
Statement operator()(Continue const&) override;
Statement operator()(Leave const&) override;
Statement operator()(Block const& _block) override;
virtual Expression translate(Expression const& _expression);

View File

@ -169,6 +169,10 @@ void ASTModifier::operator()(Continue&)
{
}
void ASTModifier::operator()(Leave&)
{
}
void ASTModifier::operator()(Block& _block)
{
walkVector(_block.statements);

View File

@ -56,6 +56,7 @@ public:
virtual void operator()(ForLoop const&);
virtual void operator()(Break const&) {}
virtual void operator()(Continue const&) {}
virtual void operator()(Leave const&) {}
virtual void operator()(Block const& _block);
virtual void visit(Statement const& _st);
@ -91,6 +92,7 @@ public:
virtual void operator()(ForLoop&);
virtual void operator()(Break&);
virtual void operator()(Continue&);
virtual void operator()(Leave&);
virtual void operator()(Block& _block);
virtual void visit(Statement& _st);

View File

@ -173,6 +173,11 @@ void BlockHasher::operator()(Continue const& _continue)
ASTWalker::operator()(_continue);
}
void BlockHasher::operator()(Leave const& _leave)
{
hash64(compileTimeLiteralHash("Leave"));
ASTWalker::operator()(_leave);
}
void BlockHasher::operator()(Block const& _block)
{

View File

@ -60,6 +60,7 @@ public:
void operator()(ForLoop const&) override;
void operator()(Break const&) override;
void operator()(Continue const&) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
static std::map<Block const*, uint64_t> run(Block const& _block);

View File

@ -159,7 +159,10 @@ void ControlFlowSimplifier::visit(Statement& _st)
isTerminating = true;
--m_numBreakStatements;
}
else if (controlFlow == TerminationFinder::ControlFlow::Terminate)
else if (
controlFlow == TerminationFinder::ControlFlow::Terminate ||
controlFlow == TerminationFinder::ControlFlow::Leave
)
isTerminating = true;
if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)

View File

@ -161,6 +161,10 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
}
ASTModifier::operator()(_fun);
// Note that the contents of return variables, storage and memory at this point
// might be incorrect due to the fact that the DataFlowAnalyzer ignores the ``leave``
// statement.
popScope();
m_value.swap(value);
swap(m_references, references);

View File

@ -64,6 +64,10 @@ struct SideEffects;
* older version of the other and thus overlapping contents would have been deleted already
* at the point of assignment.
*
* The DataFlowAnalyzer currently does not deal with the ``leave`` statement. This is because
* it only matters at the end of a function body, which is a point in the code a derived class
* can not easily deal with.
*
* Prerequisite: Disambiguator, ForLoopInitRewriter.
*/
class DataFlowAnalyzer: public ASTModifier

View File

@ -35,7 +35,7 @@ struct OptimiserStepContext;
* Optimisation stage that removes unreachable code
*
* Unreachable code is any code within a block which is preceded by a
* return, invalid, break, continue, selfdestruct or revert.
* leave, return, invalid, break, continue, selfdestruct or revert.
*
* Function definitions are retained as they might be called by earlier
* code and thus are considered reachable.

View File

@ -26,6 +26,7 @@
#include <libyul/optimiser/OptimizerUtilities.h>
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/SSAValueTracker.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/Exceptions.h>
#include <libyul/AsmData.h>
@ -62,6 +63,8 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
continue;
FunctionDefinition& fun = boost::get<FunctionDefinition>(statement);
m_functions[fun.name] = &fun;
if (LeaveFinder::containsLeave(fun))
m_noInlineFunctions.insert(fun.name);
// Always inline functions that are only called once.
if (references[fun.name] == 1)
m_singleUse.emplace(fun.name);
@ -94,7 +97,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
if (!calledFunction)
return false;
if (recursive(*calledFunction))
if (m_noInlineFunctions.count(_funCall.functionName.name) || recursive(*calledFunction))
return false;
// Inline really, really tiny functions

View File

@ -102,6 +102,8 @@ private:
/// we store pointers to functions.
Block& m_ast;
std::map<YulString, FunctionDefinition*> m_functions;
/// Functions not to be inlined (because they contain the ``leave`` statement).
std::set<YulString> m_noInlineFunctions;
/// Names of functions to always inline.
std::set<YulString> m_singleUse;
/// Variables that are constants (used for inlining heuristic)

View File

@ -70,7 +70,8 @@ void CodeSize::visit(Statement const& _statement)
else if (
_statement.type() == typeid(If) ||
_statement.type() == typeid(Break) ||
_statement.type() == typeid(Continue)
_statement.type() == typeid(Continue) ||
_statement.type() == typeid(Leave)
)
m_size += 2;
else if (_statement.type() == typeid(ForLoop))

View File

@ -104,12 +104,17 @@ void RedundantAssignEliminator::operator()(Switch const& _switch)
void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
{
std::set<YulString> outerDeclaredVariables;
std::set<YulString> outerReturnVariables;
TrackedAssignments outerAssignments;
ForLoopInfo forLoopInfo;
swap(m_declaredVariables, outerDeclaredVariables);
swap(m_returnVariables, outerReturnVariables);
swap(m_assignments, outerAssignments);
swap(m_forLoopInfo, forLoopInfo);
for (auto const& retParam: _functionDefinition.returnVariables)
m_returnVariables.insert(retParam.name);
(*this)(_functionDefinition.body);
for (auto const& param: _functionDefinition.parameters)
@ -118,6 +123,7 @@ void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDe
finalize(retParam.name, State::Used);
swap(m_declaredVariables, outerDeclaredVariables);
swap(m_returnVariables, outerReturnVariables);
swap(m_assignments, outerAssignments);
swap(m_forLoopInfo, forLoopInfo);
}
@ -200,6 +206,12 @@ void RedundantAssignEliminator::operator()(Continue const&)
m_assignments.clear();
}
void RedundantAssignEliminator::operator()(Leave const&)
{
for (YulString name: m_returnVariables)
changeUndecidedTo(name, State::Used);
}
void RedundantAssignEliminator::operator()(Block const& _block)
{
set<YulString> outerDeclaredVariables;

View File

@ -91,6 +91,8 @@ struct Dialect;
* For switch statements that have a "default"-case, there is no control-flow
* part that skips the switch.
*
* At ``leave`` statements, all return variables are set to "used".
*
* When a variable goes out of scope, all statements still in the "undecided"
* state are changed to "unused", unless the variable is the return
* parameter of a function - there, the state changes to "used".
@ -125,6 +127,7 @@ public:
void operator()(ForLoop const&) override;
void operator()(Break const&) override;
void operator()(Continue const&) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
private:
@ -163,6 +166,7 @@ private:
Dialect const* m_dialect;
std::set<YulString> m_declaredVariables;
std::set<YulString> m_returnVariables;
std::set<Assignment const*> m_pendingRemovals;
TrackedAssignments m_assignments;

View File

@ -173,6 +173,8 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
return ControlFlow::Break;
else if (_statement.type() == typeid(Continue))
return ControlFlow::Continue;
else if (_statement.type() == typeid(Leave))
return ControlFlow::Leave;
else
return ControlFlow::FlowOut;
}

View File

@ -113,6 +113,31 @@ private:
bool m_msizeFound = false;
};
/**
* Class that can be used to find out if the given function contains the ``leave`` statement.
*
* Returns true even in the case where the function definition contains another function definition
* that contains the leave statement.
*/
class LeaveFinder: public ASTWalker
{
public:
static bool containsLeave(FunctionDefinition const& _fun)
{
LeaveFinder f;
f(_fun);
return f.m_leaveFound;
}
using ASTWalker::operator();
void operator()(Leave const&) { m_leaveFound = true; }
private:
LeaveFinder() = default;
bool m_leaveFound = false;
};
/**
* Specific AST walker that determines whether an expression is movable
* and collects the referenced variables.
@ -148,12 +173,13 @@ private:
class TerminationFinder
{
public:
enum class ControlFlow { FlowOut, Break, Continue, Terminate };
// TODO check all uses of TerminationFinder!
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
/// @returns the index of the first statement in the provided sequence
/// that is an unconditional ``break``, ``continue`` or a
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
/// call to a terminating builtin function.
/// If control flow can continue at the end of the list,
/// returns `FlowOut` and ``size_t(-1)``.

View File

@ -57,6 +57,7 @@ public:
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
bool statementEqual(Break const&, Break const&) { return true; }
bool statementEqual(Continue const&, Continue const&) { return true; }
bool statementEqual(Leave const&, Leave const&) { return true; }
bool statementEqual(Block const& _lhs, Block const& _rhs);
private:
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);

View File

@ -124,30 +124,43 @@ void Interpreter::operator()(ForLoop const& _forLoop)
solAssert(_forLoop.condition, "");
openScope();
ScopeGuard g([this]{ closeScope(); });
for (auto const& statement: _forLoop.pre.statements)
{
visit(statement);
if (m_state.controlFlowState == ControlFlowState::Leave)
return;
}
while (evaluate(*_forLoop.condition) != 0)
{
m_state.loopState = LoopState::Default;
m_state.controlFlowState = ControlFlowState::Default;
(*this)(_forLoop.body);
if (m_state.loopState == LoopState::Break)
if (m_state.controlFlowState == ControlFlowState::Break || m_state.controlFlowState == ControlFlowState::Leave)
break;
m_state.loopState = LoopState::Default;
m_state.controlFlowState = ControlFlowState::Default;
(*this)(_forLoop.post);
if (m_state.controlFlowState == ControlFlowState::Leave)
break;
}
m_state.loopState = LoopState::Default;
closeScope();
if (m_state.controlFlowState != ControlFlowState::Leave)
m_state.controlFlowState = ControlFlowState::Default;
}
void Interpreter::operator()(Break const&)
{
m_state.loopState = LoopState::Break;
m_state.controlFlowState = ControlFlowState::Break;
}
void Interpreter::operator()(Continue const&)
{
m_state.loopState = LoopState::Continue;
m_state.controlFlowState = ControlFlowState::Continue;
}
void Interpreter::operator()(Leave const&)
{
m_state.controlFlowState = ControlFlowState::Leave;
}
void Interpreter::operator()(Block const& _block)
@ -171,7 +184,7 @@ void Interpreter::operator()(Block const& _block)
for (auto const& statement: _block.statements)
{
visit(statement);
if (m_state.loopState != LoopState::Default)
if (m_state.controlFlowState != ControlFlowState::Default)
break;
}
@ -245,8 +258,10 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
for (size_t i = 0; i < fun->returnVariables.size(); ++i)
variables[fun->returnVariables.at(i).name] = 0;
m_state.controlFlowState = ControlFlowState::Default;
Interpreter interpreter(m_state, m_dialect, variables, functionScopes);
interpreter(fun->body);
m_state.controlFlowState = ControlFlowState::Default;
m_values.clear();
for (auto const& retVar: fun->returnVariables)

View File

@ -53,11 +53,12 @@ class TraceLimitReached: public InterpreterTerminatedGeneric
{
};
enum class LoopState
enum class ControlFlowState
{
Default,
Continue,
Break,
Leave
};
struct InterpreterState
@ -89,7 +90,7 @@ struct InterpreterState
size_t maxTraceSize = 0;
size_t maxSteps = 0;
size_t numSteps = 0;
LoopState loopState = LoopState::Default;
ControlFlowState controlFlowState = ControlFlowState::Default;
void dumpTraceAndState(std::ostream& _out) const;
};
@ -121,6 +122,7 @@ public:
void operator()(ForLoop const&) override;
void operator()(Break const&) override;
void operator()(Continue const&) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
std::vector<std::string> const& trace() const { return m_state.trace; }