aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md1
-rw-r--r--libsolidity/ast/Types.cpp5
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp49
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp298
5 files changed, 346 insertions, 9 deletions
diff --git a/Changelog.md b/Changelog.md
index 1eb90c22..9bc465ee 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,7 @@
### 0.4.7 (unreleased)
Features:
+ * Bitshift operators.
* Type checker: Warn when ``msg.value`` is used in non-payable function.
* Code generator: Inject the Swarm hash of a metadata file into the bytecode.
* Optimizer: Some dead code elimination.
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index d9660bc0..6b4dd432 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -347,6 +347,11 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
// All integer types can be compared
if (Token::isCompareOp(_operator))
return commonType;
+ // Disable >>> here.
+ if (_operator == Token::SHR)
+ return TypePointer();
+ if (Token::isShiftOp(_operator) && !isAddress()) // && !_other->isAddress())
+ return shared_from_this();
if (Token::isBooleanOp(_operator))
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 5748d818..d28ffed8 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -211,6 +211,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
Token::Value op = _assignment.assignmentOperator();
if (op != Token::Assign) // compound assignment
{
+ Token::Value target_op = Token::AssignmentToBinaryOp(op);
solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
unsigned lvalueSize = m_currentLValue->sizeOnStack();
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
@@ -221,7 +222,11 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
// value lvalue_ref value lvalue_ref
}
m_currentLValue->retrieveValue(_assignment.location(), true);
- appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type);
+ if (Token::isShiftOp(target_op))
+ // shift only cares about the signedness of both sides
+ appendShiftOperatorCode(target_op, *_assignment.leftHandSide().annotation().type, *_assignment.rightHandSide().annotation().type);
+ else
+ appendOrdinaryBinaryOperatorCode(target_op, *_assignment.annotation().type);
if (lvalueSize > 0)
{
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
@@ -361,7 +366,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
else
{
bool cleanupNeeded = false;
- if (Token::isCompareOp(c_op))
+ if (Token::isCompareOp(c_op) || Token::isShiftOp(c_op))
cleanupNeeded = true;
if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod))
cleanupNeeded = true;
@@ -386,7 +391,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
leftExpression.accept(*this);
utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
}
- if (Token::isCompareOp(c_op))
+ if (Token::isShiftOp(c_op))
+ // shift only cares about the signedness of both sides
+ appendShiftOperatorCode(c_op, *leftExpression.annotation().type, *rightExpression.annotation().type);
+ else if (Token::isCompareOp(c_op))
appendCompareOperatorCode(c_op, commonType);
else
appendOrdinaryBinaryOperatorCode(c_op, commonType);
@@ -1326,8 +1334,6 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator
appendArithmeticOperatorCode(_operator, _type);
else if (Token::isBitOp(_operator))
appendBitOperatorCode(_operator);
- else if (Token::isShiftOp(_operator))
- appendShiftOperatorCode(_operator);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator."));
}
@@ -1390,17 +1396,44 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
}
}
-void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
+void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _leftType, Type const& _rightType)
{
- solUnimplemented("Shift operators not yet implemented.");
+ // stack: rvalue lvalue
+
+ bool c_leftSigned = false;
+ if (auto leftType = dynamic_cast<IntegerType const*>(&_leftType))
+ c_leftSigned = leftType->isSigned();
+ else
+ solUnimplemented("Only IntegerType can be shifted.");
+
+ // The RValue can be a RationalNumberType too.
+ bool c_rightSigned = false;
+ if (auto rightType = dynamic_cast<RationalNumberType const*>(&_rightType))
+ {
+ solAssert(rightType->integerType(), "integerType() called for fractional number.");
+ c_rightSigned = rightType->integerType()->isSigned();
+ }
+ else if (auto rightType = dynamic_cast<IntegerType const*>(&_rightType))
+ c_rightSigned = rightType->isSigned();
+ else
+ solUnimplemented("Not implemented yet - FixedPointType.");
+
+ // shift with negative rvalue throws exception
+ if (c_rightSigned)
+ {
+ m_context << u256(0) << Instruction::DUP3 << Instruction::SLT;
+ m_context.appendConditionalJumpTo(m_context.errorTag());
+ }
+
switch (_operator)
{
case Token::SHL:
+ m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::MUL;
break;
case Token::SAR:
+ m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_leftSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::SHR:
- break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
}
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index f08bded9..e6cf382c 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -91,7 +91,7 @@ private:
void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type);
void appendBitOperatorCode(Token::Value _operator);
- void appendShiftOperatorCode(Token::Value _operator);
+ void appendShiftOperatorCode(Token::Value _operator, Type const& _leftType, Type const& _rightType);
/// @}
/// Appends code to call a function of the given type with the given arguments.
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 2df6e9f2..837caa2d 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -8456,6 +8456,304 @@ BOOST_AUTO_TEST_CASE(shift_negative_constant_right)
BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(-0x42)));
}
+BOOST_AUTO_TEST_CASE(shift_left)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint a, uint b) returns (uint) {
+ return a << b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)) == encodeArgs(u256(0x426600)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)) == encodeArgs(u256(0x42660000)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)) == encodeArgs(u256(0x84cc0000)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)) == fromHex("4266000000000000000000000000000000000000000000000000000000000000"));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_left_uint32)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint32 a, uint32 b) returns (uint) {
+ return a << b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)) == encodeArgs(u256(0x426600)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)) == encodeArgs(u256(0x42660000)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)) == encodeArgs(u256(0x84cc0000)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(32)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_left_uint8)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint8 a, uint8 b) returns (uint) {
+ return a << b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)) == encodeArgs(u256(0x66)));
+ BOOST_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_left_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint a, uint b) returns (uint) {
+ a <<= b;
+ return a;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)) == encodeArgs(u256(0x426600)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)) == encodeArgs(u256(0x42660000)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)) == encodeArgs(u256(0x84cc0000)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)) == fromHex("4266000000000000000000000000000000000000000000000000000000000000"));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint a, uint b) returns (uint) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)) == encodeArgs(u256(0x42)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_garbled)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint8 a, uint8 b) returns (uint) {
+ assembly {
+ a := 0xffffffff
+ }
+ // Higher bits should be cleared before the shift
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x0), u256(4)) == encodeArgs(u256(0xf)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_uint32)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint32 a, uint32 b) returns (uint) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)) == encodeArgs(u256(0x42)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_uint8)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint8 a, uint8 b) returns (uint) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)) == encodeArgs(u256(0x66)));
+ BOOST_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)) == encodeArgs(u256(0x0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(uint a, uint b) returns (uint) {
+ a >>= b;
+ return a;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)) == encodeArgs(u256(0x4266)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)) == encodeArgs(u256(0x42)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int a, int b) returns (int) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)) == encodeArgs(u256(-4266)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)) == encodeArgs(u256(-16)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int a, int b) returns (int) {
+ a >>= b;
+ return a;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)) == encodeArgs(u256(-4266)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)) == encodeArgs(u256(-16)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_negative_rvalue)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int a, int b) returns (int) {
+ return a << b;
+ }
+ function g(int a, int b) returns (int) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)) == encodeArgs());
+}
+
+BOOST_AUTO_TEST_CASE(shift_negative_rvalue_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int a, int b) returns (int) {
+ a <<= b;
+ return a;
+ }
+ function g(int a, int b) returns (int) {
+ a >>= b;
+ return a;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)) == encodeArgs());
+ BOOST_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)) == encodeArgs());
+}
+
+BOOST_AUTO_TEST_CASE(shift_constant_left_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint a) {
+ a = 0x42;
+ a <<= 8;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x4200)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_constant_right_assignment)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint a) {
+ a = 0x4200;
+ a >>= 8;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x42)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_cleanup)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint16 x) {
+ x = 0xffff;
+ x += 32;
+ x <<= 8;
+ x >>= 16;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_cleanup_garbled)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint8 x) {
+ assembly {
+ x := 0xffff
+ }
+ x >>= 8;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_overflow)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function leftU(uint8 x, uint8 y) returns (uint8) {
+ return x << y;
+ }
+ function leftS(int8 x, int8 y) returns (int8) {
+ return x << y;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ BOOST_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 8) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 1) == encodeArgs(u256(254)));
+ BOOST_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 0) == encodeArgs(u256(255)));
+
+ BOOST_CHECK(callContractFunction("leftS(int8,int8)", 1, 7) == encodeArgs(u256(128)));
+ BOOST_CHECK(callContractFunction("leftS(int8,int8)", 1, 6) == encodeArgs(u256(64)));
+}
+
BOOST_AUTO_TEST_CASE(inline_assembly_in_modifiers)
{
char const* sourceCode = R"(