diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 1d3f23e69..beda01322 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -650,6 +650,9 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty case Token::Mod: m_context << (c_isSigned ? eth::Instruction::SMOD : eth::Instruction::MOD); break; + case Token::Exp: + m_context << eth::Instruction::EXP; + break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown arithmetic operator.")); } diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index fc4bdb6b5..fbe3ea974 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -465,8 +465,14 @@ void Scanner::scanToken() token = Token::Sub; break; case '*': - // * *= - token = selectToken('=', Token::AssignMul, Token::Mul); + // * ** *= + advance(); + if (m_char == '*') + token = selectToken(Token::Exp); + else if (m_char == '=') + token = selectToken(Token::AssignMul); + else + token = Token::Mul; break; case '%': // % %= diff --git a/libsolidity/Token.h b/libsolidity/Token.h index b913f0cec..ac8c618fa 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -118,6 +118,7 @@ namespace solidity T(Mul, "*", 13) \ T(Div, "/", 13) \ T(Mod, "%", 13) \ + T(Exp, "**", 14) \ \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ @@ -361,10 +362,10 @@ public: // Predicates static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Mod; } + static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Mod; } + static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } static bool isCompareOp(Value op) { return Equal <= op && op <= In; } static Value AssignmentToBinaryOp(Value op) diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 6fd9e8b4c..dcc0738d7 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -320,6 +320,16 @@ TypePointer IntegerConstantType::binaryOperatorResult(Token::Value _operator, Ty return TypePointer(); value = m_value % other.m_value; break; + case Token::Exp: + if (other.m_value < 0) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exponent can't be negative")); + else + { + value = boost::multiprecision::powm(m_value, other.m_value, bigint(2) << 256); + if (value >= (bigint(1) << 256)) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("exp result overflowed")); + } + break; default: return TypePointer(); } diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 31b80894e..748110145 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -56,6 +56,36 @@ BOOST_AUTO_TEST_CASE(empty_contract) BOOST_CHECK(callContractFunction("i_am_not_there()", bytes()).empty()); } +BOOST_AUTO_TEST_CASE(exp_operator) +{ + char const* sourceCode = R"( + contract test { + function f(uint a) returns(uint d) { return 2 ** a; } + })"; + compileAndRun(sourceCode); + testSolidityAgainstCppOnRange("f(uint256)", [](u256 const& a) -> u256 { return u256(1 << a.convert_to()); }, 0, 16); +} + +BOOST_AUTO_TEST_CASE(exp_operator_const) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint d) { return 2 ** 3; } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(8))); +} + +BOOST_AUTO_TEST_CASE(exp_operator_const_signed) +{ + char const* sourceCode = R"( + contract test { + function f() returns(int d) { return -2 ** 3; } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(-8))); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = "contract test {\n" diff --git a/test/SolidityParser.cpp b/test/SolidityParser.cpp index 7af99567b..84f36170f 100644 --- a/test/SolidityParser.cpp +++ b/test/SolidityParser.cpp @@ -387,6 +387,17 @@ BOOST_AUTO_TEST_CASE(complex_expression) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(exp_expression) +{ + char const* text = R"( + contract test { + function fun(uint256 a) { + uint256 x = 3 ** a; + } + })"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_CASE(while_loop) { char const* text = "contract test {\n"