Merge pull request #10108 from ethereum/strict-literal-to-integer-conversion

[BREAKING] Strict literal to integer conversion
This commit is contained in:
chriseth
2020-11-03 15:05:49 +01:00
committed by GitHub
28 changed files with 102 additions and 81 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ Breaking Changes:
* Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type.
* Parser: Exponentiation is right associative. ``a**b**c`` is parsed as ``a**(b**c)``.
* Code Generator: Use ``revert`` with error signature ``Panic(uint256)`` and error codes instead of invalid opcode on failing assertions.
* Type System: Explicit conversions from literals to integer type is as strict as implicit conversions.
Language Features:
* Super constructors can now be called using the member notation e.g. ``M.C(123)``.
+5 -3
View File
@@ -35,8 +35,10 @@ the compiler notifying you about it.
New Restrictions
================
* Explicit conversions from negative literals and literals larger
than ``type(uint160).max`` to ``address`` are disallowed.
* Explicit conversions from negative literals and literals larger than ``type(uint160).max`` to
``address`` are disallowed. Similarly, explicit conversions between literals and an integer type
``T`` are only allowed if the literal lies between ``type(T).min`` and ``type(T).max``. In
particular, replace usages of ``uint(-1)`` with ``type(uint).max``.
The previous behaviour was likely ambiguous.
@@ -44,4 +46,4 @@ New Restrictions
* The global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4`` have been removed.
These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly.
These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly.
+4 -4
View File
@@ -126,7 +126,7 @@ custom types without the overhead of external function calls:
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
pragma solidity >=0.6.8 <0.9.0;
struct bigint {
uint[] limbs;
@@ -145,7 +145,7 @@ custom types without the overhead of external function calls:
uint a = limb(_a, i);
uint b = limb(_b, i);
r.limbs[i] = a + b + carry;
if (a + b < a || (a + b == uint(-1) && carry > 0))
if (a + b < a || (a + b == type(uint).max && carry > 0))
carry = 1;
else
carry = 0;
@@ -175,7 +175,7 @@ custom types without the overhead of external function calls:
function f() public pure {
bigint memory x = BigInt.fromUint(7);
bigint memory y = BigInt.fromUint(uint(-1));
bigint memory y = BigInt.fromUint(type(uint).max);
bigint memory z = x.add(y);
assert(z.limb(1) > 0);
}
@@ -285,4 +285,4 @@ for any non-view and non-pure function.
This means that the actual code stored on chain for a library
is different from the code reported by the compiler as
``deployedBytecode``.
``deployedBytecode``.
+3 -3
View File
@@ -83,7 +83,7 @@ Let us rewrite the set example from the
It is also possible to extend elementary types in that way::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
pragma solidity >=0.6.8 <0.9.0;
library Search {
function indexOf(uint[] storage self, uint value)
@@ -93,7 +93,7 @@ It is also possible to extend elementary types in that way::
{
for (uint i = 0; i < self.length; i++)
if (self[i] == value) return i;
return uint(-1);
return type(uint).max;
}
}
@@ -108,7 +108,7 @@ It is also possible to extend elementary types in that way::
function replace(uint _old, uint _new) public {
// This performs the library function call
uint index = data.indexOf(_old);
if (index == uint(-1))
if (index == type(uint).max)
data.push(_new);
else
data[index] = _new;
+5
View File
@@ -114,6 +114,11 @@ that is large enough to represent it without truncation::
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
.. note::
Prior to version 0.8.0, any decimal or hexadecimal number literals could be explicitly
converted to an integer type. From 0.8.0, such explicit conversions are as strict as implicit
conversions, i.e., they are only allowed if the literal fits in the resulting range.
Fixed-Size Byte Arrays
----------------------
+2 -2
View File
@@ -124,7 +124,7 @@ the ``sum`` function iterates over to sum all the values.
::
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
pragma solidity >=0.6.8 <0.9.0;
struct IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; }
@@ -165,7 +165,7 @@ the ``sum`` function iterates over to sum all the values.
}
function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
return iterate_next(self, uint(-1));
return iterate_next(self, type(uint).max);
}
function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
+12 -5
View File
@@ -129,10 +129,13 @@ BoolResult fitsIntegerType(bigint const& _value, IntegerType const& _type)
/// if _signed is true.
bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed)
{
return fitsIntegerType(_value, *TypeProvider::integer(
_bits,
_signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
));
return fitsIntegerType(
_value,
*TypeProvider::integer(
_bits,
_signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
)
);
}
util::Result<TypePointers> transformParametersToExternal(TypePointers const& _parameters, bool _inLibrary)
@@ -968,9 +971,13 @@ BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo)
auto category = _convertTo.category();
if (category == Category::FixedBytes)
return false;
if (category == Category::Address)
else if (category == Category::Address)
{
if (isNegative() || isFractional() || integerType()->numBits() > 160)
return false;
}
else if (category == Category::Integer)
return false;
TypePointer mobType = mobileType();
return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
@@ -211,7 +211,7 @@ library strings {
}
if (a != b) {
// Mask out irrelevant bytes and check again
uint256 mask = uint256(-1); // 0xffff...
uint256 mask = type(uint256).max; // 0xffff...
if(shortest < 32) {
mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
}
+1 -1
View File
@@ -39,7 +39,7 @@ function colony_test
cd lib
rm -Rf dappsys
git clone https://github.com/solidity-external-tests/dappsys-monolithic.git -b master_060 dappsys
git clone https://github.com/solidity-external-tests/dappsys-monolithic.git -b master_080 dappsys
cd ..
truffle_run_test compile_fn test_fn
+1 -1
View File
@@ -761,7 +761,7 @@ BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced)
contract C {
function dyn() public returns (bytes memory a, uint b, bytes20[] memory c, uint d) {
a = "1234567890123456789012345678901234567890";
b = uint(-1);
b = type(uint).max;
c = new bytes20[](4);
c[0] = bytes20(uint160(1234));
c[3] = bytes20(uint160(6789));
+2 -2
View File
@@ -70,7 +70,7 @@ BOOST_AUTO_TEST_CASE(value_types)
assembly { b := 7 }
C c;
assembly { c := sub(0, 5) }
emit E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c);
emit E(10, uint16(type(uint).max - 1), uint24(uint(0x12121212)), int24(int256(-1)), bytes3(x), b, c);
}
}
)";
@@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(memory_array_two_dim)
x[0] = new int16[](3);
x[1] = new int16[](2);
x[0][0] = 7;
x[0][1] = int16(0x010203040506);
x[0][1] = int16(int(0x010203040506));
x[0][2] = -1;
x[1][0] = 4;
x[1][1] = 5;
+16 -16
View File
@@ -1061,9 +1061,9 @@ BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types)
uint120[3] x;
function f() public returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) {
uint120[] memory y = new uint120[](3);
x[0] = y[0] = uint120(-2);
x[1] = y[1] = uint120(-3);
x[2] = y[2] = uint120(-4);
x[0] = y[0] = uint120(type(uint).max - 1);
x[1] = y[1] = uint120(type(uint).max - 2);
x[2] = y[2] = uint120(type(uint).max - 3);
hash1 = keccak256(abi.encodePacked(x));
hash2 = keccak256(abi.encodePacked(y));
hash3 = keccak256(abi.encodePacked(this.f));
@@ -4407,7 +4407,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library)
for (uint i = 0; i < _haystack.length; ++i)
if (_haystack[i] == _needle)
return i;
return uint(-1);
return type(uint).max;
}
}
contract Test {
@@ -6255,7 +6255,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector)
}
function f3() public pure returns (bytes memory) {
bytes4 x = 0x12345678;
return abi.encodeWithSelector(x, uint(-1));
return abi.encodeWithSelector(x, type(uint).max);
}
}
)";
@@ -6287,7 +6287,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2)
}
function f3() public pure returns (bytes memory) {
bytes4 x = 0x12345678;
return abi.encodeWithSelector(x, uint(-1));
return abi.encodeWithSelector(x, type(uint).max);
}
struct S { uint a; string b; uint16 c; }
function f4() public pure returns (bytes memory) {
@@ -6296,7 +6296,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2)
s.a = 0x1234567;
s.b = "Lorem ipsum dolor sit ethereum........";
s.c = 0x1234;
return abi.encodeWithSelector(x, uint(-1), s, uint(3));
return abi.encodeWithSelector(x, type(uint).max, s, uint(3));
}
}
)";
@@ -6336,10 +6336,10 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature)
function f2() public pure returns (bytes memory r, uint[] memory ar) {
string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
uint[] memory y = new uint[](4);
y[0] = uint(-1);
y[1] = uint(-2);
y[2] = uint(-3);
y[3] = uint(-4);
y[0] = type(uint).max;
y[1] = type(uint).max - 1;
y[2] = type(uint).max - 2;
y[3] = type(uint).max - 3;
r = abi.encodeWithSignature(x, y);
// The hash uses temporary memory. This allocation re-uses the memory
// and should initialize it properly.
@@ -6380,10 +6380,10 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2)
function f2() public pure returns (bytes memory r, uint[] memory ar) {
string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
uint[] memory y = new uint[](4);
y[0] = uint(-1);
y[1] = uint(-2);
y[2] = uint(-3);
y[3] = uint(-4);
y[0] = type(uint).max;
y[1] = type(uint).max - 1;
y[2] = type(uint).max - 2;
y[3] = type(uint).max - 3;
r = abi.encodeWithSignature(x, y);
// The hash uses temporary memory. This allocation re-uses the memory
// and should initialize it properly.
@@ -6396,7 +6396,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2)
s.a = 0x1234567;
s.b = "Lorem ipsum dolor sit ethereum........";
s.c = 0x1234;
return abi.encodeWithSignature(s.b, uint(-1), s, uint(3));
return abi.encodeWithSignature(s.b, type(uint).max, s, uint(3));
}
}
)T";
+3 -3
View File
@@ -348,7 +348,7 @@ BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug)
function f() public returns (uint)
{
if (data[block.timestamp] == 0)
data[uint(-7)] = 5;
data[type(uint).max - 6] = 5;
return data[block.timestamp];
}
}
@@ -696,11 +696,11 @@ BOOST_AUTO_TEST_CASE(shift_optimizer_bug)
{
function f(uint x) public returns (uint)
{
return (x << 1) << uint(-1);
return (x << 1) << type(uint).max;
}
function g(uint x) public returns (uint)
{
return (x >> 1) >> uint(-1);
return (x >> 1) >> type(uint).max;
}
}
)";
@@ -15,7 +15,7 @@ contract BinarySearch {
uint256 _value
) private returns (uint256 o_position) {
if (_len == 0 || (_len == 1 && _data[_begin] != _value))
return uint256(-1); // failure
return type(uint256).max; // failure
uint256 halfLen = _len / 2;
uint256 v = _data[_begin + halfLen];
if (_value < v) return find(_data, _begin, halfLen, _value);
@@ -1,7 +1,8 @@
contract C {
function f() public pure returns (uint8 x) {
unchecked {
return uint8(0x166)**uint8(uint8(2)**uint8(8));
uint16 x = 0x166;
return uint8(x)**uint8(uint8(2)**uint8(8));
}
}
}
@@ -3,7 +3,7 @@ contract C {
// (v should be 27 or 28, not 1)
// Note that the precompile does not return zero but returns nothing.
function f() public returns (address) {
return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3)));
return ecrecover(bytes32(type(uint256).max), 1, bytes32(uint(2)), bytes32(uint(3)));
}
}
// ====
@@ -2,7 +2,7 @@ contract c {
enum Truth {False, True}
function test() public returns (uint256) {
return uint256(Truth(uint8(0x701)));
return uint256(Truth(uint8(0x1)));
}
}
@@ -1,7 +1,7 @@
contract Test {
function test() public returns (uint ret) { return uint(address(uint128(0x11223344556677889900112233445566778899001122))); }
function test() public returns (uint ret) { return uint(address(uint128(type(uint200).max))); }
}
// ====
// compileViaYul: also
// ----
// test() -> 158887387085137674884660775897412931874
// test() -> 0xffffffffffffffffffffffffffffffff
@@ -1,6 +1,6 @@
contract C {
function f() public pure returns (uint16 x) {
uint8 y = uint8(0x12345678);
uint8 y = uint8(0x78);
x = y;
}
}
@@ -6,8 +6,8 @@ contract C {
function test_simple(uint _off) public returns (uint _a, uint _b, uint _c) {
simple[_off + 2] = 3;
simple[_off + 3] = 4;
simple[uint(-1)] = 5;
_c = simple[uint(-1)];
simple[type(uint256).max] = 5;
_c = simple[type(uint256).max];
_b = simple[3 + _off];
_a = simple[2 + _off];
}
@@ -2,7 +2,7 @@ pragma experimental SMTChecker;
contract C {
function f() public pure {
uint x = uint(~1);
uint x = type(uint256).max - 1;
assert(x == 2**256 - 2);
assert(~1 == -2);
}
@@ -5,23 +5,23 @@ contract C {
// signed <- signed
int8 z = int8(-1);
assert(z == -1);
z = int8(0xf0ff);
z = int8(int(0) - 1);
assert(z == -1);
z = int8(0xcafecafef0ff);
z = int8(int(0) - 1);
assert(z == -1);
z = int8(0xcafecafe);
z = int8(int(0) - 2);
assert(z == -2);
z = int8(255);
z = int8(int(0) - 1);
assert(z == -1);
// unsigned <= unsigned
uint8 x = uint8(uint16(-1));
uint8 x = uint8(type(uint256).max);
assert(x == 255);
x = uint8(uint256(-1));
x = uint8(type(uint256).max);
assert(x == 255);
// signed <- unsigned
int8 y = int8(uint16(-1));
int8 y = int8(type(uint16).max);
assert(y == -1);
y = int8(uint16(100));
assert(y == 100);
@@ -29,7 +29,7 @@ contract C {
assert(y == -56);
// unsigned <- signed
uint8 v = uint8(int16(-1));
uint8 v = uint8(type(uint16).max);
assert(v == 255);
v = uint8(int16(300));
assert(v == 44);
@@ -10,7 +10,7 @@ contract C {
}
function g() public pure {
uint a = uint(0);
uint b = uint(-1);
uint b = type(uint256).max;
uint c = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
int d = -1;
uint e = uint(d);
@@ -20,7 +20,7 @@ contract C {
}
function h() public pure {
uint32 a = uint32(0);
uint32 b = uint32(-1);
uint32 b = type(uint32).max;
uint32 c = 4294967295;
int32 d = -1;
uint32 e = uint32(d);
@@ -11,7 +11,7 @@ contract C {
assert(x == -56);
int256 y = int256(uint256(2**255 + 10));
assert(y == -(2**255) + 10);
int256 z = int256(2**255 + 10);
int256 z = int256(uint(2**255 + 10));
assert(z == -(2**255) + 10);
int256 t = int256(bytes32(uint256(200)));
assert(t == 200);
@@ -30,17 +30,17 @@ contract C {
// unsigned <- signed
uint8 x = uint8(int8(100));
assert(x == 100);
uint8 y = uint8(int8(200));
uint16 y = uint16(int16(200));
assert(y == 200);
uint8 z = uint8(int8(-100));
assert(z == 156);
uint8 t = uint8(int8(-200));
assert(t == 56);
uint16 t = uint16(int16(-200));
assert(t == type(uint16).max - 200 + 1);
uint256 v = uint256(int256(-200));
assert(v == 2**256 - 200);
uint256 w = uint256(-2);
uint256 w = uint256(type(uint256).max - 1);
assert(w == 2**256 - 2);
bytes4 b = bytes4(uint32(-2));
bytes4 b = bytes4(uint32(type(uint256).max - 1));
assert(uint32(b) == uint32(2**32 - 2));
address a = address(type(uint160).max);
assert(uint160(a) == uint160(2**160 - 1));
@@ -71,4 +71,4 @@ contract C {
}
}
// ----
// Warning 8364: (1340-1341): Assertion checker does not yet implement type type(enum E)
// Warning 8364: (1414-1415): Assertion checker does not yet implement type type(enum E)
@@ -5,7 +5,7 @@ abstract contract D {}
contract C {
function f1() public pure {
// unsigned <- signed
uint16 x = uint16(-1);
uint16 x = type(uint16).max;
assert(x == 65535);
int8 i = int8(-1);
assert(i == -1);
@@ -19,9 +19,9 @@ contract C {
function f2() public pure {
// signed <- unsigned
int16 y = int16(uint8(65535));
int16 y = int16(uint8(uint(65535)));
assert(y == 255);
int z = int(uint8(-1));
int z = int(uint8(type(uint).max));
assert(z == 255);
z = int(uint8(255));
assert(z == 255);
@@ -29,11 +29,11 @@ contract C {
function f3() public pure {
// signed <- signed
int16 y = int16(int8(65535));
int16 y = int16(int8(uint(65535)));
assert(y == -1);
int z = int(int8(-1));
assert(z == -1);
z = int(int8(255));
z = int(int8(int(255)));
assert(z == -1);
z = int(int16(5000));
assert(z == 5000);
@@ -41,15 +41,15 @@ contract C {
function f4() public pure {
// unsigned <- unsigned
uint x = uint(uint8(-1));
uint x = uint(uint8(type(uint).max));
assert(x == 255);
x = uint(uint16(-1));
x = uint(uint16(type(uint).max));
assert(x == 65535);
x = uint(uint16(5000));
assert(x == 5000);
uint16 y = uint16(-1);
uint16 y = uint16(type(uint).max);
assert(y == 65535);
y = uint16(uint8(-1));
y = uint16(uint8(type(uint16).max));
assert(y == 255);
address a = address(uint8(0));
assert(a == address(0));
@@ -67,4 +67,4 @@ contract C {
}
}
// ----
// Warning 8364: (1144-1145): Assertion checker does not yet implement type type(contract D)
// Warning 8364: (1229-1230): Assertion checker does not yet implement type type(contract D)
@@ -3,7 +3,7 @@ contract C {
return address(2**160 -1);
}
function g() public pure returns (address) {
return address(uint(-1));
return address(type(uint160).max);
}
}
// ----
@@ -1,7 +1,9 @@
contract c {
function f() public pure {
uint a = -1;
uint b = uint(-1);
}
}
// ----
// TypeError 9574: (52-63): Type int_const -1 is not implicitly convertible to expected type uint256. Cannot implicitly convert signed literal to unsigned type.
// TypeError 9640: (82-90): Explicit type conversion not allowed from "int_const -1" to "uint256".
@@ -1,7 +1,11 @@
contract c {
contract C {
function f() public pure {
uint8 a = 256;
uint8 b = uint8(256);
int8 c = int8(-129);
}
}
// ----
// TypeError 9574: (52-65): Type int_const 256 is not implicitly convertible to expected type uint8. Literal is too large to fit in uint8.
// TypeError 9640: (85-95): Explicit type conversion not allowed from "int_const 256" to "uint8".
// TypeError 9640: (114-124): Explicit type conversion not allowed from "int_const -129" to "int8".