Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-06-10 11:30:50 +02:00
commit 6b3171c38b
60 changed files with 889 additions and 391 deletions

View File

@ -598,7 +598,7 @@ jobs:
b_ems: b_ems:
docker: docker:
- image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-1 - image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-2
environment: environment:
TERM: xterm TERM: xterm
steps: steps:

View File

@ -26,11 +26,9 @@
# contains a Makefile in the docker/ subdirectory that can be used to create the # contains a Makefile in the docker/ subdirectory that can be used to create the
# required base image using: # required base image using:
# #
# make version=1.39.15-fastcomp build # make version=1.39.15 build
# #
# TODO: switch to the upstream backend by removing "-fastcomp". FROM emscripten/emsdk:1.39.15 AS base
#
FROM emscripten/emsdk:1.39.15-fastcomp AS base
ADD emscripten.jam /usr/src ADD emscripten.jam /usr/src
RUN set -ex; \ RUN set -ex; \
@ -47,6 +45,7 @@ RUN set -ex; \
-DZ3_BUILD_TEST_EXECUTABLES=OFF \ -DZ3_BUILD_TEST_EXECUTABLES=OFF \
-DZ3_BUILD_EXECUTABLE=OFF \ -DZ3_BUILD_EXECUTABLE=OFF \
-DZ3_SINGLE_THREADED=ON \ -DZ3_SINGLE_THREADED=ON \
-DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \
..; \ ..; \
make; make install; \ make; make install; \
rm -r /usr/src/z3; \ rm -r /usr/src/z3; \
@ -61,6 +60,6 @@ RUN set -ex; \
echo "using emscripten : : em++ ;" >> project-config.jam ; \ echo "using emscripten : : em++ ;" >> project-config.jam ; \
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \ ./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
--with-system --with-filesystem --with-test --with-program_options \ --with-system --with-filesystem --with-test --with-program_options \
cxxflags="-Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \ cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
--prefix=/emsdk/emscripten/sdk/system install; \ --prefix=/emsdk/emscripten/sdk/system install; \
rm -r /usr/src/boost_1_73_0 rm -r /usr/src/boost_1_73_0

View File

@ -19,39 +19,56 @@ Bugfixes:
* NatSpec: Constructors and functions have consistent userdoc output. * NatSpec: Constructors and functions have consistent userdoc output.
### 0.6.9 (unreleased) ### 0.6.10 (unreleased)
Language Features:
Compiler Features:
* Yul: Raise warning for switch statements that only have a default and no other cases.
Bugfixes:
* SMTChecker: Fix internal error when encoding tuples of tuples.
* SMTChecker: Fix aliasing soundness after pushing to an array pointer.
### 0.6.9 (2020-06-04)
Language Features: Language Features:
* Permit calldata location for all variables. * Permit calldata location for all variables.
* NatSpec: Support NatSpec comments on state variables.
* Yul: EVM instruction `pc()` is marked deprecated and will be removed in the next breaking release. * Yul: EVM instruction `pc()` is marked deprecated and will be removed in the next breaking release.
Compiler Features: Compiler Features:
* Code Generator: Do not introduce new source references for small internal routines.
* Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism. * Build system: Update the soljson.js build to emscripten 1.39.15 and boost 1.73.0 and include Z3 for integrated SMTChecker support without the callback mechanism.
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree instead of the root of the filesystem. * Build system: Switch the emscripten build from the fastcomp backend to the upstream backend.
* Code Generator: Do not introduce new internal source references for small compiler routines.
* Commandline Interface: Adds new option ``--base-path PATH`` to use the given path as the root of the source tree (defaults to the root of the filesystem).
* SMTChecker: Support array ``length``. * SMTChecker: Support array ``length``.
* SMTChecker: Support array ``push`` and ``pop``. * SMTChecker: Support array ``push`` and ``pop``.
* SMTChecker: General support to BitVectors and the bitwise ``and`` operator. * SMTChecker: General support to BitVectors and the bitwise ``and`` operator.
* Add support for natspec comments on state variables.
Bugfixes: Bugfixes:
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
* Commandline Interface: Fix internal error when using ``--assemble`` or ``--yul`` options with ``--machine ewasm`` but without specifying ``--yul-dialect``.
* NatSpec: DocString block is terminated when encountering an empty line.
* Optimizer: Fixed a bug in BlockDeDuplicator. * Optimizer: Fixed a bug in BlockDeDuplicator.
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
* SMTChecker: Fix internal error on try/catch clauses with parameters.
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
* SMTChecker: Fix internal error when assigning to index access inside branches.
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
* Type Checker: Disallow assignments to storage variables of type ``mapping``. * Type Checker: Disallow assignments to storage variables of type ``mapping``.
* Type Checker: Disallow inline arrays of non-nameable types. * Type Checker: Disallow inline arrays of non-nameable types.
* Type Checker: Disallow usage of override with non-public state variables.
* Type Checker: Fix internal compiler error when accessing members of array slices. * Type Checker: Fix internal compiler error when accessing members of array slices.
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
* Type Checker: Fix internal compiler error when trying to decode too large static arrays. * Type Checker: Fix internal compiler error when trying to decode too large static arrays.
* Type Checker: Fix wrong compiler error when referencing an overridden function without calling it. * Type Checker: Fix wrong compiler error when referencing an overridden function without calling it.
* Type Checker: Fix internal compiler error when forward referencing non-literal constants from inline assembly.
* Type Checker: Disallow usage of override with non-public state variables.
* NatSpec: DocString block is terminated when encountering an empty line.
* Scanner: Fix bug when two empty NatSpec comments lead to scanning past EOL.
* Code Generator: Trigger proper unimplemented errors on certain array copy operations.
* SMTChecker: Fix internal error when applying arithmetic operators to fixed point variables.
* SMTChecker: Fix internal error when short circuiting Boolean expressions with function calls in state variable initialization.
* SMTChecker: Fix internal error when assigning to index access inside branches.
* SMTChecker: Fix internal error on try/catch clauses with parameters.
### 0.6.8 (2020-05-14) ### 0.6.8 (2020-05-14)

View File

@ -6,9 +6,8 @@
- [ ] Readthedocs account, access to the Solidity project - [ ] Readthedocs account, access to the Solidity project
- [ ] Write access to https://github.com/ethereum/homebrew-ethereum - [ ] Write access to https://github.com/ethereum/homebrew-ethereum
### Pre-release ### Blog Post
- [ ] Ensure that a Github project exists for the release. - [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts.
- [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``.
### Changelog ### Changelog
- [ ] Sort the changelog entries alphabetically and correct any errors you notice. - [ ] Sort the changelog entries alphabetically and correct any errors you notice.
@ -33,8 +32,9 @@
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. - [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``). - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``).
### Homebrew ### Homebrew and MacOS
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb - [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
- [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
### Documentation ### Documentation
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`) - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`)

View File

@ -16,9 +16,11 @@
include(EthCheckCXXCompilerFlag) include(EthCheckCXXCompilerFlag)
eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support) if(NOT EMSCRIPTEN)
if(NOT have_stack_protector_strong_support) eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support)
eth_add_cxx_compiler_flag_if_supported(-fstack-protector) if(NOT have_stack_protector_strong_support)
eth_add_cxx_compiler_flag_if_supported(-fstack-protector)
endif()
endif() endif()
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
@ -109,15 +111,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# Re-enable exception catching (optimisations above -O1 disable it) # Re-enable exception catching (optimisations above -O1 disable it)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
# Remove any code related to exit (such as atexit) # Remove any code related to exit (such as atexit)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXIT_RUNTIME=0")
# Remove any code related to filesystem access # Remove any code related to filesystem access
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0")
# Remove variables even if it needs to be duplicated (can improve speed at the cost of size)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s AGGRESSIVE_VARIABLE_ELIMINATION=1")
# Allow memory growth, but disable some optimisations # Allow memory growth, but disable some optimisations
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
# Disable eval() # Disable eval()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DYNAMIC_EXECUTION=0")
# Disable greedy exception catcher # Disable greedy exception catcher
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
# Abort if linking results in any undefined symbols # Abort if linking results in any undefined symbols

View File

@ -42,7 +42,8 @@ For most of the topics the compiler will provide suggestions.
storage arrays. storage arrays.
* The new keyword ``abstract`` can be used to mark contracts as abstract. It has to be used * The new keyword ``abstract`` can be used to mark contracts as abstract. It has to be used
if a contract does not implement all its functions. if a contract does not implement all its functions. Abstract contracts cannot be created using the ``new`` operator,
and it is not possible to generate bytecode for them during compilation.
* Libraries have to implement all their functions, not only the internal ones. * Libraries have to implement all their functions, not only the internal ones.

View File

@ -124,7 +124,11 @@ userDefinedTypeName
: identifier ( '.' identifier )* ; : identifier ( '.' identifier )* ;
mapping mapping
: 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ; : 'mapping' '(' mappingKey '=>' typeName ')' ;
mappingKey
: elementaryTypeName
| userDefinedTypeName ;
functionTypeName functionTypeName
: 'function' parameterList modifierList returnParameters? ; : 'function' parameterList modifierList returnParameters? ;
@ -470,7 +474,7 @@ SingleQuotedStringCharacter
: ~['\r\n\\] | ('\\' .) ; : ~['\r\n\\] | ('\\' .) ;
VersionLiteral VersionLiteral
: [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ; : [0-9]+ ( '.' [0-9]+ ('.' [0-9]+)? )? ;
WS WS
: [ \t\r\n\u000C]+ -> skip ; : [ \t\r\n\u000C]+ -> skip ;

View File

@ -24,7 +24,7 @@ Function Selector
================= =================
The first four bytes of the call data for a function call specifies the function to be called. It is the The first four bytes of the call data for a function call specifies the function to be called. It is the
first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the signature of
the function. The signature is defined as the canonical expression of the basic prototype without data the function. The signature is defined as the canonical expression of the basic prototype without data
location specifier, i.e. location specifier, i.e.
the function name with the parenthesised list of parameter types. Parameter types are split by a single the function name with the parenthesised list of parameter types. Parameter types are split by a single

View File

@ -1163,5 +1163,9 @@
"0.6.8": { "0.6.8": {
"bugs": [], "bugs": [],
"released": "2020-05-14" "released": "2020-05-14"
},
"0.6.9": {
"bugs": [],
"released": "2020-06-04"
} }
} }

View File

@ -194,10 +194,10 @@ not known in the context of the class where it is used,
although its type is known. This is similar for ordinary although its type is known. This is similar for ordinary
virtual method lookup. virtual method lookup.
.. _function-overriding:
.. index:: ! overriding;function .. index:: ! overriding;function
.. _function-overriding:
Function Overriding Function Overriding
=================== ===================
@ -317,10 +317,10 @@ of the variable:
While public state variables can override external functions, they themselves cannot While public state variables can override external functions, they themselves cannot
be overridden. be overridden.
.. _modifier-overriding:
.. index:: ! overriding;modifier .. index:: ! overriding;modifier
.. _modifier-overriding:
Modifier Overriding Modifier Overriding
=================== ===================

View File

