Merge pull request #8994 from ethereum/fixInvertedBinaryOp

Fix type inversion for shift and exp operators.
This commit is contained in:
chriseth 2020-06-03 14:11:16 +02:00 committed by GitHub
commit d12db7ec52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 66 additions and 10 deletions

View File

@ -6,6 +6,7 @@ Breaking changes:
* Deprecated the identifier `now`. * Deprecated the identifier `now`.
* JSON AST: Removes members with ``null`` value from JSON output. * JSON AST: Removes members with ``null`` value from JSON output.
* Type Checker: Disallow shifts by signed types. * Type Checker: Disallow shifts by signed types.
* Type Checker: Exponentiation and shifts of literals by non-literals will always use ``uint256`` or ``int256`` as a type.
Language Features: Language Features:
* Yul: Disallow EVM instruction `pc()`. * Yul: Disallow EVM instruction `pc()`.

View File

@ -444,6 +444,11 @@ long as the operands are integers. If any of the two is fractional, bit operatio
and exponentiation is disallowed if the exponent is fractional (because that might result in and exponentiation is disallowed if the exponent is fractional (because that might result in
a non-rational number). a non-rational number).
Shifts and exponentiation with literal numbers as left (or base) operand and integer types
as the right (exponent) operand are always performed
in the ``uint256`` (for non-negative literals) or ``int256`` (for a negative literals) type,
regardless of the type of the right (exponent) operand.
.. warning:: .. warning::
Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``.

View File

@ -96,6 +96,7 @@ public:
static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); } static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); }
static IntegerType const* uint256() { return uint(256); } static IntegerType const* uint256() { return uint(256); }
static IntegerType const* int256() { return integer(256, IntegerType::Modifier::Signed); }
static FixedPointType const* fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier); static FixedPointType const* fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier);

View File

@ -965,10 +965,38 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const*
{ {
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{ {
auto commonType = Type::commonType(this, _other); if (isFractional())
if (!commonType) return TypeResult::err("Fractional literals not supported.");
return nullptr; else if (!integerType())
return commonType->binaryOperatorResult(_operator, _other); return TypeResult::err("Literal too large.");
// Shift and exp are not symmetric, so it does not make sense to swap
// the types as below. As an exception, we always use uint here.
if (TokenTraits::isShiftOp(_operator))
{
if (!isValidShiftAndAmountType(_operator, *_other))
return nullptr;
return isNegative() ? TypeProvider::int256() : TypeProvider::uint256();
}
else if (Token::Exp == _operator)
{
if (auto const* otherIntType = dynamic_cast<IntegerType const*>(_other))
{
if (otherIntType->isSigned())
return TypeResult::err("Exponentiation power is not allowed to be a signed integer type.");
}
else if (dynamic_cast<FixedPointType const*>(_other))
return TypeResult::err("Exponent is fractional.");
return isNegative() ? TypeProvider::int256() : TypeProvider::uint256();
}
else
{
auto commonType = Type::commonType(this, _other);
if (!commonType)
return nullptr;
return commonType->binaryOperatorResult(_operator, _other);
}
} }
else if (_other->category() != category()) else if (_other->category() != category())
return nullptr; return nullptr;

View File

@ -1,5 +1,5 @@
contract C { contract C {
function f() public pure returns (uint8 x) { function f() public pure returns (uint x) {
uint8 y = uint8(2)**uint8(8); uint8 y = uint8(2)**uint8(8);
return 0**y; return 0**y;
} }

View File

@ -0,0 +1,16 @@
contract test {
function f(uint x) public pure returns (uint, int) {
uint a = 2 ** x;
int b = -2 ** x;
return (a, b);
}
}
// ----
// f(uint256): 0 -> 1, 1
// f(uint256): 1 -> 2, -2
// f(uint256): 2 -> 4, 4
// f(uint256): 13 -> 0x2000, -8192
// f(uint256): 113 -> 0x020000000000000000000000000000, -10384593717069655257060992658440192
// f(uint256): 114 -> 0x040000000000000000000000000000, 20769187434139310514121985316880384
// f(uint256): 1113 -> 0x00, 0
// f(uint256): 1114 -> 0x00, 0

View File

@ -5,4 +5,3 @@ contract test {
} }
} }
// ---- // ----
// Warning: (99-104): Result of exponentiation has type uint8 and thus might overflow. Silence this warning by converting the literal to the expected type.

View File

@ -5,4 +5,3 @@ contract test {
} }
} }
// ---- // ----
// Warning: (99-106): Result of shift has type uint8 and thus might overflow. Silence this warning by converting the literal to the expected type.

View File

@ -4,4 +4,4 @@ contract test {
} }
} }
// ---- // ----
// TypeError: (61-77): Operator ** not compatible with types int_const 3 and ufixed128x18 // TypeError: (61-77): Operator ** not compatible with types int_const 3 and ufixed128x18. Exponent is fractional.

View File

@ -4,4 +4,4 @@ contract test {
} }
} }
// ---- // ----
// TypeError: (61-78): Operator ** not compatible with types int_const 42 and fixed128x18 // TypeError: (61-78): Operator ** not compatible with types int_const 42 and fixed128x18. Exponent is fractional.

View File

@ -0,0 +1,7 @@
contract test {
function f() pure public returns(uint) {
uint x = 100;
return 10 << x;
}
}
// ----

View File

@ -7,4 +7,4 @@ contract test {
} }
} }
// ---- // ----
// UnimplementedFeatureError: Not yet implemented - FixedPointType. // TypeError: (117-123): Operator % not compatible with types rational_const 1 / 2 and fixed128x18. Fractional literals not supported.