aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorYoichi Hirai <i@yoichihirai.com>2016-12-14 23:31:28 +0800
committerGitHub <noreply@github.com>2016-12-14 23:31:27 +0800
commit18f8f29c0a3ed9404f2950c11d77f85d0117c6d5 (patch)
tree11fcb5408f609ab3e8f35d7e41fd7a42df0d1837 /libsolidity
parente53d12557162275a135a4ae086ae05691bc23fa3 (diff)
parent08a11e309f241e602cc4754b6322e4bb0da57b17 (diff)
downloaddexon-solidity-18f8f29c0a3ed9404f2950c11d77f85d0117c6d5.tar.gz
dexon-solidity-18f8f29c0a3ed9404f2950c11d77f85d0117c6d5.tar.zst
dexon-solidity-18f8f29c0a3ed9404f2950c11d77f85d0117c6d5.zip
Merge pull request #1487 from ethereum/shift-ops
Shift operators
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/ast/Types.cpp32
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp122
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h6
3 files changed, 130 insertions, 30 deletions
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index d9660bc0..03ff8471 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -251,6 +251,19 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members;
}
+bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType)
+{
+ // Disable >>> here.
+ if (_operator == Token::SHR)
+ return false;
+ else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType))
+ return !otherInt->isAddress();
+ else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
+ return otherRat->integerType() && !otherRat->integerType()->isSigned();
+ else
+ return false;
+}
+
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
@@ -340,6 +353,17 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
_other->category() != category()
)
return TypePointer();
+ if (Token::isShiftOp(_operator))
+ {
+ // Shifts are not symmetric with respect to the type
+ if (isAddress())
+ return TypePointer();
+ if (isValidShiftAndAmountType(_operator, *_other))
+ return shared_from_this();
+ else
+ return TypePointer();
+ }
+
auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point
if (!commonType)
return TypePointer();
@@ -954,6 +978,14 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
+ if (Token::isShiftOp(_operator))
+ {
+ if (isValidShiftAndAmountType(_operator, *_other))
+ return shared_from_this();
+ else
+ return TypePointer();
+ }
+
auto commonType = dynamic_pointer_cast<FixedBytesType const>(Type::commonType(shared_from_this(), _other));
if (!commonType)
return TypePointer();
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 5748d818..a7fb8408 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -197,21 +197,39 @@ bool ExpressionCompiler::visit(Conditional const& _condition)
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
+ Token::Value op = _assignment.assignmentOperator();
+ Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op);
+ Type const& leftType = *_assignment.leftHandSide().annotation().type;
+ if (leftType.category() == Type::Category::Tuple)
+ {
+ solAssert(*_assignment.annotation().type == TupleType(), "");
+ solAssert(op == Token::Assign, "");
+ }
+ else
+ solAssert(*_assignment.annotation().type == leftType, "");
+ bool cleanupNeeded = false;
+ if (op != Token::Assign)
+ cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp);
_assignment.rightHandSide().accept(*this);
// Perform some conversion already. This will convert storage types to memory and literals
// to their actual type, but will not convert e.g. memory to storage.
- TypePointer type = _assignment.rightHandSide().annotation().type->closestTemporaryType(
- _assignment.leftHandSide().annotation().type
- );
- utils().convertType(*_assignment.rightHandSide().annotation().type, *type);
+ TypePointer rightIntermediateType;
+ if (op != Token::Assign && Token::isShiftOp(binOp))
+ rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType();
+ else
+ rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
+ _assignment.leftHandSide().annotation().type
+ );
+ utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded);
_assignment.leftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved.");
- Token::Value op = _assignment.assignmentOperator();
- if (op != Token::Assign) // compound assignment
+ if (op == Token::Assign)
+ m_currentLValue->storeValue(*rightIntermediateType, _assignment.location());
+ else // compound assignment
{
- solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
+ solAssert(leftType.isValueType(), "Compound operators only available for value types.");
unsigned lvalueSize = m_currentLValue->sizeOnStack();
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
if (lvalueSize > 0)
@@ -221,7 +239,15 @@ 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);
+ utils().convertType(leftType, leftType, cleanupNeeded);
+
+ if (Token::isShiftOp(binOp))
+ appendShiftOperatorCode(binOp, leftType, *rightIntermediateType);
+ else
+ {
+ solAssert(leftType == *rightIntermediateType, "");
+ appendOrdinaryBinaryOperatorCode(binOp, leftType);
+ }
if (lvalueSize > 0)
{
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
@@ -229,8 +255,8 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
for (unsigned i = 0; i < itemSize; ++i)
m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP;
}
+ m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location());
}
- m_currentLValue->storeValue(*type, _assignment.location());
m_currentLValue.reset();
return false;
}
@@ -351,20 +377,19 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
Expression const& leftExpression = _binaryOperation.leftExpression();
Expression const& rightExpression = _binaryOperation.rightExpression();
solAssert(!!_binaryOperation.annotation().commonType, "");
- Type const& commonType = *_binaryOperation.annotation().commonType;
+ TypePointer const& commonType = _binaryOperation.annotation().commonType;
Token::Value const c_op = _binaryOperation.getOperator();
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
appendAndOrOperatorCode(_binaryOperation);
- else if (commonType.category() == Type::Category::RationalNumber)
- m_context << commonType.literalValue(nullptr);
+ else if (commonType->category() == Type::Category::RationalNumber)
+ m_context << commonType->literalValue(nullptr);
else
{
- bool cleanupNeeded = false;
- if (Token::isCompareOp(c_op))
- cleanupNeeded = true;
- if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod))
- cleanupNeeded = true;
+ bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
+
+ TypePointer leftTargetType = commonType;
+ TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType;
// for commutative operators, push the literal as late as possible to allow improved optimization
auto isLiteral = [](Expression const& _e)
@@ -375,21 +400,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
if (swap)
{
leftExpression.accept(*this);
- utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded);
rightExpression.accept(*this);
- utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded);
}
else
{
rightExpression.accept(*this);
- utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded);
leftExpression.accept(*this);
- utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded);
}
- if (Token::isCompareOp(c_op))
- appendCompareOperatorCode(c_op, commonType);
+ if (Token::isShiftOp(c_op))
+ // shift only cares about the signedness of both sides
+ appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType);
+ else if (Token::isCompareOp(c_op))
+ appendCompareOperatorCode(c_op, *commonType);
else
- appendOrdinaryBinaryOperatorCode(c_op, commonType);
+ appendOrdinaryBinaryOperatorCode(c_op, *commonType);
}
// do not visit the child nodes, we already did that explicitly
@@ -1326,8 +1354,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 +1416,45 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
}
}
-void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
+void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType)
{
- solUnimplemented("Shift operators not yet implemented.");
+ // stack: shift_amount value_to_shift
+
+ bool c_valueSigned = false;
+ if (auto valueType = dynamic_cast<IntegerType const*>(&_valueType))
+ c_valueSigned = valueType->isSigned();
+ else
+ solAssert(dynamic_cast<FixedBytesType const*>(&_valueType), "Only integer and fixed bytes type supported for shifts.");
+
+ // The amount can be a RationalNumberType too.
+ bool c_amountSigned = false;
+ if (auto amountType = dynamic_cast<RationalNumberType const*>(&_shiftAmountType))
+ {
+ // This should be handled by the type checker.
+ solAssert(amountType->integerType(), "");
+ solAssert(!amountType->integerType()->isSigned(), "");
+ }
+ else if (auto amountType = dynamic_cast<IntegerType const*>(&_shiftAmountType))
+ c_amountSigned = amountType->isSigned();
+ else
+ solAssert(false, "Invalid shift amount type.");
+
+ // shift by negative amount throws exception
+ if (c_amountSigned)
+ {
+ 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_valueSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::SHR:
- break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator."));
}
@@ -1688,6 +1742,16 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
setLValue<StorageItem>(_expression, *_expression.annotation().type);
}
+bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op)
+{
+ if (Token::isCompareOp(_op) || Token::isShiftOp(_op))
+ return true;
+ else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod))
+ return true;
+ else
+ return false;
+}
+
CompilerUtils ExpressionCompiler::utils()
{
return CompilerUtils(m_context);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index f08bded9..d0a8ac15 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& _valueType, Type const& _shiftAmountType);
/// @}
/// Appends code to call a function of the given type with the given arguments.
@@ -117,6 +117,10 @@ private:
template <class _LValueType, class... _Arguments>
void setLValue(Expression const& _expression, _Arguments const&... _arguments);
+ /// @returns true if the operator applied to the given type requires a cleanup prior to the
+ /// operation.
+ bool cleanupNeededForOp(Type::Category _type, Token::Value _op);
+
/// @returns the CompilerUtils object containing the current context.
CompilerUtils utils();