@ -82,7 +82,7 @@ TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
bool TypeChecker::visit(ContractDefinition const& _contract) bool TypeChecker::visit(ContractDefinition const& _contract)
{ {
m_scope = &_contract; m_currentContract = &_contract;
ASTNode::listAccept(_contract.baseContracts(), *this); ASTNode::listAccept(_contract.baseContracts(), *this);
@ -266,7 +266,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
solAssert(base, "Base contract not available."); solAssert(base, "Base contract not available.");
if (m_scope->isInterface() && !base->isInterface()) if (m_currentContract->isInterface() && !base->isInterface())
m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces.");
if (base->isLibrary()) if (base->isLibrary())
@ -328,8 +328,6 @@ void TypeChecker::endVisit(ModifierDefinition const& _modifier)
bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
bool isLibraryFunction = _function.inContractKind() == ContractKind::Library;
if (_function.markedVirtual()) if (_function.markedVirtual())
{ {
if (_function.annotation().contract->isInterface()) if (_function.annotation().contract->isInterface())
@ -342,7 +340,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isPayable()) if (_function.isPayable())
{ {
if (isLibraryFunction) if (_function.libraryFunction())
m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable.");
if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable."); m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
@ -352,15 +350,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
if (var.referenceLocation() != VariableDeclaration::Location::Storage) if (var.referenceLocation() != VariableDeclaration::Location::Storage)
{ {
if (!isLibraryFunction && _function.isPublic()) if (!_function.libraryFunction() && _function.isPublic())
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions."); m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
else else
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." ); m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
} }
else else
{ solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
solAssert(isLibraryFunction || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
}
} }
else else
{ {
@ -368,7 +364,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage."); m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
if (_function.isPublic()) if (_function.isPublic())
{ {
auto iType = type(var)->interfaceType(isLibraryFunction); auto iType = type(var)->interfaceType(_function.libraryFunction());
if (!iType) if (!iType)
{ {
@ -380,7 +376,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if ( if (
_function.isPublic() && _function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(var), isLibraryFunction) !typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction())
) )
m_errorReporter.typeError( m_errorReporter.typeError(
4957_error, 4957_error,
@ -419,7 +415,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
else else
modifiers.insert(decl); modifiers.insert(decl);
} }
if (m_scope->isInterface()) if (m_currentContract->isInterface())
{ {
if (_function.isImplemented()) if (_function.isImplemented())
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation.");
@ -430,14 +426,14 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isConstructor()) if (_function.isConstructor())
m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces.");
} }
else if (m_scope->contractKind() == ContractKind::Library) else if (m_currentContract->contractKind() == ContractKind::Library)
if (_function.isConstructor()) if (_function.isConstructor())
m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries.");
if (_function.isImplemented()) if (_function.isImplemented())
_function.body().accept(*this); _function.body().accept(*this);
else if (_function.isConstructor()) else if (_function.isConstructor())
m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared."); m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared.");
else if (isLibraryFunction) else if (_function.libraryFunction())
m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared.");
else if (!_function.virtualSemantics()) else if (!_function.virtualSemantics())
m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual.");
@ -1796,7 +1792,7 @@ void TypeChecker::typeCheckFunctionCall(
if (_functionType->kind() == FunctionType::Kind::Declaration) if (_functionType->kind() == FunctionType::Kind::Declaration)
{ {
if ( if (
m_scope->derivesFrom(*_functionType->declaration().annotation().contract) && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) &&
!dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented() !dynamic_cast<FunctionDefinition const&>(_functionType->declaration()).isImplemented()
) )
m_errorReporter.typeError( m_errorReporter.typeError(
@ -1832,7 +1828,7 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
{ {
solAssert(_function.isFallback(), ""); solAssert(_function.isFallback(), "");
if (_function.inContractKind() == ContractKind::Library) if (_function.libraryFunction())
m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions."); m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions.");
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
m_errorReporter.typeError( m_errorReporter.typeError(
@ -1859,7 +1855,7 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
{ {
solAssert(_function.isReceive(), ""); solAssert(_function.isReceive(), "");
if (_function.inContractKind() == ContractKind::Library) if (_function.libraryFunction())
m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions."); m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions.");
if (_function.stateMutability() != StateMutability::Payable) if (_function.stateMutability() != StateMutability::Payable)
@ -1918,7 +1914,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count( bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2 ExperimentalFeature::ABIEncoderV2
); );
@ -2318,7 +2314,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::ABIDecode: case FunctionType::Kind::ABIDecode:
{ {
bool const abiEncoderV2 = bool const abiEncoderV2 =
m_scope->sourceUnit().annotation().experimentalFeatures.count( m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2 ExperimentalFeature::ABIEncoderV2
); );
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
@ -2514,13 +2510,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (contract->abstract()) if (contract->abstract())
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract.");
solAssert(!!m_scope, ""); solAssert(!!m_currentContract, "");
m_scope->annotation().contractDependencies.insert(contract); m_currentContract->annotation().contractDependencies.insert(contract);
solAssert( solAssert(
!contract->annotation().linearizedBaseContracts.empty(), !contract->annotation().linearizedBaseContracts.empty(),
"Linearized base contracts not yet available." "Linearized base contracts not yet available."
); );
if (contractDependenciesAreCyclic(*m_scope)) if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError( m_errorReporter.typeError(
4579_error, 4579_error,
_newExpression.location(), _newExpression.location(),
@ -2567,7 +2563,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
// Retrieve the types of the arguments if this is used to call a function. // Retrieve the types of the arguments if this is used to call a function.
auto const& arguments = _memberAccess.annotation().arguments; auto const& arguments = _memberAccess.annotation().arguments;
MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName); MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName);
size_t const initialMemberCount = possibleMembers.size(); size_t const initialMemberCount = possibleMembers.size();
if (initialMemberCount > 1 && arguments) if (initialMemberCount > 1 && arguments)
{ {
@ -2593,7 +2589,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
DataLocation::Storage, DataLocation::Storage,
exprType exprType
); );
if (!storageType->members(m_scope).membersByName(memberName).empty()) if (!storageType->members(m_currentContract).membersByName(memberName).empty())
m_errorReporter.fatalTypeError( m_errorReporter.fatalTypeError(
4994_error, 4994_error,
_memberAccess.location(), _memberAccess.location(),
@ -2749,7 +2745,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
{ {
annotation.isPure = true; annotation.isPure = true;
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument()); ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
m_scope->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition());
if ( if (
memberName == "runtimeCode" && memberName == "runtimeCode" &&
@ -2761,7 +2757,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"\"runtimeCode\" is not available for contracts containing immutable variables." "\"runtimeCode\" is not available for contracts containing immutable variables."
); );
if (contractDependenciesAreCyclic(*m_scope)) if (contractDependenciesAreCyclic(*m_currentContract))
m_errorReporter.typeError( m_errorReporter.typeError(
4224_error, 4224_error,
_memberAccess.location(), _memberAccess.location(),

View File

@ -165,7 +165,7 @@ private:
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression, bool _ordinaryAssignment); void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
ContractDefinition const* m_scope = nullptr; ContractDefinition const* m_currentContract = nullptr;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;

View File

@ -276,7 +276,7 @@ void ViewPureChecker::reportMutability(
{ {
// We do not warn for library functions because they cannot be payable anyway. // We do not warn for library functions because they cannot be payable anyway.
// Also internal functions should be allowed to use `msg.value`. // Also internal functions should be allowed to use `msg.value`.
if (m_currentFunction->isPublic() && m_currentFunction->inContractKind() != ContractKind::Library) if (m_currentFunction->isPublic() && !m_currentFunction->libraryFunction())
{ {
if (_nestedLocation) if (_nestedLocation)
m_errorReporter.typeError( m_errorReporter.typeError(

View File

@ -287,11 +287,11 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
return initAnnotation<TypeDeclarationAnnotation>(); return initAnnotation<TypeDeclarationAnnotation>();
} }
ContractKind FunctionDefinition::inContractKind() const bool FunctionDefinition::libraryFunction() const
{ {
auto contractDef = dynamic_cast<ContractDefinition const*>(scope()); if (auto const* contractDef = dynamic_cast<ContractDefinition const*>(scope()))
solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set."); return contractDef->isLibrary();
return contractDef->contractKind(); return false;
} }
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const FunctionTypePointer FunctionDefinition::functionType(bool _internal) const

View File

@ -799,6 +799,7 @@ public:
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
StateMutability stateMutability() const { return m_stateMutability; } StateMutability stateMutability() const { return m_stateMutability; }
bool libraryFunction() const;
bool isOrdinary() const { return m_kind == Token::Function; } bool isOrdinary() const { return m_kind == Token::Function; }
bool isConstructor() const { return m_kind == Token::Constructor; } bool isConstructor() const { return m_kind == Token::Constructor; }
bool isFallback() const { return m_kind == Token::Fallback; } bool isFallback() const { return m_kind == Token::Fallback; }
@ -825,8 +826,6 @@ public:
/// @returns the external identifier of this function (the hash of the signature) as a hex string. /// @returns the external identifier of this function (the hash of the signature) as a hex string.
std::string externalIdentifierHex() const; std::string externalIdentifierHex() const;
ContractKind inContractKind() const;
TypePointer type() const override; TypePointer type() const override;
TypePointer typeViaContractName() const override; TypePointer typeViaContractName() const override;

View File

@ -1730,7 +1730,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
setLValue(_indexAccess, IRLValue{ setLValue(_indexAccess, IRLValue{
*arrayType.baseType(), *arrayType.baseType(),
IRLValue::Memory{memAddress} IRLValue::Memory{memAddress, arrayType.isByteArray()}
}); });
break; break;
} }
@ -1763,7 +1763,23 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
} }
} }
else if (baseType.category() == Type::Category::FixedBytes) else if (baseType.category() == Type::Category::FixedBytes)
solUnimplementedAssert(false, ""); {
auto const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()};
define(index, *_indexAccess.indexExpression());
m_code << Whiskers(R"(
if iszero(lt(<index>, <length>)) { invalid() }
let <result> := <shl248>(byte(<index>, <array>))
)")
("index", index.name())
("length", to_string(fixedBytesType.numBytes()))
("array", IRVariable(_indexAccess.baseExpression()).name())
("shl248", m_utils.shiftLeftFunction(256 - 8))
("result", IRVariable(_indexAccess).name())
.render();
}
else if (baseType.category() == Type::Category::TypeType) else if (baseType.category() == Type::Category::TypeType)
{ {
solAssert(baseType.sizeOnStack() == 0, ""); solAssert(baseType.sizeOnStack() == 0, "");

View File

@ -604,7 +604,11 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con
_target.type == VerificationTarget::Type::UnderOverflow, _target.type == VerificationTarget::Type::UnderOverflow,
"" ""
); );
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type); IntegerType const* intType = nullptr;
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
intType = type;
else
intType = TypeProvider::uint256();
solAssert(intType, ""); solAssert(intType, "");
checkCondition( checkCondition(
_target.constraints && _constraints && _target.value < smt::minValue(*intType), _target.constraints && _constraints && _target.value < smt::minValue(*intType),
@ -626,7 +630,12 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons
_target.type == VerificationTarget::Type::UnderOverflow, _target.type == VerificationTarget::Type::UnderOverflow,
"" ""
); );
auto intType = dynamic_cast<IntegerType const*>(_target.expression->annotation().type); IntegerType const* intType = nullptr;
if (auto const* type = dynamic_cast<IntegerType const*>(_target.expression->annotation().type))
intType = type;
else
intType = TypeProvider::uint256();
solAssert(intType, ""); solAssert(intType, "");
checkCondition( checkCondition(
_target.constraints && _constraints && _target.value > smt::maxValue(*intType), _target.constraints && _constraints && _target.value > smt::maxValue(*intType),

View File

@ -385,44 +385,22 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
} }
else else
{ {
auto const& type = _assignment.annotation().type; if (dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
vector<smtutil::Expression> rightArguments; tupleAssignment(_assignment.leftHandSide(), _assignment.rightHandSide());
if (auto const* tupleTypeRight = dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
{
auto symbTupleLeft = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.leftHandSide()));
solAssert(symbTupleLeft, "");
auto symbTupleRight = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.rightHandSide()));
solAssert(symbTupleRight, "");
auto const& leftComponents = symbTupleLeft->components();
auto const& rightComponents = symbTupleRight->components();
solAssert(leftComponents.size() == rightComponents.size(), "");
auto tupleTypeLeft = dynamic_cast<TupleType const*>(_assignment.leftHandSide().annotation().type);
solAssert(tupleTypeLeft, "");
solAssert(tupleTypeLeft->components().size() == leftComponents.size(), "");
auto const& typesLeft = tupleTypeLeft->components();
solAssert(tupleTypeRight->components().size() == rightComponents.size(), "");
auto const& typesRight = tupleTypeRight->components();
for (unsigned i = 0; i < rightComponents.size(); ++i)
rightArguments.push_back(symbTupleRight->component(i, typesRight.at(i), typesLeft.at(i)));
}
else else
{ {
auto const& type = _assignment.annotation().type;
auto rightHandSide = compoundOps.count(op) ? auto rightHandSide = compoundOps.count(op) ?
compoundAssignment(_assignment) : compoundAssignment(_assignment) :
expr(_assignment.rightHandSide(), type); expr(_assignment.rightHandSide(), type);
defineExpr(_assignment, rightHandSide); defineExpr(_assignment, rightHandSide);
rightArguments.push_back(expr(_assignment, type)); assignment(
_assignment.leftHandSide(),
expr(_assignment, type),
type,
_assignment.location()
);
} }
assignment(
_assignment.leftHandSide(),
rightArguments,
type,
_assignment.location()
);
} }
} }
@ -1023,38 +1001,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smtutil::Expressi
solAssert(varDecl, ""); solAssert(varDecl, "");
if (varDecl->hasReferenceOrMappingType()) if (varDecl->hasReferenceOrMappingType())
m_context.resetVariables([&](VariableDeclaration const& _var) { resetReferences(*varDecl);
if (_var == *varDecl)
return false;
// If both are state variables no need to clear knowledge.
if (_var.isStateVariable() && varDecl->isStateVariable())
return false;
TypePointer prefix = _var.type();
TypePointer originalType = typeWithoutPointer(varDecl->type());
while (
prefix->category() == Type::Category::Mapping ||
prefix->category() == Type::Category::Array
)
{
if (*originalType == *typeWithoutPointer(prefix))
return true;
if (prefix->category() == Type::Category::Mapping)
{
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
solAssert(mapPrefix, "");
prefix = mapPrefix->valueType();
}
else
{
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
solAssert(arrayPrefix, "");
prefix = arrayPrefix->baseType();
}
}
return false;
});
auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl)); auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.variable(*varDecl));
smtutil::Expression store = smtutil::Expression::store( smtutil::Expression store = smtutil::Expression::store(
@ -1160,6 +1107,8 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression
{ {
auto varDecl = identifierToVariable(*id); auto varDecl = identifierToVariable(*id);
solAssert(varDecl, ""); solAssert(varDecl, "");
if (varDecl->hasReferenceOrMappingType())
resetReferences(*varDecl);
m_context.addAssertion(m_context.newValue(*varDecl) == _array); m_context.addAssertion(m_context.newValue(*varDecl) == _array);
} }
else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr)) else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr))
@ -1213,7 +1162,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
{ {
auto type = _op.annotation().commonType; auto type = _op.annotation().commonType;
solAssert(type, ""); solAssert(type, "");
if (type->category() == Type::Category::Integer) if (type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint)
{ {
switch (_op.getOperator()) switch (_op.getOperator())
{ {
@ -1266,13 +1215,21 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
}; };
solAssert(validOperators.count(_op), ""); solAssert(validOperators.count(_op), "");
solAssert(_commonType, ""); solAssert(_commonType, "");
solAssert(_commonType->category() == Type::Category::Integer, ""); solAssert(
_commonType->category() == Type::Category::Integer || _commonType->category() == Type::Category::FixedPoint,
""
);
IntegerType const* intType = nullptr;
if (auto type = dynamic_cast<IntegerType const*>(_commonType))
intType = type;
else
intType = TypeProvider::uint256();
auto const& intType = dynamic_cast<IntegerType const&>(*_commonType);
smtutil::Expression valueNoMod( smtutil::Expression valueNoMod(
_op == Token::Add ? _left + _right : _op == Token::Add ? _left + _right :
_op == Token::Sub ? _left - _right : _op == Token::Sub ? _left - _right :
_op == Token::Div ? division(_left, _right, intType) : _op == Token::Div ? division(_left, _right, *intType) :
_op == Token::Mul ? _left * _right : _op == Token::Mul ? _left * _right :
/*_op == Token::Mod*/ _left % _right /*_op == Token::Mod*/ _left % _right
); );
@ -1280,20 +1237,23 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
if (_op == Token::Div || _op == Token::Mod) if (_op == Token::Div || _op == Token::Mod)
m_context.addAssertion(_right != 0); m_context.addAssertion(_right != 0);
smtutil::Expression intValueRange = (0 - smt::minValue(intType)) + smt::maxValue(intType) + 1; auto symbMin = smt::minValue(*intType);
auto symbMax = smt::maxValue(*intType);
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
auto value = smtutil::Expression::ite( auto value = smtutil::Expression::ite(
valueNoMod > smt::maxValue(intType), valueNoMod > symbMax,
valueNoMod % intValueRange, valueNoMod % intValueRange,
smtutil::Expression::ite( smtutil::Expression::ite(
valueNoMod < smt::minValue(intType), valueNoMod < symbMin,
valueNoMod % intValueRange, valueNoMod % intValueRange,
valueNoMod valueNoMod
) )
); );
if (intType.isSigned()) if (intType->isSigned())
value = smtutil::Expression::ite( value = smtutil::Expression::ite(
value > smt::maxValue(intType), value > symbMax,
value - intValueRange, value - intValueRange,
value value
); );
@ -1422,11 +1382,16 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp
void SMTEncoder::assignment( void SMTEncoder::assignment(
Expression const& _left, Expression const& _left,
vector<smtutil::Expression> const& _right, smtutil::Expression const& _right,
TypePointer const& _type, TypePointer const& _type,
langutil::SourceLocation const& _location langutil::SourceLocation const& _location
) )
{ {
solAssert(
_left.annotation().type->category() != Type::Category::Tuple,
"Tuple assignments should be handled by tupleAssignment."
);
if (!smt::isSupportedType(_type->category())) if (!smt::isSupportedType(_type->category()))
{ {
// Give it a new index anyway to keep the SSA scheme sound. // Give it a new index anyway to keep the SSA scheme sound.
@ -1440,26 +1405,9 @@ void SMTEncoder::assignment(
); );
} }
else if (auto varDecl = identifierToVariable(_left)) else if (auto varDecl = identifierToVariable(_left))
{ assignment(*varDecl, _right);
solAssert(_right.size() == 1, "");
assignment(*varDecl, _right.front());
}
else if (dynamic_cast<IndexAccess const*>(&_left)) else if (dynamic_cast<IndexAccess const*>(&_left))
{ arrayIndexAssignment(_left, _right);
solAssert(_right.size() == 1, "");
arrayIndexAssignment(_left, _right.front());
}
else if (auto tuple = dynamic_cast<TupleExpression const*>(&_left))
{
auto const& components = tuple->components();
if (!_right.empty())
{
solAssert(_right.size() == components.size(), "");
for (unsigned i = 0; i < _right.size(); ++i)
if (auto component = components.at(i))
assignment(*component, {_right.at(i)}, component->annotation().type, component->location());
}
}
else else
m_errorReporter.warning( m_errorReporter.warning(
8182_error, 8182_error,
@ -1468,6 +1416,52 @@ void SMTEncoder::assignment(
); );
} }
void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right)
{
auto lTuple = dynamic_cast<TupleExpression const*>(&_left);
solAssert(lTuple, "");
auto const& lComponents = lTuple->components();
// If both sides are tuple expressions, we individually and potentially
// recursively assign each pair of components.
// This is because of potential type conversion.
if (auto rTuple = dynamic_cast<TupleExpression const*>(&_right))
{
auto const& rComponents = rTuple->components();
solAssert(lComponents.size() == rComponents.size(), "");
for (unsigned i = 0; i < lComponents.size(); ++i)
{
if (!lComponents.at(i) || !rComponents.at(i))
continue;
auto const& lExpr = *lComponents.at(i);
auto const& rExpr = *rComponents.at(i);
if (lExpr.annotation().type->category() == Type::Category::Tuple)
tupleAssignment(lExpr, rExpr);
else
{
auto type = lExpr.annotation().type;
assignment(lExpr, expr(rExpr, type), type, lExpr.location());
}
}
}
else
{
auto rType = dynamic_cast<TupleType const*>(_right.annotation().type);
solAssert(rType, "");
auto const& rComponents = rType->components();
solAssert(lComponents.size() == rComponents.size(), "");
auto symbRight = expr(_right);
solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, "");
for (unsigned i = 0; i < lComponents.size(); ++i)
if (auto component = lComponents.at(i); component && rComponents.at(i))
assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type, component->location());
}
}
smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment) smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment)
{ {
static map<Token, Token> const compoundToArithmetic{ static map<Token, Token> const compoundToArithmetic{
@ -1618,6 +1612,42 @@ void SMTEncoder::resetStateVariables()
m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); }); m_context.resetVariables([&](VariableDeclaration const& _variable) { return _variable.isStateVariable(); });
} }
void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl)
{
m_context.resetVariables([&](VariableDeclaration const& _var) {
if (_var == _varDecl)
return false;
// If both are state variables no need to clear knowledge.
if (_var.isStateVariable() && _varDecl.isStateVariable())
return false;
TypePointer prefix = _var.type();
TypePointer originalType = typeWithoutPointer(_varDecl.type());
while (
prefix->category() == Type::Category::Mapping ||
prefix->category() == Type::Category::Array
)
{
if (*originalType == *typeWithoutPointer(prefix))
return true;
if (prefix->category() == Type::Category::Mapping)
{
auto mapPrefix = dynamic_cast<MappingType const*>(prefix);
solAssert(mapPrefix, "");
prefix = mapPrefix->valueType();
}
else
{
auto arrayPrefix = dynamic_cast<ArrayType const*>(prefix);
solAssert(arrayPrefix, "");
prefix = arrayPrefix->baseType();
}
}
return false;
});
}
TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type) TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type)
{ {
if (auto refType = dynamic_cast<ReferenceType const*>(_type)) if (auto refType = dynamic_cast<ReferenceType const*>(_type))

View File

@ -157,10 +157,12 @@ protected:
/// Will also be used for assignments of tuple components. /// Will also be used for assignments of tuple components.
void assignment( void assignment(
Expression const& _left, Expression const& _left,
std::vector<smtutil::Expression> const& _right, smtutil::Expression const& _right,
TypePointer const& _type, TypePointer const& _type,
langutil::SourceLocation const& _location langutil::SourceLocation const& _location
); );
/// Handle assignments between tuples.
void tupleAssignment(Expression const& _left, Expression const& _right);
/// Computes the right hand side of a compound assignment. /// Computes the right hand side of a compound assignment.
smtutil::Expression compoundAssignment(Assignment const& _assignment); smtutil::Expression compoundAssignment(Assignment const& _assignment);
@ -181,6 +183,9 @@ protected:
void initializeLocalVariables(FunctionDefinition const& _function); void initializeLocalVariables(FunctionDefinition const& _function);
void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs); void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector<smtutil::Expression> const& _callArgs);
void resetStateVariables(); void resetStateVariables();
/// Resets all references/pointers that have the same type or have
/// a subexpression of the same type as _varDecl.
void resetReferences(VariableDeclaration const& _varDecl);
/// @returns the type without storage pointer information if it has it. /// @returns the type without storage pointer information if it has it.
TypePointer typeWithoutPointer(TypePointer const& _type); TypePointer typeWithoutPointer(TypePointer const& _type);

