Skip to content

Commit a122e93

Browse files
[Yul] Adds break/continue keywords in parsing and EVM code generation.
1 parent 07a1aa7 commit a122e93

16 files changed

+111
-5
lines changed

libsolidity/analysis/ViewPureChecker.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ class AssemblyViewPureChecker: public boost::static_visitor<void>
9494
(*this)(_for.body);
9595
(*this)(_for.post);
9696
}
97+
void operator()(yul::Break const&)
98+
{
99+
}
100+
void operator()(yul::Continue const&)
101+
{
102+
}
97103
void operator()(yul::Block const& _block)
98104
{
99105
for (auto const& s: _block.statements)

libyul/AsmAnalysis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class AsmAnalyzer: public boost::static_visitor<bool>
9393
bool operator()(If const& _if);
9494
bool operator()(Switch const& _switch);
9595
bool operator()(ForLoop const& _forLoop);
96+
bool operator()(Break const&) { return true; }
97+
bool operator()(Continue const&) { return true; }
9698
bool operator()(Block const& _block);
9799

98100
private:

libyul/AsmData.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value;
7878
/// Switch statement
7979
struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; };
8080
struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; };
81+
/// Break statement (valid within for loop)
82+
struct Break { langutil::SourceLocation location; };
83+
/// Continue statement (valid within for loop)
84+
struct Continue { langutil::SourceLocation location; };
8185

8286
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
8387
{

libyul/AsmDataForward.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@ struct If;
4141
struct Switch;
4242
struct Case;
4343
struct ForLoop;
44+
struct Break;
45+
struct Continue;
4446
struct ExpressionStatement;
4547
struct Block;
4648

4749
struct TypedName;
4850

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

5254
}

libyul/AsmParser.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <libyul/AsmParser.h>
2424
#include <liblangutil/Scanner.h>
2525
#include <liblangutil/ErrorReporter.h>
26+
#include <libdevcore/Utils.h>
2627

2728
#include <boost/algorithm/string.hpp>
2829

@@ -104,6 +105,28 @@ Statement Parser::parseStatement()
104105
}
105106
case Token::For:
106107
return parseForLoop();
108+
case Token::Break:
109+
if (m_forLoopDepth > 0)
110+
{
111+
m_scanner->next();
112+
return Statement{ createWithLocation<Break>() };
113+
}
114+
else
115+
{
116+
m_errorReporter.syntaxError(location(), "Keyword break outside for-loop is not allowed.");
117+
break;
118+
}
119+
case Token::Continue:
120+
if (m_forLoopDepth > 0)
121+
{
122+
m_scanner->next();
123+
return Statement{ createWithLocation<Continue>() };
124+
}
125+
else
126+
{
127+
m_errorReporter.syntaxError(location(), "Keyword continue outside for-loop is not allowed.");
128+
break;
129+
}
107130
case Token::Assign:
108131
{
109132
if (m_dialect->flavour != AsmFlavour::Loose)
@@ -245,6 +268,7 @@ ForLoop Parser::parseForLoop()
245268
{
246269
RecursionGuard recursionGuard(*this);
247270
ForLoop forLoop = createWithLocation<ForLoop>();
271+
auto loopDepthGuard = scopedIncrement(m_forLoopDepth);
248272
expectToken(Token::For);
249273
forLoop.pre = parseBlock();
250274
forLoop.condition = make_unique<Expression>(parseExpression());

libyul/AsmParser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Parser: public langutil::ParserBase
3939
{
4040
public:
4141
explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
42-
ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
42+
ParserBase(_errorReporter), m_dialect(std::move(_dialect)), m_forLoopDepth{0} {}
4343

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

8888
private:
8989
std::shared_ptr<Dialect> m_dialect;
90+
int m_forLoopDepth;
9091
};
9192

9293
}

libyul/AsmPrinter.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) const
224224
return out;
225225
}
226226

227+
string AsmPrinter::operator()(Break const&) const
228+
{
229+
return "break";
230+
}
231+
232+
string AsmPrinter::operator()(Continue const&) const
233+
{
234+
return "continue";
235+
}
236+
227237
string AsmPrinter::operator()(Block const& _block) const
228238
{
229239
if (_block.statements.empty())

libyul/AsmPrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class AsmPrinter: public boost::static_visitor<std::string>
5050
std::string operator()(If const& _if) const;
5151
std::string operator()(Switch const& _switch) const;
5252
std::string operator()(ForLoop const& _forLoop) const;
53+
std::string operator()(Break const& _break) const;
54+
std::string operator()(Continue const& _continue) const;
5355
std::string operator()(Block const& _block) const;
5456

5557
private:

libyul/AsmScopeFiller.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class ScopeFiller: public boost::static_visitor<bool>
6363
bool operator()(If const& _if);
6464
bool operator()(Switch const& _switch);
6565
bool operator()(ForLoop const& _forLoop);
66+
bool operator()(Break const&) { return true; }
67+
bool operator()(Continue const&) { return true; }
6668
bool operator()(Block const& _block);
6769

6870
private:

libyul/backends/evm/EVMCodeTransform.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ void VariableReferenceCounter::operator()(ForLoop const& _forLoop)
6868
m_scope = originalScope;
6969
}
7070

71-
7271
void VariableReferenceCounter::operator()(Block const& _block)
7372
{
7473
Scope* originalScope = m_scope;
@@ -91,7 +90,6 @@ void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
9190
));
9291
}
9392

94-
9593
CodeTransform::CodeTransform(
9694
AbstractAssembly& _assembly,
9795
AsmAnalysisInfo& _analysisInfo,
@@ -619,8 +617,10 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
619617
// TODO: When we implement break and continue, the labels and the stack heights at that point
620618
// have to be stored in a stack.
621619
AbstractAssembly::LabelID loopStart = m_assembly.newLabelId();
622-
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
623620
AbstractAssembly::LabelID postPart = m_assembly.newLabelId();
621+
AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId();
622+
623+
m_context->forLoopStack.emplace(Context::ForLoopLabels{postPart, loopEnd});
624624

625625
m_assembly.setSourceLocation(_forLoop.location);
626626
m_assembly.appendLabel(loopStart);
@@ -642,9 +642,24 @@ void CodeTransform::operator()(ForLoop const& _forLoop)
642642
m_assembly.appendLabel(loopEnd);
643643

644644
finalizeBlock(_forLoop.pre, stackStartHeight);
645+
m_context->forLoopStack.pop();
645646
m_scope = originalScope;
646647
}
647648

649+
void CodeTransform::operator()(Break const& _break)
650+
{
651+
yulAssert(!m_context->forLoopStack.empty(), "Invalid break-statement. Requires surrounding for-loop in code generation.");
652+
m_assembly.setSourceLocation(_break.location);
653+
m_assembly.appendLabel(m_context->forLoopStack.top().done);
654+
}
655+
656+
void CodeTransform::operator()(Continue const& _continue)
657+
{
658+
yulAssert(!m_context->forLoopStack.empty(), "Invalid continue-statement. Requires surrounding for-loop in code generation.");
659+
m_assembly.setSourceLocation(_continue.location);
660+
m_assembly.appendLabel(m_context->forLoopStack.top().post);
661+
}
662+
648663
void CodeTransform::operator()(Block const& _block)
649664
{
650665
Scope* originalScope = m_scope;

0 commit comments

Comments
 (0)