Skip to content

[Yul] introduce break/continue keywords. #6136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions libsolidity/analysis/ViewPureChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ class AssemblyViewPureChecker: public boost::static_visitor<void>
(*this)(_for.body);
(*this)(_for.post);
}
void operator()(yul::Break const&)
{
}
void operator()(yul::Continue const&)
{
}
void operator()(yul::Block const& _block)
{
for (auto const& s: _block.statements)
Expand Down
25 changes: 23 additions & 2 deletions libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <libyul/AsmScope.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/Utilities.h>
#include <libyul/Exceptions.h>

#include <liblangutil/ErrorReporter.h>

Expand All @@ -33,6 +34,7 @@

#include <memory>
#include <functional>
#include <utility>

using namespace std;
using namespace dev;
Expand Down Expand Up @@ -472,7 +474,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
{
solAssert(_for.condition, "");

Scope* originalScope = m_currentScope;
Scope* outerScope = m_currentScope;

bool success = true;
if (!(*this)(_for.pre))
Expand All @@ -485,18 +487,37 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
if (!expectExpression(*_for.condition))
success = false;
m_stackHeight--;

// backup outer for-loop & create new state
auto outerForLoop = m_currentForLoop;
m_currentForLoop = &_for;

if (!(*this)(_for.body))
success = false;

if (!(*this)(_for.post))
success = false;

m_stackHeight -= scope(&_for.pre).numberOfVariables();
m_info.stackHeightInfo[&_for] = m_stackHeight;
m_currentScope = originalScope;
m_currentScope = outerScope;
m_currentForLoop = outerForLoop;

return success;
}

bool AsmAnalyzer::operator()(Break const& _break)
{
m_info.stackHeightInfo[&_break] = m_stackHeight;
return true;
}

bool AsmAnalyzer::operator()(Continue const& _continue)
{
m_info.stackHeightInfo[&_continue] = m_stackHeight;
return true;
}

bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
Expand Down
4 changes: 4 additions & 0 deletions libyul/AsmAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <boost/optional.hpp>

#include <functional>
#include <list>
#include <memory>

namespace langutil
Expand Down Expand Up @@ -93,6 +94,8 @@ class AsmAnalyzer: public boost::static_visitor<bool>
bool operator()(If const& _if);
bool operator()(Switch const& _switch);
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&);
bool operator()(Continue const&);
bool operator()(Block const& _block);

private:
Expand Down Expand Up @@ -124,6 +127,7 @@ class AsmAnalyzer: public boost::static_visitor<bool>
langutil::EVMVersion m_evmVersion;
std::shared_ptr<Dialect> m_dialect;
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
ForLoop const* m_currentForLoop = nullptr;
};

}
4 changes: 4 additions & 0 deletions libyul/AsmData.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value;
/// Switch statement
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
/// Break statement (valid within for loop)
struct Break { langutil::SourceLocation location; };
/// Continue statement (valid within for loop)
struct Continue { langutil::SourceLocation location; };

struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
{
Expand Down
4 changes: 3 additions & 1 deletion libyul/AsmDataForward.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ struct If;
struct Switch;
struct Case;
struct ForLoop;
struct Break;
struct Continue;
struct ExpressionStatement;
struct Block;

struct TypedName;

using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;

}
36 changes: 36 additions & 0 deletions libyul/AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <libyul/AsmParser.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/Common.h>

#include <boost/algorithm/string.hpp>