View File

@ -53,9 +53,6 @@ string solidity::util::toHex(uint8_t _data, HexCase _case)
string solidity::util::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case) string solidity::util::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
{ {
if (_data.empty())
return {};
std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0); std::string ret(_data.size() * 2 + (_prefix == HexPrefix::Add ? 2 : 0), 0);
size_t i = 0; size_t i = 0;

View File

@ -359,6 +359,13 @@ void AsmAnalyzer::operator()(Switch const& _switch)
{ {
yulAssert(_switch.expression, ""); yulAssert(_switch.expression, "");
if (_switch.cases.size() == 1 && !_switch.cases[0].value)
m_errorReporter.warning(
9592_error,
_switch.location,
"\"switch\" statement with only a default case."
);
YulString valueType = expectExpression(*_switch.expression); YulString valueType = expectExpression(*_switch.expression);
set<u256> cases; set<u256> cases;

View File

@ -24,6 +24,7 @@
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/map.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -130,7 +131,7 @@ bytes toBytes(Opcode _o)
return toBytes(uint8_t(_o)); return toBytes(uint8_t(_o));
} }
static std::map<string, uint8_t> const builtins = { static map<string, uint8_t> const builtins = {
{"i32.load", 0x28}, {"i32.load", 0x28},
{"i64.load", 0x29}, {"i64.load", 0x29},
{"i32.load8_s", 0x2c}, {"i32.load8_s", 0x2c},
@ -240,46 +241,56 @@ bytes lebEncodeSigned(int64_t _n)
bytes prefixSize(bytes _data) bytes prefixSize(bytes _data)
{ {
size_t size = _data.size(); size_t size = _data.size();
return lebEncode(size) + std::move(_data); return lebEncode(size) + move(_data);
} }
bytes makeSection(Section _section, bytes _data) bytes makeSection(Section _section, bytes _data)
{ {
return toBytes(_section) + prefixSize(std::move(_data)); return toBytes(_section) + prefixSize(move(_data));
} }
} }
bytes BinaryTransform::run(Module const& _module) bytes BinaryTransform::run(Module const& _module)
{ {
BinaryTransform bt; map<Type, vector<string>> const types = typeToFunctionMap(_module.imports, _module.functions);
for (size_t i = 0; i < _module.globals.size(); ++i) map<string, size_t> const globalIDs = enumerateGlobals(_module);
bt.m_globals[_module.globals[i].variableName] = i; map<string, size_t> const functionIDs = enumerateFunctions(_module);
map<string, size_t> const functionTypes = enumerateFunctionTypes(types);
size_t funID = 0; yulAssert(globalIDs.size() == _module.globals.size(), "");
for (FunctionImport const& fun: _module.imports) yulAssert(functionIDs.size() == _module.imports.size() + _module.functions.size(), "");
bt.m_functions[fun.internalName] = funID++; yulAssert(functionTypes.size() == functionIDs.size(), "");
for (FunctionDefinition const& fun: _module.functions) yulAssert(functionTypes.size() >= types.size(), "");
bt.m_functions[fun.name] = funID++;
bytes ret{0, 'a', 's', 'm'}; bytes ret{0, 'a', 's', 'm'};
// version // version
ret += bytes{1, 0, 0, 0}; ret += bytes{1, 0, 0, 0};
ret += bt.typeSection(_module.imports, _module.functions); ret += typeSection(types);
ret += bt.importSection(_module.imports); ret += importSection(_module.imports, functionTypes);
ret += bt.functionSection(_module.functions); ret += functionSection(_module.functions, functionTypes);
ret += bt.memorySection(); ret += memorySection();
ret += bt.globalSection(); ret += globalSection(_module.globals);
ret += bt.exportSection(); ret += exportSection(functionIDs);
map<string, pair<size_t, size_t>> subModulePosAndSize;
for (auto const& sub: _module.subModules) for (auto const& sub: _module.subModules)
{ {
// TODO should we prefix and / or shorten the name? // TODO should we prefix and / or shorten the name?
bytes data = BinaryTransform::run(sub.second); bytes data = BinaryTransform::run(sub.second);
size_t length = data.size(); size_t length = data.size();
ret += bt.customSection(sub.first, std::move(data)); ret += customSection(sub.first, move(data));
bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length}; subModulePosAndSize[sub.first] = {ret.size() - length, length};
} }
BinaryTransform bt(
move(globalIDs),
move(functionIDs),
move(functionTypes),
move(subModulePosAndSize)
);
ret += bt.codeSection(_module.functions); ret += bt.codeSection(_module.functions);
return ret; return ret;
} }
@ -302,7 +313,7 @@ bytes BinaryTransform::operator()(LocalVariable const& _variable)
bytes BinaryTransform::operator()(GlobalVariable const& _variable) bytes BinaryTransform::operator()(GlobalVariable const& _variable)
{ {
return toBytes(Opcode::GlobalGet) + lebEncode(m_globals.at(_variable.name)); return toBytes(Opcode::GlobalGet) + lebEncode(m_globalIDs.at(_variable.name));
} }
bytes BinaryTransform::operator()(BuiltinCall const& _call) bytes BinaryTransform::operator()(BuiltinCall const& _call)
@ -311,12 +322,12 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
// they are references to object names that should not end up in the code. // they are references to object names that should not end up in the code.
if (_call.functionName == "dataoffset") if (_call.functionName == "dataoffset")
{ {
string name = std::get<StringLiteral>(_call.arguments.at(0)).value; string name = get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first); return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first);
} }
else if (_call.functionName == "datasize") else if (_call.functionName == "datasize")
{ {
string name = std::get<StringLiteral>(_call.arguments.at(0)).value; string name = get<StringLiteral>(_call.arguments.at(0)).value;
return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second); return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second);
} }
@ -331,20 +342,24 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call)
else else
{ {
yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found");
bytes ret = std::move(args) + toBytes(builtins.at(_call.functionName)); bytes ret = move(args) + toBytes(builtins.at(_call.functionName));
if ( if (
_call.functionName.find(".load") != string::npos || _call.functionName.find(".load") != string::npos ||
_call.functionName.find(".store") != string::npos _call.functionName.find(".store") != string::npos
) )
// alignment and offset // Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it
ret += bytes{{3, 0}}; // into account to generate more efficient code but if the hint is invalid it could
// actually be more expensive. It's best to hint at 1-byte alignment if we don't plan
// to control the memory layout accordingly.
ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment
return ret; return ret;
} }
} }
bytes BinaryTransform::operator()(FunctionCall const& _call) bytes BinaryTransform::operator()(FunctionCall const& _call)
{ {
return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functions.at(_call.functionName)); return visit(_call.arguments) + toBytes(Opcode::Call) + lebEncode(m_functionIDs.at(_call.functionName));
} }
bytes BinaryTransform::operator()(LocalAssignment const& _assignment) bytes BinaryTransform::operator()(LocalAssignment const& _assignment)
@ -360,7 +375,7 @@ bytes BinaryTransform::operator()(GlobalAssignment const& _assignment)
return return
std::visit(*this, *_assignment.value) + std::visit(*this, *_assignment.value) +
toBytes(Opcode::GlobalSet) + toBytes(Opcode::GlobalSet) +
lebEncode(m_globals.at(_assignment.variableName)); lebEncode(m_globalIDs.at(_assignment.variableName));
} }
bytes BinaryTransform::operator()(If const& _if) bytes BinaryTransform::operator()(If const& _if)
@ -430,9 +445,14 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
// This is a kind of run-length-encoding of local types. Has to be adapted once // This is a kind of run-length-encoding of local types. Has to be adapted once
// we have locals of different types. // we have locals of different types.
ret += lebEncode(1); // number of locals groups if (_function.locals.size() == 0)
ret += lebEncode(_function.locals.size()); ret += lebEncode(0); // number of locals groups
ret += toBytes(ValueType::I64); else
{
ret += lebEncode(1); // number of locals groups
ret += lebEncode(_function.locals.size());
ret += toBytes(ValueType::I64);
}
m_locals.clear(); m_locals.clear();
size_t varIdx = 0; size_t varIdx = 0;
@ -448,7 +468,7 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function)
yulAssert(m_labels.empty(), "Stray labels."); yulAssert(m_labels.empty(), "Stray labels.");
return prefixSize(std::move(ret)); return prefixSize(move(ret));
} }
BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import) BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import)
@ -487,9 +507,9 @@ vector<uint8_t> BinaryTransform::encodeTypes(vector<string> const& _typeNames)
return result; return result;
} }
bytes BinaryTransform::typeSection( map<BinaryTransform::Type, vector<string>> BinaryTransform::typeToFunctionMap(
vector<FunctionImport> const& _imports, vector<wasm::FunctionImport> const& _imports,
vector<FunctionDefinition> const& _functions vector<wasm::FunctionDefinition> const& _functions
) )
{ {
map<Type, vector<string>> types; map<Type, vector<string>> types;
@ -498,12 +518,50 @@ bytes BinaryTransform::typeSection(
for (auto const& fun: _functions) for (auto const& fun: _functions)
types[typeOf(fun)].emplace_back(fun.name); types[typeOf(fun)].emplace_back(fun.name);
bytes result; return types;
size_t index = 0; }
for (auto const& [type, funNames]: types)
map<string, size_t> BinaryTransform::enumerateGlobals(Module const& _module)
{
map<string, size_t> globals;
for (size_t i = 0; i < _module.globals.size(); ++i)
globals[_module.globals[i].variableName] = i;
return globals;
}
map<string, size_t> BinaryTransform::enumerateFunctions(Module const& _module)
{
map<string, size_t> functions;
size_t funID = 0;
for (FunctionImport const& fun: _module.imports)
functions[fun.internalName] = funID++;
for (FunctionDefinition const& fun: _module.functions)
functions[fun.name] = funID++;
return functions;
}
map<string, size_t> BinaryTransform::enumerateFunctionTypes(map<Type, vector<string>> const& _typeToFunctionMap)
{
map<string, size_t> functionTypes;
size_t typeID = 0;
for (vector<string> const& funNames: _typeToFunctionMap | boost::adaptors::map_values)
{ {
for (string const& name: funNames) for (string const& name: funNames)
m_functionTypes[name] = index; functionTypes[name] = typeID;
++typeID;
}
return functionTypes;
}
bytes BinaryTransform::typeSection(map<BinaryTransform::Type, vector<string>> const& _typeToFunctionMap)
{
bytes result;
size_t index = 0;
for (Type const& type: _typeToFunctionMap | boost::adaptors::map_keys)
{
result += toBytes(ValueType::Function); result += toBytes(ValueType::Function);
result += lebEncode(type.first.size()) + type.first; result += lebEncode(type.first.size()) + type.first;
result += lebEncode(type.second.size()) + type.second; result += lebEncode(type.second.size()) + type.second;
@ -511,11 +569,12 @@ bytes BinaryTransform::typeSection(
index++; index++;
} }
return makeSection(Section::TYPE, lebEncode(index) + std::move(result)); return makeSection(Section::TYPE, lebEncode(index) + move(result));
} }
bytes BinaryTransform::importSection( bytes BinaryTransform::importSection(
vector<FunctionImport> const& _imports vector<FunctionImport> const& _imports,
map<string, size_t> const& _functionTypes
) )
{ {
bytes result = lebEncode(_imports.size()); bytes result = lebEncode(_imports.size());
@ -526,17 +585,20 @@ bytes BinaryTransform::importSection(
encodeName(import.module) + encodeName(import.module) +
encodeName(import.externalName) + encodeName(import.externalName) +
toBytes(importKind) + toBytes(importKind) +
lebEncode(m_functionTypes[import.internalName]); lebEncode(_functionTypes.at(import.internalName));
} }
return makeSection(Section::IMPORT, std::move(result)); return makeSection(Section::IMPORT, move(result));
} }
bytes BinaryTransform::functionSection(vector<FunctionDefinition> const& _functions) bytes BinaryTransform::functionSection(
vector<FunctionDefinition> const& _functions,
map<string, size_t> const& _functionTypes
)
{ {
bytes result = lebEncode(_functions.size()); bytes result = lebEncode(_functions.size());
for (auto const& fun: _functions) for (auto const& fun: _functions)
result += lebEncode(m_functionTypes.at(fun.name)); result += lebEncode(_functionTypes.at(fun.name));
return makeSection(Section::FUNCTION, std::move(result)); return makeSection(Section::FUNCTION, move(result));
} }
bytes BinaryTransform::memorySection() bytes BinaryTransform::memorySection()
@ -544,13 +606,13 @@ bytes BinaryTransform::memorySection()
bytes result = lebEncode(1); bytes result = lebEncode(1);
result.push_back(static_cast<uint8_t>(LimitsKind::Min)); result.push_back(static_cast<uint8_t>(LimitsKind::Min));
result.push_back(1); // initial length result.push_back(1); // initial length
return makeSection(Section::MEMORY, std::move(result)); return makeSection(Section::MEMORY, move(result));
} }
bytes BinaryTransform::globalSection() bytes BinaryTransform::globalSection(vector<wasm::GlobalVariableDeclaration> const& _globals)
{ {
bytes result = lebEncode(m_globals.size()); bytes result = lebEncode(_globals.size());
for (size_t i = 0; i < m_globals.size(); ++i) for (size_t i = 0; i < _globals.size(); ++i)
result += result +=
toBytes(ValueType::I64) + toBytes(ValueType::I64) +
lebEncode(static_cast<uint8_t>(Mutability::Var)) + lebEncode(static_cast<uint8_t>(Mutability::Var)) +
@ -558,21 +620,21 @@ bytes BinaryTransform::globalSection()
lebEncodeSigned(0) + lebEncodeSigned(0) +
toBytes(Opcode::End); toBytes(Opcode::End);
return makeSection(Section::GLOBAL, std::move(result)); return makeSection(Section::GLOBAL, move(result));
} }
bytes BinaryTransform::exportSection() bytes BinaryTransform::exportSection(map<string, size_t> const& _functionIDs)
{ {
bytes result = lebEncode(2); bytes result = lebEncode(2);
result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0); result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0);
result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main")); result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main"));
return makeSection(Section::EXPORT, std::move(result)); return makeSection(Section::EXPORT, move(result));
} }
bytes BinaryTransform::customSection(string const& _name, bytes _data) bytes BinaryTransform::customSection(string const& _name, bytes _data)
{ {
bytes result = encodeName(_name) + std::move(_data); bytes result = encodeName(_name) + move(_data);
return makeSection(Section::CUSTOM, std::move(result)); return makeSection(Section::CUSTOM, move(result));
} }
bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions) bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _functions)
@ -580,7 +642,7 @@ bytes BinaryTransform::codeSection(vector<wasm::FunctionDefinition> const& _func
bytes result = lebEncode(_functions.size()); bytes result = lebEncode(_functions.size());
for (FunctionDefinition const& fun: _functions) for (FunctionDefinition const& fun: _functions)
result += (*this)(fun); result += (*this)(fun);
return makeSection(Section::CODE, std::move(result)); return makeSection(Section::CODE, move(result));
} }
bytes BinaryTransform::visit(vector<Expression> const& _expressions) bytes BinaryTransform::visit(vector<Expression> const& _expressions)
@ -611,7 +673,7 @@ bytes BinaryTransform::encodeLabelIdx(string const& _label) const
yulAssert(false, "Label not found."); yulAssert(false, "Label not found.");
} }
bytes BinaryTransform::encodeName(std::string const& _name) bytes BinaryTransform::encodeName(string const& _name)
{ {
// UTF-8 is allowed here by the Wasm spec, but since all names here should stem from // UTF-8 is allowed here by the Wasm spec, but since all names here should stem from
// Solidity or Yul identifiers or similar, non-ascii characters ending up here // Solidity or Yul identifiers or similar, non-ascii characters ending up here

View File

@ -55,23 +55,49 @@ public:
bytes operator()(wasm::FunctionDefinition const& _function); bytes operator()(wasm::FunctionDefinition const& _function);
private: private:
BinaryTransform(
std::map<std::string, size_t> _globalIDs,
std::map<std::string, size_t> _functionIDs,
std::map<std::string, size_t> _functionTypes,
std::map<std::string, std::pair<size_t, size_t>> _subModulePosAndSize
):
m_globalIDs(std::move(_globalIDs)),
m_functionIDs(std::move(_functionIDs)),
m_functionTypes(std::move(_functionTypes)),
m_subModulePosAndSize(std::move(_subModulePosAndSize))
{}
using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>; using Type = std::pair<std::vector<std::uint8_t>, std::vector<std::uint8_t>>;
static Type typeOf(wasm::FunctionImport const& _import); static Type typeOf(wasm::FunctionImport const& _import);
static Type typeOf(wasm::FunctionDefinition const& _funDef); static Type typeOf(wasm::FunctionDefinition const& _funDef);
static uint8_t encodeType(std::string const& _typeName); static uint8_t encodeType(std::string const& _typeName);
static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames); static std::vector<uint8_t> encodeTypes(std::vector<std::string> const& _typeNames);
bytes typeSection(
static std::map<Type, std::vector<std::string>> typeToFunctionMap(
std::vector<wasm::FunctionImport> const& _imports, std::vector<wasm::FunctionImport> const& _imports,
std::vector<wasm::FunctionDefinition> const& _functions std::vector<wasm::FunctionDefinition> const& _functions
); );
bytes importSection(std::vector<wasm::FunctionImport> const& _imports); static std::map<std::string, size_t> enumerateGlobals(Module const& _module);
bytes functionSection(std::vector<wasm::FunctionDefinition> const& _functions); static std::map<std::string, size_t> enumerateFunctions(Module const& _module);
bytes memorySection(); static std::map<std::string, size_t> enumerateFunctionTypes(
bytes globalSection(); std::map<Type, std::vector<std::string>> const& _typeToFunctionMap
bytes exportSection(); );
bytes customSection(std::string const& _name, bytes _data);
static bytes typeSection(std::map<Type, std::vector<std::string>> const& _typeToFunctionMap);
static bytes importSection(
std::vector<wasm::FunctionImport> const& _imports,
std::map<std::string, size_t> const& _functionTypes
);
static bytes functionSection(
std::vector<wasm::FunctionDefinition> const& _functions,
std::map<std::string, size_t> const& _functionTypes
);
static bytes memorySection();
static bytes globalSection(std::vector<wasm::GlobalVariableDeclaration> const& _globals);
static bytes exportSection(std::map<std::string, size_t> const& _functionIDs);
static bytes customSection(std::string const& _name, bytes _data);
bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions); bytes codeSection(std::vector<wasm::FunctionDefinition> const& _functions);
bytes visit(std::vector<wasm::Expression> const& _expressions); bytes visit(std::vector<wasm::Expression> const& _expressions);
@ -81,12 +107,13 @@ private:
static bytes encodeName(std::string const& _name); static bytes encodeName(std::string const& _name);
std::map<std::string, size_t> const m_globalIDs;
std::map<std::string, size_t> const m_functionIDs;
std::map<std::string, size_t> const m_functionTypes;
std::map<std::string, std::pair<size_t, size_t>> const m_subModulePosAndSize;
std::map<std::string, size_t> m_locals; std::map<std::string, size_t> m_locals;
std::map<std::string, size_t> m_globals;
std::map<std::string, size_t> m_functions;
std::map<std::string, size_t> m_functionTypes;
std::vector<std::string> m_labels; std::vector<std::string> m_labels;
std::map<std::string, std::pair<size_t, size_t>> m_subModulePosAndSize;
}; };
} }

