diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 026aef975..44cf39291 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -283,14 +283,6 @@ void WhileStatement::checkTypeRequirements() m_body->checkTypeRequirements(); } -void Continue::checkTypeRequirements() -{ -} - -void Break::checkTypeRequirements() -{ -} - void Return::checkTypeRequirements() { if (!m_expression) @@ -326,8 +318,6 @@ void VariableDefinition::checkTypeRequirements() void Assignment::checkTypeRequirements() { - //@todo lefthandside actually has to be assignable - // add a feature to the type system to check that m_leftHandSide->checkTypeRequirements(); if (!m_leftHandSide->isLvalue()) BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue.")); @@ -366,8 +356,8 @@ void UnaryOperation::checkTypeRequirements() void BinaryOperation::checkTypeRequirements() { - m_right->checkTypeRequirements(); m_left->checkTypeRequirements(); + m_right->checkTypeRequirements(); if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) m_commonType = m_left->getType(); else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) @@ -446,14 +436,6 @@ void Identifier::checkTypeRequirements() if (asserts(m_referencedDeclaration)) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved.")); - //@todo these dynamic casts here are not really nice... - // is i useful to have an AST visitor here? - // or can this already be done in NameAndTypeResolver? - // the only problem we get there is that in - // var x; - // x = 2; - // var y = x; - // the type of x is not yet determined. VariableDeclaration* variable = dynamic_cast(m_referencedDeclaration); if (variable) { diff --git a/libsolidity/AST.h b/libsolidity/AST.h index f42ff47d0..793ce863f 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -390,7 +390,7 @@ class Continue: public Statement public: Continue(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements() override {} }; class Break: public Statement @@ -398,7 +398,7 @@ class Break: public Statement public: Break(Location const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; - virtual void checkTypeRequirements() override; + virtual void checkTypeRequirements() override {} }; class Return: public Statement diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 654eceadb..d05552b9e 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -252,17 +252,15 @@ bool Compiler::visit(WhileStatement& _whileStatement) bool Compiler::visit(Continue&) { - if (asserts(!m_continueTags.empty())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Jump tag not available for \"continue\".")); - m_context.appendJumpTo(m_continueTags.back()); + if (!m_continueTags.empty()) + m_context.appendJumpTo(m_continueTags.back()); return false; } bool Compiler::visit(Break&) { - if (asserts(!m_breakTags.empty())) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Jump tag not available for \"break\".")); - m_context.appendJumpTo(m_breakTags.back()); + if (!m_breakTags.empty()) + m_context.appendJumpTo(m_breakTags.back()); return false; } diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index b28b8499a..116310aed 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -151,6 +151,55 @@ BOOST_AUTO_TEST_CASE(while_loop) BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(24))); } +BOOST_AUTO_TEST_CASE(break_outside_loop) +{ + // break and continue outside loops should be simply ignored + char const* sourceCode = "contract test {\n" + " function f(uint x) returns(uint y) {\n" + " break; continue; return 2;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(2))); +} + +BOOST_AUTO_TEST_CASE(nested_loops) +{ + // tests that break and continue statements in nested loops jump to the correct place + char const* sourceCode = "contract test {\n" + " function f(uint x) returns(uint y) {\n" + " while (x > 1) {\n" + " if (x == 10) break;\n" + " while (x > 5) {\n" + " if (x == 8) break;\n" + " x--;\n" + " if (x == 6) continue;\n" + " return x;\n" + " }\n" + " x--;\n" + " if (x == 3) continue;\n" + " break;\n" + " }\n" + " return x;\n" + " }\n" + "}\n"; + ExecutionFramework framework; + framework.compileAndRun(sourceCode); + BOOST_CHECK(framework.callFunction(0, u256(0)) == toBigEndian(u256(0))); + BOOST_CHECK(framework.callFunction(0, u256(1)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(2)) == toBigEndian(u256(1))); + BOOST_CHECK(framework.callFunction(0, u256(3)) == toBigEndian(u256(2))); + BOOST_CHECK(framework.callFunction(0, u256(4)) == toBigEndian(u256(2))); + BOOST_CHECK(framework.callFunction(0, u256(5)) == toBigEndian(u256(4))); + BOOST_CHECK(framework.callFunction(0, u256(6)) == toBigEndian(u256(5))); + BOOST_CHECK(framework.callFunction(0, u256(7)) == toBigEndian(u256(5))); + BOOST_CHECK(framework.callFunction(0, u256(8)) == toBigEndian(u256(7))); + BOOST_CHECK(framework.callFunction(0, u256(9)) == toBigEndian(u256(8))); + BOOST_CHECK(framework.callFunction(0, u256(10)) == toBigEndian(u256(10))); + BOOST_CHECK(framework.callFunction(0, u256(11)) == toBigEndian(u256(10))); +} + BOOST_AUTO_TEST_CASE(calling_other_functions) { // note that the index of a function is its index in the sorted sequence of functions