Expand Down Expand Up @@ -104,6 +105,32 @@ Statement Parser::parseStatement()
}
case Token::For:
return parseForLoop();
case Token::Break:
if (m_insideForLoopBody)
{
auto stmt = Statement{ createWithLocation<Break>() };
m_scanner->next();
return stmt;
}
else
{
m_errorReporter.syntaxError(location(), "Keyword break outside for-loop body is not allowed.");
m_scanner->next();
return {};
}
case Token::Continue:
if (m_insideForLoopBody)
{
auto stmt = Statement{ createWithLocation<Continue>() };
m_scanner->next();
return stmt;
}
else
{
m_errorReporter.syntaxError(location(), "Keyword continue outside for-loop body is not allowed.");
m_scanner->next();
return {};
}
case Token::Assign:
{
if (m_dialect->flavour != AsmFlavour::Loose)
Expand Down Expand Up @@ -243,13 +270,19 @@ Case Parser::parseCase()

ForLoop Parser::parseForLoop()
{
bool outerForLoopBody = m_insideForLoopBody;
m_insideForLoopBody = false;

RecursionGuard recursionGuard(*this);
ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For);
forLoop.pre = parseBlock();
forLoop.condition = make_unique<Expression>(parseExpression());
forLoop.post = parseBlock();

m_insideForLoopBody = true;
forLoop.body = parseBlock();
m_insideForLoopBody = outerForLoopBody;
forLoop.location.end = forLoop.body.location.end;
return forLoop;
}
Expand Down Expand Up @@ -455,6 +488,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
FunctionDefinition Parser::parseFunctionDefinition()
{
RecursionGuard recursionGuard(*this);
auto outerForLoopBody = m_insideForLoopBody;
m_insideForLoopBody = false;
ScopeGuard restoreInsideForLoopBody{[&]() { m_insideForLoopBody = outerForLoopBody; }};
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
expectToken(Token::Function);
funDef.name = expectAsmIdentifier();
Expand Down
3 changes: 2 additions & 1 deletion libyul/AsmParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Parser: public langutil::ParserBase
{
public:
explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
ParserBase(_errorReporter), m_dialect(std::move(_dialect)), m_insideForLoopBody{false} {}

/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @param _reuseScanner if true, do check for end of input after the `}`.
Expand Down Expand Up @@ -87,6 +87,7 @@ class Parser: public langutil::ParserBase

private:
std::shared_ptr<Dialect> m_dialect;
bool m_insideForLoopBody;
};

}
10 changes: 10 additions & 0 deletions libyul/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const
return out;
}

string AsmPrinter::operator()(Break const&) const
{
return "break";
}

string AsmPrinter::operator()(Continue const&) const
{
return "continue";
}

string AsmPrinter::operator()(Block const& _block) const
{
if (_block.statements.empty())
Expand Down
2 changes: 2 additions & 0 deletions libyul/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class AsmPrinter: public boost::static_visitor<std::string>
std::string operator()(If const& _if) const;
std::string operator()(Switch const& _switch) const;
std::string operator()(ForLoop const& _forLoop) const;
std::string operator()(Break const& _break) const;
std::string operator()(Continue const& _continue) const;
std::string operator()(Block const& _block) const;

private:
Expand Down
2 changes: 2 additions & 0 deletions libyul/AsmScopeFiller.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class ScopeFiller: public boost::static_visitor<bool>
bool operator()(If const& _if);
bool operator()(Switch const& _switch);
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&) { return true; }
bool operator()(Continue const&) { return true; }
bool operator()(Block const& _block);

private:
Expand Down
20 changes: 15 additions & 5 deletions libyul/backends/evm/EVMCodeTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ void VariableReferenceCounter::operator()(ForLoop const& _forLoop)
m_scope = originalScope;
}


void VariableReferenceCounter::operator()(Block const& _block)
{
Scope* originalScope = m_scope;
Expand All @@ -92,7 +91,6 @@ void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
));
}