View File

@ -33,16 +33,16 @@ using namespace solidity::yul;
VarNameCleaner::VarNameCleaner( VarNameCleaner::VarNameCleaner(
Block const& _ast, Block const& _ast,
Dialect const& _dialect, Dialect const& _dialect,
set<YulString> _blacklist set<YulString> _namesToKeep
): ):
m_dialect{_dialect}, m_dialect{_dialect},
m_blacklist{std::move(_blacklist)}, m_namesToKeep{std::move(_namesToKeep)},
m_translatedNames{} m_translatedNames{}
{ {
for (auto const& statement: _ast.statements) for (auto const& statement: _ast.statements)
if (holds_alternative<FunctionDefinition>(statement)) if (holds_alternative<FunctionDefinition>(statement))
m_blacklist.insert(std::get<FunctionDefinition>(statement).name); m_namesToKeep.insert(std::get<FunctionDefinition>(statement).name);
m_usedNames = m_blacklist; m_usedNames = m_namesToKeep;
} }
void VarNameCleaner::operator()(FunctionDefinition& _funDef) void VarNameCleaner::operator()(FunctionDefinition& _funDef)
@ -51,7 +51,7 @@ void VarNameCleaner::operator()(FunctionDefinition& _funDef)
m_insideFunction = true; m_insideFunction = true;
set<YulString> globalUsedNames = std::move(m_usedNames); set<YulString> globalUsedNames = std::move(m_usedNames);
m_usedNames = m_blacklist; m_usedNames = m_namesToKeep;
map<YulString, YulString> globalTranslatedNames; map<YulString, YulString> globalTranslatedNames;
swap(globalTranslatedNames, m_translatedNames); swap(globalTranslatedNames, m_translatedNames);

