mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
5ffee049fa
@ -25,6 +25,9 @@ AST Changes:
|
||||
|
||||
### 0.7.5 (unreleased)
|
||||
|
||||
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 [![solidity at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](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 [![solidity at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](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
|
||||
~~~~~~~~~~~~~
|
||||
|
@ -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": "",
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
16
test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol
Normal file
16
test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol
Normal file
@ -0,0 +1,16 @@
|
||||
contract C {
|
||||
function f(uint a, bytes memory b, uint c)
|
||||
public pure returns (uint, uint, byte, uint) {
|
||||
return (a, b.length, b[3], c);
|
||||
}
|
||||
|
||||
function f_external(uint a, bytes calldata b, uint c)
|
||||
external pure returns (uint, uint, byte, uint) {
|
||||
return (a, b.length, b[3], c);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
||||
// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(uint a, uint16[] memory b, uint c)
|
||||
public pure returns (uint, uint, uint) {
|
||||
return (b.length, b[a], c);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9
|
13
test/libsolidity/semanticTests/abiEncoderV1/enums.sol
Normal file
13
test/libsolidity/semanticTests/abiEncoderV1/enums.sol
Normal file
@ -0,0 +1,13 @@
|
||||
contract C {
|
||||
enum E { A, B }
|
||||
function f(E e) public pure returns (uint x) {
|
||||
assembly { x := e }
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// ABIEncoderV1Only: true
|
||||
// ----
|
||||
// f(uint8): 0 -> 0
|
||||
// f(uint8): 1 -> 1
|
||||
// f(uint8): 2 -> 2
|
||||
// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xff
|
18
test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol
Normal file
18
test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol
Normal file
@ -0,0 +1,18 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
function f(uint a, bytes memory b, uint c)
|
||||
public pure returns (uint, uint, byte, uint) {
|
||||
return (a, b.length, b[3], c);
|
||||
}
|
||||
|
||||
function f_external(uint a, bytes calldata b, uint c)
|
||||
external pure returns (uint, uint, byte, uint) {
|
||||
return (a, b.length, b[3], c);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
||||
// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9
|
@ -41,6 +41,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// EVMVersion: >homestead
|
||||
// ----
|
||||
// g() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000"
|
||||
|
@ -0,0 +1,12 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint16[] memory b, uint c)
|
||||
public pure returns (uint, uint, uint) {
|
||||
return (b.length, b[a], c);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9
|
@ -0,0 +1,32 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d)
|
||||
public pure returns (uint, uint, uint, uint, uint, uint, uint) {
|
||||
return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d);
|
||||
}
|
||||
function test() public view returns (uint, uint, uint, uint, uint, uint, uint) {
|
||||
uint16[][] memory b = new uint16[][](3);
|
||||
b[0] = new uint16[](2);
|
||||
b[0][0] = 0x55;
|
||||
b[0][1] = 0x56;
|
||||
b[1] = new uint16[](4);
|
||||
b[1][0] = 0x65;
|
||||
b[1][1] = 0x66;
|
||||
b[1][2] = 0x67;
|
||||
b[1][3] = 0x68;
|
||||
|
||||
uint[2][][3] memory c;
|
||||
c[0] = new uint[2][](1);
|
||||
c[0][0][1] = 0x75;
|
||||
c[1] = new uint[2][](5);
|
||||
c[1][1][1] = 0x85;
|
||||
|
||||
return this.f(12, b, c, 13);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 12, 3, 4, 0x66, 5, 0x85, 13
|
||||
// f(uint256,uint16[][],uint256[2][][3],uint256): 12, 0x80, 0x220, 13, 3, 0x60, 0xC0, 0x160, 2, 85, 86, 4, 101, 102, 103, 104, 0, 0x60, 0xC0, 0x220, 1, 0, 117, 5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, 0 -> 12, 3, 4, 0x66, 5, 0x85, 13
|
15
test/libsolidity/semanticTests/abiEncoderV2/enums.sol
Normal file
15
test/libsolidity/semanticTests/abiEncoderV2/enums.sol
Normal file
@ -0,0 +1,15 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
enum E { A, B }
|
||||
function f(E e) public pure returns (uint x) {
|
||||
assembly { x := e }
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint8): 0 -> 0
|
||||
// f(uint8): 1 -> 1
|
||||
// f(uint8): 2 -> FAILURE
|
||||
// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE
|
@ -7,5 +7,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg"
|
||||
|
@ -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
|
@ -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
|
@ -14,5 +14,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> 7
|
||||
|
@ -17,5 +17,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> 7
|
||||
|
13
test/libsolidity/semanticTests/empty_for_loop.sol
Normal file
13
test/libsolidity/semanticTests/empty_for_loop.sol
Normal file
@ -0,0 +1,13 @@
|
||||
contract test {
|
||||
function f() public returns(uint ret) {
|
||||
ret = 1;
|
||||
for (;;) {
|
||||
ret += 1;
|
||||
if (ret >= 10) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> 10
|
@ -0,0 +1,22 @@
|
||||
contract collatz {
|
||||
function run(uint x) public returns(uint y) {
|
||||
while ((y = x) > 1) {
|
||||
if (x % 2 == 0) x = evenStep(x);
|
||||
else x = oddStep(x);
|
||||
}
|
||||
}
|
||||
function evenStep(uint x) public returns(uint y) {
|
||||
return x / 2;
|
||||
}
|
||||
function oddStep(uint x) public returns(uint y) {
|
||||
return 3 * x + 1;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// run(uint256): 0 -> 0
|
||||
// run(uint256): 1 -> 1
|
||||
// run(uint256): 2 -> 1
|
||||
// run(uint256): 8 -> 1
|
||||
// run(uint256): 127 -> 1
|
@ -16,5 +16,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// t() -> FAILURE, hex"4e487b71", 0x51
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
22
test/libsolidity/semanticTests/operators/compound_assign.sol
Normal file
22
test/libsolidity/semanticTests/operators/compound_assign.sol
Normal file
@ -0,0 +1,22 @@
|
||||
contract test {
|
||||
uint value1;
|
||||
uint value2;
|
||||
function f(uint x, uint y) public returns (uint w) {
|
||||
uint value3 = y;
|
||||
value1 += x;
|
||||
value3 *= x;
|
||||
value2 *= value3 + value1;
|
||||
return value2 += 7;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256,uint256): 0, 6 -> 7
|
||||
// f(uint256,uint256): 1, 3 -> 0x23
|
||||
// f(uint256,uint256): 2, 25 -> 0x0746
|
||||
// f(uint256,uint256): 3, 69 -> 396613
|
||||
// f(uint256,uint256): 4, 84 -> 137228105
|
||||
// f(uint256,uint256): 5, 2 -> 0xcc7c5e28
|
||||
// f(uint256,uint256): 6, 51 -> 1121839760671
|
||||
// f(uint256,uint256): 7, 48 -> 408349672884251
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user