Add InlineArrayType to support literals conversion to statically and dynamically allocated arrays.

This commit is contained in:
wechman
2022-08-29 07:18:35 +02:00
parent 7bfec3ba70
commit 1ef2f60049
63 changed files with 1581 additions and 384 deletions
+48 -74
View File
@@ -237,96 +237,68 @@ Array Literals
^^^^^^^^^^^^^^
An array literal is a comma-separated list of one or more expressions, enclosed
in square brackets (``[...]``). For example ``[1, a, f(3)]``. The type of the
array literal is determined as follows:
It is always a statically-sized memory array whose length is the
number of expressions.
The base type of the array is the type of the first expression on the list such that all
other expressions can be implicitly converted to it. It is a type error
if this is not possible.
It is not enough that there is a type all the elements can be converted to. One of the elements
has to be of that type.
In the example below, the type of ``[1, 2, 3]`` is
``uint8[3] memory``, because the type of each of these constants is ``uint8``. If
you want the result to be a ``uint[3] memory`` type, you need to convert
the first element to ``uint``.
in square brackets (``[...]``). For example ``[1, a, f(3)]``. It can be converted to
statically and dynamically-sized array if all its expressions can be implicitly
converted to the base type of the array. In the example below, the conversion is impossible
because ``-1`` cannot be implicitly converted to ``uint8``.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
pragma solidity >=0.8.16 <0.9.0;
// This will not compile
contract C {
function f() public pure {
g([uint(1), 2, 3]);
// The next line creates a type error because int_const -1
// cannot be converted to uint8 memory.
g([1, -1]);
}
function g(uint8[] memory) public pure {
// ...
}
}
A conversion into a statically-sized array can only be performed if the length of the array matches
the number of the expressions in the array literal. Therefore following is impossible:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.16 <0.9.0;
// This will not compile
contract C {
function f() public pure {
// The next line creates a type error because inline_array(int_const 1, int_const 1)
// cannot be converted to uint[3] memory.
g([1, 2]);
}
function g(uint[3] memory) public pure {
// ...
}
}
The array literal ``[1, -1]`` is invalid because the type of the first expression
is ``uint8`` while the type of the second is ``int8`` and they cannot be implicitly
converted to each other. To make it work, you can use ``[int8(1), -1]``, for example.
Since fixed-size memory arrays of different type cannot be converted into each other
(even if the base types can), you always have to specify a common base type explicitly
if you want to use two-dimensional array literals:
Array literals can also be used to initialize multi-dimensional arrays:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
pragma solidity >=0.8.16 <0.9.0;
contract C {
function f() public pure returns (uint24[2][4] memory) {
uint24[2][4] memory x = [[uint24(0x1), 1], [0xffffff, 2], [uint24(0xff), 3], [uint24(0xffff), 4]];
// The following does not work, because some of the inner arrays are not of the right type.
// uint[2][4] memory x = [[0x1, 1], [0xffffff, 2], [0xff, 3], [0xffff, 4]];
return x;
int256 [2][] a = [[1, 2], [3, 4], [5, 6]];
function f() public pure returns (uint8[][] memory) {
return [[1], [2, 3], [4, 5]];
}
}
Fixed size memory arrays cannot be assigned to dynamically-sized
memory arrays, i.e. the following is not possible:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;
// This will not compile.
contract C {
function f() public {
// The next line creates a type error because uint[3] memory
// cannot be converted to uint[] memory.
uint[] memory x = [uint(1), 3, 4];
}
}
It is planned to remove this restriction in the future, but it creates some
complications because of how arrays are passed in the ABI.
If you want to initialize dynamically-sized arrays, you have to assign the
individual elements:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract C {
function f() public pure {
uint[] memory x = new uint[](3);
x[0] = 1;
x[1] = 3;
x[2] = 4;
}
}
.. note::
Until Solidity 0.8.15, array literals were always a statically-sized memory array whose length
was the number of expressions. The base type of the array was the type of the first expression
on the list such that all other expressions could be implicitly converted to it. It was a type
error if the conversion was not possible.
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
@@ -375,7 +347,7 @@ Array Members
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
pragma solidity >=0.8.16 <0.9.0;
contract ArrayContract {
uint[2**20] aLotOfIntegers;
@@ -457,12 +429,14 @@ Array Members
}
function createMemoryArray(uint size) public pure returns (bytes memory) {
// Dynamic memory arrays are created using `new`:
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Dynamically-sized memory arrays are created using `new`:
uint[2][] memory arrayOfPairs1 = new uint[2][](size);
// Inline arrays are always statically-sized and if you only
// use literals, you have to provide at least one type.
arrayOfPairs[0] = [uint(1), 2];
// Dynamically-sized memory arrays can be created with array literals as well:
uint[2][] memory arrayOfPairs2 = [[1, 2], [3, 4], [5, 6]];
require(arrayOfPairs1.length == arrayOfPairs2.length);
require(arrayOfPairs2[1][1] == 4);
// Create a dynamic byte array:
bytes memory b = new bytes(200);