View File

@ -63,7 +63,7 @@ private:
VarNameCleaner( VarNameCleaner(
Block const& _ast, Block const& _ast,
Dialect const& _dialect, Dialect const& _dialect,
std::set<YulString> _blacklist = {} std::set<YulString> _namesToKeep = {}
); );
/// Tries to rename a list of variables. /// Tries to rename a list of variables.
@ -77,11 +77,13 @@ private:
YulString findCleanName(YulString const& name) const; YulString findCleanName(YulString const& name) const;
/// Tests whether a given name was already used within this pass /// Tests whether a given name was already used within this pass
/// or is on the blacklist. /// or was set to be kept.
bool isUsedName(YulString const& _name) const; bool isUsedName(YulString const& _name) const;
Dialect const& m_dialect; Dialect const& m_dialect;
std::set<YulString> m_blacklist;
/// These names will not be modified.
std::set<YulString> m_namesToKeep;
/// Set of names that are in use. /// Set of names that are in use.
std::set<YulString> m_usedNames; std::set<YulString> m_usedNames;

View File

@ -54,6 +54,10 @@ cmake \
-DTESTS=0 \ -DTESTS=0 \
.. ..
make -j 4 soljson make -j 4 soljson
# Patch soljson.js for backwards compatibility.
# TODO: remove this with 0.7.
# "viiiii" encodes the signature of the callback function.
sed -i -e 's/addFunction(func,sig){/addFunction(func,sig){sig=sig||"viiiii";/' libsolc/soljson.js
cd .. cd ..
mkdir -p upload mkdir -p upload

