mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
5ffee049fa
@ -25,6 +25,9 @@ AST Changes:
|
|||||||
|
|
||||||
### 0.7.5 (unreleased)
|
### 0.7.5 (unreleased)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Ability to select the abi coder using ``pragma abicoder v1`` and ``pragma abicoder v2``.
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* SMTChecker: Add division by zero checks in the CHC engine.
|
* SMTChecker: Add division by zero checks in the CHC engine.
|
||||||
* SMTChecker: Support ``selector`` for expressions with value known at compile-time.
|
* SMTChecker: Support ``selector`` for expressions with value known at compile-time.
|
||||||
@ -35,6 +38,10 @@ Bugfixes:
|
|||||||
* SMTChecker: Fix internal error on conversion from string literal to byte.
|
* SMTChecker: Fix internal error on conversion from string literal to byte.
|
||||||
* SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator.
|
* SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator.
|
||||||
* SMTChecker: Fix internal error when assigning state variable via contract's name.
|
* SMTChecker: Fix internal error when assigning state variable via contract's name.
|
||||||
|
* SMTChecker: Fix incorrect counterexamples reported by the CHC engine.
|
||||||
|
* SMTChecker: Fix false negative in modifier applied multiple times.
|
||||||
|
* SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables.
|
||||||
|
* SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment.
|
||||||
* Code generator: Fix missing creation dependency tracking for abstract contracts.
|
* Code generator: Fix missing creation dependency tracking for abstract contracts.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# The Solidity Contract-Oriented Programming Language
|
# The Solidity Contract-Oriented Programming Language
|
||||||
You can talk to us on [](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge). Questions, feedback and suggestions are welcome!
|
You can talk to us on [](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge). Questions, feedback, and suggestions are welcome!
|
||||||
|
|
||||||
Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform.
|
Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform.
|
||||||
|
|
||||||
@ -19,10 +19,10 @@ Solidity is a statically typed, contract-oriented, high-level language for imple
|
|||||||
Solidity is a statically-typed curly-braces programming language designed for developing smart contracts
|
Solidity is a statically-typed curly-braces programming language designed for developing smart contracts
|
||||||
that run on the Ethereum Virtual Machine. Smart contracts are programs that are executed inside a peer-to-peer
|
that run on the Ethereum Virtual Machine. Smart contracts are programs that are executed inside a peer-to-peer
|
||||||
network where nobody has special authority over the execution, and thus they allow to implement tokens of value,
|
network where nobody has special authority over the execution, and thus they allow to implement tokens of value,
|
||||||
ownership, voting and other kinds of logics.
|
ownership, voting, and other kinds of logic.
|
||||||
|
|
||||||
When deploying contracts, you should use the latest released version of
|
When deploying contracts, you should use the latest released version of
|
||||||
Solidity. This is because breaking changes as well as new features and bug fixes are
|
Solidity. This is because breaking changes, as well as new features and bug fixes are
|
||||||
introduced regularly. We currently use a 0.x version
|
introduced regularly. We currently use a 0.x version
|
||||||
number [to indicate this fast pace of change](https://semver.org/#spec-item-4).
|
number [to indicate this fast pace of change](https://semver.org/#spec-item-4).
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ contract HelloWorld {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an
|
To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is a
|
||||||
browser-based IDE. Here are some example contracts:
|
browser-based IDE. Here are some example contracts:
|
||||||
|
|
||||||
1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting)
|
1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting)
|
||||||
|
@ -132,7 +132,7 @@ Yul Optimizer
|
|||||||
|
|
||||||
Together with the legacy bytecode optimizer, the :doc:`Yul <yul>` optimizer is now enabled by default when you call the compiler
|
Together with the legacy bytecode optimizer, the :doc:`Yul <yul>` optimizer is now enabled by default when you call the compiler
|
||||||
with ``--optimize``. It can be disabled by calling the compiler with ``--no-optimize-yul``.
|
with ``--optimize``. It can be disabled by calling the compiler with ``--no-optimize-yul``.
|
||||||
This mostly affects code that uses ABIEncoderV2.
|
This mostly affects code that uses ABI coder v2.
|
||||||
|
|
||||||
C API Changes
|
C API Changes
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -589,8 +589,8 @@ As an example, the code
|
|||||||
::
|
::
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.19 <0.9.0;
|
pragma solidity >0.7.4 <0.9.0;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma abicoder v2;
|
||||||
|
|
||||||
contract Test {
|
contract Test {
|
||||||
struct S { uint a; uint[] b; T[] c; }
|
struct S { uint a; uint[] b; T[] c; }
|
||||||
|
@ -67,8 +67,8 @@ Function parameters can be used as any other local variable and they can also be
|
|||||||
|
|
||||||
An :ref:`external function<external-function-calls>` cannot accept a
|
An :ref:`external function<external-function-calls>` cannot accept a
|
||||||
multi-dimensional array as an input
|
multi-dimensional array as an input
|
||||||
parameter. This functionality is possible if you enable the new
|
parameter. This functionality is possible if you enable the ABI coder v2
|
||||||
``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file.
|
by adding ``pragma abicoder v2;`` to your source file.
|
||||||
|
|
||||||
An :ref:`internal function<external-function-calls>` can accept a
|
An :ref:`internal function<external-function-calls>` can accept a
|
||||||
multi-dimensional array without enabling the feature.
|
multi-dimensional array without enabling the feature.
|
||||||
@ -128,8 +128,8 @@ you must provide return values together with the return statement.
|
|||||||
.. note::
|
.. note::
|
||||||
You cannot return some types from non-internal functions, notably
|
You cannot return some types from non-internal functions, notably
|
||||||
multi-dimensional dynamic arrays and structs. If you enable the
|
multi-dimensional dynamic arrays and structs. If you enable the
|
||||||
new ``ABIEncoderV2`` feature by adding ``pragma experimental
|
ABI coder v2 by adding ``pragma abicoder v2;``
|
||||||
ABIEncoderV2;`` to your source file then more types are available, but
|
to your source file then more types are available, but
|
||||||
``mapping`` types are still limited to inside a single contract and you
|
``mapping`` types are still limited to inside a single contract and you
|
||||||
cannot transfer them.
|
cannot transfer them.
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ For macOS builds, ensure that you have the latest version of
|
|||||||
`Xcode installed <https://developer.apple.com/xcode/download/>`_.
|
`Xcode installed <https://developer.apple.com/xcode/download/>`_.
|
||||||
This contains the `Clang C++ compiler <https://en.wikipedia.org/wiki/Clang>`_, the
|
This contains the `Clang C++ compiler <https://en.wikipedia.org/wiki/Clang>`_, the
|
||||||
`Xcode IDE <https://en.wikipedia.org/wiki/Xcode>`_ and other Apple development
|
`Xcode IDE <https://en.wikipedia.org/wiki/Xcode>`_ and other Apple development
|
||||||
tools which are required for building C++ applications on OS X.
|
tools that are required for building C++ applications on OS X.
|
||||||
If you are installing Xcode for the first time, or have just installed a new
|
If you are installing Xcode for the first time, or have just installed a new
|
||||||
version then you will need to agree to the license before you can do
|
version then you will need to agree to the license before you can do
|
||||||
command-line builds:
|
command-line builds:
|
||||||
|
@ -88,6 +88,41 @@ these follow the same syntax used by `npm <https://docs.npmjs.com/misc/semver>`_
|
|||||||
required by the pragma. If it does not match, the compiler issues
|
required by the pragma. If it does not match, the compiler issues
|
||||||
an error.
|
an error.
|
||||||
|
|
||||||
|
ABI Coder Pragma
|
||||||
|
----------------
|
||||||
|
|
||||||
|
By using ``pragma abicoder v1`` or ``pragma abicoder v2`` you can
|
||||||
|
select between the two implementations of the ABI encoder and decoder.
|
||||||
|
|
||||||
|
The new ABI coder (v2) is able to encode and decode arbitrarily nested
|
||||||
|
arrays and structs. It might produce less optimal code and has not
|
||||||
|
received as much testing as the old encoder, but is considered
|
||||||
|
non-experimental as of Solidity 0.6.0. You still have to explicitly
|
||||||
|
activate it using ``pragma abicoder v2;``. Since it will be
|
||||||
|
activated by default starting from Solidity 0.8.0, there is the option to select
|
||||||
|
the old coder using ``pragma abicoder v1;``.
|
||||||
|
|
||||||
|
The set of types supported by the new encoder is a strict superset of
|
||||||
|
the ones supported by the old one. Contracts that use it can interact with ones
|
||||||
|
that do not without limitations. The reverse is possible only as long as the
|
||||||
|
non-``abicoder v2`` contract does not try to make calls that would require
|
||||||
|
decoding types only supported by the new encoder. The compiler can detect this
|
||||||
|
and will issue an error. Simply enabling ``abicoder v2`` for your contract is
|
||||||
|
enough to make the error go away.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This pragma applies to all the code defined in the file where it is activated,
|
||||||
|
regardless of where that code ends up eventually. This means that a contract
|
||||||
|
whose source file is selected to compile with ABI coder v1
|
||||||
|
can still contain code that uses the new encoder
|
||||||
|
by inheriting it from another contract. This is allowed if the new types are only
|
||||||
|
used internally and not in external function signatures.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Up to Solidity 0.7.4, it was possible to select the ABI coder v2
|
||||||
|
by using ``pragma experimental ABIEncoderV2``, but it was not possible
|
||||||
|
to explicitly select coder v1 because it was the default.
|
||||||
|
|
||||||
.. index:: ! pragma, experimental
|
.. index:: ! pragma, experimental
|
||||||
|
|
||||||
.. _experimental_pragma:
|
.. _experimental_pragma:
|
||||||
@ -103,28 +138,9 @@ The following experimental pragmas are currently supported:
|
|||||||
ABIEncoderV2
|
ABIEncoderV2
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
The new ABI encoder is able to encode and decode arbitrarily nested
|
Because the ABI coder v2 is not considered experimental anymore,
|
||||||
arrays and structs. It might produce less optimal code and has not
|
it can be selected via ``pragma abicoder v2`` (please see above)
|
||||||
received as much testing as the old encoder, but is considered
|
since Solidity 0.7.4.
|
||||||
non-experimental as of Solidity 0.6.0. You still have to explicitly
|
|
||||||
activate it using ``pragma experimental ABIEncoderV2;`` - we kept
|
|
||||||
the same pragma, even though it is not considered experimental
|
|
||||||
anymore.
|
|
||||||
|
|
||||||
The set of types supported by the new encoder is a strict superset of
|
|
||||||
the ones supported by the old one. Contracts that use it can interact with ones
|
|
||||||
that do not without limitations. The reverse is possible only as long as the
|
|
||||||
non-``ABIEncoderV2`` contract does not try to make calls that would require
|
|
||||||
decoding types only supported by the new encoder. The compiler can detect this
|
|
||||||
and will issue an error. Simply enabling ``ABIEncoderV2`` for your contract is
|
|
||||||
enough to make the error go away.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
This pragma applies to all the code defined in the file where it is activated,
|
|
||||||
regardless of where that code ends up eventually. This means that a contract
|
|
||||||
without the ``ABIEncoderV2`` pragma can still contain code that uses the new encoder
|
|
||||||
by inheriting it from another contract. This is allowed if the new types are only
|
|
||||||
used internally and not in external function signatures.
|
|
||||||
|
|
||||||
.. _smt_checker:
|
.. _smt_checker:
|
||||||
|
|
||||||
|
@ -629,26 +629,32 @@ types.
|
|||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0;
|
pragma solidity >=0.5.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
pragma experimental SMTChecker;
|
pragma experimental SMTChecker;
|
||||||
// This will report a warning
|
// This will report a warning
|
||||||
|
|
||||||
contract Aliasing
|
contract Aliasing
|
||||||
{
|
{
|
||||||
uint[] array;
|
uint[] array1;
|
||||||
|
uint[][] array2;
|
||||||
function f(
|
function f(
|
||||||
uint[] memory a,
|
uint[] memory a,
|
||||||
uint[] memory b,
|
uint[] memory b,
|
||||||
uint[][] memory c,
|
uint[][] memory c,
|
||||||
uint[] storage d
|
uint[] storage d
|
||||||
) internal view {
|
) internal {
|
||||||
require(array[0] == 42);
|
array1[0] = 42;
|
||||||
require(a[0] == 2);
|
a[0] = 2;
|
||||||
require(c[0][0] == 2);
|
c[0][0] = 2;
|
||||||
require(d[0] == 2);
|
|
||||||
b[0] = 1;
|
b[0] = 1;
|
||||||
// Erasing knowledge about memory references should not
|
// Erasing knowledge about memory references should not
|
||||||
// erase knowledge about state variables.
|
// erase knowledge about state variables.
|
||||||
assert(array[0] == 42);
|
assert(array1[0] == 42);
|
||||||
|
// However, an assignment to a storage reference will erase
|
||||||
|
// storage knowledge accordingly.
|
||||||
|
d[0] = 2;
|
||||||
|
// Fails as false positive because of the assignment above.
|
||||||
|
assert(array1[0] == 42);
|
||||||
// Fails because `a == b` is possible.
|
// Fails because `a == b` is possible.
|
||||||
assert(a[0] == 2);
|
assert(a[0] == 2);
|
||||||
// Fails because `c[i] == b` is possible.
|
// Fails because `c[i] == b` is possible.
|
||||||
@ -656,6 +662,14 @@ types.
|
|||||||
assert(d[0] == 2);
|
assert(d[0] == 2);
|
||||||
assert(b[0] == 1);
|
assert(b[0] == 1);
|
||||||
}
|
}
|
||||||
|
function g(
|
||||||
|
uint[] memory a,
|
||||||
|
uint[] memory b,
|
||||||
|
uint[][] memory c,
|
||||||
|
uint x
|
||||||
|
) public {
|
||||||
|
f(a, b, c, array2[x]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since
|
After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since
|
||||||
|
@ -290,7 +290,7 @@ Array Members
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
To use arrays of arrays in external (instead of public) functions, you need to
|
To use arrays of arrays in external (instead of public) functions, you need to
|
||||||
activate ABIEncoderV2.
|
activate ABI coder v2.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
In EVM versions before Byzantium, it was not possible to access
|
In EVM versions before Byzantium, it was not possible to access
|
||||||
|
@ -231,7 +231,7 @@ Input Description
|
|||||||
"cse": false,
|
"cse": false,
|
||||||
// Optimize representation of literal numbers and strings in code.
|
// Optimize representation of literal numbers and strings in code.
|
||||||
"constantOptimizer": false,
|
"constantOptimizer": false,
|
||||||
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2
|
// The new Yul optimizer. Mostly operates on the code of ABI coder v2
|
||||||
// and inline assembly.
|
// and inline assembly.
|
||||||
// It is activated together with the global optimizer setting
|
// It is activated together with the global optimizer setting
|
||||||
// and can be deactivated here.
|
// and can be deactivated here.
|
||||||
@ -321,8 +321,8 @@ Input Description
|
|||||||
// evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
|
// evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
|
||||||
// evm.methodIdentifiers - The list of function hashes
|
// evm.methodIdentifiers - The list of function hashes
|
||||||
// evm.gasEstimates - Function gas estimates
|
// evm.gasEstimates - Function gas estimates
|
||||||
// ewasm.wast - eWASM S-expressions format (not supported at the moment)
|
// ewasm.wast - Ewasm in WebAssembly S-expressions format
|
||||||
// ewasm.wasm - eWASM binary format (not supported at the moment)
|
// ewasm.wasm - Ewasm in WebAssembly binary format
|
||||||
//
|
//
|
||||||
// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every
|
// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every
|
||||||
// target part of that output. Additionally, `*` can be used as a wildcard to request everything.
|
// target part of that output. Additionally, `*` can be used as a wildcard to request everything.
|
||||||
@ -486,7 +486,7 @@ Output Description
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// eWASM related outputs
|
// Ewasm related outputs
|
||||||
"ewasm": {
|
"ewasm": {
|
||||||
// S-expressions format
|
// S-expressions format
|
||||||
"wast": "",
|
"wast": "",
|
||||||
|
@ -9,7 +9,7 @@ Yul
|
|||||||
Yul (previously also called JULIA or IULIA) is an intermediate language that can be
|
Yul (previously also called JULIA or IULIA) is an intermediate language that can be
|
||||||
compiled to bytecode for different backends.
|
compiled to bytecode for different backends.
|
||||||
|
|
||||||
Support for EVM 1.0, EVM 1.5 and eWASM is planned, and it is designed to
|
Support for EVM 1.0, EVM 1.5 and Ewasm is planned, and it is designed to
|
||||||
be a usable common denominator of all three
|
be a usable common denominator of all three
|
||||||
platforms. It can already be used in stand-alone mode and
|
platforms. It can already be used in stand-alone mode and
|
||||||
for "inline assembly" inside Solidity
|
for "inline assembly" inside Solidity
|
||||||
@ -1028,7 +1028,7 @@ An example Yul Object is shown below:
|
|||||||
// executing code is the constructor code)
|
// executing code is the constructor code)
|
||||||
size := datasize("runtime")
|
size := datasize("runtime")
|
||||||
offset := allocate(size)
|
offset := allocate(size)
|
||||||
// This will turn into a memory->memory copy for eWASM and
|
// This will turn into a memory->memory copy for Ewasm and
|
||||||
// a codecopy for EVM
|
// a codecopy for EVM
|
||||||
datacopy(offset, dataoffset("runtime"), size)
|
datacopy(offset, dataoffset("runtime"), size)
|
||||||
return(offset, size)
|
return(offset, size)
|
||||||
|
@ -450,7 +450,7 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
|
|||||||
|
|
||||||
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
if (_contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
|
if (*_contract.sourceUnit().annotation().useABICoderV2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_contract.isLibrary())
|
if (_contract.isLibrary())
|
||||||
@ -469,7 +469,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
|||||||
{
|
{
|
||||||
solAssert(func.second->hasDeclaration(), "Function has no declaration?!");
|
solAssert(func.second->hasDeclaration(), "Function has no declaration?!");
|
||||||
|
|
||||||
if (!func.second->declaration().sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
|
if (!*func.second->declaration().sourceUnit().annotation().useABICoderV2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto const& currentLoc = func.second->declaration().location();
|
auto const& currentLoc = func.second->declaration().location();
|
||||||
@ -489,9 +489,9 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
|||||||
errors,
|
errors,
|
||||||
std::string("Contract \"") +
|
std::string("Contract \"") +
|
||||||
_contract.name() +
|
_contract.name() +
|
||||||
"\" does not use ABIEncoderV2 but wants to inherit from a contract " +
|
"\" does not use ABI coder v2 but wants to inherit from a contract " +
|
||||||
"which uses types that require it. " +
|
"which uses types that require it. " +
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" for the inheriting contract as well to enable the feature."
|
"Use \"pragma abicoder v2;\" for the inheriting contract as well to enable the feature."
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,8 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
|
|||||||
// when reporting the warning, print the source name only
|
// when reporting the warning, print the source name only
|
||||||
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
|
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
|
||||||
}
|
}
|
||||||
|
if (!m_sourceUnit->annotation().useABICoderV2.set())
|
||||||
|
m_sourceUnit->annotation().useABICoderV2 = false;
|
||||||
m_sourceUnit = nullptr;
|
m_sourceUnit = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,9 +115,45 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
||||||
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
||||||
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||||
|
|
||||||
|
if (feature == ExperimentalFeature::ABIEncoderV2)
|
||||||
|
{
|
||||||
|
if (m_sourceUnit->annotation().useABICoderV2.set())
|
||||||
|
{
|
||||||
|
if (!*m_sourceUnit->annotation().useABICoderV2)
|
||||||
|
m_errorReporter.syntaxError(
|
||||||
|
8273_error,
|
||||||
|
_pragma.location(),
|
||||||
|
"ABI coder v1 has already been selected through \"pragma abicoder v1\"."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_sourceUnit->annotation().useABICoderV2 = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (_pragma.literals()[0] == "abicoder")
|
||||||
|
{
|
||||||
|
solAssert(m_sourceUnit, "");
|
||||||
|
if (
|
||||||
|
_pragma.literals().size() != 2 ||
|
||||||
|
!set<string>{"v1", "v2"}.count(_pragma.literals()[1])
|
||||||
|
)
|
||||||
|
m_errorReporter.syntaxError(
|
||||||
|
2745_error,
|
||||||
|
_pragma.location(),
|
||||||
|
"Expected either \"pragma abicoder v1\" or \"pragma abicoder v2\"."
|
||||||
|
);
|
||||||
|
else if (m_sourceUnit->annotation().useABICoderV2.set())
|
||||||
|
m_errorReporter.syntaxError(
|
||||||
|
3845_error,
|
||||||
|
_pragma.location(),
|
||||||
|
"ABI coder has already been selected for this source unit."
|
||||||
|
);
|
||||||
|
else
|
||||||
|
m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2");
|
||||||
|
}
|
||||||
else if (_pragma.literals()[0] == "solidity")
|
else if (_pragma.literals()[0] == "solidity")
|
||||||
{
|
{
|
||||||
vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
|
vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
|
||||||
@ -135,6 +173,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ namespace solidity::frontend
|
|||||||
* - issues deprecation warnings for unary '+'
|
* - issues deprecation warnings for unary '+'
|
||||||
* - issues deprecation warning for throw
|
* - issues deprecation warning for throw
|
||||||
* - whether the msize instruction is used and the Yul optimizer is enabled at the same time.
|
* - whether the msize instruction is used and the Yul optimizer is enabled at the same time.
|
||||||
|
* - selection of the ABI coder through pragmas.
|
||||||
*/
|
*/
|
||||||
class SyntaxChecker: private ASTConstVisitor
|
class SyntaxChecker: private ASTConstVisitor
|
||||||
{
|
{
|
||||||
|
@ -398,13 +398,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
m_errorReporter.typeError(4103_error, _var.location(), message);
|
m_errorReporter.typeError(4103_error, _var.location(), message);
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
|
!useABICoderV2() &&
|
||||||
!typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction())
|
!typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction())
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
string message =
|
string message =
|
||||||
"This type is only supported in ABIEncoderV2. "
|
"This type is only supported in ABI coder v2. "
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature.";
|
"Use \"pragma abicoder v2;\" to enable the feature.";
|
||||||
if (_function.isConstructor())
|
if (_function.isConstructor())
|
||||||
message +=
|
message +=
|
||||||
" Alternatively, make the contract abstract and supply the "
|
" Alternatively, make the contract abstract and supply the "
|
||||||
@ -570,7 +570,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
else if (_variable.visibility() >= Visibility::Public)
|
else if (_variable.visibility() >= Visibility::Public)
|
||||||
{
|
{
|
||||||
FunctionType getter(_variable);
|
FunctionType getter(_variable);
|
||||||
if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
if (!useABICoderV2())
|
||||||
{
|
{
|
||||||
vector<string> unsupportedTypes;
|
vector<string> unsupportedTypes;
|
||||||
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
|
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
|
||||||
@ -580,9 +580,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
2763_error,
|
2763_error,
|
||||||
_variable.location(),
|
_variable.location(),
|
||||||
"The following types are only supported for getters in ABIEncoderV2: " +
|
"The following types are only supported for getters in ABI coder v2: " +
|
||||||
joinHumanReadable(unsupportedTypes) +
|
joinHumanReadable(unsupportedTypes) +
|
||||||
". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
". Either remove \"public\" or use \"pragma abicoder v2;\" to enable the feature."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!getter.interfaceFunctionType())
|
if (!getter.interfaceFunctionType())
|
||||||
@ -695,14 +695,14 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|||||||
if (!type(*var)->interfaceType(false))
|
if (!type(*var)->interfaceType(false))
|
||||||
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
||||||
if (
|
if (
|
||||||
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
|
!useABICoderV2() &&
|
||||||
!typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */)
|
!typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */)
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
3061_error,
|
3061_error,
|
||||||
var->location(),
|
var->location(),
|
||||||
"This type is only supported in ABIEncoderV2. "
|
"This type is only supported in ABI coder v2. "
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (_eventDef.isAnonymous() && numIndexed > 4)
|
if (_eventDef.isAnonymous() && numIndexed > 4)
|
||||||
@ -1900,7 +1900,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
|||||||
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||||
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
|
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
|
||||||
|
|
||||||
bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
|
bool const abiEncoderV2 = useABICoderV2();
|
||||||
|
|
||||||
// Check for named arguments
|
// Check for named arguments
|
||||||
if (!_functionCall.names().empty())
|
if (!_functionCall.names().empty())
|
||||||
@ -2192,7 +2192,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
_functionType->kind() == FunctionType::Kind::Creation ||
|
_functionType->kind() == FunctionType::Kind::Creation ||
|
||||||
_functionType->kind() == FunctionType::Kind::Event;
|
_functionType->kind() == FunctionType::Kind::Event;
|
||||||
|
|
||||||
if (callRequiresABIEncoding && !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
if (callRequiresABIEncoding && !useABICoderV2())
|
||||||
{
|
{
|
||||||
solAssert(!isVariadic, "");
|
solAssert(!isVariadic, "");
|
||||||
solAssert(parameterTypes.size() == arguments.size(), "");
|
solAssert(parameterTypes.size() == arguments.size(), "");
|
||||||
@ -2208,8 +2208,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
2443_error,
|
2443_error,
|
||||||
paramArgMap[i]->location(),
|
paramArgMap[i]->location(),
|
||||||
"The type of this parameter, " + parameterTypes[i]->toString(true) + ", "
|
"The type of this parameter, " + parameterTypes[i]->toString(true) + ", "
|
||||||
"is only supported in ABIEncoderV2. "
|
"is only supported in ABI coder v2. "
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2222,8 +2222,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
2428_error,
|
2428_error,
|
||||||
_functionCall.location(),
|
_functionCall.location(),
|
||||||
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", "
|
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", "
|
||||||
"is only supported in ABIEncoderV2. "
|
"is only supported in ABI coder v2. "
|
||||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2340,7 +2340,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
{
|
{
|
||||||
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(
|
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(
|
||||||
_functionCall,
|
_functionCall,
|
||||||
experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
|
useABICoderV2()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3414,10 +3414,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
|||||||
m_errorReporter.typeError(errorId, _expression.location(), description);
|
m_errorReporter.typeError(errorId, _expression.location(), description);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const
|
bool TypeChecker::useABICoderV2() const
|
||||||
{
|
{
|
||||||
solAssert(m_currentSourceUnit, "");
|
solAssert(m_currentSourceUnit, "");
|
||||||
if (m_currentContract)
|
if (m_currentContract)
|
||||||
solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), "");
|
solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), "");
|
||||||
return m_currentSourceUnit->annotation().experimentalFeatures.count(_feature);
|
return *m_currentSourceUnit->annotation().useABICoderV2;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ private:
|
|||||||
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
||||||
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
|
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
|
||||||
|
|
||||||
bool experimentalFeatureActive(ExperimentalFeature _feature) const;
|
bool useABICoderV2() const;
|
||||||
|
|
||||||
/// @returns the current scope that can have function or type definitions.
|
/// @returns the current scope that can have function or type definitions.
|
||||||
/// This is either a contract or a source unit.
|
/// This is either a contract or a source unit.
|
||||||
|
@ -84,16 +84,6 @@ TypePointer ImportDirective::type() const
|
|||||||
return TypeProvider::module(*annotation().sourceUnit);
|
return TypeProvider::module(*annotation().sourceUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<VariableDeclaration const*> ContractDefinition::stateVariablesIncludingInherited() const
|
|
||||||
{
|
|
||||||
vector<VariableDeclaration const*> stateVars;
|
|
||||||
for (auto const& contract: annotation().linearizedBaseContracts)
|
|
||||||
for (auto var: contract->stateVariables())
|
|
||||||
if (*contract == *this || var->isVisibleInDerivedContracts())
|
|
||||||
stateVars.push_back(var);
|
|
||||||
return stateVars;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ContractDefinition::derivesFrom(ContractDefinition const& _base) const
|
bool ContractDefinition::derivesFrom(ContractDefinition const& _base) const
|
||||||
{
|
{
|
||||||
return util::contains(annotation().linearizedBaseContracts, &_base);
|
return util::contains(annotation().linearizedBaseContracts, &_base);
|
||||||
|
@ -500,7 +500,6 @@ public:
|
|||||||
std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); }
|
std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); }
|
||||||
std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); }
|
std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); }
|
||||||
std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); }
|
std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); }
|
||||||
std::vector<VariableDeclaration const*> stateVariablesIncludingInherited() const;
|
|
||||||
std::vector<ModifierDefinition const*> functionModifiers() const { return filteredNodes<ModifierDefinition>(m_subNodes); }
|
std::vector<ModifierDefinition const*> functionModifiers() const { return filteredNodes<ModifierDefinition>(m_subNodes); }
|
||||||
std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
|
std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
|
||||||
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
|
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
|
||||||
|
@ -94,6 +94,7 @@ struct SourceUnitAnnotation: ASTAnnotation
|
|||||||
SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols;
|
SetOnce<std::map<ASTString, std::vector<Declaration const*>>> exportedSymbols;
|
||||||
/// Experimental features.
|
/// Experimental features.
|
||||||
std::set<ExperimentalFeature> experimentalFeatures;
|
std::set<ExperimentalFeature> experimentalFeatures;
|
||||||
|
SetOnce<bool> useABICoderV2;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScopableAnnotation
|
struct ScopableAnnotation
|
||||||
|
@ -1172,7 +1172,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d
|
|||||||
if (
|
if (
|
||||||
!_arrayType.isByteArray() &&
|
!_arrayType.isByteArray() &&
|
||||||
_arrayType.baseType()->storageBytes() < 32 &&
|
_arrayType.baseType()->storageBytes() < 32 &&
|
||||||
m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
|
m_context.useABICoderV2()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_context << u256(32);
|
m_context << u256(32);
|
||||||
|
@ -77,11 +77,8 @@ public:
|
|||||||
|
|
||||||
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
|
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
|
||||||
|
|
||||||
/// Update currently enabled set of experimental features.
|
void setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; }
|
||||||
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
|
bool useABICoderV2() const { return m_useABICoderV2; }
|
||||||
std::set<ExperimentalFeature> const& experimentalFeaturesActive() const { return m_experimentalFeatures; }
|
|
||||||
/// @returns true if the given feature is enabled.
|
|
||||||
bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); }
|
|
||||||
|
|
||||||
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
||||||
void addImmutable(VariableDeclaration const& _declaration);
|
void addImmutable(VariableDeclaration const& _declaration);
|
||||||
@ -365,8 +362,7 @@ private:
|
|||||||
/// Version of the EVM to compile against.
|
/// Version of the EVM to compile against.
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings const m_revertStrings;
|
RevertStrings const m_revertStrings;
|
||||||
/// Activated experimental features.
|
bool m_useABICoderV2 = false;
|
||||||
std::set<ExperimentalFeature> m_experimentalFeatures;
|
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
|
@ -230,7 +230,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory)
|
void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory)
|
||||||
{
|
{
|
||||||
/// Stack: <source_offset> <length>
|
/// Stack: <source_offset> <length>
|
||||||
if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
if (m_context.useABICoderV2())
|
||||||
{
|
{
|
||||||
// Use the new Yul-based decoding function
|
// Use the new Yul-based decoding function
|
||||||
auto stackHeightBefore = m_context.stackHeight();
|
auto stackHeightBefore = m_context.stackHeight();
|
||||||
@ -412,7 +412,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// stack: <v1> <v2> ... <vn> <mem>
|
// stack: <v1> <v2> ... <vn> <mem>
|
||||||
bool const encoderV2 = m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
|
bool const encoderV2 = m_context.useABICoderV2();
|
||||||
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
|
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
|
||||||
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
||||||
for (TypePointer& t: targetTypes)
|
for (TypePointer& t: targetTypes)
|
||||||
|
@ -127,7 +127,7 @@ void ContractCompiler::initializeContext(
|
|||||||
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
|
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
|
m_context.setUseABICoderV2(*_contract.sourceUnit().annotation().useABICoderV2);
|
||||||
m_context.setOtherCompilers(_otherCompilers);
|
m_context.setOtherCompilers(_otherCompilers);
|
||||||
m_context.setMostDerivedContract(_contract);
|
m_context.setMostDerivedContract(_contract);
|
||||||
if (m_runtimeCompiler)
|
if (m_runtimeCompiler)
|
||||||
@ -1349,13 +1349,13 @@ void ContractCompiler::appendModifierOrFunctionCode()
|
|||||||
{
|
{
|
||||||
m_context.setArithmetic(Arithmetic::Checked);
|
m_context.setArithmetic(Arithmetic::Checked);
|
||||||
|
|
||||||
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
|
bool coderV2Outside = m_context.useABICoderV2();
|
||||||
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
|
m_context.setUseABICoderV2(*codeBlock->sourceUnit().annotation().useABICoderV2);
|
||||||
|
|
||||||
m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
|
m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
|
||||||
codeBlock->accept(*this);
|
codeBlock->accept(*this);
|
||||||
|
|
||||||
m_context.setExperimentalFeatures(experimentalFeaturesOutside);
|
m_context.setUseABICoderV2(coderV2Outside);
|
||||||
|
|
||||||
solAssert(!m_returnTags.empty(), "");
|
solAssert(!m_returnTags.empty(), "");
|
||||||
m_context << m_returnTags.back().first;
|
m_context << m_returnTags.back().first;
|
||||||
|
@ -1676,7 +1676,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
solAssert(memberType->calldataEncodedSize() > 0, "");
|
solAssert(memberType->calldataEncodedSize() > 0, "");
|
||||||
solAssert(memberType->storageBytes() <= 32, "");
|
solAssert(memberType->storageBytes() <= 32, "");
|
||||||
if (memberType->storageBytes() < 32 && m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
if (memberType->storageBytes() < 32 && m_context.useABICoderV2())
|
||||||
{
|
{
|
||||||
m_context << u256(32);
|
m_context << u256(32);
|
||||||
CompilerUtils(m_context).abiDecodeV2({memberType}, false);
|
CompilerUtils(m_context).abiDecodeV2({memberType}, false);
|
||||||
@ -2522,7 +2522,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
// memory pointer), but kept references to the return data for
|
// memory pointer), but kept references to the return data for
|
||||||
// (statically-sized) arrays
|
// (statically-sized) arrays
|
||||||
bool needToUpdateFreeMemoryPtr = false;
|
bool needToUpdateFreeMemoryPtr = false;
|
||||||
if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
if (dynamicReturnSize || m_context.useABICoderV2())
|
||||||
needToUpdateFreeMemoryPtr = true;
|
needToUpdateFreeMemoryPtr = true;
|
||||||
else
|
else
|
||||||
for (auto const& retType: returnTypes)
|
for (auto const& retType: returnTypes)
|
||||||
|
@ -1556,13 +1556,14 @@ string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayType const& _toType)
|
string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
solUnimplementedAssert(!_fromType.isByteArray(), "");
|
if (_fromType.isByteArray())
|
||||||
|
return copyByteArrayToStorageFunction(_fromType, _toType);
|
||||||
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
|
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||||
@ -1655,6 +1656,84 @@ string YulUtilFunctions::copyArrayToStorage(ArrayType const& _fromType, ArrayTyp
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
solAssert(_fromType.isByteArray(), "");
|
||||||
|
solAssert(_toType.isByteArray(), "");
|
||||||
|
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
|
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||||
|
return m_functionCollector.createFunction(functionName, [&](){
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
|
||||||
|
let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
|
||||||
|
// Make sure array length is sane
|
||||||
|
if gt(newLen, 0xffffffffffffffff) { <panic>() }
|
||||||
|
|
||||||
|
let oldLen := <byteArrayLength>(sload(slot))
|
||||||
|
|
||||||
|
<?fromMemory>
|
||||||
|
src := add(src, 0x20)
|
||||||
|
</fromMemory>
|
||||||
|
|
||||||
|
// This is not needed in all branches.
|
||||||
|
let dstDataArea
|
||||||
|
if or(gt(oldLen, 31), gt(newLen, 31)) {
|
||||||
|
dstDataArea := <dstDataLocation>(slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gt(oldLen, 31) {
|
||||||
|
// potentially truncate data
|
||||||
|
let deleteStart := add(dstDataArea, div(add(newLen, 31), 32))
|
||||||
|
if lt(newLen, 32) { deleteStart := dstDataArea }
|
||||||
|
<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32)))
|
||||||
|
}
|
||||||
|
switch gt(newLen, 31)
|
||||||
|
case 1 {
|
||||||
|
let loopEnd := and(newLen, not(0x1f))
|
||||||
|
let dstPtr := dstDataArea
|
||||||
|
let i := 0
|
||||||
|
for { } lt(i, loopEnd) { i := add(i, 32) } {
|
||||||
|
sstore(dstPtr, <readFromCalldataOrMemory>(add(src, i)))
|
||||||
|
dstPtr := add(dstPtr, 1)
|
||||||
|
}
|
||||||
|
if lt(loopEnd, newLen) {
|
||||||
|
let lastValue := <readFromCalldataOrMemory>(add(src, i))
|
||||||
|
sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
|
||||||
|
}
|
||||||
|
sstore(slot, add(mul(newLen, 2), 1))
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
let value := 0
|
||||||
|
if newLen {
|
||||||
|
value := <readFromCalldataOrMemory>(src)
|
||||||
|
}
|
||||||
|
sstore(slot, <byteArrayCombineShort>(value, newLen))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
|
||||||
|
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
|
||||||
|
templ("fromCalldata", fromCalldata);
|
||||||
|
templ("arrayLength", arrayLengthFunction(_fromType));
|
||||||
|
templ("panic", panicFunction(PanicCode::ResourceError));
|
||||||
|
templ("byteArrayLength", extractByteArrayLengthFunction());
|
||||||
|
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||||
|
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
|
||||||
|
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata));
|
||||||
|
templ("maskBytes", maskBytesFunctionDynamic());
|
||||||
|
templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
|
||||||
|
|
||||||
|
return templ.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
||||||
@ -2207,8 +2286,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
|||||||
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot, <offset>value) {
|
function <functionName>(slot, <offset><fromValues>) {
|
||||||
sstore(slot, <update>(sload(slot), <offset><prepare>(value)))
|
let <toValues> := <convert>(<fromValues>)
|
||||||
|
sstore(slot, <update>(sload(slot), <offset><prepare>(<toValues>)))
|
||||||
}
|
}
|
||||||
|
|
||||||
)")
|
)")
|
||||||
@ -2219,6 +2299,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
|||||||
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
||||||
)
|
)
|
||||||
("offset", _offset.has_value() ? "" : "offset, ")
|
("offset", _offset.has_value() ? "" : "offset, ")
|
||||||
|
("convert", conversionFunction(_fromType, _toType))
|
||||||
|
("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
|
||||||
|
("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
|
||||||
("prepare", prepareStoreFunction(_toType))
|
("prepare", prepareStoreFunction(_toType))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
@ -2248,7 +2331,7 @@ string YulUtilFunctions::updateStorageValueFunction(
|
|||||||
)");
|
)");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
|
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
|
||||||
templ("copyArrayToStorage", copyArrayToStorage(
|
templ("copyArrayToStorage", copyArrayToStorageFunction(
|
||||||
dynamic_cast<ArrayType const&>(_fromType),
|
dynamic_cast<ArrayType const&>(_fromType),
|
||||||
dynamic_cast<ArrayType const&>(_toType)
|
dynamic_cast<ArrayType const&>(_toType)
|
||||||
));
|
));
|
||||||
@ -2478,9 +2561,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
return templ.render();
|
return templ.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool leftAligned = false;
|
||||||
|
if (
|
||||||
|
_type.category() != Type::Category::Function ||
|
||||||
|
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
|
||||||
|
)
|
||||||
|
leftAligned = _type.leftAligned();
|
||||||
|
|
||||||
if (storageBytes == 32)
|
if (storageBytes == 32)
|
||||||
templ("cleaned", "value");
|
templ("cleaned", "value");
|
||||||
else if (_type.leftAligned())
|
else if (leftAligned)
|
||||||
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
||||||
else
|
else
|
||||||
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
||||||
@ -2491,7 +2581,8 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
|
|
||||||
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(_type.category() != Type::Category::Function, "");
|
if (_type.category() == Type::Category::Function)
|
||||||
|
solUnimplementedAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::Internal, "");
|
||||||
|
|
||||||
string functionName = "prepare_store_" + _type.identifier();
|
string functionName = "prepare_store_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
@ -2718,12 +2809,13 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
_to.identifier();
|
_to.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(addr, functionId) -> outAddr, outFunctionId {
|
function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
|
||||||
outAddr := addr
|
<?external>outAddr := addr</external>
|
||||||
outFunctionId := functionId
|
outFunctionId := functionId
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
|
("external", fromType.kind() == FunctionType::Kind::External)
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,11 @@ public:
|
|||||||
|
|
||||||
/// @returns the name of a function that will copy array from calldata or memory to storage
|
/// @returns the name of a function that will copy array from calldata or memory to storage
|
||||||
/// signature (to_slot, from_ptr) ->
|
/// signature (to_slot, from_ptr) ->
|
||||||
std::string copyArrayToStorage(ArrayType const& _fromType, ArrayType const& _toType);
|
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
||||||
|
|
||||||
|
/// @returns the name of a function that will copy a byte array from calldata or memory to storage
|
||||||
|
/// signature (to_slot, from_ptr) ->
|
||||||
|
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
||||||
|
|
||||||
/// Returns the name of a function that will convert a given length to the
|
/// Returns the name of a function that will convert a given length to the
|
||||||
/// size in memory (number of storage slots or calldata/memory bytes) it
|
/// size in memory (number of storage slots or calldata/memory bytes) it
|
||||||
|
@ -425,7 +425,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
|||||||
"(" <<
|
"(" <<
|
||||||
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
|
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
|
||||||
", " <<
|
", " <<
|
||||||
converted.name() <<
|
converted.commaSeparatedList() <<
|
||||||
")\n";
|
")\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2543,47 +2543,50 @@ string IRGeneratorForStatements::binaryOperation(
|
|||||||
!TokenTraits::isShiftOp(_operator),
|
!TokenTraits::isShiftOp(_operator),
|
||||||
"Have to use specific shift operation function for shifts."
|
"Have to use specific shift operation function for shifts."
|
||||||
);
|
);
|
||||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
string fun;
|
||||||
|
if (TokenTraits::isBitOp(_operator))
|
||||||
{
|
{
|
||||||
string fun;
|
solAssert(
|
||||||
|
_type.category() == Type::Category::Integer ||
|
||||||
|
_type.category() == Type::Category::FixedBytes,
|
||||||
|
"");
|
||||||
|
switch (_operator)
|
||||||
|
{
|
||||||
|
case Token::BitOr: fun = "or"; break;
|
||||||
|
case Token::BitXor: fun = "xor"; break;
|
||||||
|
case Token::BitAnd: fun = "and"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (TokenTraits::isArithmeticOp(_operator))
|
||||||
|
{
|
||||||
|
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
|
||||||
|
solAssert(type, "");
|
||||||
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
case Token::Add:
|
case Token::Add:
|
||||||
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
||||||
break;
|
break;
|
||||||
case Token::Sub:
|
case Token::Sub:
|
||||||
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
||||||
break;
|
break;
|
||||||
case Token::Mul:
|
case Token::Mul:
|
||||||
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
||||||
break;
|
break;
|
||||||
case Token::Div:
|
case Token::Div:
|
||||||
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
||||||
break;
|
break;
|
||||||
case Token::Mod:
|
case Token::Mod:
|
||||||
fun = m_utils.intModFunction(*type);
|
fun = m_utils.intModFunction(*type);
|
||||||
break;
|
break;
|
||||||
case Token::BitOr:
|
default:
|
||||||
fun = "or";
|
break;
|
||||||
break;
|
|
||||||
case Token::BitXor:
|
|
||||||
fun = "xor";
|
|
||||||
break;
|
|
||||||
case Token::BitAnd:
|
|
||||||
fun = "and";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
solUnimplementedAssert(!fun.empty(), "");
|
|
||||||
return fun + "(" + _left + ", " + _right + ")\n";
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
solUnimplementedAssert(false, "");
|
|
||||||
|
|
||||||
return {};
|
solUnimplementedAssert(!fun.empty(), "Type: " + _type.toString());
|
||||||
|
return fun + "(" + _left + ", " + _right + ")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IRGeneratorForStatements::shiftOperation(
|
std::string IRGeneratorForStatements::shiftOperation(
|
||||||
|
@ -134,7 +134,7 @@ void CHC::endVisit(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
setCurrentBlock(*m_constructorSummaryPredicate);
|
setCurrentBlock(*m_constructorSummaryPredicate);
|
||||||
|
|
||||||
addAssertVerificationTarget(m_currentContract, m_currentBlock, smtutil::Expression(true), errorFlag().currentValue());
|
m_queryPlaceholders[&_contract].push_back({smtutil::Expression(true), errorFlag().currentValue(), m_currentBlock});
|
||||||
connectBlocks(m_currentBlock, interface(), errorFlag().currentValue() == 0);
|
connectBlocks(m_currentBlock, interface(), errorFlag().currentValue() == 0);
|
||||||
|
|
||||||
SMTEncoder::endVisit(_contract);
|
SMTEncoder::endVisit(_contract);
|
||||||
@ -231,16 +231,14 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
|||||||
auto assertionError = errorFlag().currentValue();
|
auto assertionError = errorFlag().currentValue();
|
||||||
auto sum = summary(_function);
|
auto sum = summary(_function);
|
||||||
connectBlocks(m_currentBlock, sum);
|
connectBlocks(m_currentBlock, sum);
|
||||||
|
|
||||||
auto iface = interface();
|
auto iface = interface();
|
||||||
|
|
||||||
setCurrentBlock(*m_interfaces.at(m_currentContract));
|
setCurrentBlock(*m_interfaces.at(m_currentContract));
|
||||||
|
|
||||||
auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
||||||
if (_function.isPublic())
|
if (_function.isPublic())
|
||||||
{
|
{
|
||||||
auto txConstraints = m_context.state().txConstraints(_function);
|
auto txConstraints = m_context.state().txConstraints(_function);
|
||||||
addAssertVerificationTarget(&_function, ifacePre, txConstraints && sum, assertionError);
|
m_queryPlaceholders[&_function].push_back({txConstraints && sum, assertionError, ifacePre});
|
||||||
connectBlocks(ifacePre, iface, txConstraints && sum && (assertionError == 0));
|
connectBlocks(ifacePre, iface, txConstraints && sum && (assertionError == 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -512,30 +510,15 @@ void CHC::visitAssert(FunctionCall const& _funCall)
|
|||||||
|
|
||||||
solAssert(m_currentContract, "");
|
solAssert(m_currentContract, "");
|
||||||
solAssert(m_currentFunction, "");
|
solAssert(m_currentFunction, "");
|
||||||
if (m_currentFunction->isConstructor())
|
auto errorCondition = !m_context.expression(*args.front())->currentValue();
|
||||||
m_functionAssertions[m_currentContract].insert(&_funCall);
|
verificationTargetEncountered(&_funCall, VerificationTarget::Type::Assert, errorCondition);
|
||||||
else
|
|
||||||
m_functionAssertions[m_currentFunction].insert(&_funCall);
|
|
||||||
|
|
||||||
auto previousError = errorFlag().currentValue();
|
|
||||||
errorFlag().increaseIndex();
|
|
||||||
|
|
||||||
connectBlocks(
|
|
||||||
m_currentBlock,
|
|
||||||
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
|
||||||
currentPathConditions() && !m_context.expression(*args.front())->currentValue() && (
|
|
||||||
errorFlag().currentValue() == newErrorId(_funCall)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
m_context.addAssertion(errorFlag().currentValue() == previousError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::visitAddMulMod(FunctionCall const& _funCall)
|
void CHC::visitAddMulMod(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
solAssert(_funCall.arguments().at(2), "");
|
solAssert(_funCall.arguments().at(2), "");
|
||||||
|
|
||||||
addVerificationTarget(_funCall, VerificationTarget::Type::DivByZero, expr(*_funCall.arguments().at(2)) == 0);
|
verificationTargetEncountered(&_funCall, VerificationTarget::Type::DivByZero, expr(*_funCall.arguments().at(2)) == 0);
|
||||||
|
|
||||||
SMTEncoder::visitAddMulMod(_funCall);
|
SMTEncoder::visitAddMulMod(_funCall);
|
||||||
}
|
}
|
||||||
@ -634,7 +617,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
|
|||||||
auto symbArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
auto symbArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
||||||
solAssert(symbArray, "");
|
solAssert(symbArray, "");
|
||||||
|
|
||||||
addVerificationTarget(_arrayPop, VerificationTarget::Type::PopEmptyArray, symbArray->length() <= 0);
|
verificationTargetEncountered(&_arrayPop, VerificationTarget::Type::PopEmptyArray, symbArray->length() <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
||||||
@ -646,7 +629,7 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (_op == Token::Mod || _op == Token::Div)
|
if (_op == Token::Mod || _op == Token::Div)
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::DivByZero, _right == 0);
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::DivByZero, _right == 0);
|
||||||
|
|
||||||
auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression);
|
auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression);
|
||||||
|
|
||||||
@ -662,16 +645,16 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
|||||||
return values;
|
return values;
|
||||||
|
|
||||||
if (_op == Token::Div)
|
if (_op == Token::Div)
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||||
else if (intType->isSigned())
|
else if (intType->isSigned())
|
||||||
{
|
{
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||||
}
|
}
|
||||||
else if (_op == Token::Sub)
|
else if (_op == Token::Sub)
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
||||||
else if (_op == Token::Add || _op == Token::Mul)
|
else if (_op == Token::Add || _op == Token::Mul)
|
||||||
addVerificationTarget(_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||||
else
|
else
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
return values;
|
return values;
|
||||||
@ -679,11 +662,11 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
|||||||
|
|
||||||
void CHC::resetSourceAnalysis()
|
void CHC::resetSourceAnalysis()
|
||||||
{
|
{
|
||||||
m_verificationTargets.clear();
|
|
||||||
m_safeTargets.clear();
|
m_safeTargets.clear();
|
||||||
m_unsafeTargets.clear();
|
m_unsafeTargets.clear();
|
||||||
m_functionAssertions.clear();
|
m_functionTargetIds.clear();
|
||||||
m_errorIds.clear();
|
m_verificationTargets.clear();
|
||||||
|
m_queryPlaceholders.clear();
|
||||||
m_callGraph.clear();
|
m_callGraph.clear();
|
||||||
m_summaries.clear();
|
m_summaries.clear();
|
||||||
m_interfaces.clear();
|
m_interfaces.clear();
|
||||||
@ -713,6 +696,7 @@ void CHC::resetSourceAnalysis()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_context.clear();
|
m_context.clear();
|
||||||
|
m_context.resetUniqueId();
|
||||||
m_context.setAssertionAccumulation(false);
|
m_context.setAssertionAccumulation(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -759,15 +743,15 @@ void CHC::setCurrentBlock(Predicate const& _block)
|
|||||||
m_currentBlock = predicate(_block);
|
m_currentBlock = predicate(_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<frontend::Expression const*, CHC::IdCompare> CHC::transactionAssertions(ASTNode const* _txRoot)
|
set<unsigned> CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot)
|
||||||
{
|
{
|
||||||
set<Expression const*, IdCompare> assertions;
|
set<unsigned> verificationTargetsIds;
|
||||||
solidity::util::BreadthFirstSearch<ASTNode const*>{{_txRoot}}.run([&](auto const* function, auto&& _addChild) {
|
solidity::util::BreadthFirstSearch<ASTNode const*>{{_txRoot}}.run([&](auto const* function, auto&& _addChild) {
|
||||||
assertions.insert(m_functionAssertions[function].begin(), m_functionAssertions[function].end());
|
verificationTargetsIds.insert(m_functionTargetIds[function].begin(), m_functionTargetIds[function].end());
|
||||||
for (auto const* called: m_callGraph[function])
|
for (auto const* called: m_callGraph[function])
|
||||||
_addChild(called);
|
_addChild(called);
|
||||||
});
|
});
|
||||||
return assertions;
|
return verificationTargetsIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
SortPointer CHC::sort(FunctionDefinition const& _function)
|
SortPointer CHC::sort(FunctionDefinition const& _function)
|
||||||
@ -1101,186 +1085,171 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression c
|
|||||||
return {result, cex};
|
return {result, cex};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::addVerificationTarget(
|
void CHC::verificationTargetEncountered(
|
||||||
ASTNode const* _scope,
|
ASTNode const* const _errorNode,
|
||||||
VerificationTarget::Type _type,
|
VerificationTarget::Type _type,
|
||||||
smtutil::Expression _from,
|
smtutil::Expression const& _errorCondition
|
||||||
smtutil::Expression _constraints,
|
|
||||||
smtutil::Expression _errorId
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(m_currentContract || m_currentFunction, "");
|
solAssert(m_currentContract || m_currentFunction, "");
|
||||||
SourceUnit const* source = nullptr;
|
SourceUnit const* source = m_currentContract ? sourceUnitContaining(*m_currentContract) : sourceUnitContaining(*m_currentFunction);
|
||||||
if (m_currentContract)
|
|
||||||
source = sourceUnitContaining(*m_currentContract);
|
|
||||||
else
|
|
||||||
source = sourceUnitContaining(*m_currentFunction);
|
|
||||||
solAssert(source, "");
|
solAssert(source, "");
|
||||||
if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
|
if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_verificationTargets[_scope].push_back(CHCVerificationTarget{{_type, _from, _constraints}, _errorId});
|
bool scopeIsFunction = m_currentFunction && !m_currentFunction->isConstructor();
|
||||||
}
|
auto errorId = newErrorId();
|
||||||
|
solAssert(m_verificationTargets.count(errorId) == 0, "Error ID is not unique!");
|
||||||
void CHC::addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId)
|
m_verificationTargets.emplace(errorId, CHCVerificationTarget{{_type, _errorCondition, smtutil::Expression(true)}, errorId, _errorNode});
|
||||||
{
|
if (scopeIsFunction)
|
||||||
solAssert(m_currentContract, "");
|
m_functionTargetIds[m_currentFunction].push_back(errorId);
|
||||||
|
|
||||||
if (!m_currentFunction || m_currentFunction->isConstructor())
|
|
||||||
addVerificationTarget(_scope, _type, summary(*m_currentContract), smtutil::Expression(true), _errorId);
|
|
||||||
else
|
else
|
||||||
{
|
m_functionTargetIds[m_currentContract].push_back(errorId);
|
||||||
auto iface = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
|
||||||
auto sum = summary(*m_currentFunction);
|
|
||||||
addVerificationTarget(_scope, _type, iface, sum, _errorId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHC::addVerificationTarget(frontend::Expression const& _scope, VerificationTarget::Type _type, smtutil::Expression const& _target)
|
|
||||||
{
|
|
||||||
auto previousError = errorFlag().currentValue();
|
auto previousError = errorFlag().currentValue();
|
||||||
errorFlag().increaseIndex();
|
errorFlag().increaseIndex();
|
||||||
addVerificationTarget(&_scope, _type, errorFlag().currentValue());
|
|
||||||
|
|
||||||
m_context.addAssertion(
|
// create an error edge to the summary
|
||||||
errorFlag().currentValue() == previousError ||
|
connectBlocks(
|
||||||
(_target && errorFlag().currentValue() == newErrorId(_scope))
|
m_currentBlock,
|
||||||
|
scopeIsFunction ? summary(*m_currentFunction) : summary(*m_currentContract),
|
||||||
|
currentPathConditions() && _errorCondition && errorFlag().currentValue() == errorId
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId)
|
m_context.addAssertion(errorFlag().currentValue() == previousError);
|
||||||
{
|
|
||||||
addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::checkVerificationTargets()
|
void CHC::checkVerificationTargets()
|
||||||
{
|
{
|
||||||
for (auto const& [scope, targets]: m_verificationTargets)
|
// The verification conditions have been collected per function where they have been encountered (m_verificationTargets).
|
||||||
|
// Also, all possible contexts in which an external function can be called has been recorded (m_queryPlaceholders).
|
||||||
|
// Here we combine every context in which an external function can be called with all possible verification conditions
|
||||||
|
// in its call graph. Each such combination forms a unique verification target.
|
||||||
|
vector<CHCVerificationTarget> verificationTargets;
|
||||||
|
for (auto const& [function, placeholders]: m_queryPlaceholders)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < targets.size(); ++i)
|
auto functionTargets = transactionVerificationTargetsIds(function);
|
||||||
{
|
for (auto const& placeholder: placeholders)
|
||||||
auto const& target = targets[i];
|
for (unsigned id: functionTargets)
|
||||||
|
|
||||||
if (target.type == VerificationTarget::Type::Assert)
|
|
||||||
checkAssertTarget(scope, target);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
string satMsg;
|
auto const& target = m_verificationTargets.at(id);
|
||||||
string satMsgUnderflow;
|
verificationTargets.push_back(CHCVerificationTarget{
|
||||||
string satMsgOverflow;
|
{target.type, placeholder.fromPredicate, placeholder.constraints && placeholder.errorExpression == target.errorId},
|
||||||
string unknownMsg;
|
target.errorId,
|
||||||
ErrorId errorReporterId;
|
target.errorNode
|
||||||
ErrorId underflowErrorId = 3944_error;
|
});
|
||||||
ErrorId overflowErrorId = 4984_error;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (target.type == VerificationTarget::Type::PopEmptyArray)
|
set<unsigned> checkedErrorIds;
|
||||||
{
|
for (auto const& target: verificationTargets)
|
||||||
solAssert(dynamic_cast<FunctionCall const*>(scope), "");
|
{
|
||||||
satMsg = "Empty array \"pop\" detected here.";
|
string errorType;
|
||||||
unknownMsg = "Empty array \"pop\" might happen here.";
|
ErrorId errorReporterId;
|
||||||
errorReporterId = 2529_error;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
target.type == VerificationTarget::Type::Underflow ||
|
|
||||||
target.type == VerificationTarget::Type::Overflow
|
|
||||||
)
|
|
||||||
{
|
|
||||||
auto const* expr = dynamic_cast<Expression const*>(scope);
|
|
||||||
solAssert(expr, "");
|
|
||||||
auto const* intType = dynamic_cast<IntegerType const*>(expr->annotation().type);
|
|
||||||
if (!intType)
|
|
||||||
intType = TypeProvider::uint256();
|
|
||||||
|
|
||||||
satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")";
|
if (target.type == VerificationTarget::Type::PopEmptyArray)
|
||||||
satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")";
|
{
|
||||||
if (target.type == VerificationTarget::Type::Underflow)
|
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
|
||||||
{
|
errorType = "Empty array \"pop\"";
|
||||||
satMsg = satMsgUnderflow + " happens here.";
|
errorReporterId = 2529_error;
|
||||||
unknownMsg = satMsgUnderflow + " might happen here.";
|
}
|
||||||
errorReporterId = underflowErrorId;
|
else if (
|
||||||
}
|
target.type == VerificationTarget::Type::Underflow ||
|
||||||
else if (target.type == VerificationTarget::Type::Overflow)
|
target.type == VerificationTarget::Type::Overflow
|
||||||
{
|
)
|
||||||
satMsg = satMsgOverflow + " happens here.";
|
{
|
||||||
unknownMsg = satMsgOverflow + " might happen here.";
|
auto const* expr = dynamic_cast<Expression const*>(target.errorNode);
|
||||||
errorReporterId = overflowErrorId;
|
solAssert(expr, "");
|
||||||
}
|
auto const* intType = dynamic_cast<IntegerType const*>(expr->annotation().type);
|
||||||
}
|
if (!intType)
|
||||||
else if (target.type == VerificationTarget::Type::DivByZero)
|
intType = TypeProvider::uint256();
|
||||||
{
|
|
||||||
satMsg = "Division by zero happens here.";
|
|
||||||
unknownMsg = "Division by zero might happen here.";
|
|
||||||
errorReporterId = 4281_error;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
solAssert(false, "");
|
|
||||||
|
|
||||||
auto it = m_errorIds.find(scope->id());
|
if (target.type == VerificationTarget::Type::Underflow)
|
||||||
solAssert(it != m_errorIds.end(), "");
|
{
|
||||||
solAssert(i < it->second.size(), "");
|
errorType = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")";
|
||||||
unsigned errorId = it->second[i];
|
errorReporterId = 3944_error;
|
||||||
|
}
|
||||||
checkAndReportTarget(scope, target, errorId, errorReporterId, satMsg, unknownMsg);
|
else if (target.type == VerificationTarget::Type::Overflow)
|
||||||
|
{
|
||||||
|
errorType = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")";
|
||||||
|
errorReporterId = 4984_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (target.type == VerificationTarget::Type::DivByZero)
|
||||||
}
|
{
|
||||||
|
errorType = "Division by zero";
|
||||||
|
errorReporterId = 4281_error;
|
||||||
|
}
|
||||||
|
else if (target.type == VerificationTarget::Type::Assert)
|
||||||
|
{
|
||||||
|
errorType = "Assertion violation";
|
||||||
|
errorReporterId = 6328_error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solAssert(false, "");
|
||||||
|
|
||||||
void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target)
|
checkAndReportTarget(target, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
|
||||||
{
|
checkedErrorIds.insert(target.errorId);
|
||||||
solAssert(_target.type == VerificationTarget::Type::Assert, "");
|
|
||||||
auto assertions = transactionAssertions(_scope);
|
|
||||||
for (auto const* assertion: assertions)
|
|
||||||
{
|
|
||||||
auto it = m_errorIds.find(assertion->id());
|
|
||||||
solAssert(it != m_errorIds.end(), "");
|
|
||||||
solAssert(!it->second.empty(), "");
|
|
||||||
unsigned errorId = it->second[0];
|
|
||||||
|
|
||||||
checkAndReportTarget(assertion, _target, errorId, 6328_error, "Assertion violation happens here.", "Assertion violation might happen here.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There can be targets in internal functions that are not reachable from the external interface.
|
||||||
|
// These are safe by definition and are not even checked by the CHC engine, but this information
|
||||||
|
// must still be reported safe by the BMC engine.
|
||||||
|
set<unsigned> allErrorIds;
|
||||||
|
for (auto const& entry: m_functionTargetIds)
|
||||||
|
for (unsigned id: entry.second)
|
||||||
|
allErrorIds.insert(id);
|
||||||
|
|
||||||
|
set<unsigned> unreachableErrorIds;
|
||||||
|
set_difference(
|
||||||
|
allErrorIds.begin(),
|
||||||
|
allErrorIds.end(),
|
||||||
|
checkedErrorIds.begin(),
|
||||||
|
checkedErrorIds.end(),
|
||||||
|
inserter(unreachableErrorIds, unreachableErrorIds.begin())
|
||||||
|
);
|
||||||
|
for (auto id: unreachableErrorIds)
|
||||||
|
m_safeTargets[m_verificationTargets.at(id).errorNode].insert(m_verificationTargets.at(id).type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHC::checkAndReportTarget(
|
void CHC::checkAndReportTarget(
|
||||||
ASTNode const* _scope,
|
|
||||||
CHCVerificationTarget const& _target,
|
CHCVerificationTarget const& _target,
|
||||||
unsigned _errorId,
|
|
||||||
ErrorId _errorReporterId,
|
ErrorId _errorReporterId,
|
||||||
string _satMsg,
|
string _satMsg,
|
||||||
string _unknownMsg
|
string _unknownMsg
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (m_unsafeTargets.count(_scope) && m_unsafeTargets.at(_scope).count(_target.type))
|
if (m_unsafeTargets.count(_target.errorNode) && m_unsafeTargets.at(_target.errorNode).count(_target.type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
createErrorBlock();
|
createErrorBlock();
|
||||||
connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId));
|
connectBlocks(_target.value, error(), _target.constraints);
|
||||||
auto const& [result, model] = query(error(), _scope->location());
|
auto const& location = _target.errorNode->location();
|
||||||
|
auto const& [result, model] = query(error(), location);
|
||||||
if (result == CheckResult::UNSATISFIABLE)
|
if (result == CheckResult::UNSATISFIABLE)
|
||||||
m_safeTargets[_scope].insert(_target.type);
|
m_safeTargets[_target.errorNode].insert(_target.type);
|
||||||
else if (result == CheckResult::SATISFIABLE)
|
else if (result == CheckResult::SATISFIABLE)
|
||||||
{
|
{
|
||||||
solAssert(!_satMsg.empty(), "");
|
solAssert(!_satMsg.empty(), "");
|
||||||
m_unsafeTargets[_scope].insert(_target.type);
|
m_unsafeTargets[_target.errorNode].insert(_target.type);
|
||||||
auto cex = generateCounterexample(model, error().name);
|
auto cex = generateCounterexample(model, error().name);
|
||||||
if (cex)
|
if (cex)
|
||||||
m_outerErrorReporter.warning(
|
m_outerErrorReporter.warning(
|
||||||
_errorReporterId,
|
_errorReporterId,
|
||||||
_scope->location(),
|
location,
|
||||||
"CHC: " + _satMsg,
|
"CHC: " + _satMsg,
|
||||||
SecondarySourceLocation().append("Counterexample:\n" + *cex, SourceLocation{})
|
SecondarySourceLocation().append("Counterexample:\n" + *cex, SourceLocation{})
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_outerErrorReporter.warning(
|
m_outerErrorReporter.warning(
|
||||||
_errorReporterId,
|
_errorReporterId,
|
||||||
_scope->location(),
|
location,
|
||||||
"CHC: " + _satMsg
|
"CHC: " + _satMsg
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (!_unknownMsg.empty())
|
else if (!_unknownMsg.empty())
|
||||||
m_outerErrorReporter.warning(
|
m_outerErrorReporter.warning(
|
||||||
_errorReporterId,
|
_errorReporterId,
|
||||||
_scope->location(),
|
location,
|
||||||
"CHC: " + _unknownMsg
|
"CHC: " + _unknownMsg
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1431,14 +1400,13 @@ string CHC::contractSuffix(ContractDefinition const& _contract)
|
|||||||
return _contract.name() + "_" + to_string(_contract.id());
|
return _contract.name() + "_" + to_string(_contract.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CHC::newErrorId(frontend::Expression const& _expr)
|
unsigned CHC::newErrorId()
|
||||||
{
|
{
|
||||||
unsigned errorId = m_context.newUniqueId();
|
unsigned errorId = m_context.newUniqueId();
|
||||||
// We need to make sure the error id is not zero,
|
// We need to make sure the error id is not zero,
|
||||||
// because error id zero actually means no error in the CHC encoding.
|
// because error id zero actually means no error in the CHC encoding.
|
||||||
if (errorId == 0)
|
if (errorId == 0)
|
||||||
errorId = m_context.newUniqueId();
|
errorId = m_context.newUniqueId();
|
||||||
m_errorIds[_expr.id()].push_back(errorId);
|
|
||||||
return errorId;
|
return errorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ private:
|
|||||||
void eraseKnowledge();
|
void eraseKnowledge();
|
||||||
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
|
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
|
||||||
void setCurrentBlock(Predicate const& _block);
|
void setCurrentBlock(Predicate const& _block);
|
||||||
std::set<Expression const*, IdCompare> transactionAssertions(ASTNode const* _txRoot);
|
std::set<unsigned> transactionVerificationTargetsIds(ASTNode const* _txRoot);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Sort helpers.
|
/// Sort helpers.
|
||||||
@ -181,19 +181,14 @@ private:
|
|||||||
/// @returns <false, model> otherwise.
|
/// @returns <false, model> otherwise.
|
||||||
std::pair<smtutil::CheckResult, smtutil::CHCSolverInterface::CexGraph> query(smtutil::Expression const& _query, langutil::SourceLocation const& _location);
|
std::pair<smtutil::CheckResult, smtutil::CHCSolverInterface::CexGraph> query(smtutil::Expression const& _query, langutil::SourceLocation const& _location);
|
||||||
|
|
||||||
void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTarget::Type _type, smtutil::Expression const& _errorCondition);
|
||||||
void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId);
|
|
||||||
void addVerificationTarget(frontend::Expression const& _scope, VerificationTarget::Type _type, smtutil::Expression const& _target);
|
|
||||||
void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
|
||||||
|
|
||||||
void checkVerificationTargets();
|
void checkVerificationTargets();
|
||||||
// Forward declaration. Definition is below.
|
// Forward declaration. Definition is below.
|
||||||
struct CHCVerificationTarget;
|
struct CHCVerificationTarget;
|
||||||
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
|
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
|
||||||
void checkAndReportTarget(
|
void checkAndReportTarget(
|
||||||
ASTNode const* _scope,
|
|
||||||
CHCVerificationTarget const& _target,
|
CHCVerificationTarget const& _target,
|
||||||
unsigned _errorId,
|
|
||||||
langutil::ErrorId _errorReporterId,
|
langutil::ErrorId _errorReporterId,
|
||||||
std::string _satMsg,
|
std::string _satMsg,
|
||||||
std::string _unknownMsg = ""
|
std::string _unknownMsg = ""
|
||||||
@ -234,7 +229,7 @@ private:
|
|||||||
|
|
||||||
/// @returns a new unique error id associated with _expr and stores
|
/// @returns a new unique error id associated with _expr and stores
|
||||||
/// it into m_errorIds.
|
/// it into m_errorIds.
|
||||||
unsigned newErrorId(Expression const& _expr);
|
unsigned newErrorId();
|
||||||
|
|
||||||
smt::SymbolicState& state();
|
smt::SymbolicState& state();
|
||||||
smt::SymbolicIntVariable& errorFlag();
|
smt::SymbolicIntVariable& errorFlag();
|
||||||
@ -275,12 +270,30 @@ private:
|
|||||||
//@{
|
//@{
|
||||||
struct CHCVerificationTarget: VerificationTarget
|
struct CHCVerificationTarget: VerificationTarget
|
||||||
{
|
{
|
||||||
smtutil::Expression errorId;
|
unsigned const errorId;
|
||||||
|
ASTNode const* const errorNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Verification targets corresponding to ASTNodes. There can be multiple targets for a single ASTNode,
|
/// Query placeholder stores information necessary to create the final query edge in the CHC system.
|
||||||
/// e.g., divByZero and Overflow for signed division.
|
/// It is combined with the unique error id (and error type) to create a complete Verification Target.
|
||||||
std::map<ASTNode const*, std::vector<CHCVerificationTarget>, IdCompare> m_verificationTargets;
|
struct CHCQueryPlaceholder
|
||||||
|
{
|
||||||
|
smtutil::Expression const constraints;
|
||||||
|
smtutil::Expression const errorExpression;
|
||||||
|
smtutil::Expression const fromPredicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query placeholders for constructors, if the key has type ContractDefinition*,
|
||||||
|
/// or external functions, if the key has type FunctionDefinition*.
|
||||||
|
/// A placeholder is created for each possible context of a function (e.g. multiple contracts in contract inheritance hierarchy).
|
||||||
|
std::map<ASTNode const*, std::vector<CHCQueryPlaceholder>, IdCompare> m_queryPlaceholders;
|
||||||
|
|
||||||
|
/// Records verification conditions IDs per function encountered during an analysis of that function.
|
||||||
|
/// The key is the ASTNode of the function where the verification condition has been encountered,
|
||||||
|
/// or the ASTNode of the contract if the verification condition happens inside an implicit constructor.
|
||||||
|
std::map<ASTNode const*, std::vector<unsigned>, IdCompare> m_functionTargetIds;
|
||||||
|
/// Helper mapping unique IDs to actual verification targets.
|
||||||
|
std::map<unsigned, CHCVerificationTarget> m_verificationTargets;
|
||||||
|
|
||||||
/// Targets proven safe.
|
/// Targets proven safe.
|
||||||
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_safeTargets;
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_safeTargets;
|
||||||
@ -294,12 +307,6 @@ private:
|
|||||||
|
|
||||||
std::map<ASTNode const*, std::set<ASTNode const*, IdCompare>, IdCompare> m_callGraph;
|
std::map<ASTNode const*, std::set<ASTNode const*, IdCompare>, IdCompare> m_callGraph;
|
||||||
|
|
||||||
std::map<ASTNode const*, std::set<Expression const*>, IdCompare> m_functionAssertions;
|
|
||||||
|
|
||||||
/// Maps ASTNode ids to error ids.
|
|
||||||
/// There can be multiple errorIds associated with a single ASTNode.
|
|
||||||
std::map<unsigned, std::vector<unsigned>> m_errorIds;
|
|
||||||
|
|
||||||
/// The current block.
|
/// The current block.
|
||||||
smtutil::Expression m_currentBlock = smtutil::Expression(true);
|
smtutil::Expression m_currentBlock = smtutil::Expression(true);
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ EncodingContext::EncodingContext():
|
|||||||
void EncodingContext::reset()
|
void EncodingContext::reset()
|
||||||
{
|
{
|
||||||
resetAllVariables();
|
resetAllVariables();
|
||||||
resetUniqueId();
|
|
||||||
m_expressions.clear();
|
m_expressions.clear();
|
||||||
m_globalContext.clear();
|
m_globalContext.clear();
|
||||||
m_state.reset();
|
m_state.reset();
|
||||||
|
@ -348,6 +348,36 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SMTEncoder::visit(Assignment const& _assignment)
|
||||||
|
{
|
||||||
|
auto const& left = _assignment.leftHandSide();
|
||||||
|
auto const& right = _assignment.rightHandSide();
|
||||||
|
|
||||||
|
if (auto const* memberAccess = isEmptyPush(left))
|
||||||
|
{
|
||||||
|
right.accept(*this);
|
||||||
|
left.accept(*this);
|
||||||
|
|
||||||
|
auto const& memberExpr = memberAccess->expression();
|
||||||
|
auto& symbArray = dynamic_cast<smt::SymbolicArrayVariable&>(*m_context.expression(memberExpr));
|
||||||
|
smtutil::Expression oldElements = symbArray.elements();
|
||||||
|
smtutil::Expression length = symbArray.length();
|
||||||
|
symbArray.increaseIndex();
|
||||||
|
m_context.addAssertion(symbArray.elements() == smtutil::Expression::store(
|
||||||
|
oldElements,
|
||||||
|
length - 1,
|
||||||
|
expr(right)
|
||||||
|
));
|
||||||
|
m_context.addAssertion(symbArray.length() == length);
|
||||||
|
|
||||||
|
arrayPushPopAssign(memberExpr, symbArray.currentValue());
|
||||||
|
defineExpr(_assignment, expr(left));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SMTEncoder::endVisit(Assignment const& _assignment)
|
void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
createExpr(_assignment);
|
createExpr(_assignment);
|
||||||
@ -355,6 +385,9 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
|||||||
Token op = _assignment.assignmentOperator();
|
Token op = _assignment.assignmentOperator();
|
||||||
solAssert(TokenTraits::isAssignmentOp(op), "");
|
solAssert(TokenTraits::isAssignmentOp(op), "");
|
||||||
|
|
||||||
|
if (isEmptyPush(_assignment.leftHandSide()))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!smt::isSupportedType(*_assignment.annotation().type))
|
if (!smt::isSupportedType(*_assignment.annotation().type))
|
||||||
{
|
{
|
||||||
// Give it a new index anyway to keep the SSA scheme sound.
|
// Give it a new index anyway to keep the SSA scheme sound.
|
||||||
@ -465,14 +498,14 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
|||||||
assignment(*decl, newValue);
|
assignment(*decl, newValue);
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
dynamic_cast<IndexAccess const*>(&_op.subExpression()) ||
|
dynamic_cast<IndexAccess const*>(subExpr) ||
|
||||||
dynamic_cast<MemberAccess const*>(&_op.subExpression())
|
dynamic_cast<MemberAccess const*>(subExpr)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto innerValue = expr(*subExpr);
|
auto innerValue = expr(*subExpr);
|
||||||
auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1;
|
auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1;
|
||||||
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
|
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
|
||||||
indexOrMemberAssignment(_op.subExpression(), newValue);
|
indexOrMemberAssignment(*subExpr, newValue);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
@ -502,11 +535,12 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
|||||||
symbVar->increaseIndex();
|
symbVar->increaseIndex();
|
||||||
m_context.setZeroValue(*symbVar);
|
m_context.setZeroValue(*symbVar);
|
||||||
if (
|
if (
|
||||||
dynamic_cast<IndexAccess const*>(&_op.subExpression()) ||
|
dynamic_cast<IndexAccess const*>(subExpr) ||
|
||||||
dynamic_cast<MemberAccess const*>(&_op.subExpression())
|
dynamic_cast<MemberAccess const*>(subExpr)
|
||||||
)
|
)
|
||||||
indexOrMemberAssignment(_op.subExpression(), symbVar->currentValue());
|
indexOrMemberAssignment(*subExpr, symbVar->currentValue());
|
||||||
else
|
// Empty push added a zero value anyway, so no need to delete extra.
|
||||||
|
else if (!isEmptyPush(*subExpr))
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1968,7 +2002,7 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu
|
|||||||
|
|
||||||
void SMTEncoder::createStateVariables(ContractDefinition const& _contract)
|
void SMTEncoder::createStateVariables(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
for (auto var: _contract.stateVariablesIncludingInherited())
|
for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
|
||||||
createVariable(*var);
|
createVariable(*var);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2252,7 +2286,7 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
|
|||||||
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
|
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
|
||||||
{
|
{
|
||||||
solAssert(_contract, "");
|
solAssert(_contract, "");
|
||||||
for (auto var: _contract->stateVariablesIncludingInherited())
|
for (auto var: stateVariablesIncludingInheritedAndPrivate(*_contract))
|
||||||
m_context.variable(*var)->resetIndex();
|
m_context.variable(*var)->resetIndex();
|
||||||
if (_function)
|
if (_function)
|
||||||
{
|
{
|
||||||
@ -2309,7 +2343,7 @@ set<VariableDeclaration const*> SMTEncoder::touchedVariables(ASTNode const& _nod
|
|||||||
return m_variableUsage.touchedVariables(_node, callStack);
|
return m_variableUsage.touchedVariables(_node, callStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr)
|
Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr) const
|
||||||
{
|
{
|
||||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr))
|
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expr))
|
||||||
return identifier->annotation().referencedDeclaration;
|
return identifier->annotation().referencedDeclaration;
|
||||||
@ -2318,7 +2352,7 @@ Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr)
|
VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr) const
|
||||||
{
|
{
|
||||||
// We do not use `expressionToDeclaration` here because we are not interested in
|
// We do not use `expressionToDeclaration` here because we are not interested in
|
||||||
// struct.field, for example.
|
// struct.field, for example.
|
||||||
@ -2331,6 +2365,20 @@ VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _e
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
auto const* funCall = dynamic_cast<FunctionCall const*>(&_expr);
|
||||||
|
funCall && funCall->arguments().empty()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto const& funType = dynamic_cast<FunctionType const&>(*funCall->expression().annotation().type);
|
||||||
|
if (funType.kind() == FunctionType::Kind::ArrayPush)
|
||||||
|
return &dynamic_cast<MemberAccess const&>(funCall->expression());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
string SMTEncoder::extraComment()
|
string SMTEncoder::extraComment()
|
||||||
{
|
{
|
||||||
string extra;
|
string extra;
|
||||||
|
@ -90,6 +90,7 @@ protected:
|
|||||||
bool visit(WhileStatement const&) override { return false; }
|
bool visit(WhileStatement const&) override { return false; }
|
||||||
bool visit(ForStatement const&) override { return false; }
|
bool visit(ForStatement const&) override { return false; }
|
||||||
void endVisit(VariableDeclarationStatement const& _node) override;
|
void endVisit(VariableDeclarationStatement const& _node) override;
|
||||||
|
bool visit(Assignment const& _node) override;
|
||||||
void endVisit(Assignment const& _node) override;
|
void endVisit(Assignment const& _node) override;
|
||||||
void endVisit(TupleExpression const& _node) override;
|
void endVisit(TupleExpression const& _node) override;
|
||||||
bool visit(UnaryOperation const& _node) override;
|
bool visit(UnaryOperation const& _node) override;
|
||||||
@ -282,10 +283,14 @@ protected:
|
|||||||
|
|
||||||
/// @returns the declaration referenced by _expr, if any,
|
/// @returns the declaration referenced by _expr, if any,
|
||||||
/// and nullptr otherwise.
|
/// and nullptr otherwise.
|
||||||
Declaration const* expressionToDeclaration(Expression const& _expr);
|
Declaration const* expressionToDeclaration(Expression const& _expr) const;
|
||||||
|
|
||||||
/// @returns the VariableDeclaration referenced by an Expression or nullptr.
|
/// @returns the VariableDeclaration referenced by an Expression or nullptr.
|
||||||
VariableDeclaration const* identifierToVariable(Expression const& _expr);
|
VariableDeclaration const* identifierToVariable(Expression const& _expr) const;
|
||||||
|
|
||||||
|
/// @returns the MemberAccess <expression>.push if _expr is an empty array push call,
|
||||||
|
/// otherwise nullptr.
|
||||||
|
MemberAccess const* isEmptyPush(Expression const& _expr) const;
|
||||||
|
|
||||||
/// Creates symbolic expressions for the returned values
|
/// Creates symbolic expressions for the returned values
|
||||||
/// and set them as the components of the symbolic tuple.
|
/// and set them as the components of the symbolic tuple.
|
||||||
|
@ -332,12 +332,12 @@ smtutil::Expression SymbolicArrayVariable::valueAtIndex(unsigned _index) const
|
|||||||
return m_pair.valueAtIndex(_index);
|
return m_pair.valueAtIndex(_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
smtutil::Expression SymbolicArrayVariable::elements()
|
smtutil::Expression SymbolicArrayVariable::elements() const
|
||||||
{
|
{
|
||||||
return m_pair.component(0);
|
return m_pair.component(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
smtutil::Expression SymbolicArrayVariable::length()
|
smtutil::Expression SymbolicArrayVariable::length() const
|
||||||
{
|
{
|
||||||
return m_pair.component(1);
|
return m_pair.component(1);
|
||||||
}
|
}
|
||||||
|
@ -260,8 +260,8 @@ public:
|
|||||||
smtutil::Expression resetIndex() override { SymbolicVariable::resetIndex(); return m_pair.resetIndex(); }
|
smtutil::Expression resetIndex() override { SymbolicVariable::resetIndex(); return m_pair.resetIndex(); }
|
||||||
smtutil::Expression setIndex(unsigned _index) override { SymbolicVariable::setIndex(_index); return m_pair.setIndex(_index); }
|
smtutil::Expression setIndex(unsigned _index) override { SymbolicVariable::setIndex(_index); return m_pair.setIndex(_index); }
|
||||||
smtutil::Expression increaseIndex() override { SymbolicVariable::increaseIndex(); return m_pair.increaseIndex(); }
|
smtutil::Expression increaseIndex() override { SymbolicVariable::increaseIndex(); return m_pair.increaseIndex(); }
|
||||||
smtutil::Expression elements();
|
smtutil::Expression elements() const;
|
||||||
smtutil::Expression length();
|
smtutil::Expression length() const;
|
||||||
|
|
||||||
smtutil::SortPointer tupleSort() { return m_pair.sort(); }
|
smtutil::SortPointer tupleSort() { return m_pair.sort(); }
|
||||||
|
|
||||||
|
@ -73,28 +73,6 @@ BOOST_AUTO_TEST_CASE(value_types)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(enums)
|
|
||||||
{
|
|
||||||
string sourceCode = R"(
|
|
||||||
contract C {
|
|
||||||
enum E { A, B }
|
|
||||||
function f(E e) public pure returns (uint x) {
|
|
||||||
assembly { x := e }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2;
|
|
||||||
BOTH_ENCODERS(
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
ABI_CHECK(callContractFunction("f(uint8)", 0), encodeArgs(u256(0)));
|
|
||||||
ABI_CHECK(callContractFunction("f(uint8)", 1), encodeArgs(u256(1)));
|
|
||||||
// The old decoder was not as strict about enums
|
|
||||||
ABI_CHECK(callContractFunction("f(uint8)", 2), (newDecoder ? encodeArgs() : encodeArgs(2)));
|
|
||||||
ABI_CHECK(callContractFunction("f(uint8)", u256(-1)), (newDecoder? encodeArgs() : encodeArgs(u256(0xff))));
|
|
||||||
newDecoder = true;
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(cleanup)
|
BOOST_AUTO_TEST_CASE(cleanup)
|
||||||
{
|
{
|
||||||
string sourceCode = R"(
|
string sourceCode = R"(
|
||||||
@ -184,113 +162,6 @@ BOOST_AUTO_TEST_CASE(fixed_arrays)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(dynamic_arrays)
|
|
||||||
{
|
|
||||||
string sourceCode = R"(
|
|
||||||
contract C {
|
|
||||||
function f(uint a, uint16[] memory b, uint c)
|
|
||||||
public pure returns (uint, uint, uint) {
|
|
||||||
return (b.length, b[a], c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
BOTH_ENCODERS(
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
bytes args = encodeArgs(
|
|
||||||
6, 0x60, 9,
|
|
||||||
7,
|
|
||||||
11, 12, 13, 14, 15, 16, 17
|
|
||||||
);
|
|
||||||
ABI_CHECK(
|
|
||||||
callContractFunction("f(uint256,uint16[],uint256)", args),
|
|
||||||
encodeArgs(u256(7), u256(17), u256(9))
|
|
||||||
);
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(dynamic_nested_arrays)
|
|
||||||
{
|
|
||||||
string sourceCode = R"(
|
|
||||||
contract C {
|
|
||||||
function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d)
|
|
||||||
public pure returns (uint, uint, uint, uint, uint, uint, uint) {
|
|
||||||
return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d);
|
|
||||||
}
|
|
||||||
function test() public view returns (uint, uint, uint, uint, uint, uint, uint) {
|
|
||||||
uint16[][] memory b = new uint16[][](3);
|
|
||||||
b[0] = new uint16[](2);
|
|
||||||
b[0][0] = 0x55;
|
|
||||||
b[0][1] = 0x56;
|
|
||||||
b[1] = new uint16[](4);
|
|
||||||
b[1][0] = 0x65;
|
|
||||||
b[1][1] = 0x66;
|
|
||||||
b[1][2] = 0x67;
|
|
||||||
b[1][3] = 0x68;
|
|
||||||
|
|
||||||
uint[2][][3] memory c;
|
|
||||||
c[0] = new uint[2][](1);
|
|
||||||
c[0][0][1] = 0x75;
|
|
||||||
c[1] = new uint[2][](5);
|
|
||||||
c[1][1][1] = 0x85;
|
|
||||||
|
|
||||||
return this.f(0x12, b, c, 0x13);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
NEW_ENCODER(
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
bytes args = encodeArgs(
|
|
||||||
0x12, 4 * 0x20, 17 * 0x20, 0x13,
|
|
||||||
// b
|
|
||||||
3, 3 * 0x20, 6 * 0x20, 11 * 0x20,
|
|
||||||
2, 85, 86,
|
|
||||||
4, 101, 102, 103, 104,
|
|
||||||
0,
|
|
||||||
// c
|
|
||||||
3 * 0x20, 6 * 0x20, 17 * 0x20,
|
|
||||||
1, 0, 117,
|
|
||||||
5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
bytes expectation = encodeArgs(0x12, 3, 4, 0x66, 5, 0x85, 0x13);
|
|
||||||
ABI_CHECK(callContractFunction("test()"), expectation);
|
|
||||||
ABI_CHECK(callContractFunction("f(uint256,uint16[][],uint256[2][][3],uint256)", args), expectation);
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(byte_arrays)
|
|
||||||
{
|
|
||||||
string sourceCode = R"(
|
|
||||||
contract C {
|
|
||||||
function f(uint a, bytes memory b, uint c)
|
|
||||||
public pure returns (uint, uint, byte, uint) {
|
|
||||||
return (a, b.length, b[3], c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function f_external(uint a, bytes calldata b, uint c)
|
|
||||||
external pure returns (uint, uint, byte, uint) {
|
|
||||||
return (a, b.length, b[3], c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
BOTH_ENCODERS(
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
bytes args = encodeArgs(
|
|
||||||
6, 0x60, 9,
|
|
||||||
7, "abcdefg"
|
|
||||||
);
|
|
||||||
ABI_CHECK(
|
|
||||||
callContractFunction("f(uint256,bytes,uint256)", args),
|
|
||||||
encodeArgs(u256(6), u256(7), "d", 9)
|
|
||||||
);
|
|
||||||
ABI_CHECK(
|
|
||||||
callContractFunction("f_external(uint256,bytes,uint256)", args),
|
|
||||||
encodeArgs(u256(6), u256(7), "d", 9)
|
|
||||||
);
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(calldata_arrays_too_large)
|
BOOST_AUTO_TEST_CASE(calldata_arrays_too_large)
|
||||||
{
|
{
|
||||||
string sourceCode = R"(
|
string sourceCode = R"(
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <libsolidity/codegen/Compiler.h>
|
#include <libsolidity/codegen/Compiler.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
@ -61,6 +62,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
|||||||
BOOST_CHECK(!!sourceUnit);
|
BOOST_CHECK(!!sourceUnit);
|
||||||
|
|
||||||
Scoper::assignScopes(*sourceUnit);
|
Scoper::assignScopes(*sourceUnit);
|
||||||
|
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||||
GlobalContext globalContext;
|
GlobalContext globalContext;
|
||||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||||
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
|
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
|
||||||
|
@ -537,39 +537,6 @@ BOOST_AUTO_TEST_CASE(for_loop)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(for_loop_empty)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract test {
|
|
||||||
function f() public returns(uint ret) {
|
|
||||||
ret = 1;
|
|
||||||
for (;;) {
|
|
||||||
ret += 1;
|
|
||||||
if (ret >= 10) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
|
|
||||||
auto for_loop_empty_cpp = []() -> u256
|
|
||||||
{
|
|
||||||
u256 ret = 1;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
ret += 1;
|
|
||||||
if (ret >= 10) break;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
testContractAgainstCpp("f()", for_loop_empty_cpp);
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
|
BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -649,85 +616,6 @@ BOOST_AUTO_TEST_CASE(for_loop_break_continue)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(calling_other_functions)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract collatz {
|
|
||||||
function run(uint x) public returns(uint y) {
|
|
||||||
while ((y = x) > 1) {
|
|
||||||
if (x % 2 == 0) x = evenStep(x);
|
|
||||||
else x = oddStep(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function evenStep(uint x) public returns(uint y) {
|
|
||||||
return x / 2;
|
|
||||||
}
|
|
||||||
function oddStep(uint x) public returns(uint y) {
|
|
||||||
return 3 * x + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
auto evenStep_cpp = [](u256 const& n) -> u256
|
|
||||||
{
|
|
||||||
return n / 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto oddStep_cpp = [](u256 const& n) -> u256
|
|
||||||
{
|
|
||||||
return 3 * n + 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto collatz_cpp = [&evenStep_cpp, &oddStep_cpp](u256 n) -> u256
|
|
||||||
{
|
|
||||||
u256 y;
|
|
||||||
while ((y = n) > 1)
|
|
||||||
{
|
|
||||||
if (n % 2 == 0)
|
|
||||||
n = evenStep_cpp(n);
|
|
||||||
else
|
|
||||||
n = oddStep_cpp(n);
|
|
||||||
}
|
|
||||||
return y;
|
|
||||||
};
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
|
|
||||||
testContractAgainstCpp("run(uint256)", collatz_cpp, u256(0));
|
|
||||||
testContractAgainstCpp("run(uint256)", collatz_cpp, u256(1));
|
|
||||||
testContractAgainstCpp("run(uint256)", collatz_cpp, u256(2));
|
|
||||||
testContractAgainstCpp("run(uint256)", collatz_cpp, u256(8));
|
|
||||||
testContractAgainstCpp("run(uint256)", collatz_cpp, u256(127));
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(many_local_variables)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract test {
|
|
||||||
function run(uint x1, uint x2, uint x3) public returns(uint y) {
|
|
||||||
uint8 a = 0x1; uint8 b = 0x10; uint16 c = 0x100;
|
|
||||||
y = a + b + c + x1 + x2 + x3;
|
|
||||||
y += b + x2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256
|
|
||||||
{
|
|
||||||
u256 a = 0x1;
|
|
||||||
u256 b = 0x10;
|
|
||||||
u256 c = 0x100;
|
|
||||||
u256 y = a + b + c + x1 + x2 + x3;
|
|
||||||
return y + b + x2;
|
|
||||||
};
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
testContractAgainstCpp("run(uint256,uint256,uint256)", f, u256(0x1000), u256(0x10000), u256(0x100000));
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(short_circuiting)
|
BOOST_AUTO_TEST_CASE(short_circuiting)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -826,149 +714,6 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types)
|
|||||||
testContractAgainstCpp("run()", small_unsigned_types_cpp);
|
testContractAgainstCpp("run()", small_unsigned_types_cpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(small_signed_types)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract test {
|
|
||||||
function run() public returns(int256 y) {
|
|
||||||
return -int32(10) * -int64(20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
auto small_signed_types_cpp = []() -> u256
|
|
||||||
{
|
|
||||||
return -int32_t(10) * -int64_t(20);
|
|
||||||
};
|
|
||||||
testContractAgainstCpp("run()", small_signed_types_cpp);
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(compound_assign)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract test {
|
|
||||||
uint value1;
|
|
||||||
uint value2;
|
|
||||||
function f(uint x, uint y) public returns (uint w) {
|
|
||||||
uint value3 = y;
|
|
||||||
value1 += x;
|
|
||||||
value3 *= x;
|
|
||||||
value2 *= value3 + value1;
|
|
||||||
return value2 += 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
|
|
||||||
u256 value1;
|
|
||||||
u256 value2;
|
|
||||||
auto f = [&](u256 const& _x, u256 const& _y) -> u256
|
|
||||||
{
|
|
||||||
u256 value3 = _y;
|
|
||||||
value1 += _x;
|
|
||||||
value3 *= _x;
|
|
||||||
value2 *= value3 + value1;
|
|
||||||
return value2 += 7;
|
|
||||||
};
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(0), u256(6));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(1), u256(3));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(2), u256(25));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(3), u256(69));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(4), u256(84));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(5), u256(2));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(6), u256(51));
|
|
||||||
testContractAgainstCpp("f(uint256,uint256)", f, u256(7), u256(48));
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(mapping_state)
|
|
||||||
{
|
|
||||||
char const* sourceCode = R"(
|
|
||||||
contract Ballot {
|
|
||||||
mapping(address => bool) canVote;
|
|
||||||
mapping(address => uint) voteCount;
|
|
||||||
mapping(address => bool) voted;
|
|
||||||
function getVoteCount(address addr) public returns (uint retVoteCount) {
|
|
||||||
return voteCount[addr];
|
|
||||||
}
|
|
||||||
function grantVoteRight(address addr) public {
|
|
||||||
canVote[addr] = true;
|
|
||||||
}
|
|
||||||
function vote(address voter, address vote) public returns (bool success) {
|
|
||||||
if (!canVote[voter] || voted[voter]) return false;
|
|
||||||
voted[voter] = true;
|
|
||||||
voteCount[vote] = voteCount[vote] + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
class Ballot
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
u256 getVoteCount(u160 _address) { return m_voteCount[_address]; }
|
|
||||||
void grantVoteRight(u160 _address) { m_canVote[_address] = true; }
|
|
||||||
bool vote(u160 _voter, u160 _vote)
|
|
||||||
{
|
|
||||||
if (!m_canVote[_voter] || m_voted[_voter]) return false;
|
|
||||||
m_voted[_voter] = true;
|
|
||||||
m_voteCount[_vote]++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
map<u160, bool> m_canVote;
|
|
||||||
map<u160, u256> m_voteCount;
|
|
||||||
map<u160, bool> m_voted;
|
|
||||||
};
|
|
||||||
ALSO_VIA_YUL(
|
|
||||||
DISABLE_EWASM_TESTRUN()
|
|
||||||
|
|
||||||
compileAndRun(sourceCode);
|
|
||||||
Ballot ballot;
|
|
||||||
|
|
||||||
auto getVoteCount = bind(&Ballot::getVoteCount, &ballot, _1);
|
|
||||||
auto grantVoteRight = bind(&Ballot::grantVoteRight, &ballot, _1);
|
|
||||||
auto vote = bind(&Ballot::vote, &ballot, _1, _2);
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
// voting without vote right should be rejected
|
|
||||||
testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
// grant vote rights
|
|
||||||
testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(0));
|
|
||||||
testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(1));
|
|
||||||
// vote, should increase 2's vote count
|
|
||||||
testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(2));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
// vote again, should be rejected
|
|
||||||
testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
// vote without right to vote
|
|
||||||
testContractAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
// grant vote right and now vote again
|
|
||||||
testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(2));
|
|
||||||
testContractAgainstCpp("vote(address,address)", vote, u160(2), u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1));
|
|
||||||
testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2));
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
|
BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -2764,11 +2509,14 @@ BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
|
|||||||
bytes data;
|
bytes data;
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("---", 7), bytes());
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true));
|
ABI_CHECK(callContractFunction("---", 7), bytes());
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
|
ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true));
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
|
BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
|
||||||
@ -2780,13 +2528,16 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
|
|||||||
bytes data;
|
bytes data;
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true));
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
sendMessage(bytes(), false);
|
ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true));
|
||||||
BOOST_CHECK(m_transactionSuccessful);
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
BOOST_CHECK(m_output.empty());
|
sendMessage(bytes(), false);
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
BOOST_CHECK(m_transactionSuccessful);
|
||||||
|
BOOST_CHECK(m_output.empty());
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
|
BOOST_AUTO_TEST_CASE(copy_removes_bytes_data)
|
||||||
@ -3032,17 +2783,21 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
|
DISABLE_EWASM_TESTRUN()
|
||||||
|
|
||||||
string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9));
|
compileAndRun(sourceCode);
|
||||||
string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3));
|
|
||||||
bytes calldata = encodeArgs(
|
string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9));
|
||||||
12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13,
|
string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3));
|
||||||
u256(innercalldata1.length()), innercalldata1,
|
bytes calldata = encodeArgs(
|
||||||
u256(innercalldata2.length()), innercalldata2);
|
12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13,
|
||||||
ABI_CHECK(
|
u256(innercalldata1.length()), innercalldata1,
|
||||||
callContractFunction("test(uint256,bytes,bytes,uint256)", calldata),
|
u256(innercalldata2.length()), innercalldata2);
|
||||||
encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length()))
|
ABI_CHECK(
|
||||||
|
callContractFunction("test(uint256,bytes,bytes,uint256)", calldata),
|
||||||
|
encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length()))
|
||||||
|
);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3146,12 +2901,16 @@ BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup)
|
|||||||
function clear() public { delete data; }
|
function clear() public { delete data; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
DISABLE_EWASM_TESTRUN()
|
||||||
ABI_CHECK(callContractFunction("fill()"), encodeArgs(8));
|
|
||||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
ABI_CHECK(callContractFunction("clear()"), bytes());
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
ABI_CHECK(callContractFunction("fill()"), encodeArgs(8));
|
||||||
|
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||||
|
ABI_CHECK(callContractFunction("clear()"), bytes());
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn)
|
BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn)
|
||||||
@ -3268,20 +3027,24 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
bytes valueSequence;
|
DISABLE_EWASM_TESTRUN()
|
||||||
for (size_t i = 0; i < 101; ++i)
|
|
||||||
valueSequence += toBigEndian(u256(i));
|
compileAndRun(sourceCode);
|
||||||
ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence);
|
bytes valueSequence;
|
||||||
ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence);
|
for (size_t i = 0; i < 101; ++i)
|
||||||
ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence);
|
valueSequence += toBigEndian(u256(i));
|
||||||
ABI_CHECK(callContractFunction("test4()"),
|
ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence);
|
||||||
encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) +
|
ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence);
|
||||||
encodeArgs(101) + valueSequence +
|
ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence);
|
||||||
encodeArgs(101) + valueSequence +
|
ABI_CHECK(callContractFunction("test4()"),
|
||||||
encodeArgs(101) + valueSequence +
|
encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) +
|
||||||
encodeArgs(101) + valueSequence +
|
encodeArgs(101) + valueSequence +
|
||||||
encodeArgs(101) + valueSequence
|
encodeArgs(101) + valueSequence +
|
||||||
|
encodeArgs(101) + valueSequence +
|
||||||
|
encodeArgs(101) + valueSequence +
|
||||||
|
encodeArgs(101) + valueSequence
|
||||||
|
);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3307,9 +3070,12 @@ BOOST_AUTO_TEST_CASE(array_pop_uint16_transition)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18));
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18));
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
|
BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
|
||||||
@ -3334,9 +3100,12 @@ BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10));
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10));
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(array_pop_array_transition)
|
BOOST_AUTO_TEST_CASE(array_pop_array_transition)
|
||||||
@ -3382,9 +3151,12 @@ BOOST_AUTO_TEST_CASE(array_pop_storage_empty)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
|
BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
|
||||||
@ -3402,9 +3174,12 @@ BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty)
|
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty)
|
||||||
@ -3427,9 +3202,12 @@ BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(true));
|
DISABLE_EWASM_TESTRUN()
|
||||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs(true));
|
||||||
|
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref)
|
BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref)
|
||||||
@ -3505,15 +3283,19 @@ BOOST_AUTO_TEST_CASE(bytes_index_access)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
compileAndRun(sourceCode);
|
|
||||||
string array{
|
string array{
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||||
30, 31, 32, 33};
|
30, 31, 32, 33};
|
||||||
ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
|
ALSO_VIA_YUL(
|
||||||
ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
|
DISABLE_EWASM_TESTRUN()
|
||||||
ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193));
|
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
|
||||||
|
ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33));
|
||||||
|
ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193));
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
|
BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <libsolidity/parsing/Parser.h>
|
#include <libsolidity/parsing/Parser.h>
|
||||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||||
#include <libsolidity/analysis/Scoper.h>
|
#include <libsolidity/analysis/Scoper.h>
|
||||||
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <libsolidity/analysis/DeclarationTypeChecker.h>
|
#include <libsolidity/analysis/DeclarationTypeChecker.h>
|
||||||
#include <libsolidity/codegen/CompilerContext.h>
|
#include <libsolidity/codegen/CompilerContext.h>
|
||||||
#include <libsolidity/codegen/ExpressionCompiler.h>
|
#include <libsolidity/codegen/ExpressionCompiler.h>
|
||||||
@ -93,18 +94,20 @@ Declaration const& resolveDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bytes compileFirstExpression(
|
bytes compileFirstExpression(
|
||||||
const string& _sourceCode,
|
string const& _sourceCode,
|
||||||
vector<vector<string>> _functions = {},
|
vector<vector<string>> _functions = {},
|
||||||
vector<vector<string>> _localVariables = {}
|
vector<vector<string>> _localVariables = {}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
string sourceCode = "pragma solidity >=0.0; // SPDX-License-Identifier: GPL-3\n" + _sourceCode;
|
||||||
|
|
||||||
ASTPointer<SourceUnit> sourceUnit;
|
ASTPointer<SourceUnit> sourceUnit;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ErrorList errors;
|
ErrorList errors;
|
||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse(
|
sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse(
|
||||||
make_shared<Scanner>(CharStream(_sourceCode, ""))
|
make_shared<Scanner>(CharStream(sourceCode, ""))
|
||||||
);
|
);
|
||||||
if (!sourceUnit)
|
if (!sourceUnit)
|
||||||
return bytes();
|
return bytes();
|
||||||
@ -119,6 +122,7 @@ bytes compileFirstExpression(
|
|||||||
ErrorReporter errorReporter(errors);
|
ErrorReporter errorReporter(errors);
|
||||||
GlobalContext globalContext;
|
GlobalContext globalContext;
|
||||||
Scoper::assignScopes(*sourceUnit);
|
Scoper::assignScopes(*sourceUnit);
|
||||||
|
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||||
resolver.registerDeclarations(*sourceUnit);
|
resolver.registerDeclarations(*sourceUnit);
|
||||||
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
|
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
|
||||||
@ -187,7 +191,7 @@ BOOST_AUTO_TEST_CASE(literal_false)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { bool x = false; }
|
function f() public { bool x = false; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -200,7 +204,7 @@ BOOST_AUTO_TEST_CASE(int_literal)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { uint x = 0x12345678901234567890; }
|
function f() public { uint x = 0x12345678901234567890; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -229,7 +233,7 @@ BOOST_AUTO_TEST_CASE(int_with_gwei_ether_subdenomination)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function test () {
|
function f() public {
|
||||||
uint x = 1 gwei;
|
uint x = 1 gwei;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,7 +263,7 @@ BOOST_AUTO_TEST_CASE(comparison)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { bool x = (0x10aa < 0x11aa) != true; }
|
function f() public { bool x = (0x10aa < 0x11aa) != true; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -291,7 +295,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { bool x = true != (4 <= 8 + 10 || 9 != 2); }
|
function f() public { bool x = true != (4 <= 8 + 10 || 9 != 2); }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -322,7 +326,7 @@ BOOST_AUTO_TEST_CASE(arithmetic)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f(uint y) { unchecked { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } }
|
function f(uint y) public { unchecked { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
||||||
@ -418,7 +422,7 @@ BOOST_AUTO_TEST_CASE(unary_operators)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f(int y) { unchecked { !(~- y == 2); } }
|
function f(int y) public { unchecked { !(~- y == 2); } }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
||||||
@ -508,7 +512,7 @@ BOOST_AUTO_TEST_CASE(assignment)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f(uint a, uint b) { unchecked { (a += b) * 2; } }
|
function f(uint a, uint b) public { unchecked { (a += b) * 2; } }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
|
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
|
||||||
@ -546,7 +550,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { int8 x = -0x80; }
|
function f() public { int8 x = -0x80; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -559,7 +563,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { int64 x = ~0xabc; }
|
function f() public { int64 x = ~0xabc; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -574,7 +578,7 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals)
|
|||||||
// have been applied
|
// have been applied
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }
|
function f() public { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
bytes code = compileFirstExpression(sourceCode);
|
bytes code = compileFirstExpression(sourceCode);
|
||||||
@ -587,7 +591,7 @@ BOOST_AUTO_TEST_CASE(blockhash)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() {
|
function f() public {
|
||||||
blockhash(3);
|
blockhash(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -619,7 +623,7 @@ BOOST_AUTO_TEST_CASE(selfbalance)
|
|||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
function f() returns (uint) {
|
function f() public returns (uint) {
|
||||||
return address(this).balance;
|
return address(this).balance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,5 +20,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb
|
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb
|
||||||
|
16
test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol
Normal file
16
test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint a, bytes memory b, uint c)
|
||||||
|
public pure returns (uint, uint, byte, uint) {
|
||||||
|
return (a, b.length, b[3], c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f_external(uint a, bytes calldata b, uint c)
|
||||||
|
external pure returns (uint, uint, byte, uint) {
|
||||||
|
return (a, b.length, b[3], c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
||||||
|
// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint a, uint16[] memory b, uint c)
|
||||||
|
public pure returns (uint, uint, uint) {
|
||||||
|
return (b.length, b[a], c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9
|
13
test/libsolidity/semanticTests/abiEncoderV1/enums.sol
Normal file
13
test/libsolidity/semanticTests/abiEncoderV1/enums.sol
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
contract C {
|
||||||
|
enum E { A, B }
|
||||||
|
function f(E e) public pure returns (uint x) {
|
||||||
|
assembly { x := e }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// ABIEncoderV1Only: true
|
||||||
|
// ----
|
||||||
|
// f(uint8): 0 -> 0
|
||||||
|
// f(uint8): 1 -> 1
|
||||||
|
// f(uint8): 2 -> 2
|
||||||
|
// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xff
|
18
test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol
Normal file
18
test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint a, bytes memory b, uint c)
|
||||||
|
public pure returns (uint, uint, byte, uint) {
|
||||||
|
return (a, b.length, b[3], c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f_external(uint a, bytes calldata b, uint c)
|
||||||
|
external pure returns (uint, uint, byte, uint) {
|
||||||
|
return (a, b.length, b[3], c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
||||||
|
// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
@ -41,6 +41,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ====
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// EVMVersion: >homestead
|
// EVMVersion: >homestead
|
||||||
// ----
|
// ----
|
||||||
// g() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000"
|
// g() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000"
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint a, uint16[] memory b, uint c)
|
||||||
|
public pure returns (uint, uint, uint) {
|
||||||
|
return (b.length, b[a], c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9
|
@ -0,0 +1,32 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d)
|
||||||
|
public pure returns (uint, uint, uint, uint, uint, uint, uint) {
|
||||||
|
return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d);
|
||||||
|
}
|
||||||
|
function test() public view returns (uint, uint, uint, uint, uint, uint, uint) {
|
||||||
|
uint16[][] memory b = new uint16[][](3);
|
||||||
|
b[0] = new uint16[](2);
|
||||||
|
b[0][0] = 0x55;
|
||||||
|
b[0][1] = 0x56;
|
||||||
|
b[1] = new uint16[](4);
|
||||||
|
b[1][0] = 0x65;
|
||||||
|
b[1][1] = 0x66;
|
||||||
|
b[1][2] = 0x67;
|
||||||
|
b[1][3] = 0x68;
|
||||||
|
|
||||||
|
uint[2][][3] memory c;
|
||||||
|
c[0] = new uint[2][](1);
|
||||||
|
c[0][0][1] = 0x75;
|
||||||
|
c[1] = new uint[2][](5);
|
||||||
|
c[1][1][1] = 0x85;
|
||||||
|
|
||||||
|
return this.f(12, b, c, 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> 12, 3, 4, 0x66, 5, 0x85, 13
|
||||||
|
// f(uint256,uint16[][],uint256[2][][3],uint256): 12, 0x80, 0x220, 13, 3, 0x60, 0xC0, 0x160, 2, 85, 86, 4, 101, 102, 103, 104, 0, 0x60, 0xC0, 0x220, 1, 0, 117, 5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, 0 -> 12, 3, 4, 0x66, 5, 0x85, 13
|
15
test/libsolidity/semanticTests/abiEncoderV2/enums.sol
Normal file
15
test/libsolidity/semanticTests/abiEncoderV2/enums.sol
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
enum E { A, B }
|
||||||
|
function f(E e) public pure returns (uint x) {
|
||||||
|
assembly { x := e }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint8): 0 -> 0
|
||||||
|
// f(uint8): 1 -> 1
|
||||||
|
// f(uint8): 2 -> FAILURE
|
||||||
|
// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
@ -7,5 +7,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg"
|
// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg"
|
||||||
|
@ -14,5 +14,7 @@ contract c {
|
|||||||
uint8(data[97]) == 97;
|
uint8(data[97]) == 97;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test1() -> true
|
// test1() -> true
|
||||||
|
@ -10,6 +10,8 @@ contract c {
|
|||||||
|
|
||||||
bytes data;
|
bytes data;
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// getLength() -> 0
|
// getLength() -> 0
|
||||||
// set(): 1, 2 -> true
|
// set(): 1, 2 -> true
|
||||||
|
@ -6,5 +6,7 @@ contract C {
|
|||||||
return s[0];
|
return s[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> "a"
|
// f() -> "a"
|
||||||
|
@ -5,5 +5,7 @@ contract C {
|
|||||||
return s[0];
|
return s[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(bytes): 0x20, 0x08, "abcdefgh" -> "a"
|
// f(bytes): 0x20, 0x08, "abcdefgh" -> "a"
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
struct S {
|
||||||
|
uint16 x;
|
||||||
|
bytes a;
|
||||||
|
uint16 y;
|
||||||
|
bytes b;
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
uint padding;
|
||||||
|
S data;
|
||||||
|
|
||||||
|
function f() public returns (bytes memory, bytes memory) {
|
||||||
|
S memory x;
|
||||||
|
x.x = 7;
|
||||||
|
x.b = "1234567890123456789012345678901 1234567890123456789012345678901 123456789";
|
||||||
|
x.a = "abcdef";
|
||||||
|
x.y = 9;
|
||||||
|
data = x;
|
||||||
|
return (data.a, data.b);
|
||||||
|
}
|
||||||
|
function g() public returns (bytes memory, bytes memory) {
|
||||||
|
S memory x;
|
||||||
|
x.x = 7;
|
||||||
|
x.b = "12345678923456789";
|
||||||
|
x.a = "1234567890123456789012345678901 1234567890123456789012345678901 123456789";
|
||||||
|
x.y = 9;
|
||||||
|
data = x;
|
||||||
|
return (data.a, data.b);
|
||||||
|
}
|
||||||
|
function h() public returns (bytes memory, bytes memory) {
|
||||||
|
S memory x;
|
||||||
|
data = x;
|
||||||
|
return (data.a, data.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000
|
||||||
|
// g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000
|
||||||
|
// h() -> 0x40, 0x60, 0x00, 0x00
|
||||||
|
// storage: empty
|
@ -0,0 +1,50 @@
|
|||||||
|
function dataslot() pure returns (bytes32) {
|
||||||
|
return keccak256(abi.encode(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDataSlot(uint offset) view returns (bytes32 r) {
|
||||||
|
bytes32 s = dataslot();
|
||||||
|
assembly { r := sload(add(s, offset)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDataSlot() view returns (bytes32) {
|
||||||
|
return readDataSlot(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readHead() view returns (bytes32 r) {
|
||||||
|
assembly { r := sload(1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
uint padding;
|
||||||
|
bytes data;
|
||||||
|
|
||||||
|
function f() public returns (uint) {
|
||||||
|
bytes32 zero;
|
||||||
|
if (!(readDataSlot() == zero)) return 1;
|
||||||
|
data = "abc";
|
||||||
|
if (!(readDataSlot() == zero)) return 2;
|
||||||
|
data = "1234567890123456789012345678901234567890123456789012345678901234567890";
|
||||||
|
if (!(readDataSlot() != zero)) return 3;
|
||||||
|
if (!(readDataSlot(1) != zero)) return 4;
|
||||||
|
if (!(readDataSlot(2) != zero)) return 5;
|
||||||
|
if (!(readDataSlot(3) == zero)) return 6;
|
||||||
|
if (!(readDataSlot(4) == zero)) return 7;
|
||||||
|
data = "abc";
|
||||||
|
if (!(readDataSlot() == zero)) return 8;
|
||||||
|
if (!(readDataSlot(1) == zero)) return 9;
|
||||||
|
if (!(readDataSlot(2) == zero)) return 10;
|
||||||
|
if (!(readDataSlot(3) == zero)) return 11;
|
||||||
|
data = "1234567890123456789012345678901234567890123456789012345678901234567890";
|
||||||
|
data = "123456789012345678901234567890123456";
|
||||||
|
if (!(readDataSlot() != zero)) return 12;
|
||||||
|
if (!(readDataSlot(1) != zero)) return 13;
|
||||||
|
if (!(readDataSlot(2) == zero)) return 14;
|
||||||
|
if (!(readDataSlot(3) == zero)) return 15;
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0xff
|
@ -17,6 +17,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// one() -> 3
|
// one() -> 3
|
||||||
// two() -> FAILURE, hex"4e487b71", 0x51
|
// two() -> FAILURE, hex"4e487b71", 0x51
|
||||||
|
@ -5,6 +5,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256): 0 -> 0x20, 0x4, "This"
|
// f(uint256): 0 -> 0x20, 0x4, "This"
|
||||||
// f(uint256): 1 -> 0x20, 0x2, "is"
|
// f(uint256): 1 -> 0x20, 0x2, "is"
|
||||||
|
@ -13,5 +13,7 @@ contract C {
|
|||||||
return this.h(a);
|
return this.h(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// g() -> 0x0700000000000000000000000000000000000000000000000000000000000000
|
// g() -> 0x0700000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -16,6 +16,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// use(uint256): 3 -> 6
|
// use(uint256): 3 -> 6
|
||||||
// result_in_constructor() -> 4
|
// result_in_constructor() -> 4
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
contract C {
|
||||||
|
uint16 public result_in_constructor;
|
||||||
|
function(uint16) returns (uint16) internal x;
|
||||||
|
uint16 public other = 0x1fff;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
x = doubleInv;
|
||||||
|
result_in_constructor = use(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doubleInv(uint16 _arg) public returns (uint16 _ret) {
|
||||||
|
_ret = ~(_arg * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function use(uint16 _arg) public returns (uint16) {
|
||||||
|
return x(_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// use(uint16): 3 -> 0xfff9
|
||||||
|
// result_in_constructor() -> 0xfffb
|
||||||
|
// other() -> 0x1fff
|
@ -14,5 +14,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// t() -> 7
|
// t() -> 7
|
||||||
|
@ -17,5 +17,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// t() -> 7
|
// t() -> 7
|
||||||
|
13
test/libsolidity/semanticTests/empty_for_loop.sol
Normal file
13
test/libsolidity/semanticTests/empty_for_loop.sol
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
contract test {
|
||||||
|
function f() public returns(uint ret) {
|
||||||
|
ret = 1;
|
||||||
|
for (;;) {
|
||||||
|
ret += 1;
|
||||||
|
if (ret >= 10) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 10
|
@ -0,0 +1,22 @@
|
|||||||
|
contract collatz {
|
||||||
|
function run(uint x) public returns(uint y) {
|
||||||
|
while ((y = x) > 1) {
|
||||||
|
if (x % 2 == 0) x = evenStep(x);
|
||||||
|
else x = oddStep(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function evenStep(uint x) public returns(uint y) {
|
||||||
|
return x / 2;
|
||||||
|
}
|
||||||
|
function oddStep(uint x) public returns(uint y) {
|
||||||
|
return 3 * x + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// run(uint256): 0 -> 0
|
||||||
|
// run(uint256): 1 -> 1
|
||||||
|
// run(uint256): 2 -> 1
|
||||||
|
// run(uint256): 8 -> 1
|
||||||
|
// run(uint256): 127 -> 1
|
@ -16,5 +16,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// t() -> FAILURE, hex"4e487b71", 0x51
|
// t() -> FAILURE, hex"4e487b71", 0x51
|
||||||
|
@ -20,6 +20,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// set() -> 7
|
// set() -> 7
|
||||||
// ca() -> 7
|
// ca() -> 7
|
||||||
|
@ -6,5 +6,7 @@ contract C {
|
|||||||
return [this.f, this.g][0]{value: 1}();
|
return [this.f, this.g][0]{value: 1}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// h(), 1 ether -> 1
|
// h(), 1 ether -> 1
|
||||||
|
@ -25,6 +25,8 @@ contract Flow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// success() -> false
|
// success() -> false
|
||||||
// f() -> 7
|
// f() -> 7
|
||||||
|
@ -11,5 +11,7 @@ contract C {
|
|||||||
return x + 1;
|
return x + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256): 7 -> 8
|
// f(uint256): 7 -> 8
|
||||||
|
@ -14,5 +14,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> true
|
// test() -> true
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
struct S {
|
||||||
|
uint16 a;
|
||||||
|
function() returns (uint) x;
|
||||||
|
uint16 b;
|
||||||
|
}
|
||||||
|
contract Flow {
|
||||||
|
S[2] t;
|
||||||
|
|
||||||
|
function X() internal pure returns (uint) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Y() internal pure returns (uint) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
t[0].a = 0xff07;
|
||||||
|
t[0].b = 0xff07;
|
||||||
|
t[1].x = Y;
|
||||||
|
t[1].a = 0xff07;
|
||||||
|
t[1].b = 0xff07;
|
||||||
|
t[0].x = X;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() public returns (uint, uint) {
|
||||||
|
return (t[0].x(), t[1].x());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 1, 2
|
@ -7,5 +7,7 @@ contract Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> FAILURE, hex"4e487b71", 0x51
|
// f() -> FAILURE, hex"4e487b71", 0x51
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
contract test {
|
||||||
|
function run(uint x1, uint x2, uint x3) public returns(uint y) {
|
||||||
|
uint8 a = 0x1; uint8 b = 0x10; uint16 c = 0x100;
|
||||||
|
y = a + b + c + x1 + x2 + x3;
|
||||||
|
y += b + x2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// run(uint256,uint256,uint256): 0x1000, 0x10000, 0x100000 -> 0x121121
|
@ -0,0 +1,9 @@
|
|||||||
|
contract test {
|
||||||
|
function run() public returns(int256 y) {
|
||||||
|
return -int32(10) * -int64(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// run() -> 200
|
@ -29,6 +29,8 @@ contract Homer is ERC165, Simpson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
@ -40,6 +40,8 @@ contract Lisa is ERC165MappingImplementation, Simpson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||||
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||||
|
@ -14,6 +14,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// g() -> 2
|
// g() -> 2
|
||||||
// h() -> FAILURE, hex"4e487b71", 0x51
|
// h() -> FAILURE, hex"4e487b71", 0x51
|
||||||
|
22
test/libsolidity/semanticTests/operators/compound_assign.sol
Normal file
22
test/libsolidity/semanticTests/operators/compound_assign.sol
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
contract test {
|
||||||
|
uint value1;
|
||||||
|
uint value2;
|
||||||
|
function f(uint x, uint y) public returns (uint w) {
|
||||||
|
uint value3 = y;
|
||||||
|
value1 += x;
|
||||||
|
value3 *= x;
|
||||||
|
value2 *= value3 + value1;
|
||||||
|
return value2 += 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint256): 0, 6 -> 7
|
||||||
|
// f(uint256,uint256): 1, 3 -> 0x23
|
||||||
|
// f(uint256,uint256): 2, 25 -> 0x0746
|
||||||
|
// f(uint256,uint256): 3, 69 -> 396613
|
||||||
|
// f(uint256,uint256): 4, 84 -> 137228105
|
||||||
|
// f(uint256,uint256): 5, 2 -> 0xcc7c5e28
|
||||||
|
// f(uint256,uint256): 6, 51 -> 1121839760671
|
||||||
|
// f(uint256,uint256): 7, 48 -> 408349672884251
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user