mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11742 from ethereum/develop
Merge develop into breaking
This commit is contained in:
@@ -89,14 +89,14 @@ New Features
|
||||
This section lists things that were not possible prior to Solidity 0.6.0
|
||||
or were more difficult to achieve.
|
||||
|
||||
* The :ref:`try/catch statement <try-catch>` allows you to react on failed external calls.
|
||||
* ``struct`` and ``enum`` types can be declared at file level.
|
||||
* Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))``
|
||||
is a low-level way to decode the function call payload.
|
||||
* Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``.
|
||||
* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function.
|
||||
* Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where
|
||||
``x`` must be of type ``address``.
|
||||
* The :ref:`try/catch statement <try-catch>` allows you to react on failed external calls.
|
||||
* ``struct`` and ``enum`` types can be declared at file level.
|
||||
* Array slices can be used for calldata arrays, for example ``abi.decode(msg.data[4:], (uint, uint))``
|
||||
is a low-level way to decode the function call payload.
|
||||
* Natspec supports multiple return parameters in developer documentation, enforcing the same naming check as ``@param``.
|
||||
* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function.
|
||||
* Conversions from ``address`` to ``address payable`` are now possible via ``payable(x)``, where
|
||||
``x`` must be of type ``address``.
|
||||
|
||||
|
||||
Interface Changes
|
||||
|
||||
+138
-135
@@ -77,8 +77,9 @@ The following (fixed-size) array type exists:
|
||||
|
||||
- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type.
|
||||
|
||||
.. note::
|
||||
While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler.
|
||||
.. note::
|
||||
|
||||
While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler.
|
||||
|
||||
The following non-fixed-size types exist:
|
||||
|
||||
@@ -124,13 +125,13 @@ Design Criteria for the Encoding
|
||||
|
||||
The encoding is designed to have the following properties, which are especially useful if some arguments are nested arrays:
|
||||
|
||||
1. The number of reads necessary to access a value is at most the depth of the value
|
||||
inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a
|
||||
previous version of the ABI, the number of reads scaled linearly with the total number of dynamic
|
||||
parameters in the worst case.
|
||||
1. The number of reads necessary to access a value is at most the depth of the value
|
||||
inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a
|
||||
previous version of the ABI, the number of reads scaled linearly with the total number of dynamic
|
||||
parameters in the worst case.
|
||||
|
||||
2. The data of a variable or array element is not interleaved with other data and it is
|
||||
relocatable, i.e. it only uses relative "addresses".
|
||||
2. The data of a variable or array element is not interleaved with other data and it is
|
||||
relocatable, i.e. it only uses relative "addresses".
|
||||
|
||||
|
||||
Formal Specification of the Encoding
|
||||
@@ -236,6 +237,7 @@ Examples
|
||||
Given the contract:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.16 <0.9.0;
|
||||
@@ -312,21 +314,21 @@ these are directly the values we want to pass, whereas for the dynamic types ``u
|
||||
we use the offset in bytes to the start of their data area, measured from the start of the value
|
||||
encoding (i.e. not counting the first four bytes containing the hash of the function signature). These are:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part)
|
||||
- ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right)
|
||||
- ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below))
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000123`` (``0x123`` padded to 32 bytes)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000080`` (offset to start of data part of second parameter, 4*32 bytes, exactly the size of the head part)
|
||||
- ``0x3132333435363738393000000000000000000000000000000000000000000000`` (``"1234567890"`` padded to 32 bytes on the right)
|
||||
- ``0x00000000000000000000000000000000000000000000000000000000000000e0`` (offset to start of data part of fourth parameter = offset to start of data part of first dynamic parameter + size of data part of first dynamic parameter = 4\*32 + 3\*32 (see below))
|
||||
|
||||
After this, the data part of the first dynamic argument, ``[0x456, 0x789]`` follows:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements of the array, 2)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000456`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000789`` (second element)
|
||||
|
||||
Finally, we encode the data part of the second dynamic argument, ``"Hello, world!"``:
|
||||
|
||||
- ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13)
|
||||
- ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right)
|
||||
- ``0x000000000000000000000000000000000000000000000000000000000000000d`` (number of elements (bytes in this case): 13)
|
||||
- ``0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000`` (``"Hello, world!"`` padded to 32 bytes on the right)
|
||||
|
||||
All together, the encoding is (newline after function selector and each 32-bytes for clarity):
|
||||
|
||||
@@ -348,14 +350,14 @@ with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most
|
||||
|
||||
First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element)
|
||||
|
||||
Then we encode the length and data of the second embedded dynamic array ``[3]`` of the first root array ``[[1, 2], [3]]``:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element)
|
||||
|
||||
Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``.
|
||||
To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]``
|
||||
@@ -380,12 +382,12 @@ thus ``b = 0x00000000000000000000000000000000000000000000000000000000000000a0``.
|
||||
|
||||
Then we encode the embedded strings of the second root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``)
|
||||
- ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``)
|
||||
- ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``)
|
||||
- ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``)
|
||||
- ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``)
|
||||
- ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``)
|
||||
- ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``)
|
||||
|
||||
In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``:
|
||||
|
||||
@@ -416,11 +418,11 @@ and have the same encodings for a function with a signature ``g(string[],uint[][
|
||||
|
||||
Then we encode the length of the first root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``)
|
||||
|
||||
Then we encode the length of the second root array:
|
||||
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``)
|
||||
- ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``)
|
||||
|
||||
Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and
|
||||
``["one", "two", "three"]``, and assemble parts in the correct order:
|
||||
@@ -529,12 +531,12 @@ i.e. ``0xcf479181``, ``uint256(0)``, ``uint256(amount)``.
|
||||
The error selectors ``0x00000000`` and ``0xffffffff`` are reserved for future use.
|
||||
|
||||
.. warning::
|
||||
Never trust error data.
|
||||
The error data by default bubbles up through the chain of external calls, which
|
||||
means that a contract may receive an error not defined in any of the contracts
|
||||
it calls directly.
|
||||
Furthermore, any contract can fake any error by returning data that matches
|
||||
an error signature, even if the error is not defined anywhere.
|
||||
Never trust error data.
|
||||
The error data by default bubbles up through the chain of external calls, which
|
||||
means that a contract may receive an error not defined in any of the contracts
|
||||
it calls directly.
|
||||
Furthermore, any contract can fake any error by returning data that matches
|
||||
an error signature, even if the error is not defined anywhere.
|
||||
|
||||
.. _abi_json:
|
||||
|
||||
@@ -618,24 +620,24 @@ would result in the JSON:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[{
|
||||
"type":"error",
|
||||
"inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}],
|
||||
"name":"InsufficientBalance"
|
||||
}, {
|
||||
"type":"event",
|
||||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
|
||||
"name":"Event"
|
||||
}, {
|
||||
"type":"event",
|
||||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
|
||||
"name":"Event2"
|
||||
}, {
|
||||
"type":"function",
|
||||
"inputs": [{"name":"a","type":"uint256"}],
|
||||
"name":"foo",
|
||||
"outputs": []
|
||||
}]
|
||||
[{
|
||||
"type":"error",
|
||||
"inputs": [{"name":"available","type":"uint256"},{"name":"required","type":"uint256"}],
|
||||
"name":"InsufficientBalance"
|
||||
}, {
|
||||
"type":"event",
|
||||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
|
||||
"name":"Event"
|
||||
}, {
|
||||
"type":"event",
|
||||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
|
||||
"name":"Event2"
|
||||
}, {
|
||||
"type":"function",
|
||||
"inputs": [{"name":"a","type":"uint256"}],
|
||||
"name":"foo",
|
||||
"outputs": []
|
||||
}]
|
||||
|
||||
Handling tuple types
|
||||
--------------------
|
||||
@@ -656,7 +658,7 @@ As an example, the code
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.4 <0.9.0;
|
||||
pragma solidity >=0.7.5 <0.9.0;
|
||||
pragma abicoder v2;
|
||||
|
||||
contract Test {
|
||||
@@ -670,61 +672,61 @@ would result in the JSON:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"name": "f",
|
||||
"type": "function",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "s",
|
||||
"type": "tuple",
|
||||
"components": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"type": "tuple[]",
|
||||
"components": [
|
||||
{
|
||||
"name": "x",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "t",
|
||||
"type": "tuple",
|
||||
"components": [
|
||||
{
|
||||
"name": "x",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"name": "f",
|
||||
"type": "function",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "s",
|
||||
"type": "tuple",
|
||||
"components": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"type": "tuple[]",
|
||||
"components": [
|
||||
{
|
||||
"name": "x",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "t",
|
||||
"type": "tuple",
|
||||
"components": [
|
||||
{
|
||||
"name": "x",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
}
|
||||
]
|
||||
|
||||
.. _abi_packed_mode:
|
||||
|
||||
@@ -761,18 +763,19 @@ As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string("
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
|
||||
|
||||
More specifically:
|
||||
- During the encoding, everything is encoded in-place. This means that there is
|
||||
no distinction between head and tail, as in the ABI encoding, and the length
|
||||
of an array is not encoded.
|
||||
- The direct arguments of ``abi.encodePacked`` are encoded without padding,
|
||||
as long as they are not arrays (or ``string`` or ``bytes``).
|
||||
- The encoding of an array is the concatenation of the
|
||||
encoding of its elements **with** padding.
|
||||
- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded
|
||||
without their length field.
|
||||
- The encoding of ``string`` or ``bytes`` does not apply padding at the end
|
||||
unless it is part of an array or struct (then it is padded to a multiple of
|
||||
32 bytes).
|
||||
|
||||
- During the encoding, everything is encoded in-place. This means that there is
|
||||
no distinction between head and tail, as in the ABI encoding, and the length
|
||||
of an array is not encoded.
|
||||
- The direct arguments of ``abi.encodePacked`` are encoded without padding,
|
||||
as long as they are not arrays (or ``string`` or ``bytes``).
|
||||
- The encoding of an array is the concatenation of the
|
||||
encoding of its elements **with** padding.
|
||||
- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded
|
||||
without their length field.
|
||||
- The encoding of ``string`` or ``bytes`` does not apply padding at the end
|
||||
unless it is part of an array or struct (then it is padded to a multiple of
|
||||
32 bytes).
|
||||
|
||||
In general, the encoding is ambiguous as soon as there are two dynamically-sized elements,
|
||||
because of the missing length field.
|
||||
@@ -784,12 +787,12 @@ for prepending a function selector. Since the encoding is ambiguous, there is no
|
||||
|
||||
.. warning::
|
||||
|
||||
If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types,
|
||||
it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and
|
||||
vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``.
|
||||
If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make
|
||||
sure to always use the same types and check that at most one of them is dynamic.
|
||||
Unless there is a compelling reason, ``abi.encode`` should be preferred.
|
||||
If you use ``keccak256(abi.encodePacked(a, b))`` and both ``a`` and ``b`` are dynamic types,
|
||||
it is easy to craft collisions in the hash value by moving parts of ``a`` into ``b`` and
|
||||
vice-versa. More specifically, ``abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")``.
|
||||
If you use ``abi.encodePacked`` for signatures, authentication or data integrity, make
|
||||
sure to always use the same types and check that at most one of them is dynamic.
|
||||
Unless there is a compelling reason, ``abi.encode`` should be preferred.
|
||||
|
||||
|
||||
.. _indexed_event_encoding:
|
||||
@@ -801,13 +804,13 @@ Indexed event parameters that are not value types, i.e. arrays and structs are n
|
||||
stored directly but instead a keccak256-hash of an encoding is stored. This encoding
|
||||
is defined as follows:
|
||||
|
||||
- the encoding of a ``bytes`` and ``string`` value is just the string contents
|
||||
without any padding or length prefix.
|
||||
- the encoding of a struct is the concatenation of the encoding of its members,
|
||||
always padded to a multiple of 32 bytes (even ``bytes`` and ``string``).
|
||||
- the encoding of an array (both dynamically- and statically-sized) is
|
||||
the concatenation of the encoding of its elements, always padded to a multiple
|
||||
of 32 bytes (even ``bytes`` and ``string``) and without any length prefix
|
||||
- the encoding of a ``bytes`` and ``string`` value is just the string contents
|
||||
without any padding or length prefix.
|
||||
- the encoding of a struct is the concatenation of the encoding of its members,
|
||||
always padded to a multiple of 32 bytes (even ``bytes`` and ``string``).
|
||||
- the encoding of an array (both dynamically- and statically-sized) is
|
||||
the concatenation of the encoding of its elements, always padded to a multiple
|
||||
of 32 bytes (even ``bytes`` and ``string``) and without any length prefix
|
||||
|
||||
In the above, as usual, a negative number is padded by sign extension and not zero padded.
|
||||
``bytesNN`` types are padded on the right while ``uintNN`` / ``intNN`` are padded on the left.
|
||||
|
||||
+1
-1
@@ -234,7 +234,7 @@ This means that the allocatable memory starts at ``0x80``, which is the initial
|
||||
of the free memory pointer.
|
||||
|
||||
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this is
|
||||
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
|
||||
even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
|
||||
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
|
||||
first slot of the array and followed by the array elements.
|
||||
|
||||
|
||||
+10
-10
@@ -19,16 +19,16 @@ which can be used to check which bugs affect a specific version of the compiler.
|
||||
Contract source verification tools and also other tools interacting with
|
||||
contracts should consult this list according to the following criteria:
|
||||
|
||||
- It is mildly suspicious if a contract was compiled with a nightly
|
||||
compiler version instead of a released version. This list does not keep
|
||||
track of unreleased or nightly versions.
|
||||
- It is also mildly suspicious if a contract was compiled with a version that was
|
||||
not the most recent at the time the contract was created. For contracts
|
||||
created from other contracts, you have to follow the creation chain
|
||||
back to a transaction and use the date of that transaction as creation date.
|
||||
- It is highly suspicious if a contract was compiled with a compiler that
|
||||
contains a known bug and the contract was created at a time where a newer
|
||||
compiler version containing a fix was already released.
|
||||
- It is mildly suspicious if a contract was compiled with a nightly
|
||||
compiler version instead of a released version. This list does not keep
|
||||
track of unreleased or nightly versions.
|
||||
- It is also mildly suspicious if a contract was compiled with a version that was
|
||||
not the most recent at the time the contract was created. For contracts
|
||||
created from other contracts, you have to follow the creation chain
|
||||
back to a transaction and use the date of that transaction as creation date.
|
||||
- It is highly suspicious if a contract was compiled with a compiler that
|
||||
contains a known bug and the contract was created at a time where a newer
|
||||
compiler version containing a fix was already released.
|
||||
|
||||
The JSON file of known bugs below is an array of objects, one for each bug,
|
||||
with the following keys:
|
||||
|
||||
+5
-4
@@ -158,7 +158,8 @@ Global Variables
|
||||
Function Visibility Specifiers
|
||||
==============================
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
function myFunction() <visibility specifier> returns (bool) {
|
||||
return true;
|
||||
@@ -192,8 +193,8 @@ Reserved Keywords
|
||||
|
||||
These keywords are reserved in Solidity. They might become part of the syntax in the future:
|
||||
|
||||
``after``, ``alias``, ``apply``, ``auto``, ``case``, ``copyof``, ``default``,
|
||||
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
||||
``after``, ``alias``, ``apply``, ``auto``, ``byte``, ``case``, ``copyof``, ``default``,
|
||||
``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
||||
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
||||
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
|
||||
``unchecked``.
|
||||
``var``.
|
||||
|
||||
@@ -130,6 +130,7 @@ The use of **function modifiers** makes these
|
||||
restrictions highly readable.
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
@@ -293,6 +294,7 @@ function finishes.
|
||||
will run even if the function explicitly returns.
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
+22
-4
@@ -23,9 +23,11 @@ from pygments_lexer_solidity import SolidityLexer, YulLexer
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
|
||||
ROOT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
sys.path.insert(0, os.path.join(ROOT_PATH, 'ext'))
|
||||
|
||||
def setup(sphinx):
|
||||
thisdir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, thisdir + '/utils')
|
||||
sphinx.add_lexer('Solidity', SolidityLexer)
|
||||
sphinx.add_lexer('Yul', YulLexer)
|
||||
|
||||
@@ -39,7 +41,10 @@ def setup(sphinx):
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [ 'sphinx_a4doc' ]
|
||||
extensions = [
|
||||
'sphinx_a4doc',
|
||||
'html_extra_template_renderer',
|
||||
]
|
||||
|
||||
a4_base_path = os.path.dirname(__file__) + '/grammar'
|
||||
|
||||
@@ -156,7 +161,20 @@ html_js_files = ["js/toggle.js"]
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
html_extra_path = ["_static/css", "_static/robots.txt"]
|
||||
html_extra_path = ["_static/css"]
|
||||
|
||||
# List of templates of static files to be included in the HTML output.
|
||||
# Keys represent paths to input files and values are dicts containing:
|
||||
# - target: The path where the rendered template should be placed.
|
||||
# - context: A dictionary listing variables that can be used inside the template.
|
||||
# All paths must be absolute.
|
||||
# Rendered templates are automatically added to html_extra_path setting.
|
||||
html_extra_templates = {
|
||||
os.path.join(ROOT_PATH, "robots.txt.template"): {
|
||||
'target': os.path.join(ROOT_PATH, "_static/robots.txt"),
|
||||
'context': {'LATEST_VERSION': version},
|
||||
}
|
||||
}
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
|
||||
+13
-13
@@ -73,7 +73,7 @@ four indexed arguments rather than three.
|
||||
In particular, it is possible to "fake" the signature of another event
|
||||
using an anonymous event.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.21 <0.9.0;
|
||||
@@ -97,7 +97,7 @@ four indexed arguments rather than three.
|
||||
|
||||
The use in the JavaScript API is as follows:
|
||||
|
||||
::
|
||||
.. code-block:: javascript
|
||||
|
||||
var abi = /* abi as generated by the compiler */;
|
||||
var ClientReceipt = web3.eth.contract(abi);
|
||||
@@ -124,17 +124,17 @@ The output of the above looks like the following (trimmed):
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"returnValues": {
|
||||
"_from": "0x1111…FFFFCCCC",
|
||||
"_id": "0x50…sd5adb20",
|
||||
"_value": "0x420042"
|
||||
},
|
||||
"raw": {
|
||||
"data": "0x7f…91385",
|
||||
"topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
|
||||
}
|
||||
}
|
||||
{
|
||||
"returnValues": {
|
||||
"_from": "0x1111…FFFFCCCC",
|
||||
"_id": "0x50…sd5adb20",
|
||||
"_value": "0x420042"
|
||||
},
|
||||
"raw": {
|
||||
"data": "0x7f…91385",
|
||||
"topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
|
||||
}
|
||||
}
|
||||
|
||||
Additional Resources for Understanding Events
|
||||
==============================================
|
||||
|
||||
@@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
pragma solidity >=0.7.1 <0.9.0;
|
||||
|
||||
contract owned {
|
||||
constructor() { owner = payable(msg.sender); }
|
||||
|
||||
@@ -15,7 +15,7 @@ that call them, similar to internal library functions.
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
pragma solidity >=0.7.1 <0.9.0;
|
||||
|
||||
function sum(uint[] memory _arr) pure returns (uint s) {
|
||||
for (uint i = 0; i < _arr.length; i++)
|
||||
|
||||
@@ -130,9 +130,10 @@ internal functions in libraries in order to implement
|
||||
custom types without the overhead of external function calls:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
struct bigint {
|
||||
uint[] limbs;
|
||||
@@ -150,11 +151,14 @@ custom types without the overhead of external function calls:
|
||||
for (uint i = 0; i < r.limbs.length; ++i) {
|
||||
uint a = limb(_a, i);
|
||||
uint b = limb(_b, i);
|
||||
r.limbs[i] = a + b + carry;
|
||||
if (a + b < a || (a + b == type(uint).max && carry > 0))
|
||||
carry = 1;
|
||||
else
|
||||
carry = 0;
|
||||
unchecked {
|
||||
r.limbs[i] = a + b + carry;
|
||||
|
||||
if (a + b < a || (a + b == type(uint).max && carry > 0))
|
||||
carry = 1;
|
||||
else
|
||||
carry = 0;
|
||||
}
|
||||
}
|
||||
if (carry > 0) {
|
||||
// too bad, we have to add a limb
|
||||
@@ -223,14 +227,14 @@ following an internal naming schema and arguments of types not supported in the
|
||||
|
||||
The following identifiers are used for the types in the signatures:
|
||||
|
||||
- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI.
|
||||
- Non-storage array types follow the same convention as in the contract ABI, i.e. ``<type>[]`` for dynamic arrays and
|
||||
``<type>[M]`` for fixed-size arrays of ``M`` elements.
|
||||
- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``.
|
||||
- Storage pointer mappings use ``mapping(<keyType> => <valueType>) storage`` where ``<keyType>`` and ``<valueType>`` are
|
||||
the identifiers for the key and value types of the mapping, respectively.
|
||||
- Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space
|
||||
followed by ``storage`` to it.
|
||||
- Value types, non-storage ``string`` and non-storage ``bytes`` use the same identifiers as in the contract ABI.
|
||||
- Non-storage array types follow the same convention as in the contract ABI, i.e. ``<type>[]`` for dynamic arrays and
|
||||
``<type>[M]`` for fixed-size arrays of ``M`` elements.
|
||||
- Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``.
|
||||
- Storage pointer mappings use ``mapping(<keyType> => <valueType>) storage`` where ``<keyType>`` and ``<valueType>`` are
|
||||
the identifiers for the key and value types of the mapping, respectively.
|
||||
- Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space
|
||||
followed by ``storage`` to it.
|
||||
|
||||
The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a
|
||||
``uint256`` value referring to the storage slot to which they point.
|
||||
|
||||
@@ -188,16 +188,24 @@ The next example is more complex:
|
||||
uint a;
|
||||
bytes3 b;
|
||||
mapping (uint => uint) map;
|
||||
uint[3] c;
|
||||
uint[] d;
|
||||
bytes e;
|
||||
}
|
||||
mapping (uint => mapping(bool => Data[])) public data;
|
||||
}
|
||||
|
||||
It generates a function of the following form. The mapping in the struct is omitted
|
||||
because there is no good way to provide the key for the mapping:
|
||||
It generates a function of the following form. The mapping and arrays (with the
|
||||
exception of byte arrays) in the struct are omitted because there is no good way
|
||||
to select individual struct members or provide a key for the mapping:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
|
||||
function data(uint arg1, bool arg2, uint arg3)
|
||||
public
|
||||
returns (uint a, bytes3 b, bytes memory e)
|
||||
{
|
||||
a = data[arg1][arg2][arg3].a;
|
||||
b = data[arg1][arg2][arg3].b;
|
||||
e = data[arg1][arg2][arg3].e;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ starting from the current directory. The required file is called ``libevmone.so`
|
||||
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that
|
||||
use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``,
|
||||
``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
|
||||
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.7.0>`_
|
||||
`GitHub <https://github.com/ethereum/evmone/releases/tag/v0.8.0>`_
|
||||
and place it in the project root path or inside the ``deps`` folder.
|
||||
|
||||
If the ``libz3`` library is not installed on your system, you should disable the
|
||||
@@ -324,7 +324,7 @@ from the documentation or the other tests:
|
||||
# extract from tests:
|
||||
path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp
|
||||
# extract from documentation:
|
||||
path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs
|
||||
path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs
|
||||
|
||||
The AFL documentation states that the corpus (the initial input files) should not be
|
||||
too large. The files themselves should not be larger than 1 kB and there should be
|
||||
|
||||
@@ -660,6 +660,7 @@ The following example shows how you can use ``require`` to check conditions on i
|
||||
and ``assert`` for internal error checking.
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0 <0.9.0;
|
||||
@@ -786,7 +787,7 @@ A failure in an external call can be caught using a try/catch statement, as foll
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
pragma solidity >=0.8.1;
|
||||
|
||||
interface DataFeed { function getData(address token) external returns (uint value); }
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ invalid bids.
|
||||
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import os.path
|
||||
|
||||
|
||||
def render_html_extra_templates(app):
|
||||
if app.builder.format != 'html':
|
||||
# Non-HTML builders do not provide .templates.render_string(). Note that a HTML
|
||||
# builder is still used also when building some other formats like json or epub.
|
||||
return
|
||||
|
||||
for input_path, template_config in app.config.html_extra_templates.items():
|
||||
# Requiring absolute paths simplifies the implementation.
|
||||
if not os.path.isabs(input_path):
|
||||
raise RuntimeError(f"Template input path is not absolute: {input_path}")
|
||||
if not os.path.isabs(template_config['target']):
|
||||
raise RuntimeError(f"Template target path is not absolute: {template_config['target']}")
|
||||
|
||||
with open(input_path, 'r') as input_file:
|
||||
# This runs Jinja2, which supports rendering {{ }} tags among other things.
|
||||
rendered_template = app.builder.templates.render_string(
|
||||
input_file.read(),
|
||||
template_config['context'],
|
||||
)
|
||||
|
||||
with open(template_config['target'], 'w') as target_file:
|
||||
target_file.write(rendered_template)
|
||||
|
||||
app.config.html_extra_path.append(template_config['target'])
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('html_extra_templates', default={}, rebuild='', types=dict)
|
||||
|
||||
# Register a handler for the env-before-read-docs event. Any event that's fired before static
|
||||
# files get copied would do.
|
||||
app.connect(
|
||||
'env-before-read-docs',
|
||||
lambda app, env, docnames: render_html_extra_templates(app)
|
||||
)
|
||||
|
||||
return {
|
||||
# NOTE: Need to access _raw_config here because setup() runs before app.config is ready.
|
||||
'version': app.config._raw_config['version'], # pylint: disable=protected-access
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
||||
@@ -19,7 +19,7 @@ Solidity always places new objects at the free memory pointer and
|
||||
memory is never freed (this might change in the future).
|
||||
|
||||
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this
|
||||
is even true for ``byte[]``, but not for ``bytes`` and ``string``).
|
||||
is even true for ``bytes1[]``, but not for ``bytes`` and ``string``).
|
||||
Multi-dimensional memory arrays are pointers to memory arrays. The length of a
|
||||
dynamic array is stored at the first slot of the array and followed by the array
|
||||
elements.
|
||||
|
||||
@@ -127,7 +127,7 @@ The type of the value is ``uint256``, so it uses a single slot.
|
||||
------------------------
|
||||
|
||||
``bytes`` and ``string`` are encoded identically.
|
||||
In general, the encoding is similar to ``byte1[]``, in the sense that there is a slot for the array itself and
|
||||
In general, the encoding is similar to ``bytes1[]``, in the sense that there is a slot for the array itself and
|
||||
a data area that is computed using a ``keccak256`` hash of that slot's position.
|
||||
However, for short values (shorter than 32 bytes) the array elements are stored together with the length in the same slot.
|
||||
|
||||
|
||||
+117
-111
@@ -49,6 +49,8 @@ differences, for example, functions may be inlined, combined, or rewritten to el
|
||||
redundancies, etc. (compare the output between the flags ``--ir`` and
|
||||
``--optimize --ir-optimized``).
|
||||
|
||||
.. _optimizer-parameter-runs:
|
||||
|
||||
Optimizer Parameter Runs
|
||||
========================
|
||||
|
||||
@@ -89,21 +91,21 @@ the sequence:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
PUSH 32
|
||||
PUSH 0
|
||||
CALLDATALOAD
|
||||
PUSH 100
|
||||
DUP2
|
||||
MSTORE
|
||||
KECCAK256
|
||||
PUSH 32
|
||||
PUSH 0
|
||||
CALLDATALOAD
|
||||
PUSH 100
|
||||
DUP2
|
||||
MSTORE
|
||||
KECCAK256
|
||||
|
||||
or the equivalent Yul
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
let x := calldataload(0)
|
||||
mstore(x, 100)
|
||||
let value := keccak256(x, 32)
|
||||
let x := calldataload(0)
|
||||
mstore(x, 100)
|
||||
let value := keccak256(x, 32)
|
||||
|
||||
In this case, the optimizer tracks the value at a memory location ``calldataload(0)`` and then
|
||||
realizes that the Keccak-256 hash can be evaluated at compile time. This only works if there is no
|
||||
@@ -116,14 +118,14 @@ For example,
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
let x := calldataload(0)
|
||||
mstore(x, 100)
|
||||
// Current knowledge memory location x -> 100
|
||||
let y := add(x, 32)
|
||||
// Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32)
|
||||
mstore(y, 200)
|
||||
// This Keccak-256 can now be evaluated
|
||||
let value := keccak256(x, 32)
|
||||
let x := calldataload(0)
|
||||
mstore(x, 100)
|
||||
// Current knowledge memory location x -> 100
|
||||
let y := add(x, 32)
|
||||
// Does not clear the knowledge that x -> 100, since y does not write to [x, x + 32)
|
||||
mstore(y, 200)
|
||||
// This Keccak-256 can now be evaluated
|
||||
let value := keccak256(x, 32)
|
||||
|
||||
Therefore, modifications to storage and memory locations, of say location ``l``, must erase
|
||||
knowledge about storage or memory locations which may be equal to ``l``. More specifically, for
|
||||
@@ -239,8 +241,8 @@ for all references to ``tag_f`` leaving it unused, s.t. it can be removed, yield
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
...body of function f...
|
||||
...opcodes after call to f...
|
||||
...body of function f...
|
||||
...opcodes after call to f...
|
||||
|
||||
So the call to function ``f`` is inlined and the original definition of ``f`` can be removed.
|
||||
|
||||
@@ -269,11 +271,11 @@ backtracking.
|
||||
All components of the Yul-based optimizer module are explained below.
|
||||
The following transformation steps are the main components:
|
||||
|
||||
- SSA Transform
|
||||
- Common Subexpression Eliminator
|
||||
- Expression Simplifier
|
||||
- Redundant Assign Eliminator
|
||||
- Full Function Inliner
|
||||
- SSA Transform
|
||||
- Common Subexpression Eliminator
|
||||
- Expression Simplifier
|
||||
- Redundant Assign Eliminator
|
||||
- Full Function Inliner
|
||||
|
||||
Optimizer Steps
|
||||
---------------
|
||||
@@ -281,36 +283,36 @@ Optimizer Steps
|
||||
This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information
|
||||
on the individual steps and their sequence below.
|
||||
|
||||
- :ref:`block-flattener`.
|
||||
- :ref:`circular-reference-pruner`.
|
||||
- :ref:`common-subexpression-eliminator`.
|
||||
- :ref:`conditional-simplifier`.
|
||||
- :ref:`conditional-unsimplifier`.
|
||||
- :ref:`control-flow-simplifier`.
|
||||
- :ref:`dead-code-eliminator`.
|
||||
- :ref:`equivalent-function-combiner`.
|
||||
- :ref:`expression-joiner`.
|
||||
- :ref:`expression-simplifier`.
|
||||
- :ref:`expression-splitter`.
|
||||
- :ref:`for-loop-condition-into-body`.
|
||||
- :ref:`for-loop-condition-out-of-body`.
|
||||
- :ref:`for-loop-init-rewriter`.
|
||||
- :ref:`functional-inliner`.
|
||||
- :ref:`function-grouper`.
|
||||
- :ref:`function-hoister`.
|
||||
- :ref:`function-specializer`.
|
||||
- :ref:`literal-rematerialiser`.
|
||||
- :ref:`load-resolver`.
|
||||
- :ref:`loop-invariant-code-motion`.
|
||||
- :ref:`redundant-assign-eliminator`.
|
||||
- :ref:`reasoning-based-simplifier`.
|
||||
- :ref:`rematerialiser`.
|
||||
- :ref:`SSA-reverser`.
|
||||
- :ref:`SSA-transform`.
|
||||
- :ref:`structural-simplifier`.
|
||||
- :ref:`unused-function-parameter-pruner`.
|
||||
- :ref:`unused-pruner`.
|
||||
- :ref:`var-decl-initializer`.
|
||||
- :ref:`block-flattener`.
|
||||
- :ref:`circular-reference-pruner`.
|
||||
- :ref:`common-subexpression-eliminator`.
|
||||
- :ref:`conditional-simplifier`.
|
||||
- :ref:`conditional-unsimplifier`.
|
||||
- :ref:`control-flow-simplifier`.
|
||||
- :ref:`dead-code-eliminator`.
|
||||
- :ref:`equivalent-function-combiner`.
|
||||
- :ref:`expression-joiner`.
|
||||
- :ref:`expression-simplifier`.
|
||||
- :ref:`expression-splitter`.
|
||||
- :ref:`for-loop-condition-into-body`.
|
||||
- :ref:`for-loop-condition-out-of-body`.
|
||||
- :ref:`for-loop-init-rewriter`.
|
||||
- :ref:`functional-inliner`.
|
||||
- :ref:`function-grouper`.
|
||||
- :ref:`function-hoister`.
|
||||
- :ref:`function-specializer`.
|
||||
- :ref:`literal-rematerialiser`.
|
||||
- :ref:`load-resolver`.
|
||||
- :ref:`loop-invariant-code-motion`.
|
||||
- :ref:`redundant-assign-eliminator`.
|
||||
- :ref:`reasoning-based-simplifier`.
|
||||
- :ref:`rematerialiser`.
|
||||
- :ref:`SSA-reverser`.
|
||||
- :ref:`SSA-transform`.
|
||||
- :ref:`structural-simplifier`.
|
||||
- :ref:`unused-function-parameter-pruner`.
|
||||
- :ref:`unused-pruner`.
|
||||
- :ref:`var-decl-initializer`.
|
||||
|
||||
Selecting Optimizations
|
||||
-----------------------
|
||||
@@ -375,7 +377,7 @@ After this step, a program has the following normal form:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{ I F... }
|
||||
{ I F... }
|
||||
|
||||
Where ``I`` is a (potentially empty) block that does not contain any function definitions (not even recursively)
|
||||
and ``F`` is a list of function definitions such that no function contains a function definition.
|
||||
@@ -519,7 +521,7 @@ compact again at the end.
|
||||
ExpressionSplitter
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))``
|
||||
The expression splitter turns expressions like ``add(mload(0x123), mul(mload(0x456), 0x20))``
|
||||
into a sequence of declarations of unique variables that are assigned sub-expressions
|
||||
of that expression so that each function call has only variables or literals
|
||||
as arguments.
|
||||
@@ -529,9 +531,9 @@ The above would be transformed into
|
||||
.. code-block:: yul
|
||||
|
||||
{
|
||||
let _1 := mload(y)
|
||||
let _1 := mload(0x123)
|
||||
let _2 := mul(_1, 0x20)
|
||||
let _3 := mload(x)
|
||||
let _3 := mload(0x456)
|
||||
let z := add(_3, _2)
|
||||
}
|
||||
|
||||
@@ -589,8 +591,8 @@ For any variable ``a`` that is assigned to somewhere in the code
|
||||
(variables that are declared with value and never re-assigned
|
||||
are not modified) perform the following transforms:
|
||||
|
||||
- replace ``let a := v`` by ``let a_i := v let a := a_i``
|
||||
- replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused.
|
||||
- replace ``let a := v`` by ``let a_i := v let a := a_i``
|
||||
- replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused.
|
||||
|
||||
Furthermore, always record the current value of ``i`` used for ``a`` and replace each
|
||||
reference to ``a`` by ``a_i``.
|
||||
@@ -633,7 +635,7 @@ The SSA transform converts this snippet to the following:
|
||||
|
||||
{
|
||||
let a_1 := 1
|
||||
a := a_1
|
||||
let a := a_1
|
||||
let a_2 := mload(a_1)
|
||||
a := a_2
|
||||
let a_3 := sload(a_2)
|
||||
@@ -677,9 +679,9 @@ joins, the two mappings coming from the two branches are combined in the followi
|
||||
Statements that are only in one mapping or have the same state are used unchanged.
|
||||
Conflicting values are resolved in the following way:
|
||||
|
||||
- "unused", "undecided" -> "undecided"
|
||||
- "unused", "used" -> "used"
|
||||
- "undecided, "used" -> "used"
|
||||
- "unused", "undecided" -> "undecided"
|
||||
- "unused", "used" -> "used"
|
||||
- "undecided, "used" -> "used"
|
||||
|
||||
For for-loops, the condition, body and post-part are visited twice, taking
|
||||
the joining control-flow at the condition into account.
|
||||
@@ -696,7 +698,7 @@ operation where ``unused = 0``, ``undecided = 1`` and ``used = 2``.
|
||||
|
||||
The proper way would be to compute
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
max(s, f(s), f(f(s)), f(f(f(s))), ...)
|
||||
|
||||
@@ -705,7 +707,7 @@ iterating it has to reach a cycle after at most three iterations,
|
||||
and thus ``f(f(f(s)))`` has to equal one of ``s``, ``f(s)``, or ``f(f(s))``
|
||||
and thus
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...).
|
||||
|
||||
@@ -735,10 +737,10 @@ is side-effect free and its evaluation only depends on the values of variables
|
||||
and the call-constant state of the environment. Most expressions are movable.
|
||||
The following parts make an expression non-movable:
|
||||
|
||||
- function calls (might be relaxed in the future if all statements in the function are movable)
|
||||
- opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``)
|
||||
- opcodes that read or write memory, storage or external state information
|
||||
- opcodes that depend on the current PC, memory size or returndata size
|
||||
- function calls (might be relaxed in the future if all statements in the function are movable)
|
||||
- opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``)
|
||||
- opcodes that read or write memory, storage or external state information
|
||||
- opcodes that depend on the current PC, memory size or returndata size
|
||||
|
||||
DataflowAnalyzer
|
||||
^^^^^^^^^^^^^^^^
|
||||
@@ -836,8 +838,8 @@ ReasoningBasedSimplifier
|
||||
|
||||
This optimizer uses SMT solvers to check whether ``if`` conditions are constant.
|
||||
|
||||
- If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed.
|
||||
- If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``.
|
||||
- If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed.
|
||||
- If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``.
|
||||
|
||||
The simplifications above can only be applied if the condition is movable.
|
||||
|
||||
@@ -872,13 +874,13 @@ we cannot assign a specific value.
|
||||
|
||||
Current features:
|
||||
|
||||
- switch cases: insert "<condition> := <caseLabel>"
|
||||
- after if statement with terminating control-flow, insert "<condition> := 0"
|
||||
- switch cases: insert "<condition> := <caseLabel>"
|
||||
- after if statement with terminating control-flow, insert "<condition> := 0"
|
||||
|
||||
Future features:
|
||||
|
||||
- allow replacements by "1"
|
||||
- take termination of user-defined functions into account
|
||||
- allow replacements by "1"
|
||||
- take termination of user-defined functions into account
|
||||
|
||||
Works best with SSA form and if dead code removal has run before.
|
||||
|
||||
@@ -898,15 +900,15 @@ ControlFlowSimplifier
|
||||
|
||||
Simplifies several control-flow structures:
|
||||
|
||||
- replace if with empty body with pop(condition)
|
||||
- remove empty default switch case
|
||||
- remove empty switch case if no default case exists
|
||||
- replace switch with no cases with pop(expression)
|
||||
- turn switch with single case into if
|
||||
- replace switch with only default case with pop(expression) and body
|
||||
- replace switch with const expr with matching case body
|
||||
- replace ``for`` with terminating control flow and without other break/continue by ``if``
|
||||
- remove ``leave`` at the end of a function.
|
||||
- replace if with empty body with pop(condition)
|
||||
- remove empty default switch case
|
||||
- remove empty switch case if no default case exists
|
||||
- replace switch with no cases with pop(expression)
|
||||
- turn switch with single case into if
|
||||
- replace switch with only default case with pop(expression) and body
|
||||
- replace switch with const expr with matching case body
|
||||
- replace ``for`` with terminating control flow and without other break/continue by ``if``
|
||||
- remove ``leave`` at the end of a function.
|
||||
|
||||
None of these operations depend on the data flow. The StructuralSimplifier
|
||||
performs similar tasks that do depend on data flow.
|
||||
@@ -956,13 +958,13 @@ StructuralSimplifier
|
||||
This is a general step that performs various kinds of simplifications on
|
||||
a structural level:
|
||||
|
||||
- replace if statement with empty body by ``pop(condition)``
|
||||
- replace if statement with true condition by its body
|
||||
- remove if statement with false condition
|
||||
- turn switch with single case into if
|
||||
- replace switch with only default case by ``pop(expression)`` and body
|
||||
- replace switch with literal expression by matching case body
|
||||
- replace for loop with false condition by its initialization part
|
||||
- replace if statement with empty body by ``pop(condition)``
|
||||
- replace if statement with true condition by its body
|
||||
- remove if statement with false condition
|
||||
- turn switch with single case into if
|
||||
- replace switch with only default case by ``pop(expression)`` and body
|
||||
- replace switch with literal expression by matching case body
|
||||
- replace for loop with false condition by its initialization part
|
||||
|
||||
This component uses the Dataflow Analyzer.
|
||||
|
||||
@@ -1008,8 +1010,8 @@ declarations inside conditional branches will not be moved out of the loop.
|
||||
|
||||
Requirements:
|
||||
|
||||
- The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront.
|
||||
- Expression splitter and SSA transform should be run upfront to obtain better result.
|
||||
- The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront.
|
||||
- Expression splitter and SSA transform should be run upfront to obtain better result.
|
||||
|
||||
|
||||
Function-Level Optimizations
|
||||
@@ -1053,8 +1055,8 @@ remove the parameter and create a new "linking" function as follows:
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
function f(a,b) -> x { x := div(a,b) }
|
||||
function f2(a,b,c) -> x, y { x := f(a,b) }
|
||||
function f(a,b) -> x { x := div(a,b) }
|
||||
function f2(a,b,c) -> x, y { x := f(a,b) }
|
||||
|
||||
and replace all references to ``f`` by ``f2``.
|
||||
The inliner should be run afterwards to make sure that all references to ``f2`` are replaced by
|
||||
@@ -1089,15 +1091,15 @@ FunctionalInliner
|
||||
This component of the optimizer performs restricted function inlining by inlining functions that can be
|
||||
inlined inside functional expressions, i.e. functions that:
|
||||
|
||||
- return a single value.
|
||||
- have a body like ``r := <functional expression>``.
|
||||
- neither reference themselves nor ``r`` in the right hand side.
|
||||
- return a single value.
|
||||
- have a body like ``r := <functional expression>``.
|
||||
- neither reference themselves nor ``r`` in the right hand side.
|
||||
|
||||
Furthermore, for all parameters, all of the following need to be true:
|
||||
|
||||
- The argument is movable.
|
||||
- The parameter is either referenced less than twice in the function body, or the argument is rather cheap
|
||||
("cost" of at most 1, like a constant up to 0xff).
|
||||
- The argument is movable.
|
||||
- The parameter is either referenced less than twice in the function body, or the argument is rather cheap
|
||||
("cost" of at most 1, like a constant up to 0xff).
|
||||
|
||||
Example: The function to be inlined has the form of ``function f(...) -> r { r := E }`` where
|
||||
``E`` is an expression that does not reference ``r`` and all arguments in the function call are movable expressions.
|
||||
@@ -1186,16 +1188,18 @@ The SSA transform rewrites
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
a := E
|
||||
let a := calldataload(0)
|
||||
mstore(a, 1)
|
||||
|
||||
to
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
let a_1 := E
|
||||
a := a_1
|
||||
let a_1 := calldataload(0)
|
||||
let a := a_1
|
||||
mstore(a_1, 1)
|
||||
let a_2 := calldataload(0x20)
|
||||
a := a_2
|
||||
|
||||
The problem is that instead of ``a``, the variable ``a_1`` is used
|
||||
whenever ``a`` was referenced. The SSA transform changes statements
|
||||
@@ -1204,9 +1208,11 @@ snippet is turned into
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
a := E
|
||||
let a := calldataload(0)
|
||||
let a_1 := a
|
||||
mstore(a_1, 1)
|
||||
a := calldataload(0x20)
|
||||
let a_2 := a
|
||||
|
||||
This is a very simple equivalence transform, but when we now run the
|
||||
Common Subexpression Eliminator, it will replace all occurrences of ``a_1``
|
||||
@@ -1259,7 +1265,7 @@ Reverses the transformation of ForLoopConditionIntoBody.
|
||||
|
||||
For any movable ``c``, it turns
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
for { ... } 1 { ... } {
|
||||
if iszero(c) { break }
|
||||
@@ -1268,7 +1274,7 @@ For any movable ``c``, it turns
|
||||
|
||||
into
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
for { ... } c { ... } {
|
||||
...
|
||||
@@ -1276,7 +1282,7 @@ into
|
||||
|
||||
and it turns
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
for { ... } 1 { ... } {
|
||||
if c { break }
|
||||
@@ -1285,7 +1291,7 @@ and it turns
|
||||
|
||||
into
|
||||
|
||||
::
|
||||
.. code-block:: none
|
||||
|
||||
for { ... } iszero(c) { ... } {
|
||||
...
|
||||
|
||||
@@ -56,8 +56,8 @@ used in a single modifier.
|
||||
In order to compress these source mappings especially for bytecode, the
|
||||
following rules are used:
|
||||
|
||||
- If a field is empty, the value of the preceding element is used.
|
||||
- If a ``:`` is missing, all following fields are considered empty.
|
||||
- If a field is empty, the value of the preceding element is used.
|
||||
- If a ``:`` is missing, all following fields are considered empty.
|
||||
|
||||
This means the following source mappings represent the same information:
|
||||
|
||||
|
||||
@@ -140,7 +140,9 @@ of a keypair belonging to :ref:`external accounts<accounts>`.
|
||||
The keyword ``public`` automatically generates a function that allows you to access the current value of the state
|
||||
variable from outside of the contract. Without this keyword, other contracts have no way to access the variable.
|
||||
The code of the function generated by the compiler is equivalent
|
||||
to the following (ignore ``external`` and ``view`` for now)::
|
||||
to the following (ignore ``external`` and ``view`` for now):
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function minter() external view returns (address) { return minter; }
|
||||
|
||||
@@ -162,7 +164,9 @@ even better, keep a list, or use a more suitable data type.
|
||||
|
||||
The :ref:`getter function<getter-functions>` created by the ``public`` keyword
|
||||
is more complex in the case of a mapping. It looks like the
|
||||
following::
|
||||
following:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function balances(address _account) external view returns (uint) {
|
||||
return balances[_account];
|
||||
|
||||
+172
-139
@@ -1,6 +1,6 @@
|
||||
********************************
|
||||
*********************************
|
||||
Solidity IR-based Codegen Changes
|
||||
********************************
|
||||
*********************************
|
||||
|
||||
This section highlights the main differences between the old and the IR-based codegen,
|
||||
along with the reasoning behind the changes and how to update affected code.
|
||||
@@ -11,180 +11,211 @@ Semantic Only Changes
|
||||
This section lists the changes that are semantic-only, thus potentially
|
||||
hiding new and different behavior in existing code.
|
||||
|
||||
* When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
|
||||
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
|
||||
- When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
|
||||
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
|
||||
|
||||
.. code-block:: solidity
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0;
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.1;
|
||||
|
||||
contract C {
|
||||
struct S {
|
||||
uint64 y;
|
||||
uint64 z;
|
||||
}
|
||||
S s;
|
||||
function f() public {
|
||||
// ...
|
||||
delete s;
|
||||
// s occupies only first 16 bytes of the 32 bytes slot
|
||||
// delete will write zero to the full slot
|
||||
}
|
||||
}
|
||||
contract C {
|
||||
struct S {
|
||||
uint64 y;
|
||||
uint64 z;
|
||||
}
|
||||
S s;
|
||||
function f() public {
|
||||
// ...
|
||||
delete s;
|
||||
// s occupies only first 16 bytes of the 32 bytes slot
|
||||
// delete will write zero to the full slot
|
||||
}
|
||||
}
|
||||
|
||||
We have the same behavior for implicit delete, for example when array of structs is shortened.
|
||||
We have the same behavior for implicit delete, for example when array of structs is shortened.
|
||||
|
||||
* Function modifiers are implemented in a slightly different way regarding function parameters.
|
||||
This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier.
|
||||
In the old code generator, each function parameter has a fixed slot on the stack. If the function
|
||||
is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the
|
||||
function parameter's value is visible in the next execution of the function.
|
||||
The new code generator implements modifiers using actual functions and passes function parameters on.
|
||||
This means that multiple executions of a function will get the same values for the parameters.
|
||||
- Function modifiers are implemented in a slightly different way regarding function parameters.
|
||||
This especially has an effect if the placeholder ``_;`` is evaluated multiple times in a modifier.
|
||||
In the old code generator, each function parameter has a fixed slot on the stack. If the function
|
||||
is run multiple times because ``_;`` is used multiple times or used in a loop, then a change to the
|
||||
function parameter's value is visible in the next execution of the function.
|
||||
The new code generator implements modifiers using actual functions and passes function parameters on.
|
||||
This means that multiple executions of a function will get the same values for the parameters.
|
||||
|
||||
.. code-block:: solidity
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0;
|
||||
contract C {
|
||||
function f(uint _a) public pure mod() returns (uint _r) {
|
||||
_r = _a++;
|
||||
}
|
||||
modifier mod() { _; _; }
|
||||
}
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.0;
|
||||
contract C {
|
||||
function f(uint _a) public pure mod() returns (uint _r) {
|
||||
_r = _a++;
|
||||
}
|
||||
modifier mod() { _; _; }
|
||||
}
|
||||
|
||||
If you execute ``f(0)`` in the old code generator, it will return ``2``, while
|
||||
it will return ``1`` when using the new code generator.
|
||||
If you execute ``f(0)`` in the old code generator, it will return ``2``, while
|
||||
it will return ``1`` when using the new code generator.
|
||||
|
||||
* The order of contract initialization has changed in case of inheritance.
|
||||
- The order of contract initialization has changed in case of inheritance.
|
||||
|
||||
The order used to be:
|
||||
- All state variables are zero-initialized at the beginning.
|
||||
- Evaluate base constructor arguments from most derived to most base contract.
|
||||
- Initialize all state variables in the whole inheritance hierarchy from most base to most derived.
|
||||
- Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived.
|
||||
The order used to be:
|
||||
|
||||
New order:
|
||||
- All state variables are zero-initialized at the beginning.
|
||||
- Evaluate base constructor arguments from most derived to most base contract.
|
||||
- For every contract in order from most base to most derived in the linearized hierarchy execute:
|
||||
1. If present at declaration, initial values are assigned to state variables.
|
||||
2. Constructor, if present.
|
||||
- All state variables are zero-initialized at the beginning.
|
||||
- Evaluate base constructor arguments from most derived to most base contract.
|
||||
- Initialize all state variables in the whole inheritance hierarchy from most base to most derived.
|
||||
- Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived.
|
||||
|
||||
New order:
|
||||
|
||||
- All state variables are zero-initialized at the beginning.
|
||||
- Evaluate base constructor arguments from most derived to most base contract.
|
||||
- For every contract in order from most base to most derived in the linearized hierarchy execute:
|
||||
|
||||
1. If present at declaration, initial values are assigned to state variables.
|
||||
2. Constructor, if present.
|
||||
|
||||
This causes differences in some contracts, for example:
|
||||
|
||||
.. code-block:: solidity
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0;
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.7.1;
|
||||
|
||||
contract A {
|
||||
uint x;
|
||||
constructor() {
|
||||
x = 42;
|
||||
}
|
||||
function f() public view returns(uint256) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
contract B is A {
|
||||
uint public y = f();
|
||||
}
|
||||
contract A {
|
||||
uint x;
|
||||
constructor() {
|
||||
x = 42;
|
||||
}
|
||||
function f() public view returns(uint256) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
contract B is A {
|
||||
uint public y = f();
|
||||
}
|
||||
|
||||
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
|
||||
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
|
||||
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
|
||||
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
|
||||
|
||||
* Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot).
|
||||
This causes differences in some contracts, for example:
|
||||
- Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot).
|
||||
This causes differences in some contracts, for example:
|
||||
|
||||
.. code-block:: solidity
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.1;
|
||||
|
||||
contract C {
|
||||
bytes x;
|
||||
function f() public returns (uint _r) {
|
||||
bytes memory m = "tmp";
|
||||
assembly {
|
||||
mstore(m, 8)
|
||||
mstore(add(m, 32), "deadbeef15dead")
|
||||
}
|
||||
x = m;
|
||||
assembly {
|
||||
_r := sload(x.slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
contract C {
|
||||
bytes x;
|
||||
function f() public returns (uint _r) {
|
||||
bytes memory m = "tmp";
|
||||
assembly {
|
||||
mstore(m, 8)
|
||||
mstore(add(m, 32), "deadbeef15dead")
|
||||
}
|
||||
x = m;
|
||||
assembly {
|
||||
_r := sload(x.slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
|
||||
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data).
|
||||
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
|
||||
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data).
|
||||
|
||||
.. index:: ! evaluation order; expression
|
||||
.. index:: ! evaluation order; expression
|
||||
|
||||
* For the old code generator, the evaluation order of expressions is unspecified.
|
||||
- For the old code generator, the evaluation order of expressions is unspecified.
|
||||
For the new code generator, we try to evaluate in source order (left to right), but do not guarantee it.
|
||||
This can lead to semantic differences.
|
||||
|
||||
For example:
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
function preincr_u8(uint8 _a) public pure returns (uint8) {
|
||||
return ++_a + _a;
|
||||
}
|
||||
}
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.1;
|
||||
contract C {
|
||||
function preincr_u8(uint8 _a) public pure returns (uint8) {
|
||||
return ++_a + _a;
|
||||
}
|
||||
}
|
||||
|
||||
The function ``preincr_u8(1)`` returns the following values:
|
||||
- Old code generator: 3 (``1 + 2``) but the return value is unspecified in general
|
||||
- New code generator: 4 (``2 + 2``) but the return value is not guaranteed
|
||||
The function ``preincr_u8(1)`` returns the following values:
|
||||
|
||||
.. index:: ! evaluation order; function arguments
|
||||
- Old code generator: 3 (``1 + 2``) but the return value is unspecified in general
|
||||
- New code generator: 4 (``2 + 2``) but the return value is not guaranteed
|
||||
|
||||
On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
|
||||
For example:
|
||||
.. index:: ! evaluation order; function arguments
|
||||
|
||||
.. code-block:: solidity
|
||||
On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
|
||||
For example:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
function add(uint8 _a, uint8 _b) public pure returns (uint8) {
|
||||
return _a + _b;
|
||||
}
|
||||
function g(uint8 _a, uint8 _b) public pure returns (uint8) {
|
||||
return add(++_a + ++_b, _a + _b);
|
||||
}
|
||||
}
|
||||
.. code-block:: solidity
|
||||
|
||||
The function ``g(1, 2)`` returns the following values:
|
||||
- Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general
|
||||
- New code generator: ``10`` but the return value is not guaranteed
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.1;
|
||||
contract C {
|
||||
function add(uint8 _a, uint8 _b) public pure returns (uint8) {
|
||||
return _a + _b;
|
||||
}
|
||||
function g(uint8 _a, uint8 _b) public pure returns (uint8) {
|
||||
return add(++_a + ++_b, _a + _b);
|
||||
}
|
||||
}
|
||||
|
||||
The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator
|
||||
and left-to-right by the new code generator.
|
||||
For example:
|
||||
The function ``g(1, 2)`` returns the following values:
|
||||
|
||||
::
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
function f() public pure returns (uint256 aMod, uint256 mMod) {
|
||||
uint256 x = 3;
|
||||
// Old code gen: add/mulmod(5, 4, 3)
|
||||
// New code gen: add/mulmod(4, 5, 5)
|
||||
aMod = addmod(++x, ++x, x);
|
||||
mMod = mulmod(++x, ++x, x);
|
||||
}
|
||||
}
|
||||
- Old code generator: ``10`` (``add(2 + 3, 2 + 3)``) but the return value is unspecified in general
|
||||
- New code generator: ``10`` but the return value is not guaranteed
|
||||
|
||||
The function ``f()`` returns the following values:
|
||||
- Old code generator: ``aMod = 0`` and ``mMod = 2``
|
||||
- New code generator: ``aMod = 4`` and ``mMod = 0``
|
||||
The arguments to the global functions ``addmod`` and ``mulmod`` are evaluated right-to-left by the old code generator
|
||||
and left-to-right by the new code generator.
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.1;
|
||||
contract C {
|
||||
function f() public pure returns (uint256 aMod, uint256 mMod) {
|
||||
uint256 x = 3;
|
||||
// Old code gen: add/mulmod(5, 4, 3)
|
||||
// New code gen: add/mulmod(4, 5, 5)
|
||||
aMod = addmod(++x, ++x, x);
|
||||
mMod = mulmod(++x, ++x, x);
|
||||
}
|
||||
}
|
||||
|
||||
The function ``f()`` returns the following values:
|
||||
|
||||
- Old code generator: ``aMod = 0`` and ``mMod = 2``
|
||||
- New code generator: ``aMod = 4`` and ``mMod = 0``
|
||||
|
||||
- The new code generator imposes a hard limit of ``type(uint64).max`` (``0xffffffffffffffff``) for the free memory pointer. Allocations that would increase its value beyond this limit revert. The old code generator does not have this limit.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
contract C {
|
||||
function f() public {
|
||||
uint[] memory arr;
|
||||
// allocation size: 576460752303423481
|
||||
// assumes freeMemPtr points to 0x80 initially
|
||||
uint solYulMaxAllocationBeforeMemPtrOverflow = (type(uint64).max - 0x80 - 31) / 32;
|
||||
// freeMemPtr overflows UINT64_MAX
|
||||
arr = new uint[](solYulMaxAllocationBeforeMemPtrOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
The function `f()` behaves as follows:
|
||||
|
||||
- Old code generator: runs out of gas while zeroing the array contents after the large memory allocation
|
||||
- New code generator: reverts due to free memory pointer overflow (does not run out of gas)
|
||||
|
||||
|
||||
Internals
|
||||
@@ -219,9 +250,10 @@ The new code generator performs cleanup after any operation that can result in d
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
pragma solidity >=0.8.1;
|
||||
contract C {
|
||||
function f(uint8 _a) public pure returns (uint _r1, uint _r2)
|
||||
{
|
||||
@@ -234,6 +266,7 @@ For example:
|
||||
}
|
||||
|
||||
The function ``f(1)`` returns the following values:
|
||||
|
||||
- Old code generator: (``fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe``, ``00000000000000000000000000000000000000000000000000000000000000fe``)
|
||||
- New code generator: (``00000000000000000000000000000000000000000000000000000000000000fe``, ``00000000000000000000000000000000000000000000000000000000000000fe``)
|
||||
|
||||
|
||||
@@ -180,9 +180,9 @@ a `default export <https://developer.mozilla.org/en-US/docs/web/javascript/refer
|
||||
|
||||
At a global level, you can use import statements of the following form:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
import "filename";
|
||||
import "filename";
|
||||
|
||||
The ``filename`` part is called an *import path*.
|
||||
This statement imports all global symbols from "filename" (and symbols imported there) into the
|
||||
@@ -195,15 +195,15 @@ symbols explicitly.
|
||||
The following example creates a new global symbol ``symbolName`` whose members are all
|
||||
the global symbols from ``"filename"``:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
import * as symbolName from "filename";
|
||||
import * as symbolName from "filename";
|
||||
|
||||
which results in all global symbols being available in the format ``symbolName.symbol``.
|
||||
|
||||
A variant of this syntax that is not part of ES6, but possibly useful is:
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
import "filename" as symbolName;
|
||||
|
||||
@@ -213,9 +213,9 @@ If there is a naming collision, you can rename symbols while importing. For exam
|
||||
the code below creates new global symbols ``alias`` and ``symbol2`` which reference
|
||||
``symbol1`` and ``symbol2`` from inside ``"filename"``, respectively.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
import {symbol1 as alias, symbol2} from "filename";
|
||||
import {symbol1 as alias, symbol2} from "filename";
|
||||
|
||||
.. index:: virtual filesystem, source unit name, import; path, filesystem path, import callback, Remix IDE
|
||||
|
||||
@@ -253,14 +253,14 @@ Comments
|
||||
|
||||
Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
// This is a single-line comment.
|
||||
// This is a single-line comment.
|
||||
|
||||
/*
|
||||
This is a
|
||||
multi-line comment.
|
||||
*/
|
||||
/*
|
||||
This is a
|
||||
multi-line comment.
|
||||
*/
|
||||
|
||||
.. note::
|
||||
A single-line comment is terminated by any unicode line terminator
|
||||
|
||||
+37
-35
@@ -31,24 +31,24 @@ reduce whitespace to a minimum and sort the keys of all objects to arrive at a
|
||||
unique formatting. Comments are not permitted and used here only for
|
||||
explanatory purposes.
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
// Required: The version of the metadata format
|
||||
version: "1",
|
||||
"version": "1",
|
||||
// Required: Source code language, basically selects a "sub-version"
|
||||
// of the specification
|
||||
language: "Solidity",
|
||||
"language": "Solidity",
|
||||
// Required: Details about the compiler, contents are specific
|
||||
// to the language.
|
||||
compiler: {
|
||||
"compiler": {
|
||||
// Required for Solidity: Version of the compiler
|
||||
version: "0.4.6+commit.2dabbdf0.Emscripten.clang",
|
||||
"version": "0.4.6+commit.2dabbdf0.Emscripten.clang",
|
||||
// Optional: Hash of the compiler binary which produced this output
|
||||
keccak256: "0x123..."
|
||||
"keccak256": "0x123..."
|
||||
},
|
||||
// Required: Compilation source files/source units, keys are file names
|
||||
sources:
|
||||
"sources":
|
||||
{
|
||||
"myFile.sol": {
|
||||
// Required: keccak256 hash of the source file
|
||||
@@ -68,59 +68,59 @@ explanatory purposes.
|
||||
}
|
||||
},
|
||||
// Required: Compiler settings
|
||||
settings:
|
||||
"settings":
|
||||
{
|
||||
// Required for Solidity: Sorted list of remappings
|
||||
remappings: [ ":g=/dir" ],
|
||||
"remappings": [ ":g=/dir" ],
|
||||
// Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated
|
||||
// and are only given for backwards-compatibility.
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 500,
|
||||
details: {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 500,
|
||||
"details": {
|
||||
// peephole defaults to "true"
|
||||
peephole: true,
|
||||
"peephole": true,
|
||||
// inliner defaults to "true"
|
||||
inliner: true,
|
||||
"inliner": true,
|
||||
// jumpdestRemover defaults to "true"
|
||||
jumpdestRemover: true,
|
||||
orderLiterals: false,
|
||||
deduplicate: false,
|
||||
cse: false,
|
||||
constantOptimizer: false,
|
||||
yul: true,
|
||||
"jumpdestRemover": true,
|
||||
"orderLiterals": false,
|
||||
"deduplicate": false,
|
||||
"cse": false,
|
||||
"constantOptimizer": false,
|
||||
"yul": true,
|
||||
// Optional: Only present if "yul" is "true"
|
||||
yulDetails: {
|
||||
stackAllocation: false,
|
||||
optimizerSteps: "dhfoDgvulfnTUtnIf..."
|
||||
"yulDetails": {
|
||||
"stackAllocation": false,
|
||||
"optimizerSteps": "dhfoDgvulfnTUtnIf..."
|
||||
}
|
||||
}
|
||||
},
|
||||
metadata: {
|
||||
"metadata": {
|
||||
// Reflects the setting used in the input json, defaults to false
|
||||
useLiteralContent: true,
|
||||
"useLiteralContent": true,
|
||||
// Reflects the setting used in the input json, defaults to "ipfs"
|
||||
bytecodeHash: "ipfs"
|
||||
}
|
||||
"bytecodeHash": "ipfs"
|
||||
},
|
||||
// Required for Solidity: File and name of the contract or library this
|
||||
// metadata is created for.
|
||||
compilationTarget: {
|
||||
"compilationTarget": {
|
||||
"myFile.sol": "MyContract"
|
||||
},
|
||||
// Required for Solidity: Addresses for libraries used
|
||||
libraries: {
|
||||
"libraries": {
|
||||
"MyLib": "0x123123..."
|
||||
}
|
||||
},
|
||||
// Required: Generated information about the contract.
|
||||
output:
|
||||
"output":
|
||||
{
|
||||
// Required: ABI definition of the contract
|
||||
abi: [ ... ],
|
||||
"abi": [/* ... */],
|
||||
// Required: NatSpec user documentation of the contract
|
||||
userdoc: [ ... ],
|
||||
"userdoc": [/* ... */],
|
||||
// Required: NatSpec developer documentation of the contract
|
||||
devdoc: [ ... ],
|
||||
"devdoc": [/* ... */]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,9 @@ the mapping ``{"ipfs": <IPFS hash>, "solc": <compiler version>}`` is stored
|
||||
contain more keys (see below) and the beginning of that
|
||||
encoding is not easy to find, its length is added in a two-byte big-endian
|
||||
encoding. The current version of the Solidity compiler usually adds the following
|
||||
to the end of the deployed bytecode::
|
||||
to the end of the deployed bytecode
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0xa2
|
||||
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
|
||||
|
||||
@@ -58,7 +58,7 @@ The following example shows a contract and a function using all available tags.
|
||||
|
||||
This may change in the future.
|
||||
|
||||
.. code:: Solidity
|
||||
.. code-block:: Solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.8.2 < 0.9.0;
|
||||
@@ -166,9 +166,9 @@ Inheritance Notes
|
||||
Functions without NatSpec will automatically inherit the documentation of their
|
||||
base function. Exceptions to this are:
|
||||
|
||||
* When the parameter names are different.
|
||||
* When there is more than one base function.
|
||||
* When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit.
|
||||
* When the parameter names are different.
|
||||
* When there is more than one base function.
|
||||
* When there is an explicit ``@inheritdoc`` tag which specifies which contract should be used to inherit.
|
||||
|
||||
.. _header-output:
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ Direct Imports
|
||||
|
||||
An import that does not start with ``./`` or ``../`` is a *direct import*.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
|
||||
import "/project/lib/util.sol"; // source unit name: /project/lib/util.sol
|
||||
import "lib/util.sol"; // source unit name: lib/util.sol
|
||||
@@ -464,7 +464,8 @@ Here are the detailed rules governing the behaviour of remappings:
|
||||
|
||||
#. **Prefix cannot be empty but context and target are optional.**
|
||||
|
||||
If ``target`` is omitted, it defaults to the value of the ``prefix``.
|
||||
- If ``target`` is the empty string, ``prefix`` is simply removed from import paths.
|
||||
- Empty ``context`` means that the remapping applies to all imports in all source units.
|
||||
|
||||
.. index:: Remix IDE, file://
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
sphinx_rtd_theme>=0.3.1
|
||||
# Older versions of sphinx-rtd-theme do not work with never docutils but have a bug in the dependency
|
||||
# which could result in it being installed anyway and the style (especially bullet points) being broken.
|
||||
# See https://github.com/readthedocs/sphinx_rtd_theme/issues/1115
|
||||
sphinx_rtd_theme>=0.5.2
|
||||
|
||||
pygments-lexer-solidity>=0.7.0
|
||||
sphinx-a4doc>=1.2.1
|
||||
|
||||
|
||||
@@ -211,6 +211,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
||||
}
|
||||
|
||||
function transferTo(address payable dest, uint amount) public {
|
||||
// THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
|
||||
require(tx.origin == owner);
|
||||
dest.transfer(amount);
|
||||
}
|
||||
|
||||
+134
-93
@@ -15,7 +15,7 @@ difference between what you did (the specification) and how you did it
|
||||
is what you wanted and that you did not miss any unintended effects of it.
|
||||
|
||||
Solidity implements a formal verification approach based on
|
||||
`SMT <https://en.wikipedia.org/wiki/Satisfiability_modulo_theories>`_ and
|
||||
`SMT (Satisfiability Modulo Theories) <https://en.wikipedia.org/wiki/Satisfiability_modulo_theories>`_ and
|
||||
`Horn <https://en.wikipedia.org/wiki/Horn-satisfiability>`_ solving.
|
||||
The SMTChecker module automatically tries to prove that the code satisfies the
|
||||
specification given by ``require`` and ``assert`` statements. That is, it considers
|
||||
@@ -44,23 +44,24 @@ where the default is no engine. Selecting the engine enables the SMTChecker on a
|
||||
|
||||
.. note::
|
||||
|
||||
Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via
|
||||
``pragma experimental SMTChecker;`` and only the contracts containing the
|
||||
pragma would be analyzed. That pragma has been deprecated, and although it
|
||||
still enables the SMTChecker for backwards compatibility, it will be removed
|
||||
in Solidity 0.9.0. Note also that now using the pragma even in a single file
|
||||
enables the SMTChecker for all files.
|
||||
Prior to Solidity 0.8.4, the default way to enable the SMTChecker was via
|
||||
``pragma experimental SMTChecker;`` and only the contracts containing the
|
||||
pragma would be analyzed. That pragma has been deprecated, and although it
|
||||
still enables the SMTChecker for backwards compatibility, it will be removed
|
||||
in Solidity 0.9.0. Note also that now using the pragma even in a single file
|
||||
enables the SMTChecker for all files.
|
||||
|
||||
.. note::
|
||||
The lack of warnings for a verification target represents an undisputed
|
||||
mathematical proof of correctness, assuming no bugs in the SMTChecker and
|
||||
the underlying solver. Keep in mind that these problems are
|
||||
*very hard* and sometimes *impossible* to solve automatically in the
|
||||
general case. Therefore, several properties might not be solved or might
|
||||
lead to false positives for large contracts. Every proven property should
|
||||
be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning <smtchecker_options>`
|
||||
to learn a few options that might help proving more complex
|
||||
properties.
|
||||
|
||||
The lack of warnings for a verification target represents an undisputed
|
||||
mathematical proof of correctness, assuming no bugs in the SMTChecker and
|
||||
the underlying solver. Keep in mind that these problems are
|
||||
*very hard* and sometimes *impossible* to solve automatically in the
|
||||
general case. Therefore, several properties might not be solved or might
|
||||
lead to false positives for large contracts. Every proven property should
|
||||
be seen as an important achievement. For advanced users, see :ref:`SMTChecker Tuning <smtchecker_options>`
|
||||
to learn a few options that might help proving more complex
|
||||
properties.
|
||||
|
||||
********
|
||||
Tutorial
|
||||
@@ -96,7 +97,7 @@ The SMTChecker will, by default, check every reachable arithmetic operation
|
||||
in the contract for potential underflow and overflow.
|
||||
Here, it reports the following:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: text
|
||||
|
||||
Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here.
|
||||
Counterexample:
|
||||
@@ -202,8 +203,9 @@ Note that in this example the SMTChecker will automatically try to prove three p
|
||||
3. The assertion is always true.
|
||||
|
||||
.. note::
|
||||
The properties involve loops, which makes it *much much* harder than the previous
|
||||
examples, so beware of loops!
|
||||
|
||||
The properties involve loops, which makes it *much much* harder than the previous
|
||||
examples, so beware of loops!
|
||||
|
||||
All the properties are correctly proven safe. Feel free to change the
|
||||
properties and/or add restrictions on the array to see different results.
|
||||
@@ -231,20 +233,20 @@ For example, changing the code to
|
||||
|
||||
gives us:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: text
|
||||
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
|
||||
_a = [0, 0, 0, 0, 0]
|
||||
= 0
|
||||
_a = [0, 0, 0, 0, 0]
|
||||
= 0
|
||||
|
||||
Transaction trace:
|
||||
Test.constructor()
|
||||
Test.max([0, 0, 0, 0, 0])
|
||||
--> max.sol:14:4:
|
||||
|
|
||||
14 | assert(m > _a[i]);
|
||||
Transaction trace:
|
||||
Test.constructor()
|
||||
Test.max([0, 0, 0, 0, 0])
|
||||
--> max.sol:14:4:
|
||||
|
|
||||
14 | assert(m > _a[i]);
|
||||
|
||||
|
||||
State Properties
|
||||
@@ -321,28 +323,28 @@ reachable, by adding the following function.
|
||||
This property is false, and while proving that the property is false,
|
||||
the SMTChecker tells us exactly *how* to reach (2, 4):
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: text
|
||||
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
x = 2, y = 4
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
x = 2, y = 4
|
||||
|
||||
Transaction trace:
|
||||
Robot.constructor()
|
||||
State: x = 0, y = 0
|
||||
Robot.moveLeftUp()
|
||||
State: x = (- 1), y = 1
|
||||
Robot.moveRightUp()
|
||||
State: x = 0, y = 2
|
||||
Robot.moveRightUp()
|
||||
State: x = 1, y = 3
|
||||
Robot.moveRightUp()
|
||||
State: x = 2, y = 4
|
||||
Robot.reach_2_4()
|
||||
--> r.sol:35:4:
|
||||
|
|
||||
35 | assert(!(x == 2 && y == 4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Transaction trace:
|
||||
Robot.constructor()
|
||||
State: x = 0, y = 0
|
||||
Robot.moveLeftUp()
|
||||
State: x = (- 1), y = 1
|
||||
Robot.moveRightUp()
|
||||
State: x = 0, y = 2
|
||||
Robot.moveRightUp()
|
||||
State: x = 1, y = 3
|
||||
Robot.moveRightUp()
|
||||
State: x = 2, y = 4
|
||||
Robot.reach_2_4()
|
||||
--> r.sol:35:4:
|
||||
|
|
||||
35 | assert(!(x == 2 && y == 4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Note that the path above is not necessarily deterministic, as there are
|
||||
other paths that could reach (2, 4). The choice of which path is shown
|
||||
@@ -367,36 +369,36 @@ anything, including reenter the caller contract.
|
||||
pragma solidity >=0.8.0;
|
||||
|
||||
interface Unknown {
|
||||
function run() external;
|
||||
function run() external;
|
||||
}
|
||||
|
||||
contract Mutex {
|
||||
uint x;
|
||||
bool lock;
|
||||
uint x;
|
||||
bool lock;
|
||||
|
||||
Unknown immutable unknown;
|
||||
Unknown immutable unknown;
|
||||
|
||||
constructor(Unknown _u) {
|
||||
require(address(_u) != address(0));
|
||||
unknown = _u;
|
||||
}
|
||||
constructor(Unknown _u) {
|
||||
require(address(_u) != address(0));
|
||||
unknown = _u;
|
||||
}
|
||||
|
||||
modifier mutex {
|
||||
require(!lock);
|
||||
lock = true;
|
||||
_;
|
||||
lock = false;
|
||||
}
|
||||
modifier mutex {
|
||||
require(!lock);
|
||||
lock = true;
|
||||
_;
|
||||
lock = false;
|
||||
}
|
||||
|
||||
function set(uint _x) mutex public {
|
||||
x = _x;
|
||||
}
|
||||
function set(uint _x) mutex public {
|
||||
x = _x;
|
||||
}
|
||||
|
||||
function run() mutex public {
|
||||
uint xPre = x;
|
||||
unknown.run();
|
||||
assert(xPre == x);
|
||||
}
|
||||
function run() mutex public {
|
||||
uint xPre = x;
|
||||
unknown.run();
|
||||
assert(xPre == x);
|
||||
}
|
||||
}
|
||||
|
||||
The example above shows a contract that uses a mutex flag to forbid reentrancy.
|
||||
@@ -408,22 +410,22 @@ If we "forget" to use the ``mutex`` modifier on function ``set``, the
|
||||
SMTChecker is able to synthesize the behavior of the externally called code so
|
||||
that the assertion fails:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: text
|
||||
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
x = 1, lock = true, unknown = 1
|
||||
Warning: CHC: Assertion violation happens here.
|
||||
Counterexample:
|
||||
x = 1, lock = true, unknown = 1
|
||||
|
||||
Transaction trace:
|
||||
Mutex.constructor(1)
|
||||
State: x = 0, lock = false, unknown = 1
|
||||
Mutex.run()
|
||||
unknown.run() -- untrusted external call, synthesized as:
|
||||
Mutex.set(1) -- reentrant call
|
||||
--> m.sol:32:3:
|
||||
|
|
||||
32 | assert(xPre == x);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
Transaction trace:
|
||||
Mutex.constructor(1)
|
||||
State: x = 0, lock = false, unknown = 1
|
||||
Mutex.run()
|
||||
unknown.run() -- untrusted external call, synthesized as:
|
||||
Mutex.set(1) -- reentrant call
|
||||
--> m.sol:32:3:
|
||||
|
|
||||
32 | assert(xPre == x);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
.. _smtchecker_options:
|
||||
@@ -472,6 +474,14 @@ A common subset of targets might be, for example:
|
||||
There is no precise heuristic on how and when to split verification targets,
|
||||
but it can be useful especially when dealing with large contracts.
|
||||
|
||||
Unproved Targets
|
||||
================
|
||||
|
||||
If there are any unproved targets, the SMTChecker issues one warning stating
|
||||
how many unproved targets there are. If the user wishes to see all the specific
|
||||
unproved targets, the CLI option ``--model-checker-show-unproved true`` and
|
||||
the JSON option ``settings.modelChecker.showUnproved = true`` can be used.
|
||||
|
||||
Verified Contracts
|
||||
==================
|
||||
|
||||
@@ -492,14 +502,12 @@ allowed) of <source>:<contract> pairs in the CLI:
|
||||
and via the object ``settings.modelChecker.contracts`` in the :ref:`JSON input<compiler-api>`,
|
||||
which has the following form:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
contracts
|
||||
{
|
||||
"source1.sol": ["contract1"],
|
||||
"source2.sol": ["contract2", "contract3"]
|
||||
}
|
||||
.. code-block:: json
|
||||
|
||||
"contracts": {
|
||||
"source1.sol": ["contract1"],
|
||||
"source2.sol": ["contract2", "contract3"]
|
||||
}
|
||||
|
||||
.. _smtchecker_engines:
|
||||
|
||||
@@ -557,6 +565,39 @@ calls assume the called code is unknown and can do anything.
|
||||
The CHC engine is much more powerful than BMC in terms of what it can prove,
|
||||
and might require more computing resources.
|
||||
|
||||
SMT and Horn solvers
|
||||
====================
|
||||
|
||||
The two engines detailed above use automated theorem provers as their logical
|
||||
backends. BMC uses an SMT solver, whereas CHC uses a Horn solver. Often the
|
||||
same tool can act as both, as seen in `z3 <https://github.com/Z3Prover/z3>`_,
|
||||
which is primarily an SMT solver and makes `Spacer
|
||||
<https://spacer.bitbucket.io/>`_ available as a Horn solver, and `Eldarica
|
||||
<https://github.com/uuverifiers/eldarica>`_ which does both.
|
||||
|
||||
The user can choose which solvers should be used, if available, via the CLI
|
||||
option ``--model-checker-solvers {all,cvc4,smtlib2,z3}`` or the JSON option
|
||||
``settings.modelChecker.solvers=[smtlib2,z3]``, where:
|
||||
|
||||
- ``cvc4`` is only available if the ``solc`` binary is compiled with it. Only BMC uses ``cvc4``.
|
||||
- ``smtlib2`` outputs SMT/Horn queries in the `smtlib2 <http://smtlib.cs.uiowa.edu/>`_ format.
|
||||
These can be used together with the compiler's `callback mechanism <https://github.com/ethereum/solc-js>`_ so that
|
||||
any solver binary from the system can be employed to synchronously return the results of the queries to the compiler.
|
||||
This is currently the only way to use Eldarica, for example, since it does not have a C++ API.
|
||||
This can be used by both BMC and CHC depending on which solvers are called.
|
||||
- ``z3`` is available
|
||||
|
||||
- if ``solc`` is compiled with it;
|
||||
- if a dynamic ``z3`` library of version 4.8.x is installed in a Linux system (from Solidity 0.7.6);
|
||||
- statically in ``soljson.js`` (from Solidity 0.6.9), that is, the Javascript binary of the compiler.
|
||||
|
||||
Since both BMC and CHC use ``z3``, and ``z3`` is available in a greater variety
|
||||
of environments, including in the browser, most users will almost never need to be
|
||||
concerned about this option. More advanced users might apply this option to try
|
||||
alternative solvers on more complex problems.
|
||||
|
||||
Please note that certain combinations of chosen engine and solver will lead to
|
||||
the SMTChecker doing nothing, for example choosing CHC and ``cvc4``.
|
||||
|
||||
*******************************
|
||||
Abstraction and False Positives
|
||||
|
||||
@@ -50,7 +50,7 @@ contracts.
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.7.0 <0.9.0;
|
||||
pragma solidity >=0.7.1 <0.9.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
function bid() public payable { // Function
|
||||
|
||||
@@ -410,7 +410,9 @@ No:
|
||||
|
||||
spam( ham[ 1 ], Coin( { name: "ham" } ) );
|
||||
|
||||
Exception::
|
||||
Exception:
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
function singleLine() public { spam(); }
|
||||
|
||||
@@ -996,6 +998,7 @@ No:
|
||||
Yes:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
x = 3;
|
||||
x = 100 / 10;
|
||||
@@ -1005,6 +1008,7 @@ Yes:
|
||||
No:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
x=3;
|
||||
x = 100/10;
|
||||
|
||||
@@ -122,6 +122,7 @@ top of them and iterate over that. For example, the code below implements an
|
||||
the ``sum`` function iterates over to sum all the values.
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.6.8 <0.9.0;
|
||||
|
||||
@@ -27,13 +27,6 @@ Every reference type has an additional
|
||||
annotation, the "data location", about where it is stored. There are three data locations:
|
||||
``memory``, ``storage`` and ``calldata``. Calldata is a non-modifiable,
|
||||
non-persistent area where function arguments are stored, and behaves mostly like memory.
|
||||
It is required for parameters of external functions but can also be used for other variables.
|
||||
|
||||
|
||||
.. note::
|
||||
Prior to version 0.5.0 the data location could be omitted, and would default to different locations
|
||||
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
||||
data location.
|
||||
|
||||
.. note::
|
||||
If you can, try to use ``calldata`` as data location because it will avoid copies and
|
||||
@@ -41,6 +34,17 @@ It is required for parameters of external functions but can also be used for oth
|
||||
data location can also be returned from functions, but it is not possible to
|
||||
allocate such types.
|
||||
|
||||
.. note::
|
||||
Prior to version 0.6.9 data location for reference-type arguments was limited to
|
||||
``calldata`` in external functions, ``memory`` in public functions and either
|
||||
``memory`` or ``storage`` in internal and private ones.
|
||||
Now ``memory`` and ``calldata`` are allowed in all functions regardless of their visibility.
|
||||
|
||||
.. note::
|
||||
Prior to version 0.5.0 the data location could be omitted, and would default to different locations
|
||||
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
|
||||
data location.
|
||||
|
||||
.. _data-location-assignment:
|
||||
|
||||
Data location and assignment behaviour
|
||||
@@ -139,7 +143,7 @@ a reference to it.
|
||||
``bytes`` and ``string`` as Arrays
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
|
||||
Variables of type ``bytes`` and ``string`` are special arrays. The ``bytes`` type is similar to ``bytes1[]``,
|
||||
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
|
||||
length or index access.
|
||||
|
||||
@@ -148,8 +152,8 @@ third-party string libraries. You can also compare two strings by their keccak25
|
||||
``keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))`` and
|
||||
concatenate two strings using ``bytes.concat(bytes(s1), bytes(s2))``.
|
||||
|
||||
You should use ``bytes`` over ``byte[]`` because it is cheaper,
|
||||
since ``byte[]`` adds 31 padding bytes between the elements. As a general rule,
|
||||
You should use ``bytes`` over ``bytes1[]`` because it is cheaper,
|
||||
since ``bytes1[]`` adds 31 padding bytes between the elements. As a general rule,
|
||||
use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
|
||||
string (UTF-8) data. If you can limit the length to a certain number of bytes,
|
||||
always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
|
||||
@@ -492,7 +496,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
|
||||
.. code-block:: solidity
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.4 <0.9.0;
|
||||
pragma solidity >=0.8.5 <0.9.0;
|
||||
contract Proxy {
|
||||
/// @dev Address of the client contract managed by proxy i.e., this contract
|
||||
address client;
|
||||
@@ -559,7 +563,7 @@ shown in the following example:
|
||||
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
|
||||
campaignID = numCampaigns++; // campaignID is return variable
|
||||
// We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"
|
||||
// because the RHS creates a memory-struct "Campaign" that contains a mapping.
|
||||
// because the right hand side creates a memory-struct "Campaign" that contains a mapping.
|
||||
Campaign storage c = campaigns[campaignID];
|
||||
c.beneficiary = beneficiary;
|
||||
c.fundingGoal = goal;
|
||||
|
||||
+30
-23
@@ -128,10 +128,10 @@ The modulo operation ``a % n`` yields the remainder ``r`` after the division of
|
||||
by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo
|
||||
results in the same sign as its left operand (or zero) and ``a % n == -(-a % n)`` holds for negative ``a``:
|
||||
|
||||
* ``int256(5) % int256(2) == int256(1)``
|
||||
* ``int256(5) % int256(-2) == int256(1)``
|
||||
* ``int256(-5) % int256(2) == int256(-1)``
|
||||
* ``int256(-5) % int256(-2) == int256(-1)``
|
||||
* ``int256(5) % int256(2) == int256(1)``
|
||||
* ``int256(5) % int256(-2) == int256(1)``
|
||||
* ``int256(-5) % int256(2) == int256(-1)``
|
||||
* ``int256(-5) % int256(-2) == int256(-1)``
|
||||
|
||||
.. note::
|
||||
Modulo with zero causes a :ref:`Panic error<assert-and-require>`. This check can **not** be disabled through ``unchecked { ... }``.
|
||||
@@ -184,8 +184,8 @@ Address
|
||||
|
||||
The address type comes in two flavours, which are largely identical:
|
||||
|
||||
- ``address``: Holds a 20 byte value (size of an Ethereum address).
|
||||
- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``.
|
||||
- ``address``: Holds a 20 byte value (size of an Ethereum address).
|
||||
- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``.
|
||||
|
||||
The idea behind this distinction is that ``address payable`` is an address you can send Ether to,
|
||||
while a plain ``address`` cannot be sent Ether.
|
||||
@@ -239,6 +239,7 @@ It is possible to query the balance of an address using the property ``balance``
|
||||
and to send Ether (in units of wei) to a payable address using the ``transfer`` function:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
address payable x = address(0x123);
|
||||
address myAddress = address(this);
|
||||
@@ -398,7 +399,7 @@ Members:
|
||||
* ``.length`` yields the fixed length of the byte array (read-only).
|
||||
|
||||
.. note::
|
||||
The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes
|
||||
The type ``bytes1[]`` is an array of bytes, but due to padding rules, it wastes
|
||||
31 bytes of space for each element (except in storage). It is better to use the ``bytes``
|
||||
type instead.
|
||||
|
||||
@@ -510,27 +511,32 @@ String literals can only contain printable ASCII characters, which means the cha
|
||||
|
||||
Additionally, string literals also support the following escape characters:
|
||||
|
||||
- ``\<newline>`` (escapes an actual newline)
|
||||
- ``\\`` (backslash)
|
||||
- ``\'`` (single quote)
|
||||
- ``\"`` (double quote)
|
||||
- ``\b`` (backspace)
|
||||
- ``\f`` (form feed)
|
||||
- ``\n`` (newline)
|
||||
- ``\r`` (carriage return)
|
||||
- ``\t`` (tab)
|
||||
- ``\v`` (vertical tab)
|
||||
- ``\xNN`` (hex escape, see below)
|
||||
- ``\uNNNN`` (unicode escape, see below)
|
||||
- ``\<newline>`` (escapes an actual newline)
|
||||
- ``\\`` (backslash)
|
||||
- ``\'`` (single quote)
|
||||
- ``\"`` (double quote)
|
||||
- ``\n`` (newline)
|
||||
- ``\r`` (carriage return)
|
||||
- ``\t`` (tab)
|
||||
- ``\xNN`` (hex escape, see below)
|
||||
- ``\uNNNN`` (unicode escape, see below)
|
||||
|
||||
``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence.
|
||||
|
||||
.. note::
|
||||
|
||||
Until version 0.8.0 there were three additional escape sequences: ``\b``, ``\f`` and ``\v``.
|
||||
They are commonly available in other languages but rarely needed in practice.
|
||||
If you do need them, they can still be inserted via hexadecimal escapes, i.e. ``\x08``, ``\x0c``
|
||||
and ``\x0b``, respectively, just as any other ASCII character.
|
||||
|
||||
The string in the following example has a length of ten bytes.
|
||||
It starts with a newline byte, followed by a double quote, a single
|
||||
quote a backslash character and then (without separator) the
|
||||
character sequence ``abcdef``.
|
||||
|
||||
::
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
"\n\"\'\\abc\
|
||||
def"
|
||||
@@ -637,6 +643,7 @@ be passed via and returned from external function calls.
|
||||
Function types are notated as follows:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]
|
||||
|
||||
@@ -656,9 +663,9 @@ their parameter types are identical, their return types are identical,
|
||||
their internal/external property is identical and the state mutability of ``A``
|
||||
is more restrictive than the state mutability of ``B``. In particular:
|
||||
|
||||
- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions
|
||||
- ``view`` functions can be converted to ``non-payable`` functions
|
||||
- ``payable`` functions can be converted to ``non-payable`` functions
|
||||
- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions
|
||||
- ``view`` functions can be converted to ``non-payable`` functions
|
||||
- ``payable`` functions can be converted to ``non-payable`` functions
|
||||
|
||||
No other conversions between function types are possible.
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ Ether Units
|
||||
A literal number can take a suffix of ``wei``, ``gwei`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
assert(1 wei == 1);
|
||||
assert(1 gwei == 1e9);
|
||||
@@ -29,11 +30,11 @@ Suffixes like ``seconds``, ``minutes``, ``hours``, ``days`` and ``weeks``
|
||||
after literal numbers can be used to specify units of time where seconds are the base
|
||||
unit and units are considered naively in the following way:
|
||||
|
||||
* ``1 == 1 seconds``
|
||||
* ``1 minutes == 60 seconds``
|
||||
* ``1 hours == 60 minutes``
|
||||
* ``1 days == 24 hours``
|
||||
* ``1 weeks == 7 days``
|
||||
* ``1 == 1 seconds``
|
||||
* ``1 minutes == 60 seconds``
|
||||
* ``1 hours == 60 minutes``
|
||||
* ``1 days == 24 hours``
|
||||
* ``1 weeks == 7 days``
|
||||
|
||||
Take care if you perform calendar calculations using these units, because
|
||||
not every year equals 365 days and not even every day has 24 hours
|
||||
|
||||
+26
-24
@@ -30,8 +30,8 @@ set it to ``--optimize-runs=1``. If you expect many transactions and do not care
|
||||
output size, set ``--optimize-runs`` to a high number.
|
||||
This parameter has effects on the following (this might change in the future):
|
||||
|
||||
- the size of the binary search in the function dispatch routine
|
||||
- the way constants like large numbers or strings are stored
|
||||
- the size of the binary search in the function dispatch routine
|
||||
- the way constants like large numbers or strings are stored
|
||||
|
||||
.. index:: allowed paths, --allow-paths, base path, --base-path
|
||||
|
||||
@@ -41,7 +41,7 @@ Base Path and Import Remapping
|
||||
The commandline compiler will automatically read imported files from the filesystem, but
|
||||
it is also possible to provide :ref:`path redirects <import-remapping>` using ``prefix=path`` in the following way:
|
||||
|
||||
::
|
||||
.. code-block:: bash
|
||||
|
||||
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol
|
||||
|
||||
@@ -134,15 +134,15 @@ On the command line, you can select the EVM version as follows:
|
||||
In the :ref:`standard JSON interface <compiler-api>`, use the ``"evmVersion"``
|
||||
key in the ``"settings"`` field:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"sources": { ... },
|
||||
"settings": {
|
||||
"optimizer": { ... },
|
||||
"evmVersion": "<VERSION>"
|
||||
{
|
||||
"sources": {/* ... */},
|
||||
"settings": {
|
||||
"optimizer": {/* ... */},
|
||||
"evmVersion": "<VERSION>"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Target Options
|
||||
--------------
|
||||
@@ -198,7 +198,7 @@ Comments are of course not permitted and used here only for explanatory purposes
|
||||
Input Description
|
||||
-----------------
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
// Required: Source code language. Currently supported are "Solidity" and "Yul".
|
||||
@@ -310,7 +310,7 @@ Input Description
|
||||
// "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
|
||||
// "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
|
||||
"revertStrings": "default"
|
||||
}
|
||||
},
|
||||
// Metadata settings (optional)
|
||||
"metadata": {
|
||||
// Use only literal content and not URLs (false by default)
|
||||
@@ -331,7 +331,7 @@ Input Description
|
||||
"myFile.sol": {
|
||||
"MyLib": "0x123123..."
|
||||
}
|
||||
}
|
||||
},
|
||||
// The following can be used to select desired outputs based
|
||||
// on file and contract names.
|
||||
// If this field is omitted, then the compiler loads and does type checking,
|
||||
@@ -395,13 +395,15 @@ Input Description
|
||||
"modelChecker":
|
||||
{
|
||||
// Chose which contracts should be analyzed as the deployed one.
|
||||
contracts:
|
||||
"contracts":
|
||||
{
|
||||
"source1.sol": ["contract1"],
|
||||
"source2.sol": ["contract2", "contract3"]
|
||||
},
|
||||
// Choose which model checker engine to use: all (default), bmc, chc, none.
|
||||
"engine": "chc",
|
||||
// Choose whether to output all unproved targets. The default is `false`.
|
||||
"showUnproved": true,
|
||||
// Choose which targets should be checked: constantCondition,
|
||||
// underflow, overflow, divByZero, balance, assert, popEmptyArray, outOfBounds.
|
||||
// If the option is not given all targets are checked by default.
|
||||
@@ -420,7 +422,7 @@ Input Description
|
||||
Output Description
|
||||
------------------
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
// Optional: not present if no errors/warnings were encountered
|
||||
@@ -431,7 +433,7 @@ Output Description
|
||||
"file": "sourceFile.sol",
|
||||
"start": 0,
|
||||
"end": 100
|
||||
],
|
||||
},
|
||||
// Optional: Further locations (e.g. places of conflicting declarations)
|
||||
"secondarySourceLocations": [
|
||||
{
|
||||
@@ -463,7 +465,7 @@ Output Description
|
||||
// Identifier of the source (used in source maps)
|
||||
"id": 1,
|
||||
// The AST object
|
||||
"ast": {},
|
||||
"ast": {}
|
||||
}
|
||||
},
|
||||
// This contains the contract-level outputs.
|
||||
@@ -476,7 +478,7 @@ Output Description
|
||||
// See https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||
"abi": [],
|
||||
// See the Metadata Output documentation (serialised JSON string)
|
||||
"metadata": "{...}",
|
||||
"metadata": "{/* ... */}",
|
||||
// User documentation (natspec)
|
||||
"userdoc": {},
|
||||
// Developer documentation (natspec)
|
||||
@@ -484,7 +486,7 @@ Output Description
|
||||
// Intermediate representation (string)
|
||||
"ir": "",
|
||||
// See the Storage Layout documentation.
|
||||
"storageLayout": {"storage": [...], "types": {...} },
|
||||
"storageLayout": {"storage": [/* ... */], "types": {/* ... */} },
|
||||
// EVM-related outputs
|
||||
"evm": {
|
||||
// Assembly (string)
|
||||
@@ -514,14 +516,14 @@ Output Description
|
||||
// contains a single Yul file.
|
||||
"generatedSources": [{
|
||||
// Yul AST
|
||||
"ast": { ... }
|
||||
"ast": {/* ... */},
|
||||
// Source file in its text form (may contain comments)
|
||||
"contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }",
|
||||
// Source file ID, used for source references, same "namespace" as the Solidity source files
|
||||
"id": 2,
|
||||
"language": "Yul",
|
||||
"name": "#utility.yul"
|
||||
}]
|
||||
}],
|
||||
// If given, this is an unlinked object.
|
||||
"linkReferences": {
|
||||
"libraryFile.sol": {
|
||||
@@ -535,7 +537,7 @@ Output Description
|
||||
}
|
||||
},
|
||||
"deployedBytecode": {
|
||||
..., // The same layout as above.
|
||||
/* ..., */ // The same layout as above.
|
||||
"immutableReferences": {
|
||||
// There are two references to the immutable with AST ID 3, both 32 bytes long. One is
|
||||
// at bytecode offset 42, the other at bytecode offset 80.
|
||||
@@ -779,9 +781,9 @@ Running the Upgrade
|
||||
|
||||
It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument.
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: bash
|
||||
|
||||
$ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol
|
||||
solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol
|
||||
|
||||
The command above applies all changes as shown below. Please review them carefully (the pragmas will
|
||||
have to be updated manually.)
|
||||
|
||||
+48
-43
@@ -157,16 +157,16 @@ where an object is expected.
|
||||
Inside a code block, the following elements can be used
|
||||
(see the later sections for more details):
|
||||
|
||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||
- calls to builtin functions, e.g. ``add(1, mload(0))``
|
||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned)
|
||||
- identifiers (variables), e.g. ``add(3, x)``
|
||||
- assignments, e.g. ``x := add(y, 3)``
|
||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
||||
- if statements, e.g. ``if lt(a, b) { sstore(0, 1) }``
|
||||
- switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }``
|
||||
- for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }``
|
||||
- function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }```
|
||||
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||
- calls to builtin functions, e.g. ``add(1, mload(0))``
|
||||
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned)
|
||||
- identifiers (variables), e.g. ``add(3, x)``
|
||||
- assignments, e.g. ``x := add(y, 3)``
|
||||
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
|
||||
- if statements, e.g. ``if lt(a, b) { sstore(0, 1) }``
|
||||
- switch statements, e.g. ``switch mload(0) case 0 { revert() } default { mstore(0, 1) }``
|
||||
- for loops, e.g. ``for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) }``
|
||||
- function definitions, e.g. ``function f(a, b) -> c { c := add(a, b) }```
|
||||
|
||||
Multiple syntactical elements can follow each other simply separated by
|
||||
whitespace, i.e. there is no terminating ``;`` or newline required.
|
||||
@@ -198,7 +198,8 @@ has to be specified after a colon:
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
let x := and("abc":uint32, add(3:uint256, 2:uint256))
|
||||
// This will not compile (u32 and u256 type not implemented yet)
|
||||
let x := and("abc":u32, add(3:u256, 2:u256))
|
||||
|
||||
|
||||
Function Calls
|
||||
@@ -212,10 +213,9 @@ they have to be assigned to local variables.
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
function f(x, y) -> a, b { /* ... */ }
|
||||
mstore(0x80, add(mload(0x80), 3))
|
||||
// Here, the user-defined function `f` returns
|
||||
// two values. The definition of the function
|
||||
// is missing from the example.
|
||||
// Here, the user-defined function `f` returns two values.
|
||||
let x, y := f(1, mload(0))
|
||||
|
||||
For built-in functions of the EVM, functional expressions
|
||||
@@ -271,9 +271,10 @@ that returns multiple values.
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
// This will not compile (u32 and u256 type not implemented yet)
|
||||
{
|
||||
let zero:uint32 := 0:uint32
|
||||
let v:uint256, t:uint32 := f()
|
||||
let zero:u32 := 0:u32
|
||||
let v:u256, t:u32 := f()
|
||||
let x, y := g()
|
||||
}
|
||||
|
||||
@@ -314,7 +315,7 @@ you need multiple alternatives.
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
if eq(value, 0) { revert(0, 0) }
|
||||
if lt(calldatasize(), 4) { revert(0, 0) }
|
||||
|
||||
The curly braces for the body are required.
|
||||
|
||||
@@ -545,11 +546,18 @@ as explained below) and all declarations
|
||||
introduce new identifiers into these scopes.
|
||||
|
||||
Identifiers are visible in
|
||||
the block they are defined in (including all sub-nodes and sub-blocks).
|
||||
the block they are defined in (including all sub-nodes and sub-blocks):
|
||||
Functions are visible in the whole block (even before their definitions) while
|
||||
variables are only visible starting from the statement after the ``VariableDeclaration``.
|
||||
|
||||
As an exception, the scope of the "init" part of the or-loop
|
||||
In particular,
|
||||
variables cannot be referenced in the right hand side of their own variable
|
||||
declaration.
|
||||
Functions can be referenced already before their declaration (if they are visible).
|
||||
|
||||
As an exception to the general scoping rule, the scope of the "init" part of the for-loop
|
||||
(the first block) extends across all other parts of the for loop.
|
||||
This means that variables declared in the init part (but not inside a
|
||||
This means that variables (and functions) declared in the init part (but not inside a
|
||||
block inside the init part) are visible in all other parts of the for-loop.
|
||||
|
||||
Identifiers declared in the other parts of the for loop respect the regular
|
||||
@@ -558,21 +566,15 @@ syntactical scoping rules.
|
||||
This means a for-loop of the form ``for { I... } C { P... } { B... }`` is equivalent
|
||||
to ``{ I... for {} C { P... } { B... } }``.
|
||||
|
||||
|
||||
The parameters and return parameters of functions are visible in the
|
||||
function body and their names have to be distinct.
|
||||
|
||||
Variables can only be referenced after their declaration. In particular,
|
||||
variables cannot be referenced in the right hand side of their own variable
|
||||
declaration.
|
||||
Functions can be referenced already before their declaration (if they are visible).
|
||||
Inside functions, it is not possible to reference a variable that was declared
|
||||
outside of that function.
|
||||
|
||||
Shadowing is disallowed, i.e. you cannot declare an identifier at a point
|
||||
where another identifier with the same name is also visible, even if it is
|
||||
not accessible.
|
||||
|
||||
Inside functions, it is not possible to access a variable that was declared
|
||||
outside of that function.
|
||||
not possible to reference it because it was declared outside the current function.
|
||||
|
||||
Formal Specification
|
||||
--------------------
|
||||
@@ -984,9 +986,10 @@ that are not known to the Yul compiler. It also allows you to create
|
||||
bytecode sequences that will not be modified by the optimizer.
|
||||
|
||||
The functions are ``verbatim_<n>i_<m>o("<data>", ...)``, where
|
||||
- ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables
|
||||
- ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables
|
||||
- ``data`` is a string literal that contains the sequence of bytes
|
||||
|
||||
- ``n`` is a decimal between 0 and 99 that specifies the number of input stack slots / variables
|
||||
- ``m`` is a decimal between 0 and 99 that specifies the number of output stack slots / variables
|
||||
- ``data`` is a string literal that contains the sequence of bytes
|
||||
|
||||
If you for example want to define a function that multiplies the input
|
||||
by two, without the optimizer touching the constant two, you can use
|
||||
@@ -1021,15 +1024,15 @@ verbatim bytecode that are not checked by
|
||||
the compiler. Violations of these restrictions can result in
|
||||
undefined behaviour.
|
||||
|
||||
- Control-flow should not jump into or out of verbatim blocks,
|
||||
but it can jump within the same verbatim block.
|
||||
- Stack contents apart from the input and output parameters
|
||||
should not be accessed.
|
||||
- The stack height difference should be exactly ``m - n``
|
||||
(output slots minus input slots).
|
||||
- Verbatim bytecode cannot make any assumptions about the
|
||||
surrounding bytecode. All required parameters have to be
|
||||
passed in as stack variables.
|
||||
- Control-flow should not jump into or out of verbatim blocks,
|
||||
but it can jump within the same verbatim block.
|
||||
- Stack contents apart from the input and output parameters
|
||||
should not be accessed.
|
||||
- The stack height difference should be exactly ``m - n``
|
||||
(output slots minus input slots).
|
||||
- Verbatim bytecode cannot make any assumptions about the
|
||||
surrounding bytecode. All required parameters have to be
|
||||
passed in as stack variables.
|
||||
|
||||
The optimizer does not analyze verbatim bytecode and always
|
||||
assumes that it modifies all aspects of state and thus can only
|
||||
@@ -1175,11 +1178,13 @@ intermediate states. This allows for easy debugging and verification of the opti
|
||||
Please refer to the general :ref:`optimizer documentation <optimizer>`
|
||||
for more details about the different optimization stages and how to use the optimizer.
|
||||
|
||||
If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``:
|
||||
If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``
|
||||
and optionally specify the :ref:`expected number of contract executions <optimizer-parameter-runs>` with
|
||||
``--optimize-runs``:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
solc --strict-assembly --optimize
|
||||
solc --strict-assembly --optimize --optimize-runs 200
|
||||
|
||||
In Solidity mode, the Yul optimizer is activated together with the regular optimizer.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user