View File

@ -740,7 +740,7 @@ remap paths using the context:prefix=path syntax.
Example: Example:
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
Allowed options)").c_str(), General Information)").c_str(),
po::options_description::m_default_line_length, po::options_description::m_default_line_length,
po::options_description::m_default_line_length - 23 po::options_description::m_default_line_length - 23
); );
@ -748,47 +748,67 @@ Allowed options)").c_str(),
(g_argHelp.c_str(), "Show help message and exit.") (g_argHelp.c_str(), "Show help message and exit.")
(g_argVersion.c_str(), "Show version and exit.") (g_argVersion.c_str(), "Show version and exit.")
(g_strLicense.c_str(), "Show licensing information and exit.") (g_strLicense.c_str(), "Show licensing information and exit.")
;
po::options_description inputOptions("Input Options");
inputOptions.add_options()
(
g_argBasePath.c_str(),
po::value<string>()->value_name("path"),
"Use the given path as the root of the source tree instead of the root of the filesystem."
)
(
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
)
(
g_argIgnoreMissingFiles.c_str(),
"Ignore missing files."
)
(
g_argErrorRecovery.c_str(),
"Enables additional parser error recovery."
)
;
desc.add(inputOptions);
po::options_description outputOptions("Output Options");
outputOptions.add_options()
(
(g_argOutputDir + ",o").c_str(),
po::value<string>()->value_name("path"),
"If given, creates one file per component and contract/file at the specified directory."
)
(
g_strOverwrite.c_str(),
"Overwrite existing files (used together with -o)."
)
( (
g_strEVMVersion.c_str(), g_strEVMVersion.c_str(),
po::value<string>()->value_name("version"), po::value<string>()->value_name("version"),
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
"byzantium, constantinople, petersburg, istanbul (default) or berlin." "byzantium, constantinople, petersburg, istanbul (default) or berlin."
) )
(g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
(
g_argLibraries.c_str(),
po::value<vector<string>>()->value_name("libs"),
"Direct string or file containing library addresses. Syntax: "
"<libraryName>:<address> [, or whitespace] ...\n"
"Address is interpreted as a hex string optionally prefixed by 0x."
)
( (
g_strRevertStrings.c_str(), g_strRevertStrings.c_str(),
po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")), po::value<string>()->value_name(boost::join(g_revertStringsArgs, ",")),
"Strip revert (and require) reason strings or add additional debugging information." "Strip revert (and require) reason strings or add additional debugging information."
) )
( ;
(g_argOutputDir + ",o").c_str(), desc.add(outputOptions);
po::value<string>()->value_name("path"),
"If given, creates one file per component and contract/file at the specified directory." po::options_description alternativeInputModes("Alternative Input Modes");
) alternativeInputModes.add_options()
(g_strOverwrite.c_str(), "Overwrite existing files (used together with -o).")
(
g_argCombinedJson.c_str(),
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
"Output a single json document containing the specified information."
)
(g_argGas.c_str(), "Print an estimate of the maximal gas usage for each function.")
( (
g_argStandardJSON.c_str(), g_argStandardJSON.c_str(),
"Switch to Standard JSON input / output mode, ignoring all options. " "Switch to Standard JSON input / output mode, ignoring all options. "
"It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output." "It reads from standard input, if no input file was given, otherwise it reads from the provided input file. The result will be written to standard output."
) )
( (
g_argImportAst.c_str(), g_argLink.c_str(),
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " "and modify binaries in place.").c_str()
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
) )
( (
g_argAssemble.c_str(), g_argAssemble.c_str(),
@ -809,58 +829,62 @@ Allowed options)").c_str(),
"and assumes input is strict assembly.").c_str() "and assumes input is strict assembly.").c_str()
) )
( (
g_strYulDialect.c_str(), g_argImportAst.c_str(),
po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")), ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Input dialect to use in assembly or yul mode." "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
) )
;
desc.add(alternativeInputModes);
po::options_description assemblyModeOptions("Assembly Mode Options");
assemblyModeOptions.add_options()
( (
g_argMachine.c_str(), g_argMachine.c_str(),
po::value<string>()->value_name(boost::join(g_machineArgs, ",")), po::value<string>()->value_name(boost::join(g_machineArgs, ",")),
"Target machine in assembly or Yul mode." "Target machine in assembly or Yul mode."
) )
( (
g_argLink.c_str(), g_strYulDialect.c_str(),
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " po::value<string>()->value_name(boost::join(g_yulDialectArgs, ",")),
"and modify binaries in place.").c_str() "Input dialect to use in assembly or yul mode."
)
;
desc.add(assemblyModeOptions);
po::options_description linkerModeOptions("Linker Mode Options");
linkerModeOptions.add_options()
(
g_argLibraries.c_str(),
po::value<vector<string>>()->value_name("libs"),
"Direct string or file containing library addresses. Syntax: "
"<libraryName>:<address> [, or whitespace] ...\n"
"Address is interpreted as a hex string optionally prefixed by 0x."
)
;
desc.add(linkerModeOptions);
po::options_description outputFormatting("Output Formatting");
outputFormatting.add_options()
(
g_argPrettyJson.c_str(),
"Output JSON in pretty format. Currently it only works with the combined JSON output."
) )
( (
g_argMetadataHash.c_str(), g_argColor.c_str(),
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")), "Force colored output."
"Choose hash method for the bytecode metadata or disable it."
)
(g_argMetadataLiteral.c_str(), "Store referenced sources as literal data in the metadata output.")
(
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
) )
( (
g_argBasePath.c_str(), g_argNoColor.c_str(),
po::value<string>()->value_name("path"), "Explicitly disable colored output, disabling terminal auto-detection."
"Use the given path as the root of the source tree instead of the root of the filesystem."
) )
(g_argColor.c_str(), "Force colored output.")
(g_argNoColor.c_str(), "Explicitly disable colored output, disabling terminal auto-detection.")
(g_argOldReporter.c_str(), "Enables old diagnostics reporter.")
(g_argErrorRecovery.c_str(), "Enables additional parser error recovery.")
(g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
po::options_description optimizerOptions("Optimizer options");
optimizerOptions.add_options()
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
( (
g_argOptimizeRuns.c_str(), g_argOldReporter.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200), "Enables old diagnostics reporter (legacy option, will be removed)."
"Set for how many contract runs to optimize. "
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
) )
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str()) ;
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.") desc.add(outputFormatting);
(
g_strYulOptimizations.c_str(),
po::value<string>()->value_name("steps"),
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
);
desc.add(optimizerOptions);
po::options_description outputComponents("Output Components"); po::options_description outputComponents("Output Components");
outputComponents.add_options() outputComponents.add_options()
(g_argAstJson.c_str(), "AST of all source files in JSON format.") (g_argAstJson.c_str(), "AST of all source files in JSON format.")
@ -878,9 +902,66 @@ Allowed options)").c_str(),
(g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
(g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
(g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables."); (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.")
;
desc.add(outputComponents); desc.add(outputComponents);
po::options_description extraOutput("Extra Output");
extraOutput.add_options()
(
g_argGas.c_str(),
"Print an estimate of the maximal gas usage for each function."
)
(
g_argCombinedJson.c_str(),
po::value<string>()->value_name(boost::join(g_combinedJsonArgs, ",")),
"Output a single json document containing the specified information."
)
;
desc.add(extraOutput);
po::options_description metadataOptions("Metadata Options");
metadataOptions.add_options()
(
g_argMetadataHash.c_str(),
po::value<string>()->value_name(boost::join(g_metadataHashArgs, ",")),
"Choose hash method for the bytecode metadata or disable it."
)
(
g_argMetadataLiteral.c_str(),
"Store referenced sources as literal data in the metadata output."
)
;
desc.add(metadataOptions);
po::options_description optimizerOptions("Optimizer Options");
optimizerOptions.add_options()
(
g_argOptimize.c_str(),
"Enable bytecode optimizer."
)
(
g_argOptimizeRuns.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200),
"Set for how many contract runs to optimize. "
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
)
(
g_strOptimizeYul.c_str(),
("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str()
)
(
g_strNoOptimizeYul.c_str(),
"Disable Yul optimizer in Solidity."
)
(
g_strYulOptimizations.c_str(),
po::value<string>()->value_name("steps"),
"Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one."
)
;
desc.add(optimizerOptions);
po::options_description allOptions = desc; po::options_description allOptions = desc;
allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file"); allOptions.add_options()(g_argInputFile.c_str(), po::value<vector<string>>(), "input file");

View File

@ -45,7 +45,7 @@ object "object" {
Binary representation: Binary representation:
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acd01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b4101007e02402000a7200110043703002000a74208a76aada7200210043703002000a74210a76aada7200310043703002000a74218a76aada7200410043703000b0b 0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b
Text representation: Text representation:
(module (module

View File

@ -154,7 +154,7 @@ object "object" {
Binary representation: Binary representation:
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa9090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290300100c21084200a74208a76aada7290300100c21094200a74210a76aada7290300100c210a4200a74218a76aada7290300100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b4101007e02402000a72001100c3703002000a74208a76aada72002100c3703002000a74210a76aada72003100c3703002000a74218a76aada72004100c3703000b0b2701007e024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b 0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
Text representation: Text representation:
(module (module

View File

@ -0,0 +1 @@
--yul --yul-dialect ewasm --machine ewasm

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,8 @@
object "object" {
code {
function main()
{
i64.store8(0x01:i32, 42:i64)
}
}
}

View File

@ -0,0 +1,27 @@
======= wasm_to_wasm_memory_instructions_alignment/input.yul (Ewasm) =======
Pretty printed source:
object "object" {
code {
function main()
{ i64.store8(0x01:i32, 42) }
}
}
Binary representation:
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0f010d0002404201a7422a3c00000b0b
Text representation:
(module
(memory $memory (export "memory") 1)
(export "main" (func $main))
(func $main
(block $label_
(i64.store8 (i32.wrap_i64 (i64.const 1)) (i64.const 42))
)
)
)

View File

@ -31,14 +31,14 @@
{ {
"id": 4, "id": 4,
"nodeType": "Block", "nodeType": "Block",
"src": "42:48:1", "src": "42:58:1",
"statements": "statements":
[ [
{ {
"AST": "AST":
{ {
"nodeType": "YulBlock", "nodeType": "YulBlock",
"src": "61:23:1", "src": "61:33:1",
"statements": "statements":
[ [
{ {
@ -48,11 +48,29 @@
"body": "body":
{ {
"nodeType": "YulBlock", "nodeType": "YulBlock",
"src": "80:2:1", "src": "79:2:1",
"statements": [] "statements": []
}, },
"nodeType": "YulCase", "nodeType": "YulCase",
"src": "72:10:1", "src": "72:9:1",
"value":
{
"kind": "number",
"nodeType": "YulLiteral",
"src": "77:1:1",
"type": "",
"value": "0"
}
},
{
"body":
{
"nodeType": "YulBlock",
"src": "90:2:1",
"statements": []
},
"nodeType": "YulCase",
"src": "82:10:1",
"value": "default" "value": "default"
} }
], ],
@ -65,7 +83,7 @@
"value": "0" "value": "0"
}, },
"nodeType": "YulSwitch", "nodeType": "YulSwitch",
"src": "63:19:1" "src": "63:29:1"
} }
] ]
}, },
@ -73,7 +91,7 @@
"externalReferences": [], "externalReferences": [],
"id": 3, "id": 3,
"nodeType": "InlineAssembly", "nodeType": "InlineAssembly",
"src": "52:32:1" "src": "52:42:1"
} }
] ]
}, },
@ -99,15 +117,15 @@
"src": "42:0:1" "src": "42:0:1"
}, },
"scope": 6, "scope": 6,
"src": "17:73:1", "src": "17:83:1",
"stateMutability": "view", "stateMutability": "view",
"virtual": false, "virtual": false,
"visibility": "public" "visibility": "public"
} }
], ],
"scope": 7, "scope": 7,
"src": "0:92:1" "src": "0:102:1"
} }
], ],
"src": "0:93:1" "src": "0:103:1"
} }

