-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support shift operators for variables #527
Conversation
Nice! Some comments:
|
You mean signed integers? My understanding is the following: all shift operations should work on the internal representation of numbers, which is two's complement for The different shifts:
The above is taken from Javascript and is what the Solidity parser had. I am not fully convinced we need SAR - if omit it, I would suggest to have >> for SHR. |
I would hold off on this until after the fixed point and rationals are implemented....there are some major changes that kind of dork what you're trying to do in your set up :) |
@axic no I mean integers in themselves. There is no such thing as "shifting an integer", it is just a number, you should not have to assume that it is in two's complement. At the point where you explicitly convert the integer into a Having said that: I realize how used people are to applying shift operators to integers... So if we add shift operators, I would prefer to drop One advantage of that is that we can say that "shifting an integer" means multiplying / dividing by appropriate powers of two and thus get rid of the number representation issue. Concerning the break with javascript: Solidity is typed, so the distinction between |
consider getting rid of the |
@chriseth It is more clear now. Coming from a C background, I only have a notion of "integers" which behave like "bytesNN". Do I understand it right that you want a left and right shift operator, denoted by << (SHL) and >> (SHR) respectively and that it behaves according to the type information. For signed integers the right shift would behave like an arithmetic right shift (= Is this correct? I think @VoR0220 added support for shift operations on constants as a PR to my branch. I'll merge that soon. |
Following up with the previous question. Do we agree with the following?
If yes, I'll push an update later today. |
@@ -625,6 625,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ | |||
{ | |||
rational value; | |||
bool fractional = isFractional() || other.isFractional(); | |||
using boost::multiprecision::pow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't move that up here, it is only three letters and the distance between declaration and usage is quite high.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my bad...I did that. Mainly because its being reused...figured it would be unnecessary to have multiple declarations.
As per our discussion, for the moment the following assumptions are made:
|
Should I change this to include @@ -363,7 363,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
bool cleanupNeeded = false;
if (Token::isCompareOp(c_op))
cleanupNeeded = true;
- if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod))
if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod || Token::isShiftOp(c_op)))
cleanupNeeded = true;
// for commutative operators, push the literal as late as possible to allow improved optimization Also where do I add the check for negative right value? The same visitor? After cleanup? Something like this? @@ -386,6 386,11 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
leftExpression.accept(*this);
utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
}
if (Token::isShiftOp(c_op))
{
IntegerType const& type = dynamic_cast<IntegerType const&>(*rightExpression.annotation().type);
solAssert(!type.isSigned(), "Shift with negative right value is not supported.");
}
if (Token::isCompareOp(c_op))
appendCompareOperatorCode(c_op, commonType);
else |
I think it would be good to perform cleanup before every shift operation. The cleanup for the left operand is not necessary for left shifts, but I think it is fine to do it still. The check that the right operand is not negative has to be done in asm. Even if the right operand has a signed type, it can still be positive. Finally, I think we should ensure that the second operand has to be an integer (i.e. not a bytesN) - please add a test for that, not sure if we already do. |
I think it is good to promote the |
@axic what's the status of this? |
@VoR0220 I've been away, will look into my open Solidity issues this week. |
Any idea how this could be broken?
Also, how to test with signed ints? There doesn't seem to be an Removed the catch-all and I get this:
|
Do we check that in every single case or only if it a signed type? I've added a piece of code to |
@axic signed integers: just use |
BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0x16)) == encodeArgs(u256(0))); | ||
BOOST_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0x17)) == encodeArgs(u256(0))); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add tests for:
- smaller types
- checks that high order bit cleanup is performed correctly
- right shift of negative left hand sides
- shifts by negative amounts (both for signed and unsigned right hand sides)
- more complex usage of shifts in some actually useful snippet
- shift assignment (
a <<= 2
)
Add tests for:
|
@@ -214,7 214,9 @@ a non-rational number). | |||
String Literals | |||
--------------- | |||
|
|||
String Literals are written with double quotes (``"abc"``). As with integer literals, their type can vary, but they are implicitly convertible to ``bytes`` if they fit, to ``bytes`` and to ``string``. | |||
String Literals are written with double quotes (``"abc"``). As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32`` if they fit, to ``bytes`` and to ``string``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should rebase this.
I'll squash some of the commits and rebase against |
// Disable >>> here. | ||
if (_operator == Token::SHR) | ||
return TypePointer(); | ||
if (Token::isShiftOp(_operator) && !isAddress()) // && !_other->isAddress()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chriseth Should we check here for the other side to be an address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you have to do this whole thing even before line 342. The following should be possible (please add a test):
uint x = 70;
int8 y = 2;
x << y;
x
and y
do not have a common type, and so currently, it would fail in line 345.
Yes, we should perform some more checks for the other side. I would say rational number is fine as long as it is an integer. Fixed point is fine, but we have to perform an integer check at run-time. Integer is fine as long as it is not an address.
// Disable >>> here. | ||
if (_operator == Token::SHR) | ||
return TypePointer(); | ||
if (Token::isShiftOp(_operator) && !isAddress()) // && !_other->isAddress()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you have to do this whole thing even before line 342. The following should be possible (please add a test):
uint x = 70;
int8 y = 2;
x << y;
x
and y
do not have a common type, and so currently, it would fail in line 345.
Yes, we should perform some more checks for the other side. I would say rational number is fine as long as it is an integer. Fixed point is fine, but we have to perform an integer check at run-time. Integer is fine as long as it is not an address.
I added a test case about cleaning up higher bits. |
Closing in Favour of #1487 (with a branch in this repository). |
Add tests for:
Do not merge yet - I would like to get feedback before finishing this work. Also some commits should be squashed later.
First two commits could be merged as those are only trivial fixes.
Regarding this rest:
(Fixes #33.)