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
206 files changed
+1420
-862
No files matched your search
@@ -25,6 +25,9 @@ AST Changes:
|
||||
|
||||
### 0.7.5 (unreleased)
|
||||
|
||||
Language Features:
|
||||
* Ability to select the abi coder using ``pragma abicoder v1`` and ``pragma abicoder v2``.
|
||||
|
||||
Compiler Features:
|
||||
* SMTChecker: Add division by zero checks in the CHC engine.
|
||||
* 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 when using tuples of rational literals inside the conditional operator.
|
||||
* 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.
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 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.
|
||||
|
||||
@@ -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
|
||||
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,
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
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
|
||||
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
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
+2
-2
@@ -589,8 +589,8 @@ As an example, the code
|
||||
::
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.4.19 <0.9.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma solidity >0.7.4 <0.9.0;
|
||||
pragma abicoder v2;
|
||||
|
||||
contract Test {
|
||||
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
|
||||
multi-dimensional array as an input
|
||||
parameter. This functionality is possible if you enable the new
|
||||
``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file.
|
||||
parameter. This functionality is possible if you enable the ABI coder v2
|
||||
by adding ``pragma abicoder v2;`` to your source file.
|
||||
|
||||
An :ref:`internal function<external-function-calls>` can accept a
|
||||
multi-dimensional array without enabling the feature.
|
||||
@@ -128,8 +128,8 @@ you must provide return values together with the return statement.
|
||||
.. note::
|
||||
You cannot return some types from non-internal functions, notably
|
||||
multi-dimensional dynamic arrays and structs. If you enable the
|
||||
new ``ABIEncoderV2`` feature by adding ``pragma experimental
|
||||
ABIEncoderV2;`` to your source file then more types are available, but
|
||||
ABI coder v2 by adding ``pragma abicoder v2;``
|
||||
to your source file then more types are available, but
|
||||
``mapping`` types are still limited to inside a single contract and you
|
||||
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/>`_.
|
||||
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
|
||||
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
|
||||
version then you will need to agree to the license before you can do
|
||||
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
|
||||
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
|
||||
|
||||
.. _experimental_pragma:
|
||||
@@ -103,28 +138,9 @@ The following experimental pragmas are currently supported:
|
||||
ABIEncoderV2
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The new ABI encoder 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 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.
|
||||
Because the ABI coder v2 is not considered experimental anymore,
|
||||
it can be selected via ``pragma abicoder v2`` (please see above)
|
||||
since Solidity 0.7.4.
|
||||
|
||||
.. _smt_checker:
|
||||
|
||||
|
||||
@@ -629,26 +629,32 @@ types.
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >=0.5.0;
|
||||
pragma experimental ABIEncoderV2;
|
||||
pragma experimental SMTChecker;
|
||||
// This will report a warning
|
||||
|
||||
contract Aliasing
|
||||
{
|
||||
uint[] array;
|
||||
uint[] array1;
|
||||
uint[][] array2;
|
||||
function f(
|
||||
uint[] memory a,
|
||||
uint[] memory b,
|
||||
uint[][] memory c,
|
||||
uint[] storage d
|
||||
) internal view {
|
||||
require(array[0] == 42);
|
||||
require(a[0] == 2);
|
||||
require(c[0][0] == 2);
|
||||
require(d[0] == 2);
|
||||
) internal {
|
||||
array1[0] = 42;
|
||||
a[0] = 2;
|
||||
c[0][0] = 2;
|
||||
b[0] = 1;
|
||||
// Erasing knowledge about memory references should not
|
||||
// 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.
|
||||
assert(a[0] == 2);
|
||||
// Fails because `c[i] == b` is possible.
|
||||
@@ -656,6 +662,14 @@ types.
|
||||
assert(d[0] == 2);
|
||||
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
|
||||
|
||||
@@ -290,7 +290,7 @@ Array Members
|
||||
|
||||
.. note::
|
||||
To use arrays of arrays in external (instead of public) functions, you need to
|
||||
activate ABIEncoderV2.
|
||||
activate ABI coder v2.
|
||||
|
||||
.. note::
|
||||
In EVM versions before Byzantium, it was not possible to access
|
||||
|
||||
@@ -231,7 +231,7 @@ Input Description
|
||||
"cse": false,
|
||||
// Optimize representation of literal numbers and strings in code.
|
||||
"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.
|
||||
// It is activated together with the global optimizer setting
|
||||
// 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.methodIdentifiers - The list of function hashes
|
||||
// evm.gasEstimates - Function gas estimates
|
||||
// ewasm.wast - eWASM S-expressions format (not supported at the moment)
|
||||
// ewasm.wasm - eWASM binary format (not supported at the moment)
|
||||
// ewasm.wast - Ewasm in WebAssembly S-expressions format
|
||||
// ewasm.wasm - Ewasm in WebAssembly binary format
|
||||
//
|
||||
// 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.
|
||||
@@ -486,7 +486,7 @@ Output Description
|
||||
}
|
||||
}
|
||||
},
|
||||
// eWASM related outputs
|
||||
// Ewasm related outputs
|
||||
"ewasm": {
|
||||
// S-expressions format
|
||||
"wast": "",
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ Yul
|
||||
Yul (previously also called JULIA or IULIA) is an intermediate language that can be
|
||||
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
|
||||
platforms. It can already be used in stand-alone mode and
|
||||
for "inline assembly" inside Solidity
|
||||
@@ -1028,7 +1028,7 @@ An example Yul Object is shown below:
|
||||
// executing code is the constructor code)
|
||||
size := datasize("runtime")
|
||||
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
|
||||
datacopy(offset, dataoffset("runtime"), size)
|
||||
return(offset, size)
|
||||
|
||||
@@ -450,7 +450,7 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
|
||||
|
||||
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
||||
{
|
||||
if (_contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
|
||||
if (*_contract.sourceUnit().annotation().useABICoderV2)
|
||||
return;
|
||||
|
||||
if (_contract.isLibrary())
|
||||
@@ -469,7 +469,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
||||
{
|
||||
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;
|
||||
|
||||
auto const& currentLoc = func.second->declaration().location();
|
||||
@@ -489,9 +489,9 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
||||
errors,
|
||||
std::string("Contract \"") +
|
||||
_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. " +
|
||||
"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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -113,9 +115,45 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
m_sourceUnit->annotation().experimentalFeatures.insert(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.");
|
||||
|
||||
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")
|
||||
{
|
||||
vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
|
||||
@@ -135,6 +173,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
}
|
||||
else
|
||||
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace solidity::frontend
|
||||
* - issues deprecation warnings for unary '+'
|
||||
* - issues deprecation warning for throw
|
||||
* - 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
|
||||
{
|
||||
|
||||
@@ -398,13 +398,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
m_errorReporter.typeError(4103_error, _var.location(), message);
|
||||
}
|
||||
else if (
|
||||
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
|
||||
!useABICoderV2() &&
|
||||
!typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction())
|
||||
)
|
||||
{
|
||||
string message =
|
||||
"This type is only supported in ABIEncoderV2. "
|
||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature.";
|
||||
"This type is only supported in ABI coder v2. "
|
||||
"Use \"pragma abicoder v2;\" to enable the feature.";
|
||||
if (_function.isConstructor())
|
||||
message +=
|
||||
" Alternatively, make the contract abstract and supply the "
|
||||
@@ -570,7 +570,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
else if (_variable.visibility() >= Visibility::Public)
|
||||
{
|
||||
FunctionType getter(_variable);
|
||||
if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
||||
if (!useABICoderV2())
|
||||
{
|
||||
vector<string> unsupportedTypes;
|
||||
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
|
||||
@@ -580,9 +580,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
m_errorReporter.typeError(
|
||||
2763_error,
|
||||
_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) +
|
||||
". 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())
|
||||
@@ -695,14 +695,14 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
||||
if (!type(*var)->interfaceType(false))
|
||||
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
||||
if (
|
||||
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
|
||||
!useABICoderV2() &&
|
||||
!typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */)
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
3061_error,
|
||||
var->location(),
|
||||
"This type is only supported in ABIEncoderV2. "
|
||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
||||
"This type is only supported in ABI coder v2. "
|
||||
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||
);
|
||||
}
|
||||
if (_eventDef.isAnonymous() && numIndexed > 4)
|
||||
@@ -1900,7 +1900,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
|
||||
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
|
||||
|
||||
bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
|
||||
bool const abiEncoderV2 = useABICoderV2();
|
||||
|
||||
// Check for named arguments
|
||||
if (!_functionCall.names().empty())
|
||||
@@ -2192,7 +2192,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
_functionType->kind() == FunctionType::Kind::Creation ||
|
||||
_functionType->kind() == FunctionType::Kind::Event;
|
||||
|
||||
if (callRequiresABIEncoding && !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
||||
if (callRequiresABIEncoding && !useABICoderV2())
|
||||
{
|
||||
solAssert(!isVariadic, "");
|
||||
solAssert(parameterTypes.size() == arguments.size(), "");
|
||||
@@ -2208,8 +2208,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
2443_error,
|
||||
paramArgMap[i]->location(),
|
||||
"The type of this parameter, " + parameterTypes[i]->toString(true) + ", "
|
||||
"is only supported in ABIEncoderV2. "
|
||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
||||
"is only supported in ABI coder v2. "
|
||||
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2222,8 +2222,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
2428_error,
|
||||
_functionCall.location(),
|
||||
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", "
|
||||
"is only supported in ABIEncoderV2. "
|
||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
||||
"is only supported in ABI coder v2. "
|
||||
"Use \"pragma abicoder v2;\" to enable the feature."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2340,7 +2340,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(
|
||||
_functionCall,
|
||||
experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
|
||||
useABICoderV2()
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -3414,10 +3414,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
|
||||
m_errorReporter.typeError(errorId, _expression.location(), description);
|
||||
}
|
||||
|
||||
bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const
|
||||
bool TypeChecker::useABICoderV2() const
|
||||
{
|
||||
solAssert(m_currentSourceUnit, "");
|
||||
if (m_currentContract)
|
||||
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.
|
||||
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.
|
||||
/// This is either a contract or a source unit.
|
||||
|
||||
@@ -84,16 +84,6 @@ TypePointer ImportDirective::type() const
|
||||
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
|
||||
{
|
||||
return util::contains(annotation().linearizedBaseContracts, &_base);
|
||||
|
||||
@@ -500,7 +500,6 @@ public:
|
||||
std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(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*> stateVariablesIncludingInherited() const;
|
||||
std::vector<ModifierDefinition const*> functionModifiers() const { return filteredNodes<ModifierDefinition>(m_subNodes); }
|
||||
std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(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;
|
||||
/// Experimental features.
|
||||
std::set<ExperimentalFeature> experimentalFeatures;
|
||||
SetOnce<bool> useABICoderV2;
|
||||
};
|
||||
|
||||
struct ScopableAnnotation
|
||||
|
||||
@@ -1172,7 +1172,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d
|
||||
if (
|
||||
!_arrayType.isByteArray() &&
|
||||
_arrayType.baseType()->storageBytes() < 32 &&
|
||||
m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
|
||||
m_context.useABICoderV2()
|
||||
)
|
||||
{
|
||||
m_context << u256(32);
|
||||
|
||||
@@ -77,11 +77,8 @@ public:
|
||||
|
||||
langutil::EVMVersion const& evmVersion() const { return m_evmVersion; }
|
||||
|
||||
/// Update currently enabled set of experimental features.
|
||||
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
|
||||
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 setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; }
|
||||
bool useABICoderV2() const { return m_useABICoderV2; }
|
||||
|
||||
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
||||
void addImmutable(VariableDeclaration const& _declaration);
|
||||
@@ -365,8 +362,7 @@ private:
|
||||
/// Version of the EVM to compile against.
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
RevertStrings const m_revertStrings;
|
||||
/// Activated experimental features.
|
||||
std::set<ExperimentalFeature> m_experimentalFeatures;
|
||||
bool m_useABICoderV2 = false;
|
||||
/// Other already compiled contracts to be used in contract creation calls.
|
||||
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
|
||||
/// 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)
|
||||
{
|
||||
/// Stack: <source_offset> <length>
|
||||
if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
||||
if (m_context.useABICoderV2())
|
||||
{
|
||||
// Use the new Yul-based decoding function
|
||||
auto stackHeightBefore = m_context.stackHeight();
|
||||
@@ -412,7 +412,7 @@ void CompilerUtils::encodeToMemory(
|
||||
)
|
||||
{
|
||||
// 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;
|
||||
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
||||
for (TypePointer& t: targetTypes)
|
||||
|
||||
@@ -127,7 +127,7 @@ void ContractCompiler::initializeContext(
|
||||
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.setMostDerivedContract(_contract);
|
||||
if (m_runtimeCompiler)
|
||||
@@ -1349,13 +1349,13 @@ void ContractCompiler::appendModifierOrFunctionCode()
|
||||
{
|
||||
m_context.setArithmetic(Arithmetic::Checked);
|
||||
|
||||
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
|
||||
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
|
||||
bool coderV2Outside = m_context.useABICoderV2();
|
||||
m_context.setUseABICoderV2(*codeBlock->sourceUnit().annotation().useABICoderV2);
|
||||
|
||||
m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
|
||||
codeBlock->accept(*this);
|
||||
|
||||
m_context.setExperimentalFeatures(experimentalFeaturesOutside);
|
||||
m_context.setUseABICoderV2(coderV2Outside);
|
||||
|
||||
solAssert(!m_returnTags.empty(), "");
|
||||
m_context << m_returnTags.back().first;
|
||||
|
||||
@@ -1676,7 +1676,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
solAssert(memberType->calldataEncodedSize() > 0, "");
|
||||
solAssert(memberType->storageBytes() <= 32, "");
|
||||
if (memberType->storageBytes() < 32 && m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
||||
if (memberType->storageBytes() < 32 && m_context.useABICoderV2())
|
||||
{
|
||||
m_context << u256(32);
|
||||
CompilerUtils(m_context).abiDecodeV2({memberType}, false);
|
||||
@@ -2522,7 +2522,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
// memory pointer), but kept references to the return data for
|
||||
// (statically-sized) arrays
|
||||
bool needToUpdateFreeMemoryPtr = false;
|
||||
if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
|
||||
if (dynamicReturnSize || m_context.useABICoderV2())
|
||||
needToUpdateFreeMemoryPtr = true;
|
||||
else
|
||||
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(
|
||||
*_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), "");
|
||||
|
||||
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 functionName = "array_convert_length_to_size_" + _type.identifier();
|
||||
@@ -2207,8 +2286,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(slot, <offset>value) {
|
||||
sstore(slot, <update>(sload(slot), <offset><prepare>(value)))
|
||||
function <functionName>(slot, <offset><fromValues>) {
|
||||
let <toValues> := <convert>(<fromValues>)
|
||||
sstore(slot, <update>(sload(slot), <offset><prepare>(<toValues>)))
|
||||
}
|
||||
|
||||
)")
|
||||
@@ -2219,6 +2299,9 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
updateByteSliceFunctionDynamic(_toType.storageBytes())
|
||||
)
|
||||
("offset", _offset.has_value() ? "" : "offset, ")
|
||||
("convert", conversionFunction(_fromType, _toType))
|
||||
("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
|
||||
("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
|
||||
("prepare", prepareStoreFunction(_toType))
|
||||
.render();
|
||||
}
|
||||
@@ -2248,7 +2331,7 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
|
||||
templ("copyArrayToStorage", copyArrayToStorage(
|
||||
templ("copyArrayToStorage", copyArrayToStorageFunction(
|
||||
dynamic_cast<ArrayType const&>(_fromType),
|
||||
dynamic_cast<ArrayType const&>(_toType)
|
||||
));
|
||||
@@ -2478,9 +2561,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
||||
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)
|
||||
templ("cleaned", "value");
|
||||
else if (_type.leftAligned())
|
||||
else if (leftAligned)
|
||||
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
||||
else
|
||||
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)
|
||||
{
|
||||
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();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
@@ -2718,12 +2809,13 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
_to.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(addr, functionId) -> outAddr, outFunctionId {
|
||||
outAddr := addr
|
||||
function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
|
||||
<?external>outAddr := addr</external>
|
||||
outFunctionId := functionId
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("external", fromType.kind() == FunctionType::Kind::External)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -205,7 +205,11 @@ public:
|
||||
|
||||
/// @returns the name of a function that will copy array from calldata or memory to storage
|
||||
/// 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
|
||||
/// 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()) + ")") <<
|
||||
", " <<
|
||||
converted.name() <<
|
||||
converted.commaSeparatedList() <<
|
||||
")\n";
|
||||
}
|
||||
}
|
||||
@@ -2543,47 +2543,50 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
!TokenTraits::isShiftOp(_operator),
|
||||
"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;
|
||||
switch (_operator)
|
||||
{
|
||||
case Token::Add:
|
||||
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
||||
break;
|
||||
case Token::Sub:
|
||||
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
||||
break;
|
||||
case Token::Mul:
|
||||
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
||||
break;
|
||||
case Token::Div:
|
||||
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
||||
break;
|
||||
case Token::Mod:
|
||||
fun = m_utils.intModFunction(*type);
|
||||
break;
|
||||
case Token::BitOr:
|
||||
fun = "or";
|
||||
break;
|
||||
case Token::BitXor:
|
||||
fun = "xor";
|
||||
break;
|
||||
case Token::BitAnd:
|
||||
fun = "and";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case Token::Add:
|
||||
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
||||
break;
|
||||
case Token::Sub:
|
||||
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
||||
break;
|
||||
case Token::Mul:
|
||||
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
||||
break;
|
||||
case Token::Div:
|
||||
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
||||
break;
|
||||
case Token::Mod:
|
||||
fun = m_utils.intModFunction(*type);
|
||||
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(
|
||||
|
||||
+127
-159
@@ -134,7 +134,7 @@ void CHC::endVisit(ContractDefinition const& _contract)
|
||||
|
||||
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);
|
||||
|
||||
SMTEncoder::endVisit(_contract);
|
||||
@@ -231,16 +231,14 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
||||
auto assertionError = errorFlag().currentValue();
|
||||
auto sum = summary(_function);
|
||||
connectBlocks(m_currentBlock, sum);
|
||||
|
||||
auto iface = interface();
|
||||
|
||||
setCurrentBlock(*m_interfaces.at(m_currentContract));
|
||||
|
||||
auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
||||
if (_function.isPublic())
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -512,30 +510,15 @@ void CHC::visitAssert(FunctionCall const& _funCall)
|
||||
|
||||
solAssert(m_currentContract, "");
|
||||
solAssert(m_currentFunction, "");
|
||||
if (m_currentFunction->isConstructor())
|
||||
m_functionAssertions[m_currentContract].insert(&_funCall);
|
||||
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);
|
||||
auto errorCondition = !m_context.expression(*args.front())->currentValue();
|
||||
verificationTargetEncountered(&_funCall, VerificationTarget::Type::Assert, errorCondition);
|
||||
}
|
||||
|
||||
void CHC::visitAddMulMod(FunctionCall const& _funCall)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -634,7 +617,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
|
||||
auto symbArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression()));
|
||||
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(
|
||||
@@ -646,7 +629,7 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
||||
)
|
||||
{
|
||||
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);
|
||||
|
||||
@@ -662,16 +645,16 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
||||
return values;
|
||||
|
||||
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())
|
||||
{
|
||||
addVerificationTarget(_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
||||
addVerificationTarget(_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||
verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue());
|
||||
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||
}
|
||||
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)
|
||||
addVerificationTarget(_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||
verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue());
|
||||
else
|
||||
solAssert(false, "");
|
||||
return values;
|
||||
@@ -679,11 +662,11 @@ pair<smtutil::Expression, smtutil::Expression> CHC::arithmeticOperation(
|
||||
|
||||
void CHC::resetSourceAnalysis()
|
||||
{
|
||||
m_verificationTargets.clear();
|
||||
m_safeTargets.clear();
|
||||
m_unsafeTargets.clear();
|
||||
m_functionAssertions.clear();
|
||||
m_errorIds.clear();
|
||||
m_functionTargetIds.clear();
|
||||
m_verificationTargets.clear();
|
||||
m_queryPlaceholders.clear();
|
||||
m_callGraph.clear();
|
||||
m_summaries.clear();
|
||||
m_interfaces.clear();
|
||||
@@ -713,6 +696,7 @@ void CHC::resetSourceAnalysis()
|
||||
}
|
||||
|
||||
m_context.clear();
|
||||
m_context.resetUniqueId();
|
||||
m_context.setAssertionAccumulation(false);
|
||||
}
|
||||
|
||||
@@ -759,15 +743,15 @@ void CHC::setCurrentBlock(Predicate const& _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) {
|
||||
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])
|
||||
_addChild(called);
|
||||
});
|
||||
return assertions;
|
||||
return verificationTargetsIds;
|
||||
}
|
||||
|
||||
SortPointer CHC::sort(FunctionDefinition const& _function)
|
||||
@@ -1101,186 +1085,171 @@ pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression c
|
||||
return {result, cex};
|
||||
}
|
||||
|
||||
void CHC::addVerificationTarget(
|
||||
ASTNode const* _scope,
|
||||
void CHC::verificationTargetEncountered(
|
||||
ASTNode const* const _errorNode,
|
||||
VerificationTarget::Type _type,
|
||||
smtutil::Expression _from,
|
||||
smtutil::Expression _constraints,
|
||||
smtutil::Expression _errorId
|
||||
smtutil::Expression const& _errorCondition
|
||||
)
|
||||
{
|
||||
solAssert(m_currentContract || m_currentFunction, "");
|
||||
SourceUnit const* source = nullptr;
|
||||
if (m_currentContract)
|
||||
source = sourceUnitContaining(*m_currentContract);
|
||||
else
|
||||
source = sourceUnitContaining(*m_currentFunction);
|
||||
SourceUnit const* source = m_currentContract ? sourceUnitContaining(*m_currentContract) : sourceUnitContaining(*m_currentFunction);
|
||||
solAssert(source, "");
|
||||
if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
|
||||
return;
|
||||
|
||||
m_verificationTargets[_scope].push_back(CHCVerificationTarget{{_type, _from, _constraints}, _errorId});
|
||||
}
|
||||
|
||||
void CHC::addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId)
|
||||
{
|
||||
solAssert(m_currentContract, "");
|
||||
|
||||
if (!m_currentFunction || m_currentFunction->isConstructor())
|
||||
addVerificationTarget(_scope, _type, summary(*m_currentContract), smtutil::Expression(true), _errorId);
|
||||
bool scopeIsFunction = m_currentFunction && !m_currentFunction->isConstructor();
|
||||
auto errorId = newErrorId();
|
||||
solAssert(m_verificationTargets.count(errorId) == 0, "Error ID is not unique!");
|
||||
m_verificationTargets.emplace(errorId, CHCVerificationTarget{{_type, _errorCondition, smtutil::Expression(true)}, errorId, _errorNode});
|
||||
if (scopeIsFunction)
|
||||
m_functionTargetIds[m_currentFunction].push_back(errorId);
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_functionTargetIds[m_currentContract].push_back(errorId);
|
||||
auto previousError = errorFlag().currentValue();
|
||||
errorFlag().increaseIndex();
|
||||
addVerificationTarget(&_scope, _type, errorFlag().currentValue());
|
||||
|
||||
m_context.addAssertion(
|
||||
errorFlag().currentValue() == previousError ||
|
||||
(_target && errorFlag().currentValue() == newErrorId(_scope))
|
||||
// create an error edge to the summary
|
||||
connectBlocks(
|
||||
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)
|
||||
{
|
||||
addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId);
|
||||
m_context.addAssertion(errorFlag().currentValue() == previousError);
|
||||
}
|
||||
|
||||
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 const& target = targets[i];
|
||||
|
||||
if (target.type == VerificationTarget::Type::Assert)
|
||||
checkAssertTarget(scope, target);
|
||||
else
|
||||
auto functionTargets = transactionVerificationTargetsIds(function);
|
||||
for (auto const& placeholder: placeholders)
|
||||
for (unsigned id: functionTargets)
|
||||
{
|
||||
string satMsg;
|
||||
string satMsgUnderflow;
|
||||
string satMsgOverflow;
|
||||
string unknownMsg;
|
||||
ErrorId errorReporterId;
|
||||
ErrorId underflowErrorId = 3944_error;
|
||||
ErrorId overflowErrorId = 4984_error;
|
||||
auto const& target = m_verificationTargets.at(id);
|
||||
verificationTargets.push_back(CHCVerificationTarget{
|
||||
{target.type, placeholder.fromPredicate, placeholder.constraints && placeholder.errorExpression == target.errorId},
|
||||
target.errorId,
|
||||
target.errorNode
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (target.type == VerificationTarget::Type::PopEmptyArray)
|
||||
{
|
||||
solAssert(dynamic_cast<FunctionCall const*>(scope), "");
|
||||
satMsg = "Empty array \"pop\" detected here.";
|
||||
unknownMsg = "Empty array \"pop\" might happen here.";
|
||||
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();
|
||||
set<unsigned> checkedErrorIds;
|
||||
for (auto const& target: verificationTargets)
|
||||
{
|
||||
string errorType;
|
||||
ErrorId errorReporterId;
|
||||
|
||||
satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")";
|
||||
satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")";
|
||||
if (target.type == VerificationTarget::Type::Underflow)
|
||||
{
|
||||
satMsg = satMsgUnderflow + " happens here.";
|
||||
unknownMsg = satMsgUnderflow + " might happen here.";
|
||||
errorReporterId = underflowErrorId;
|
||||
}
|
||||
else if (target.type == VerificationTarget::Type::Overflow)
|
||||
{
|
||||
satMsg = satMsgOverflow + " happens here.";
|
||||
unknownMsg = satMsgOverflow + " might happen here.";
|
||||
errorReporterId = overflowErrorId;
|
||||
}
|
||||
}
|
||||
else if (target.type == VerificationTarget::Type::DivByZero)
|
||||
{
|
||||
satMsg = "Division by zero happens here.";
|
||||
unknownMsg = "Division by zero might happen here.";
|
||||
errorReporterId = 4281_error;
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
if (target.type == VerificationTarget::Type::PopEmptyArray)
|
||||
{
|
||||
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
|
||||
errorType = "Empty array \"pop\"";
|
||||
errorReporterId = 2529_error;
|
||||
}
|
||||
else if (
|
||||
target.type == VerificationTarget::Type::Underflow ||
|
||||
target.type == VerificationTarget::Type::Overflow
|
||||
)
|
||||
{
|
||||
auto const* expr = dynamic_cast<Expression const*>(target.errorNode);
|
||||
solAssert(expr, "");
|
||||
auto const* intType = dynamic_cast<IntegerType const*>(expr->annotation().type);
|
||||
if (!intType)
|
||||
intType = TypeProvider::uint256();
|
||||
|
||||
auto it = m_errorIds.find(scope->id());
|
||||
solAssert(it != m_errorIds.end(), "");
|
||||
solAssert(i < it->second.size(), "");
|
||||
unsigned errorId = it->second[i];
|
||||
|
||||
checkAndReportTarget(scope, target, errorId, errorReporterId, satMsg, unknownMsg);
|
||||
if (target.type == VerificationTarget::Type::Underflow)
|
||||
{
|
||||
errorType = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")";
|
||||
errorReporterId = 3944_error;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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.");
|
||||
checkAndReportTarget(target, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
|
||||
checkedErrorIds.insert(target.errorId);
|
||||
}
|
||||
|
||||
// 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(
|
||||
ASTNode const* _scope,
|
||||
CHCVerificationTarget const& _target,
|
||||
unsigned _errorId,
|
||||
ErrorId _errorReporterId,
|
||||
string _satMsg,
|
||||
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;
|
||||
|
||||
createErrorBlock();
|
||||
connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId));
|
||||
auto const& [result, model] = query(error(), _scope->location());
|
||||
connectBlocks(_target.value, error(), _target.constraints);
|
||||
auto const& location = _target.errorNode->location();
|
||||
auto const& [result, model] = query(error(), location);
|
||||
if (result == CheckResult::UNSATISFIABLE)
|
||||
m_safeTargets[_scope].insert(_target.type);
|
||||
m_safeTargets[_target.errorNode].insert(_target.type);
|
||||
else if (result == CheckResult::SATISFIABLE)
|
||||
{
|
||||
solAssert(!_satMsg.empty(), "");
|
||||
m_unsafeTargets[_scope].insert(_target.type);
|
||||
m_unsafeTargets[_target.errorNode].insert(_target.type);
|
||||
auto cex = generateCounterexample(model, error().name);
|
||||
if (cex)
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
location,
|
||||
"CHC: " + _satMsg,
|
||||
SecondarySourceLocation().append("Counterexample:\n" + *cex, SourceLocation{})
|
||||
);
|
||||
else
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
location,
|
||||
"CHC: " + _satMsg
|
||||
);
|
||||
}
|
||||
else if (!_unknownMsg.empty())
|
||||
m_outerErrorReporter.warning(
|
||||
_errorReporterId,
|
||||
_scope->location(),
|
||||
location,
|
||||
"CHC: " + _unknownMsg
|
||||
);
|
||||
}
|
||||
@@ -1431,14 +1400,13 @@ string CHC::contractSuffix(ContractDefinition const& _contract)
|
||||
return _contract.name() + "_" + to_string(_contract.id());
|
||||
}
|
||||
|
||||
unsigned CHC::newErrorId(frontend::Expression const& _expr)
|
||||
unsigned CHC::newErrorId()
|
||||
{
|
||||
unsigned errorId = m_context.newUniqueId();
|
||||
// We need to make sure the error id is not zero,
|
||||
// because error id zero actually means no error in the CHC encoding.
|
||||
if (errorId == 0)
|
||||
errorId = m_context.newUniqueId();
|
||||
m_errorIds[_expr.id()].push_back(errorId);
|
||||
return errorId;
|
||||
}
|
||||
|
||||
|
||||
+25
-18
@@ -114,7 +114,7 @@ private:
|
||||
void eraseKnowledge();
|
||||
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
|
||||
void setCurrentBlock(Predicate const& _block);
|
||||
std::set<Expression const*, IdCompare> transactionAssertions(ASTNode const* _txRoot);
|
||||
std::set<unsigned> transactionVerificationTargetsIds(ASTNode const* _txRoot);
|
||||
//@}
|
||||
|
||||
/// Sort helpers.
|
||||
@@ -181,19 +181,14 @@ private:
|
||||
/// @returns <false, model> otherwise.
|
||||
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 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 verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTarget::Type _type, smtutil::Expression const& _errorCondition);
|
||||
|
||||
void checkVerificationTargets();
|
||||
// Forward declaration. Definition is below.
|
||||
struct CHCVerificationTarget;
|
||||
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
|
||||
void checkAndReportTarget(
|
||||
ASTNode const* _scope,
|
||||
CHCVerificationTarget const& _target,
|
||||
unsigned _errorId,
|
||||
langutil::ErrorId _errorReporterId,
|
||||
std::string _satMsg,
|
||||
std::string _unknownMsg = ""
|
||||
@@ -234,7 +229,7 @@ private:
|
||||
|
||||
/// @returns a new unique error id associated with _expr and stores
|
||||
/// it into m_errorIds.
|
||||
unsigned newErrorId(Expression const& _expr);
|
||||
unsigned newErrorId();
|
||||
|
||||
smt::SymbolicState& state();
|
||||
smt::SymbolicIntVariable& errorFlag();
|
||||
@@ -275,12 +270,30 @@ private:
|
||||
//@{
|
||||
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,
|
||||
/// e.g., divByZero and Overflow for signed division.
|
||||
std::map<ASTNode const*, std::vector<CHCVerificationTarget>, IdCompare> m_verificationTargets;
|
||||
/// Query placeholder stores information necessary to create the final query edge in the CHC system.
|
||||
/// It is combined with the unique error id (and error type) to create a complete Verification Target.
|
||||
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.
|
||||
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<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.
|
||||
smtutil::Expression m_currentBlock = smtutil::Expression(true);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ EncodingContext::EncodingContext():
|
||||
void EncodingContext::reset()
|
||||
{
|
||||
resetAllVariables();
|
||||
resetUniqueId();
|
||||
m_expressions.clear();
|
||||
m_globalContext.clear();
|
||||
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)
|
||||
{
|
||||
createExpr(_assignment);
|
||||
@@ -355,6 +385,9 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
Token op = _assignment.assignmentOperator();
|
||||
solAssert(TokenTraits::isAssignmentOp(op), "");
|
||||
|
||||
if (isEmptyPush(_assignment.leftHandSide()))
|
||||
return;
|
||||
|
||||
if (!smt::isSupportedType(*_assignment.annotation().type))
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
else if (
|
||||
dynamic_cast<IndexAccess const*>(&_op.subExpression()) ||
|
||||
dynamic_cast<MemberAccess const*>(&_op.subExpression())
|
||||
dynamic_cast<IndexAccess const*>(subExpr) ||
|
||||
dynamic_cast<MemberAccess const*>(subExpr)
|
||||
)
|
||||
{
|
||||
auto innerValue = expr(*subExpr);
|
||||
auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1;
|
||||
defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
|
||||
indexOrMemberAssignment(_op.subExpression(), newValue);
|
||||
indexOrMemberAssignment(*subExpr, newValue);
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
@@ -502,11 +535,12 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
symbVar->increaseIndex();
|
||||
m_context.setZeroValue(*symbVar);
|
||||
if (
|
||||
dynamic_cast<IndexAccess const*>(&_op.subExpression()) ||
|
||||
dynamic_cast<MemberAccess const*>(&_op.subExpression())
|
||||
dynamic_cast<IndexAccess const*>(subExpr) ||
|
||||
dynamic_cast<MemberAccess const*>(subExpr)
|
||||
)
|
||||
indexOrMemberAssignment(_op.subExpression(), symbVar->currentValue());
|
||||
else
|
||||
indexOrMemberAssignment(*subExpr, symbVar->currentValue());
|
||||
// Empty push added a zero value anyway, so no need to delete extra.
|
||||
else if (!isEmptyPush(*subExpr))
|
||||
solAssert(false, "");
|
||||
}
|
||||
break;
|
||||
@@ -1968,7 +2002,7 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu
|
||||
|
||||
void SMTEncoder::createStateVariables(ContractDefinition const& _contract)
|
||||
{
|
||||
for (auto var: _contract.stateVariablesIncludingInherited())
|
||||
for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
|
||||
createVariable(*var);
|
||||
}
|
||||
|
||||
@@ -2252,7 +2286,7 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices)
|
||||
void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function)
|
||||
{
|
||||
solAssert(_contract, "");
|
||||
for (auto var: _contract->stateVariablesIncludingInherited())
|
||||
for (auto var: stateVariablesIncludingInheritedAndPrivate(*_contract))
|
||||
m_context.variable(*var)->resetIndex();
|
||||
if (_function)
|
||||
{
|
||||
@@ -2309,7 +2343,7 @@ set<VariableDeclaration const*> SMTEncoder::touchedVariables(ASTNode const& _nod
|
||||
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))
|
||||
return identifier->annotation().referencedDeclaration;
|
||||
@@ -2318,7 +2352,7 @@ Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr)
|
||||
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
|
||||
// struct.field, for example.
|
||||
@@ -2331,6 +2365,20 @@ VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _e
|
||||
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 extra;
|
||||
|
||||
@@ -90,6 +90,7 @@ protected:
|
||||
bool visit(WhileStatement const&) override { return false; }
|
||||
bool visit(ForStatement const&) override { return false; }
|
||||
void endVisit(VariableDeclarationStatement const& _node) override;
|
||||
bool visit(Assignment const& _node) override;
|
||||
void endVisit(Assignment const& _node) override;
|
||||
void endVisit(TupleExpression const& _node) override;
|
||||
bool visit(UnaryOperation const& _node) override;
|
||||
@@ -282,10 +283,14 @@ protected:
|
||||
|
||||
/// @returns the declaration referenced by _expr, if any,
|
||||
/// 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.
|
||||
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
|
||||
/// 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);
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicArrayVariable::elements()
|
||||
smtutil::Expression SymbolicArrayVariable::elements() const
|
||||
{
|
||||
return m_pair.component(0);
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicArrayVariable::length()
|
||||
smtutil::Expression SymbolicArrayVariable::length() const
|
||||
{
|
||||
return m_pair.component(1);
|
||||
}
|
||||
|
||||
@@ -260,8 +260,8 @@ public:
|
||||
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 increaseIndex() override { SymbolicVariable::increaseIndex(); return m_pair.increaseIndex(); }
|
||||
smtutil::Expression elements();
|
||||
smtutil::Expression length();
|
||||
smtutil::Expression elements() const;
|
||||
smtutil::Expression length() const;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
string sourceCode = R"(
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <libsolidity/codegen/Compiler.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@@ -61,6 +62,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
||||
BOOST_CHECK(!!sourceUnit);
|
||||
|
||||
Scoper::assignScopes(*sourceUnit);
|
||||
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||
GlobalContext globalContext;
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@@ -826,149 +714,6 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types)
|
||||
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)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@@ -2764,11 +2509,14 @@ BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
|
||||
bytes data;
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("---", 7), bytes());
|
||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||
ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("---", 7), bytes());
|
||||
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)
|
||||
@@ -2780,13 +2528,16 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
|
||||
bytes data;
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true));
|
||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||
sendMessage(bytes(), false);
|
||||
BOOST_CHECK(m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output.empty());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true));
|
||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||
sendMessage(bytes(), false);
|
||||
BOOST_CHECK(m_transactionSuccessful);
|
||||
BOOST_CHECK(m_output.empty());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
);
|
||||
}
|
||||
|
||||
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));
|
||||
string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3));
|
||||
bytes calldata = encodeArgs(
|
||||
12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13,
|
||||
u256(innercalldata1.length()), innercalldata1,
|
||||
u256(innercalldata2.length()), innercalldata2);
|
||||
ABI_CHECK(
|
||||
callContractFunction("test(uint256,bytes,bytes,uint256)", calldata),
|
||||
encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length()))
|
||||
compileAndRun(sourceCode);
|
||||
|
||||
string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9));
|
||||
string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3));
|
||||
bytes calldata = encodeArgs(
|
||||
12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13,
|
||||
u256(innercalldata1.length()), innercalldata1,
|
||||
u256(innercalldata2.length()), innercalldata2);
|
||||
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; }
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
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));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
|
||||
compileAndRun(sourceCode);
|
||||
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)
|
||||
@@ -3268,20 +3027,24 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
bytes valueSequence;
|
||||
for (size_t i = 0; i < 101; ++i)
|
||||
valueSequence += toBigEndian(u256(i));
|
||||
ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test4()"),
|
||||
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
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
|
||||
compileAndRun(sourceCode);
|
||||
bytes valueSequence;
|
||||
for (size_t i = 0; i < 101; ++i)
|
||||
valueSequence += toBigEndian(u256(i));
|
||||
ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence);
|
||||
ABI_CHECK(callContractFunction("test4()"),
|
||||
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
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3307,9 +3070,12 @@ BOOST_AUTO_TEST_CASE(array_pop_uint16_transition)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
|
||||
@@ -3334,9 +3100,12 @@ BOOST_AUTO_TEST_CASE(array_pop_uint24_transition)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(array_pop_array_transition)
|
||||
@@ -3382,9 +3151,12 @@ BOOST_AUTO_TEST_CASE(array_pop_storage_empty)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
|
||||
@@ -3402,9 +3174,12 @@ BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs());
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(true));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
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)
|
||||
@@ -3505,15 +3283,19 @@ BOOST_AUTO_TEST_CASE(bytes_index_access)
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
string array{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32, 33};
|
||||
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));
|
||||
ALSO_VIA_YUL(
|
||||
DISABLE_EWASM_TESTRUN()
|
||||
|
||||
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)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <libsolidity/parsing/Parser.h>
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <libsolidity/analysis/Scoper.h>
|
||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||
#include <libsolidity/analysis/DeclarationTypeChecker.h>
|
||||
#include <libsolidity/codegen/CompilerContext.h>
|
||||
#include <libsolidity/codegen/ExpressionCompiler.h>
|
||||
@@ -93,18 +94,20 @@ Declaration const& resolveDeclaration(
|
||||
}
|
||||
|
||||
bytes compileFirstExpression(
|
||||
const string& _sourceCode,
|
||||
string const& _sourceCode,
|
||||
vector<vector<string>> _functions = {},
|
||||
vector<vector<string>> _localVariables = {}
|
||||
)
|
||||
{
|
||||
string sourceCode = "pragma solidity >=0.0; // SPDX-License-Identifier: GPL-3\n" + _sourceCode;
|
||||
|
||||
ASTPointer<SourceUnit> sourceUnit;
|
||||
try
|
||||
{
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter(errors);
|
||||
sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse(
|
||||
make_shared<Scanner>(CharStream(_sourceCode, ""))
|
||||
make_shared<Scanner>(CharStream(sourceCode, ""))
|
||||
);
|
||||
if (!sourceUnit)
|
||||
return bytes();
|
||||
@@ -119,6 +122,7 @@ bytes compileFirstExpression(
|
||||
ErrorReporter errorReporter(errors);
|
||||
GlobalContext globalContext;
|
||||
Scoper::assignScopes(*sourceUnit);
|
||||
BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit));
|
||||
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
|
||||
resolver.registerDeclarations(*sourceUnit);
|
||||
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
|
||||
@@ -187,7 +191,7 @@ BOOST_AUTO_TEST_CASE(literal_false)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { bool x = false; }
|
||||
function f() public { bool x = false; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -200,7 +204,7 @@ BOOST_AUTO_TEST_CASE(int_literal)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { uint x = 0x12345678901234567890; }
|
||||
function f() public { uint x = 0x12345678901234567890; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -229,7 +233,7 @@ BOOST_AUTO_TEST_CASE(int_with_gwei_ether_subdenomination)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function test () {
|
||||
function f() public {
|
||||
uint x = 1 gwei;
|
||||
}
|
||||
}
|
||||
@@ -259,7 +263,7 @@ BOOST_AUTO_TEST_CASE(comparison)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { bool x = (0x10aa < 0x11aa) != true; }
|
||||
function f() public { bool x = (0x10aa < 0x11aa) != true; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -291,7 +295,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
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);
|
||||
@@ -322,7 +326,7 @@ BOOST_AUTO_TEST_CASE(arithmetic)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
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"}});
|
||||
@@ -418,7 +422,7 @@ BOOST_AUTO_TEST_CASE(unary_operators)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(int y) { unchecked { !(~- y == 2); } }
|
||||
function f(int y) public { unchecked { !(~- y == 2); } }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
|
||||
@@ -508,7 +512,7 @@ BOOST_AUTO_TEST_CASE(assignment)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
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"}});
|
||||
@@ -546,7 +550,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { int8 x = -0x80; }
|
||||
function f() public { int8 x = -0x80; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -559,7 +563,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { int64 x = ~0xabc; }
|
||||
function f() public { int64 x = ~0xabc; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -574,7 +578,7 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals)
|
||||
// have been applied
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }
|
||||
function f() public { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; }
|
||||
}
|
||||
)";
|
||||
bytes code = compileFirstExpression(sourceCode);
|
||||
@@ -587,7 +591,7 @@ BOOST_AUTO_TEST_CASE(blockhash)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() {
|
||||
function f() public {
|
||||
blockhash(3);
|
||||
}
|
||||
}
|
||||
@@ -619,7 +623,7 @@ BOOST_AUTO_TEST_CASE(selfbalance)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() returns (uint) {
|
||||
function f() public returns (uint) {
|
||||
return address(this).balance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
// ----
|
||||
// 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
|
||||
@@ -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"
|
||||
@@ -14,5 +14,7 @@ contract c {
|
||||
uint8(data[97]) == 97;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test1() -> true
|
||||
@@ -10,6 +10,8 @@ contract c {
|
||||
|
||||
bytes data;
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// getLength() -> 0
|
||||
// set(): 1, 2 -> true
|
||||
|
||||
@@ -6,5 +6,7 @@ contract C {
|
||||
return s[0];
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> "a"
|
||||
@@ -5,5 +5,7 @@ contract C {
|
||||
return s[0];
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// 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
|
||||
+2
@@ -17,6 +17,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// one() -> 3
|
||||
// two() -> FAILURE, hex"4e487b71", 0x51
|
||||
@@ -5,6 +5,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256): 0 -> 0x20, 0x4, "This"
|
||||
// f(uint256): 1 -> 0x20, 0x2, "is"
|
||||
|
||||
@@ -13,5 +13,7 @@ contract C {
|
||||
return this.h(a);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g() -> 0x0700000000000000000000000000000000000000000000000000000000000000
|
||||
@@ -16,6 +16,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// use(uint256): 3 -> 6
|
||||
// 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
|
||||
+2
@@ -14,5 +14,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> 7
|
||||
+2
@@ -17,5 +17,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> 7
|
||||
@@ -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
|
||||
+2
@@ -16,5 +16,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> FAILURE, hex"4e487b71", 0x51
|
||||
@@ -20,6 +20,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// set() -> 7
|
||||
// ca() -> 7
|
||||
|
||||
@@ -6,5 +6,7 @@ contract C {
|
||||
return [this.f, this.g][0]{value: 1}();
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// h(), 1 ether -> 1
|
||||
@@ -25,6 +25,8 @@ contract Flow {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// success() -> false
|
||||
// f() -> 7
|
||||
|
||||
@@ -11,5 +11,7 @@ contract C {
|
||||
return x + 1;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256): 7 -> 8
|
||||
+2
@@ -14,5 +14,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// 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
|
||||
+2
@@ -7,5 +7,7 @@ contract Test {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// 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(0x01ffc9a7) -> true
|
||||
|
||||
@@ -40,6 +40,8 @@ contract Lisa is ERC165MappingImplementation, Simpson {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// supportsInterface(bytes4): left(0x01ffc9a0) -> false
|
||||
// supportsInterface(bytes4): left(0x01ffc9a7) -> true
|
||||
|
||||
@@ -14,6 +14,8 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g() -> 2
|
||||
// h() -> FAILURE, hex"4e487b71", 0x51
|
||||
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 206 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user