diff --git a/Changelog.md b/Changelog.md index ee1060471..1ae2f57ee 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.4.5 (unreleased) +Features: + * Do-while loops: support for a C-style do{}while(); control structure + ### 0.4.4 (2016-10-31) Bugfixes: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 597829d31..51f430150 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -2,14 +2,14 @@ Expressions and Control Structures ################################## -.. index:: if, else, while, for, break, continue, return, switch, goto +.. index:: if, else, while, do/while, for, break, continue, return, switch, goto Control Structures =================== Most of the control structures from C or JavaScript are available in Solidity except for ``switch`` and ``goto``. So -there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with +there is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with the usual semantics known from C or JavaScript. Parentheses can *not* be omitted for conditionals, but curly brances can be omitted diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 7ed4ddce8..6c3f52bcc 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1005,18 +1005,22 @@ public: SourceLocation const& _location, ASTPointer const& _docString, ASTPointer const& _condition, - ASTPointer const& _body + ASTPointer const& _body, + bool _isDoWhile ): - BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body) {} + BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body), + m_isDoWhile(_isDoWhile) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& condition() const { return *m_condition; } Statement const& body() const { return *m_body; } + bool isDoWhile() const { return m_isDoWhile; } private: ASTPointer m_condition; ASTPointer m_body; + bool m_isDoWhile; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index b573feda7..3fce11804 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -264,7 +264,11 @@ bool ASTJsonConverter::visit(IfStatement const& _node) bool ASTJsonConverter::visit(WhileStatement const& _node) { - addJsonNode(_node, "WhileStatement", {}, true); + addJsonNode( + _node, + _node.isDoWhile() ? "DoWhileStatement" : "WhileStatement", + {}, + true); return true; } diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index a9de457a7..272669688 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -208,7 +208,7 @@ bool ASTPrinter::visit(IfStatement const& _node) bool ASTPrinter::visit(WhileStatement const& _node) { - writeLine("WhileStatement"); + writeLine(_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement"); printSourcePart(_node); return goDeeper(); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebb84784e..1404963f1 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -611,12 +611,25 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement) m_breakTags.push_back(loopEnd); m_context << loopStart; - compileExpression(_whileStatement.condition()); - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); + + // While loops have the condition prepended + if (!_whileStatement.isDoWhile()) + { + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } _whileStatement.body().accept(*this); + // Do-while loops have the condition appended + if (_whileStatement.isDoWhile()) + { + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopEnd); + } + m_context.appendJumpTo(loopStart); m_context << loopEnd; diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 813fa3abe..5934d5936 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -410,6 +410,16 @@ bool Why3Translator::visit(WhileStatement const& _node) { addSourceFromDocStrings(_node.annotation()); + // Why3 does not appear to support do-while loops, + // so we will simulate them by performing a while + // loop with the body prepended once. + + if (_node.isDoWhile()) + { + visitIndentedUnlessBlock(_node.body()); + newLine(); + } + add("while "); _node.condition().accept(*this); newLine(); diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index d84ee10c6..586c41e95 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -41,7 +41,7 @@ ArrayTypeName = TypeName StorageLocation? '[' Expression? ']' StorageLocation = 'memory' | 'storage' Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | ForStatement | Block | +Statement = IfStatement | WhileStatement | DoWhileStatement | ForStatement | Block | ( PlaceholderStatement | Continue | Break | Return | Throw | SimpleStatement ) ';' @@ -51,6 +51,7 @@ WhileStatement = 'while' '(' Expression ')' Statement PlaceholderStatement = '_' SimpleStatement = VariableDefinition | ExpressionStatement ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement +DoWhileStatement = 'do' Statement 'while' '(' Expression ')' ';' Continue = 'continue' Break = 'break' Return = 'return' Expression? diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 0e99d1e73..52b536196 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -722,6 +722,8 @@ ASTPointer Parser::parseStatement() return parseIfStatement(docString); case Token::While: return parseWhileStatement(docString); + case Token::Do: + return parseDoWhileStatement(docString); case Token::For: return parseForStatement(docString); case Token::LBrace: @@ -816,9 +818,24 @@ ASTPointer Parser::parseWhileStatement(ASTPointer con expectToken(Token::RParen); ASTPointer body = parseStatement(); nodeFactory.setEndPositionFromNode(body); - return nodeFactory.createNode(_docString, condition, body); + return nodeFactory.createNode(_docString, condition, body, false); } +ASTPointer Parser::parseDoWhileStatement(ASTPointer const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Do); + ASTPointer body = parseStatement(); + expectToken(Token::While); + expectToken(Token::LParen); + ASTPointer condition = parseExpression(); + expectToken(Token::RParen); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode(_docString, condition, body, true); +} + + ASTPointer Parser::parseForStatement(ASTPointer const& _docString) { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 9c30cf608..26f347cb5 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -85,6 +85,7 @@ private: ASTPointer parseInlineAssembly(ASTPointer const& _docString = {}); ASTPointer parseIfStatement(ASTPointer const& _docString); ASTPointer parseWhileStatement(ASTPointer const& _docString); + ASTPointer parseDoWhileStatement(ASTPointer const& _docString); ASTPointer parseForStatement(ASTPointer const& _docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer parseSimpleStatement(ASTPointer const& _docString); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8600443d7..a1430b02c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -353,6 +353,34 @@ BOOST_AUTO_TEST_CASE(while_loop) testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5); } + +BOOST_AUTO_TEST_CASE(do_while_loop) +{ + char const* sourceCode = "contract test {\n" + " function f(uint n) returns(uint nfac) {\n" + " nfac = 1;\n" + " var i = 2;\n" + " do { nfac *= i++; } while (i <= n);\n" + " }\n" + "}\n"; + compileAndRun(sourceCode); + + auto do_while_loop_cpp = [](u256 const& n) -> u256 + { + u256 nfac = 1; + u256 i = 2; + do + { + nfac *= i++; + } + while (i <= n); + + return nfac; + }; + + testSolidityAgainstCppOnRange("f(uint256)", do_while_loop_cpp, 0, 5); +} + BOOST_AUTO_TEST_CASE(nested_loops) { // tests that break and continue statements in nested loops jump to the correct place