View File

@ -1,6 +1,6 @@
contract C { contract C {
function g() view public { function g() view public {
assembly { switch 0 default {} } assembly { switch 0 case 0 {} default {} }
} }
} }

View File

@ -91,30 +91,30 @@
[ [
null null
], ],
"operations": "{\n switch 0\n default { }\n}" "operations": "{\n switch 0\n case 0 { }\n default { }\n}"
}, },
"children": [], "children": [],
"id": 3, "id": 3,
"name": "InlineAssembly", "name": "InlineAssembly",
"src": "52:32:1" "src": "52:42:1"
} }
], ],
"id": 4, "id": 4,
"name": "Block", "name": "Block",
"src": "42:48:1" "src": "42:58:1"
} }
], ],
"id": 5, "id": 5,
"name": "FunctionDefinition", "name": "FunctionDefinition",
"src": "17:73:1" "src": "17:83:1"
} }
], ],
"id": 6, "id": 6,
"name": "ContractDefinition", "name": "ContractDefinition",
"src": "0:92:1" "src": "0:102:1"
} }
], ],
"id": 7, "id": 7,
"name": "SourceUnit", "name": "SourceUnit",
"src": "0:93:1" "src": "0:103:1"
} }

View File

@ -314,9 +314,17 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression) BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{ {
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected."); CHECK_PARSE_ERROR("{ switch {} case 1 {} default {} }", ParserError, "Literal or identifier expected.");
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'"); CHECK_PARSE_ERROR(
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); "{ switch mload case 1 {} default {} }",
ParserError,
"Expected '(' but got reserved keyword 'case'"
);
CHECK_PARSE_ERROR(
"{ switch mstore(1, 1) case 1 {} default {} }",
TypeError,
"Expected expression to evaluate to one value, but got 0 values instead."
);
} }
BOOST_AUTO_TEST_CASE(switch_default_before_case) BOOST_AUTO_TEST_CASE(switch_default_before_case)

View File

@ -0,0 +1,20 @@
contract C {
function to_little_endian_64(uint64 value) public pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}
}
// ====
// compileViaYul: also
// ----
// to_little_endian_64(uint64): 0 -> 0x20, 8, 0x00
// to_little_endian_64(uint64): 0x0102030405060708 -> 0x20, 8, 0x0807060504030201000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,29 @@
contract C {
struct Y {
uint a;
uint b;
}
mapping(uint256 => Y)[] public m;
mapping(uint256 => Y)[3] public n;
constructor() public {
m.push();
m.push();
m[1][0].a = 1;
m[1][0].b = 2;
m[1][1].a = 3;
m[1][1].b = 4;
n[1][0].a = 7;
n[1][0].b = 8;
n[1][1].a = 9;
n[1][1].b = 10;
}
}
// ----
// m(uint256,uint256): 0, 0 -> 0x00, 0x00
// m(uint256,uint256): 1, 0 -> 1, 2
// m(uint256,uint256): 1, 1 -> 3, 4
// m(uint256,uint256): 1, 2 -> 0x00, 0x00
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
// n(uint256,uint256): 1, 0 -> 7, 8
// n(uint256,uint256): 1, 1 -> 9, 0x0a
// n(uint256,uint256): 1, 2 -> 0x00, 0x00

View File

@ -0,0 +1,27 @@
contract C {
struct Y {
uint a;
uint b;
}
mapping(uint256 => Y[]) public m;
mapping(uint256 => Y[3]) public n;
constructor() public {
m[1].push().a = 1;
m[1][0].b = 2;
m[1].push().a = 3;
m[1][1].b = 4;
n[1][0].a = 7;
n[1][0].b = 8;
n[1][1].a = 9;
n[1][1].b = 10;
}
}
// ----
// m(uint256,uint256): 0, 0 -> FAILURE
// m(uint256,uint256): 1, 0 -> 1, 2
// m(uint256,uint256): 1, 1 -> 3, 4
// m(uint256,uint256): 1, 2 -> FAILURE
// n(uint256,uint256): 0, 0 -> 0x00, 0x00
// n(uint256,uint256): 1, 0 -> 7, 8
// n(uint256,uint256): 1, 1 -> 9, 0x0a
// n(uint256,uint256): 1, 2 -> 0x00, 0x00

View File

@ -0,0 +1,18 @@
contract C {
mapping(string => uint8[3]) public x;
constructor() public {
x["abc"][0] = 1;
x["abc"][2] = 3;
x["abc"][1] = 2;
x["def"][1] = 9;
}
}
// ====
// compileViaYul: also
// ----
// x(string,uint256): 0x40, 0, 3, "abc" -> 1
// x(string,uint256): 0x40, 1, 3, "abc" -> 2
// x(string,uint256): 0x40, 2, 3, "abc" -> 3
// x(string,uint256): 0x40, 0, 3, "def" -> 0x00
// x(string,uint256): 0x40, 1, 3, "def" -> 9
// x(string,uint256): 0x40, 2, 3, "def" -> 0x00

View File

@ -0,0 +1,18 @@
contract C {
struct S {
uint a;
bytes b;
mapping(uint => uint) c;
uint[] d;
}
uint shifter;
S public s;
constructor() public {
s.a = 7;
s.b = "abc";
s.c[0] = 9;
s.d.push(10);
}
}
// ----
// s() -> 7, 0x40, 3, 0x6162630000000000000000000000000000000000000000000000000000000000

View File

@ -0,0 +1,23 @@
pragma experimental SMTChecker;
contract C {
uint[][] a;
uint[][][] c;
uint[] d;
function f() public {
a.push();
uint[] storage b = a[0];
c[0][0][0] = 12;
d[5] = 7;
b.push(8);
assert(a[0].length == 0);
// Safe but knowledge about `c` is erased because `b` could be pointing to `c[x][y]`.
assert(c[0][0][0] == 12);
// Safe but knowledge about `d` is erased because `b` could be pointing to `d`.
assert(d[5] == 7);
}
}
// ----
// Warning: (193-217): Assertion violation happens here
// Warning: (309-333): Assertion violation happens here
// Warning: (419-436): Assertion violation happens here

View File

@ -0,0 +1,12 @@
pragma experimental SMTChecker;
contract test {
function f() internal pure {
ufixed a = uint64(1) + ufixed(2);
}
}
// ----
// Warning: (80-88): Unused local variable.
// Warning: (91-100): Type conversion is not yet fully supported and might yield false positives.
// Warning: (103-112): Type conversion is not yet fully supported and might yield false positives.
// Warning: (91-112): Underflow (resulting value less than 0) happens here
// Warning: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here

View File

@ -0,0 +1,8 @@
pragma experimental SMTChecker;
contract C {
fixed[] b;
function f() internal { b[0] += 1; }
}
// ----
// Warning: (84-93): Underflow (resulting value less than 0) happens here
// Warning: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here

View File

@ -0,0 +1,6 @@
pragma experimental SMTChecker;
contract C {
function f3() public pure {
((, ), ) = ((7, 8), 9);
}
}

View File

@ -1,15 +1,7 @@
contract C { contract C {
struct S { bool f; } struct S { bool f; }
S s; S s;
function f(uint256 a) internal pure { function f(bool flag) internal pure {
S storage c;
assembly {
switch a
default { c_slot := s_slot }
}
c;
}
function g(bool flag) internal pure {
S storage c; S storage c;
assembly { assembly {
switch flag switch flag
@ -18,7 +10,7 @@ contract C {
} }
c; c;
} }
function h(uint256 a) internal pure { function g(uint256 a) internal pure {
S storage c; S storage c;
assembly { assembly {
switch a switch a

View File

@ -1,20 +1,14 @@
contract C { contract C {
struct S { bool f; } struct S { bool f; }
S s; S s;
function f(uint256 a) internal pure returns (S storage c) { function f(bool flag) internal pure returns (S storage c) {
assembly {
switch a
default { c_slot := s_slot }
}
}
function g(bool flag) internal pure returns (S storage c) {
assembly { assembly {
switch flag switch flag
case 0 { c_slot := s_slot } case 0 { c_slot := s_slot }
default { c_slot := s_slot } default { c_slot := s_slot }
} }
} }
function h(uint256 a) internal pure returns (S storage c) { function g(uint256 a) internal pure returns (S storage c) {
assembly { assembly {
switch a switch a
case 0 { revert(0, 0) } case 0 { revert(0, 0) }

View File

@ -0,0 +1,12 @@
contract C {
struct S { bool f; }
S s;
function f(uint256 a) internal pure returns (S storage c) {
assembly {
switch a
default { c_slot := s_slot }
}
}
}
// ----
// Warning: (142-195): "switch" statement with only a default case.

View File

@ -87,12 +87,12 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); shared_ptr<Object> parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false);
if (!parserResult) if (!parserResult)
return {}; return {};
if (!parserResult->code || !errorReporter.errors().empty()) if (!parserResult->code || errorReporter.hasErrors())
return {}; return {};
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>(); shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames()); AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
// TODO this should be done recursively. // TODO this should be done recursively.
if (!analyzer.analyze(*parserResult->code) || !errorReporter.errors().empty()) if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors())
return {}; return {};
return {std::move(parserResult->code), std::move(analysisInfo)}; return {std::move(parserResult->code), std::move(analysisInfo)};
} }

View File

@ -332,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture)
BOOST_AUTO_TEST_CASE(switch_statement_tiny) BOOST_AUTO_TEST_CASE(switch_statement_tiny)
{ {
BOOST_CHECK_EQUAL(codeSize( BOOST_CHECK_EQUAL(codeSize(
"{ switch calldatasize() default {} }" "{ switch calldatasize() case 0 {} }"
), 4); ), 4);
} }

View File

@ -362,7 +362,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c
ErrorList errors; ErrorList errors;
soltestAssert(m_dialect, ""); soltestAssert(m_dialect, "");
std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors);
if (!m_ast || !m_analysisInfo || !errors.empty()) if (!m_ast || !m_analysisInfo || !Error::containsOnlyWarnings(errors))
{ {
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
printErrors(_stream, errors); printErrors(_stream, errors);

View File

@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position
BOOST_TEST(mutatedChromosome.length() > chromosome.length()); BOOST_TEST(mutatedChromosome.length() > chromosome.length());
vector<string> suffix( vector<string> suffix(
mutatedChromosome.optimisationSteps().end() - chromosome.length(), mutatedChromosome.optimisationSteps().end() - static_cast<ptrdiff_t>(chromosome.length()),
mutatedChromosome.optimisationSteps().end() mutatedChromosome.optimisationSteps().end()
); );
BOOST_TEST(suffix == chromosome.optimisationSteps()); BOOST_TEST(suffix == chromosome.optimisationSteps());
@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position)
vector<string> prefix( vector<string> prefix(
mutatedChromosome.optimisationSteps().begin(), mutatedChromosome.optimisationSteps().begin(),
mutatedChromosome.optimisationSteps().begin() + chromosome.length() mutatedChromosome.optimisationSteps().begin() + static_cast<ptrdiff_t>(chromosome.length())
); );
BOOST_TEST(prefix == chromosome.optimisationSteps()); BOOST_TEST(prefix == chromosome.optimisationSteps());
} }
@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g
for (size_t i = 0; i < 10; ++i) for (size_t i = 0; i < 10; ++i)
{ {
Chromosome mutatedChromosome = mutation(chromosome); Chromosome mutatedChromosome = mutation(chromosome);
cCount += static_cast<int>(mutatedChromosome == Chromosome("c")); cCount += (mutatedChromosome == Chromosome("c") ? 1 : 0);
fCount += static_cast<int>(mutatedChromosome == Chromosome("f")); fCount += (mutatedChromosome == Chromosome("f") ? 1 : 0);
} }
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance. // This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.

View File

@ -135,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_
size_t maxLength = 5; size_t maxLength = 5;
assert(chromosomeCount % maxLength == 0); assert(chromosomeCount % maxLength == 0);
auto nextLength = [counter = 0, maxLength]() mutable { return counter++ % maxLength; }; auto nextLength = [counter = 0ul, maxLength]() mutable { return counter++ % maxLength; };
auto population = Population::makeRandom(m_fitnessMetric, chromosomeCount, nextLength); auto population = Population::makeRandom(m_fitnessMetric, chromosomeCount, nextLength);
// We can't rely on the order since the population sorts its chromosomes immediately but // We can't rely on the order since the population sorts its chromosomes immediately but

View File

@ -72,9 +72,13 @@ size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome
{ {
size_t count = 0; size_t count = 0;
for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i) for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i)
count += static_cast<int>(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]); if (_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i])
++count;
return count + abs(static_cast<int>(_chromosome1.length() - _chromosome2.length())); return count + static_cast<size_t>(abs(
static_cast<long>(_chromosome1.length()) -
static_cast<long>(_chromosome2.length())
));
} }
TemporaryDirectory::TemporaryDirectory(std::string const& _prefix): TemporaryDirectory::TemporaryDirectory(std::string const& _prefix):

