Skip to content

Commit 05e2d36

Browse files
Christian Parpartchristianparpart
authored andcommitted
[Yul] Adds break/continue statements and some general tests for for-loop syntax.
1 parent 4704ef8 commit 05e2d36

24 files changed

+277
-10
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.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <libyul/AsmScope.h>
2626
#include <libyul/AsmAnalysisInfo.h>
2727
#include <libyul/Utilities.h>
28+
#include <libyul/Exceptions.h>
2829

2930
#include <liblangutil/ErrorReporter.h>
3031

@@ -33,6 +34,7 @@
3334

3435
#include <memory>
3536
#include <functional>
37+
#include <utility>
3638

3739
using namespace std;
3840
using namespace dev;
@@ -472,7 +474,7 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
472474
{
473475
solAssert(_for.condition, "");
474476

475-
Scope* originalScope = m_currentScope;
477+
Scope* outerScope = m_currentScope;
476478

477479
bool success = true;
478480
if (!(*this)(_for.pre))
@@ -485,18 +487,37 @@ bool AsmAnalyzer::operator()(ForLoop const& _for)
485487
if (!expectExpression(*_for.condition))
486488
success = false;
487489
m_stackHeight--;
490+
491+
// backup outer for-loop & create new state
492+
auto outerForLoop = m_currentForLoop;
493+
m_currentForLoop = &_for;
494+
488495
if (!(*this)(_for.body))
489496
success = false;
497+
490498
if (!(*this)(_for.post))
491499
success = false;
492500

493501
m_stackHeight -= scope(&_for.pre).numberOfVariables();
494502
m_info.stackHeightInfo[&_for] = m_stackHeight;
495-
m_currentScope = originalScope;
503+
m_currentScope = outerScope;
504+
m_currentForLoop = outerForLoop;
496505

497506
return success;
498507
}
499508

509+
bool AsmAnalyzer::operator()(Break const& _break)
510+
{
511+
m_info.stackHeightInfo[&_break] = m_stackHeight;
512+
return true;
513+
}
514+
515+
bool AsmAnalyzer::operator()(Continue const& _continue)
516+
{
517+
m_info.stackHeightInfo[&_continue] = m_stackHeight;
518+
return true;
519+
}
520+
500521
bool AsmAnalyzer::operator()(Block const& _block)
501522
{
502523
bool success = true;

libyul/AsmAnalysis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <boost/optional.hpp>
3535

3636
#include <functional>
37+
#include <list>
3738
#include <memory>
3839

3940
namespace langutil
@@ -93,6 +94,8 @@ class AsmAnalyzer: public boost::static_visitor<bool>
9394
bool operator()(If const& _if);
9495
bool operator()(Switch const& _switch);
9596
bool operator()(ForLoop const& _forLoop);
97+
bool operator()(Break const&);
98+
bool operator()(Continue const&);
9699
bool operator()(Block const& _block);
97100

98101
private:
@@ -124,6 +127,7 @@ class AsmAnalyzer: public boost::static_visitor<bool>
124127
langutil::EVMVersion m_evmVersion;
125128
std::shared_ptr<Dialect> m_dialect;
126129
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
130+
ForLoop const* m_currentForLoop = nullptr;
127131
};
128132

129133
}

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: 36 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/Common.h>
2627

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

@@ -104,6 +105,32 @@ Statement Parser::parseStatement()
104105
}
105106
case Token::For:
106107
return parseForLoop();
108+
case Token::Break:
109+
if (m_insideForLoopBody)
110+
{
111+
auto stmt = Statement{ createWithLocation<Break>() };
112+
m_scanner->next();
113+
return stmt;
114+
}
115+
else
116+
{
117+
m_errorReporter.syntaxError(location(), "Keyword break outside for-loop body is not allowed.");
118+
m_scanner->next();
119+
return {};
120+
}
121+
case Token::Continue:
122+
if (m_insideForLoopBody)
123+
{
124+
auto stmt = Statement{ createWithLocation<Continue>() };
125+
m_scanner->next();
126+
return stmt;
127+
}
128+
else
129+
{
130+
m_errorReporter.syntaxError(location(), "Keyword continue outside for-loop body is not allowed.");
131+
m_scanner->next();
132+
return {};
133+
}
107134
case Token::Assign:
108135
{
109136
if (m_dialect->flavour != AsmFlavour::Loose)
@@ -243,13 +270,19 @@ Case Parser::parseCase()
243270

244271
ForLoop Parser::parseForLoop()
245272
{
273+
bool outerForLoopBody = m_insideForLoopBody;
274+
m_insideForLoopBody = false;
275+
246276
RecursionGuard recursionGuard(*this);
247277
ForLoop forLoop = createWithLocation<ForLoop>();
248278
expectToken(Token::For);
249279
forLoop.pre = parseBlock();
250280
forLoop.condition = make_unique<Expression>(parseExpression());
251281
forLoop.post = parseBlock();
282+
283+
m_insideForLoopBody = true;
252284
forLoop.body = parseBlock();
285+
m_insideForLoopBody = outerForLoopBody;
253286
forLoop.location.end = forLoop.body.location.end;
254287
return forLoop;
255288
}
@@ -455,6 +488,9 @@ VariableDeclaration Parser::parseVariableDeclaration()
455488
FunctionDefinition Parser::parseFunctionDefinition()
456489
{
457490
RecursionGuard recursionGuard(*this);
491+
auto outerForLoopBody = m_insideForLoopBody;
492+
m_insideForLoopBody = false;
493+
ScopeGuard restoreInsideForLoopBody{[&]() { m_insideForLoopBody = outerForLoopBody; }};
458494
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
459495
expectToken(Token::Function);
460496
funDef.name = expectAsmIdentifier();

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_insideForLoopBody{false} {}
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+
bool m_insideForLoopBody;
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:

0 commit comments

Comments
 (0)