CodeTransform::CodeTransform(
AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo,
Expand Down Expand Up @@ -605,11 +603,9 @@ void CodeTransform::operator()(ForLoop const& _forLoop)

visitStatements(_forLoop.pre.statements);

// TODO: When we implement break and continue, the labels and the stack heights at that point
// have to be stored in a stack.
AbstractAssembly::LabelID loopStart = m_assembly.newLabelId();
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
AbstractAssembly::LabelID postPart = m_assembly.newLabelId();
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();

m_assembly.setSourceLocation(_forLoop.location);
m_assembly.appendLabel(loopStart);
Expand All @@ -619,6 +615,8 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
m_assembly.appendInstruction(solidity::Instruction::ISZERO);
m_assembly.appendJumpToIf(loopEnd);

int const stackHeightBody = m_assembly.stackHeight();
m_context->forLoopStack.emplace(Context::ForLoopLabels{ {postPart, stackHeightBody}, {loopEnd, stackHeightBody} });
(*this)(_forLoop.body);

m_assembly.setSourceLocation(_forLoop.location);
Expand All @@ -631,7 +629,19 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
m_assembly.appendLabel(loopEnd);

finalizeBlock(_forLoop.pre, stackStartHeight);
m_context->forLoopStack.pop();
m_scope = originalScope;
checkStackHeight(&_forLoop);
}

void CodeTransform::operator()(Break const&)
{
yulAssert(false, "Code generation for break statement in Yul is not implemented yet.");
}

void CodeTransform::operator()(Continue const&)
{
yulAssert(false, "Code generation for continue statement in Yul is not implemented yet.");
}

void CodeTransform::operator()(Block const& _block)
Expand Down
18 changes: 18 additions & 0 deletions libyul/backends/evm/EVMCodeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <boost/variant.hpp>
#include <boost/optional.hpp>

#include <stack>

namespace langutil
{
class ErrorReporter;
Expand Down Expand Up @@ -57,6 +59,20 @@ struct CodeTransformContext
std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
std::map<Scope::Variable const*, int> variableStackHeights;
std::map<Scope::Variable const*, unsigned> variableReferences;

struct JumpInfo
{
AbstractAssembly::LabelID label; ///< Jump's LabelID to jump to.
int targetStackHeight; ///< Stack height after the jump.
};

struct ForLoopLabels
{
JumpInfo post; ///< Jump info for jumping to post branch.
JumpInfo done; ///< Jump info for jumping to done branch.
};

std::stack<ForLoopLabels> forLoopStack;
};

/**
Expand Down Expand Up @@ -166,6 +182,8 @@ class CodeTransform: public boost::static_visitor<>
void operator()(Switch const& _switch);
void operator()(FunctionDefinition const&);
void operator()(ForLoop const&);
void operator()(Break const&);
void operator()(Continue const&);
void operator()(Block const& _block);

private:
Expand Down
9 changes: 9 additions & 0 deletions libyul/optimiser/ASTCopier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ Statement ASTCopier::operator()(ForLoop const& _forLoop)
translate(_forLoop.body)
};
}
Statement ASTCopier::operator()(Break const& _break)
{
return Break{ _break };
}

Statement ASTCopier::operator()(Continue const& _continue)
{
return Continue{ _continue };
}

Statement ASTCopier::operator ()(Block const& _block)
{
Expand Down
4 changes: 4 additions & 0 deletions libyul/optimiser/ASTCopier.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class StatementCopier: public boost::static_visitor<Statement>
virtual Statement operator()(Switch const& _switch) = 0;
virtual Statement operator()(FunctionDefinition const&) = 0;
virtual Statement operator()(ForLoop const&) = 0;
virtual Statement operator()(Break const&) = 0;
virtual Statement operator()(Continue const&) = 0;
virtual Statement operator()(Block const& _block) = 0;
};

Expand All @@ -83,6 +85,8 @@ class ASTCopier: public ExpressionCopier, public StatementCopier
Statement operator()(Switch const& _switch) override;
Statement operator()(FunctionDefinition const&) override;
Statement operator()(ForLoop const&) override;
Statement operator()(Break const&) override;
Statement operator()(Continue const&) override;
Statement operator()(Block const& _block) override;

virtual Expression translate(Expression const& _expression);
Expand Down
8 changes: 8 additions & 0 deletions libyul/optimiser/ASTWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ void ASTModifier::operator()(ForLoop& _for)
(*this)(_for.body);
}

void ASTModifier::operator()(Break&)
{
}

void ASTModifier::operator()(Continue&)
{
}

void ASTModifier::operator()(Block& _block)
{
walkVector(_block.statements);
Expand Down
Loading