View File

@ -155,7 +155,7 @@ Population ClassicGeneticAlgorithm::select(Population _population, size_t _selec
vector<Individual> selectedIndividuals; vector<Individual> selectedIndividuals;
for (size_t i = 0; i < _selectionSize; ++i) for (size_t i = 0; i < _selectionSize; ++i)
{ {
uint32_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1); size_t ball = SimulationRNG::uniformInt(0, rouletteRange - 1);
size_t cumulativeFitness = 0; size_t cumulativeFitness = 0;
for (auto const& individual: _population.individuals()) for (auto const& individual: _population.individuals())

View File

@ -126,12 +126,12 @@ ChromosomePair fixedPointSwap(
return { return {
Chromosome( Chromosome(
vector<string>(begin1, begin1 + _crossoverPoint) + vector<string>(begin1, begin1 + static_cast<ptrdiff_t>(_crossoverPoint)) +
vector<string>(begin2 + _crossoverPoint, end2) vector<string>(begin2 + static_cast<ptrdiff_t>(_crossoverPoint), end2)
), ),
Chromosome( Chromosome(
vector<string>(begin2, begin2 + _crossoverPoint) + vector<string>(begin2, begin2 + static_cast<ptrdiff_t>(_crossoverPoint)) +
vector<string>(begin1 + _crossoverPoint, end1) vector<string>(begin1 + static_cast<ptrdiff_t>(_crossoverPoint), end1)
), ),
}; };
} }
@ -196,8 +196,8 @@ ChromosomePair fixedTwoPointSwap(
assert(_crossoverPoint2 <= _chromosome1.length()); assert(_crossoverPoint2 <= _chromosome1.length());
assert(_crossoverPoint2 <= _chromosome2.length()); assert(_crossoverPoint2 <= _chromosome2.length());
size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2); auto lowPoint = static_cast<ptrdiff_t>(min(_crossoverPoint1, _crossoverPoint2));
size_t highPoint = max(_crossoverPoint1, _crossoverPoint2); auto highPoint = static_cast<ptrdiff_t>(max(_crossoverPoint1, _crossoverPoint2));
auto begin1 = _chromosome1.optimisationSteps().begin(); auto begin1 = _chromosome1.optimisationSteps().begin();
auto begin2 = _chromosome2.optimisationSteps().begin(); auto begin2 = _chromosome2.optimisationSteps().begin();
@ -282,17 +282,17 @@ ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _ch
if (_chromosome1.length() > minLength) if (_chromosome1.length() > minLength)
{ {
if (swapTail) if (swapTail)
steps2.insert(steps2.end(), begin1 + minLength, end1); steps2.insert(steps2.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
else else
steps1.insert(steps1.end(), begin1 + minLength, end1); steps1.insert(steps1.end(), begin1 + static_cast<ptrdiff_t>(minLength), end1);
} }
if (_chromosome2.length() > minLength) if (_chromosome2.length() > minLength)
{ {
if (swapTail) if (swapTail)
steps1.insert(steps1.end(), begin2 + minLength, end2); steps1.insert(steps1.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
else else
steps2.insert(steps2.end(), begin2 + minLength, end2); steps2.insert(steps2.end(), begin2 + static_cast<ptrdiff_t>(minLength), end2);
} }
return {Chromosome(steps1), Chromosome(steps2)}; return {Chromosome(steps1), Chromosome(steps2)};

View File

@ -30,7 +30,7 @@ vector<tuple<size_t, size_t>> RandomPairSelection::materialise(size_t _poolSize)
if (_poolSize < 2) if (_poolSize < 2)
return {}; return {};
size_t count = static_cast<size_t>(round(_poolSize * m_selectionSize)); auto count = static_cast<size_t>(round(_poolSize * m_selectionSize));
vector<tuple<size_t, size_t>> selection; vector<tuple<size_t, size_t>> selection;
for (size_t i = 0; i < count; ++i) for (size_t i = 0; i < count; ++i)
@ -64,7 +64,10 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
} while (selectedIndices.size() % 2 != 0); } while (selectedIndices.size() % 2 != 0);
} }
else else
selectedIndices.erase(selectedIndices.begin() + SimulationRNG::uniformInt(0, selectedIndices.size() - 1)); selectedIndices.erase(
selectedIndices.begin() +
static_cast<ptrdiff_t>(SimulationRNG::uniformInt(0, selectedIndices.size() - 1))
);
} }
assert(selectedIndices.size() % 2 == 0); assert(selectedIndices.size() % 2 == 0);
@ -73,14 +76,14 @@ vector<tuple<size_t, size_t>> PairsFromRandomSubset::materialise(size_t _poolSiz
{ {
size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1); size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
size_t value1 = selectedIndices[position1]; size_t value1 = selectedIndices[position1];
selectedIndices.erase(selectedIndices.begin() + position1); selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position1));
size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1); size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
size_t value2 = selectedIndices[position2]; size_t value2 = selectedIndices[position2];
selectedIndices.erase(selectedIndices.begin() + position2); selectedIndices.erase(selectedIndices.begin() + static_cast<ptrdiff_t>(position2));
selectedPairs.push_back({value1, value2}); selectedPairs.emplace_back(value1, value2);
} }
assert(selectedIndices.size() == 0); assert(selectedIndices.empty());
return selectedPairs; return selectedPairs;
} }

View File

@ -17,12 +17,17 @@
#include <tools/yulPhaser/SimulationRNG.h> #include <tools/yulPhaser/SimulationRNG.h>
// NOTE: The code would work with std::random but the results for a given seed would not be reproducible
// across different STL implementations. Boost does not guarantee this either but at least it has only one
// implementation. Reproducibility is not a hard requirement for yul-phaser but it's nice to have.
#include <boost/random/bernoulli_distribution.hpp> #include <boost/random/bernoulli_distribution.hpp>
#include <boost/random/binomial_distribution.hpp> #include <boost/random/binomial_distribution.hpp>
#include <boost/random/uniform_int_distribution.hpp> #include <boost/random/uniform_int_distribution.hpp>
#include <ctime> #include <ctime>
#include <limits>
using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::phaser; using namespace solidity::phaser;
@ -30,23 +35,27 @@ thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::ge
bool SimulationRNG::bernoulliTrial(double _successProbability) bool SimulationRNG::bernoulliTrial(double _successProbability)
{ {
boost::random::bernoulli_distribution<> distribution(_successProbability); boost::random::bernoulli_distribution<double> distribution(_successProbability);
return static_cast<bool>(distribution(s_generator));
}
uint32_t SimulationRNG::uniformInt(uint32_t _min, uint32_t _max)
{
boost::random::uniform_int_distribution<> distribution(_min, _max);
return distribution(s_generator); return distribution(s_generator);
} }
uint32_t SimulationRNG::binomialInt(uint32_t _numTrials, double _successProbability) size_t SimulationRNG::uniformInt(size_t _min, size_t _max)
{ {
boost::random::binomial_distribution<> distribution(_numTrials, _successProbability); boost::random::uniform_int_distribution<size_t> distribution(_min, _max);
return distribution(s_generator); return distribution(s_generator);
} }
size_t SimulationRNG::binomialInt(size_t _numTrials, double _successProbability)
{
// NOTE: binomial_distribution<size_t> would not work because it internally tries to use abs()
// and fails to compile due to ambiguous conversion.
assert(_numTrials <= static_cast<size_t>(numeric_limits<long>::max()));
boost::random::binomial_distribution<long> distribution(static_cast<long>(_numTrials), _successProbability);
return static_cast<size_t>(distribution(s_generator));
}
uint32_t SimulationRNG::generateSeed() uint32_t SimulationRNG::generateSeed()
{ {
// This is not a secure way to seed the generator but it's good enough for simulation purposes. // This is not a secure way to seed the generator but it's good enough for simulation purposes.

View File

@ -38,8 +38,8 @@ class SimulationRNG
{ {
public: public:
static bool bernoulliTrial(double _successProbability); static bool bernoulliTrial(double _successProbability);
static uint32_t uniformInt(uint32_t _min, uint32_t _max); static size_t uniformInt(size_t _min, size_t _max);
static uint32_t binomialInt(uint32_t _numTrials, double _successProbability); static size_t binomialInt(size_t _numTrials, double _successProbability);
/// Resets generator to a known state given by the @a seed. Given the same seed, a fixed /// Resets generator to a known state given by the @a seed. Given the same seed, a fixed
/// sequence of calls to the members generating random values is guaranteed to produce the /// sequence of calls to the members generating random values is guaranteed to produce the