diff --git a/.circleci/config.yml b/.circleci/config.yml index 624af2f0e..4bebea97f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -598,7 +598,7 @@ jobs: b_ems: docker: - - image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-1 + - image: ethereum/solidity-buildpack-deps:emsdk-1.39.15-2 environment: TERM: xterm steps: diff --git a/.circleci/docker/Dockerfile.emscripten b/.circleci/docker/Dockerfile.emscripten index 67432f6c5..d2456bb2a 100644 --- a/.circleci/docker/Dockerfile.emscripten +++ b/.circleci/docker/Dockerfile.emscripten @@ -26,11 +26,9 @@ # contains a Makefile in the docker/ subdirectory that can be used to create the # 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-fastcomp AS base +FROM emscripten/emsdk:1.39.15 AS base ADD emscripten.jam /usr/src RUN set -ex; \ @@ -47,6 +45,7 @@ RUN set -ex; \ -DZ3_BUILD_TEST_EXECUTABLES=OFF \ -DZ3_BUILD_EXECUTABLE=OFF \ -DZ3_SINGLE_THREADED=ON \ + -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \ ..; \ make; make install; \ rm -r /usr/src/z3; \ @@ -61,6 +60,6 @@ RUN set -ex; \ echo "using emscripten : : em++ ;" >> project-config.jam ; \ ./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \ --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; \ rm -r /usr/src/boost_1_73_0 diff --git a/Changelog.md b/Changelog.md index a882bd780..e5f4da7b5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -19,39 +19,56 @@ Bugfixes: * 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: * 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. 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. - * 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 ``push`` and ``pop``. * SMTChecker: General support to BitVectors and the bitwise ``and`` operator. - * Add support for natspec comments on state variables. 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. + * 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 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 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 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) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index b6422654e..116b3dbe9 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -6,9 +6,8 @@ - [ ] Readthedocs account, access to the Solidity project - [ ] Write access to https://github.com/ethereum/homebrew-ethereum -### Pre-release - - [ ] Ensure that a Github project exists for the release. - - [ ] Check that all issues and pull requests from the Github project to be released are merged to ``develop``. +### Blog Post + - [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts. ### Changelog - [ ] 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``. - [ ] 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 + - [ ] 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 - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index b36af7e3f..1f7600d9b 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -16,9 +16,11 @@ include(EthCheckCXXCompilerFlag) -eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support) -if(NOT have_stack_protector_strong_support) - eth_add_cxx_compiler_flag_if_supported(-fstack-protector) +if(NOT EMSCRIPTEN) + eth_add_cxx_compiler_flag_if_supported(-fstack-protector-strong have_stack_protector_strong_support) + if(NOT have_stack_protector_strong_support) + eth_add_cxx_compiler_flag_if_supported(-fstack-protector) + endif() endif() 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) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # 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 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1") - # 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") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0") # Allow memory growth, but disable some optimisations set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1") # 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 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0") # Abort if linking results in any undefined symbols diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 1a131a2c8..3173bca69 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -42,7 +42,8 @@ For most of the topics the compiler will provide suggestions. storage arrays. * 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. diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 index a415c19f9..fdc80822b 100644 --- a/docs/Solidity.g4 +++ b/docs/Solidity.g4 @@ -124,7 +124,11 @@ userDefinedTypeName : identifier ( '.' identifier )* ; mapping - : 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ; + : 'mapping' '(' mappingKey '=>' typeName ')' ; + +mappingKey + : elementaryTypeName + | userDefinedTypeName ; functionTypeName : 'function' parameterList modifierList returnParameters? ; @@ -470,7 +474,7 @@ SingleQuotedStringCharacter : ~['\r\n\\] | ('\\' .) ; VersionLiteral - : [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ; + : [0-9]+ ( '.' [0-9]+ ('.' [0-9]+)? )? ; WS : [ \t\r\n\u000C]+ -> skip ; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index aba9c78b5..b51dd4e17 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -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 -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 location specifier, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 248517b8b..2d81e8783 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1163,5 +1163,9 @@ "0.6.8": { "bugs": [], "released": "2020-05-14" + }, + "0.6.9": { + "bugs": [], + "released": "2020-06-04" } } \ No newline at end of file diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 7ef17a690..0839cd6e0 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -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 virtual method lookup. -.. _function-overriding: - .. index:: ! overriding;function +.. _function-overriding: + Function Overriding =================== @@ -317,10 +317,10 @@ of the variable: While public state variables can override external functions, they themselves cannot be overridden. -.. _modifier-overriding: - .. index:: ! overriding;modifier +.. _modifier-overriding: + Modifier Overriding =================== diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 757867d1d..49b28bada 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -82,7 +82,7 @@ TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const bool TypeChecker::visit(ContractDefinition const& _contract) { - m_scope = &_contract; + m_currentContract = &_contract; ASTNode::listAccept(_contract.baseContracts(), *this); @@ -266,7 +266,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) auto base = dynamic_cast(&dereference(_inheritance.name())); 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."); if (base->isLibrary()) @@ -328,8 +328,6 @@ void TypeChecker::endVisit(ModifierDefinition const& _modifier) bool TypeChecker::visit(FunctionDefinition const& _function) { - bool isLibraryFunction = _function.inContractKind() == ContractKind::Library; - if (_function.markedVirtual()) { if (_function.annotation().contract->isInterface()) @@ -342,7 +340,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isPayable()) { - if (isLibraryFunction) + if (_function.libraryFunction()) m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) 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 (!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."); else m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." ); } else - { - solAssert(isLibraryFunction || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions."); - } + solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions."); } 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."); if (_function.isPublic()) { - auto iType = type(var)->interfaceType(isLibraryFunction); + auto iType = type(var)->interfaceType(_function.libraryFunction()); if (!iType) { @@ -380,7 +376,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if ( _function.isPublic() && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && - !typeSupportedByOldABIEncoder(*type(var), isLibraryFunction) + !typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction()) ) m_errorReporter.typeError( 4957_error, @@ -419,7 +415,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_scope->isInterface()) + if (m_currentContract->isInterface()) { if (_function.isImplemented()) 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()) 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()) m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) 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."); else if (!_function.virtualSemantics()) 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 ( - m_scope->derivesFrom(*_functionType->declaration().annotation().contract) && + m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && !dynamic_cast(_functionType->declaration()).isImplemented() ) m_errorReporter.typeError( @@ -1832,7 +1828,7 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function) { solAssert(_function.isFallback(), ""); - if (_function.inContractKind() == ContractKind::Library) + if (_function.libraryFunction()) m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions."); if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) m_errorReporter.typeError( @@ -1859,7 +1855,7 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) { solAssert(_function.isReceive(), ""); - if (_function.inContractKind() == ContractKind::Library) + if (_function.libraryFunction()) m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions."); if (_function.stateMutability() != StateMutability::Payable) @@ -1918,7 +1914,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; 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 ); @@ -2318,7 +2314,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) case FunctionType::Kind::ABIDecode: { bool const abiEncoderV2 = - m_scope->sourceUnit().annotation().experimentalFeatures.count( + m_currentContract->sourceUnit().annotation().experimentalFeatures.count( ExperimentalFeature::ABIEncoderV2 ); returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); @@ -2514,13 +2510,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->abstract()) m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - solAssert(!!m_scope, ""); - m_scope->annotation().contractDependencies.insert(contract); + solAssert(!!m_currentContract, ""); + m_currentContract->annotation().contractDependencies.insert(contract); solAssert( !contract->annotation().linearizedBaseContracts.empty(), "Linearized base contracts not yet available." ); - if (contractDependenciesAreCyclic(*m_scope)) + if (contractDependenciesAreCyclic(*m_currentContract)) m_errorReporter.typeError( 4579_error, _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. 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(); if (initialMemberCount > 1 && arguments) { @@ -2593,7 +2589,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) DataLocation::Storage, exprType ); - if (!storageType->members(m_scope).membersByName(memberName).empty()) + if (!storageType->members(m_currentContract).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( 4994_error, _memberAccess.location(), @@ -2749,7 +2745,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { annotation.isPure = true; ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - m_scope->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); if ( memberName == "runtimeCode" && @@ -2761,7 +2757,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "\"runtimeCode\" is not available for contracts containing immutable variables." ); - if (contractDependenciesAreCyclic(*m_scope)) + if (contractDependenciesAreCyclic(*m_currentContract)) m_errorReporter.typeError( 4224_error, _memberAccess.location(), diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 5c327bf2e..9997c700a 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -165,7 +165,7 @@ private: /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression, bool _ordinaryAssignment); - ContractDefinition const* m_scope = nullptr; + ContractDefinition const* m_currentContract = nullptr; langutil::EVMVersion m_evmVersion; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 230e0a8ba..09c8a34b3 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -276,7 +276,7 @@ void ViewPureChecker::reportMutability( { // We do not warn for library functions because they cannot be payable anyway. // 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) m_errorReporter.typeError( diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 3edb08476..c11a05a02 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -287,11 +287,11 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const return initAnnotation(); } -ContractKind FunctionDefinition::inContractKind() const +bool FunctionDefinition::libraryFunction() const { - auto contractDef = dynamic_cast(scope()); - solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set."); - return contractDef->contractKind(); + if (auto const* contractDef = dynamic_cast(scope())) + return contractDef->isLibrary(); + return false; } FunctionTypePointer FunctionDefinition::functionType(bool _internal) const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 721e76791..c1c02b2bb 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -799,6 +799,7 @@ public: void accept(ASTConstVisitor& _visitor) const override; StateMutability stateMutability() const { return m_stateMutability; } + bool libraryFunction() const; bool isOrdinary() const { return m_kind == Token::Function; } bool isConstructor() const { return m_kind == Token::Constructor; } 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. std::string externalIdentifierHex() const; - ContractKind inContractKind() const; - TypePointer type() const override; TypePointer typeViaContractName() const override; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 075cad1f3..a62251ab1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1730,7 +1730,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) setLValue(_indexAccess, IRLValue{ *arrayType.baseType(), - IRLValue::Memory{memAddress} + IRLValue::Memory{memAddress, arrayType.isByteArray()} }); break; } @@ -1763,7 +1763,23 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } } else if (baseType.category() == Type::Category::FixedBytes) - solUnimplementedAssert(false, ""); + { + auto const& fixedBytesType = dynamic_cast(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(, )) { invalid() } + let := (byte(, )) + )") + ("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) { solAssert(baseType.sizeOnStack() == 0, ""); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 8ec6b3564..8f74dbb3e 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -604,7 +604,11 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con _target.type == VerificationTarget::Type::UnderOverflow, "" ); - auto intType = dynamic_cast(_target.expression->annotation().type); + IntegerType const* intType = nullptr; + if (auto const* type = dynamic_cast(_target.expression->annotation().type)) + intType = type; + else + intType = TypeProvider::uint256(); solAssert(intType, ""); checkCondition( _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, "" ); - auto intType = dynamic_cast(_target.expression->annotation().type); + IntegerType const* intType = nullptr; + if (auto const* type = dynamic_cast(_target.expression->annotation().type)) + intType = type; + else + intType = TypeProvider::uint256(); + solAssert(intType, ""); checkCondition( _target.constraints && _constraints && _target.value > smt::maxValue(*intType), diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index b06abbe6e..b9c43f2c5 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -385,44 +385,22 @@ void SMTEncoder::endVisit(Assignment const& _assignment) } else { - auto const& type = _assignment.annotation().type; - vector rightArguments; - if (auto const* tupleTypeRight = dynamic_cast(_assignment.rightHandSide().annotation().type)) - { - auto symbTupleLeft = dynamic_pointer_cast(m_context.expression(_assignment.leftHandSide())); - solAssert(symbTupleLeft, ""); - auto symbTupleRight = dynamic_pointer_cast(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(_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))); - } + if (dynamic_cast(_assignment.rightHandSide().annotation().type)) + tupleAssignment(_assignment.leftHandSide(), _assignment.rightHandSide()); else { + auto const& type = _assignment.annotation().type; auto rightHandSide = compoundOps.count(op) ? compoundAssignment(_assignment) : expr(_assignment.rightHandSide(), type); 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, ""); if (varDecl->hasReferenceOrMappingType()) - 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(prefix); - solAssert(mapPrefix, ""); - prefix = mapPrefix->valueType(); - } - else - { - auto arrayPrefix = dynamic_cast(prefix); - solAssert(arrayPrefix, ""); - prefix = arrayPrefix->baseType(); - } - } - return false; - }); + resetReferences(*varDecl); auto symbArray = dynamic_pointer_cast(m_context.variable(*varDecl)); smtutil::Expression store = smtutil::Expression::store( @@ -1160,6 +1107,8 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression { auto varDecl = identifierToVariable(*id); solAssert(varDecl, ""); + if (varDecl->hasReferenceOrMappingType()) + resetReferences(*varDecl); m_context.addAssertion(m_context.newValue(*varDecl) == _array); } else if (auto const* indexAccess = dynamic_cast(&_expr)) @@ -1213,7 +1162,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op) { auto type = _op.annotation().commonType; solAssert(type, ""); - if (type->category() == Type::Category::Integer) + if (type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint) { switch (_op.getOperator()) { @@ -1266,13 +1215,21 @@ pair SMTEncoder::arithmeticOperation( }; solAssert(validOperators.count(_op), ""); 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(_commonType)) + intType = type; + else + intType = TypeProvider::uint256(); - auto const& intType = dynamic_cast(*_commonType); smtutil::Expression valueNoMod( _op == Token::Add ? _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::Mod*/ _left % _right ); @@ -1280,20 +1237,23 @@ pair SMTEncoder::arithmeticOperation( if (_op == Token::Div || _op == Token::Mod) 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( - valueNoMod > smt::maxValue(intType), + valueNoMod > symbMax, valueNoMod % intValueRange, smtutil::Expression::ite( - valueNoMod < smt::minValue(intType), + valueNoMod < symbMin, valueNoMod % intValueRange, valueNoMod ) ); - if (intType.isSigned()) + if (intType->isSigned()) value = smtutil::Expression::ite( - value > smt::maxValue(intType), + value > symbMax, value - intValueRange, value ); @@ -1422,11 +1382,16 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp void SMTEncoder::assignment( Expression const& _left, - vector const& _right, + smtutil::Expression const& _right, TypePointer const& _type, langutil::SourceLocation const& _location ) { + solAssert( + _left.annotation().type->category() != Type::Category::Tuple, + "Tuple assignments should be handled by tupleAssignment." + ); + if (!smt::isSupportedType(_type->category())) { // 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)) - { - solAssert(_right.size() == 1, ""); - assignment(*varDecl, _right.front()); - } + assignment(*varDecl, _right); else if (dynamic_cast(&_left)) - { - solAssert(_right.size() == 1, ""); - arrayIndexAssignment(_left, _right.front()); - } - else if (auto tuple = dynamic_cast(&_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()); - } - } + arrayIndexAssignment(_left, _right); else m_errorReporter.warning( 8182_error, @@ -1468,6 +1416,52 @@ void SMTEncoder::assignment( ); } +void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) +{ + auto lTuple = dynamic_cast(&_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(&_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(_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) { static map const compoundToArithmetic{ @@ -1618,6 +1612,42 @@ void SMTEncoder::resetStateVariables() 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(prefix); + solAssert(mapPrefix, ""); + prefix = mapPrefix->valueType(); + } + else + { + auto arrayPrefix = dynamic_cast(prefix); + solAssert(arrayPrefix, ""); + prefix = arrayPrefix->baseType(); + } + } + return false; + }); +} + TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type) { if (auto refType = dynamic_cast(_type)) diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 0e4c170fc..824d39b34 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -157,10 +157,12 @@ protected: /// Will also be used for assignments of tuple components. void assignment( Expression const& _left, - std::vector const& _right, + smtutil::Expression const& _right, TypePointer const& _type, 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. smtutil::Expression compoundAssignment(Assignment const& _assignment); @@ -181,6 +183,9 @@ protected: void initializeLocalVariables(FunctionDefinition const& _function); void initializeFunctionCallParameters(CallableDeclaration const& _function, std::vector const& _callArgs); 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. TypePointer typeWithoutPointer(TypePointer const& _type); diff --git a/libsolutil/CommonData.cpp b/libsolutil/CommonData.cpp index d04dd15df..72071acde 100644 --- a/libsolutil/CommonData.cpp +++ b/libsolutil/CommonData.cpp @@ -53,9 +53,6 @@ string solidity::util::toHex(uint8_t _data, 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); size_t i = 0; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 4de5496fb..2b6908142 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -359,6 +359,13 @@ void AsmAnalyzer::operator()(Switch const& _switch) { 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); set cases; diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 84e365016..ce97225e0 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -24,6 +24,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -130,7 +131,7 @@ bytes toBytes(Opcode _o) return toBytes(uint8_t(_o)); } -static std::map const builtins = { +static map const builtins = { {"i32.load", 0x28}, {"i64.load", 0x29}, {"i32.load8_s", 0x2c}, @@ -240,46 +241,56 @@ bytes lebEncodeSigned(int64_t _n) bytes prefixSize(bytes _data) { size_t size = _data.size(); - return lebEncode(size) + std::move(_data); + return lebEncode(size) + move(_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) { - BinaryTransform bt; + map> const types = typeToFunctionMap(_module.imports, _module.functions); - for (size_t i = 0; i < _module.globals.size(); ++i) - bt.m_globals[_module.globals[i].variableName] = i; + map const globalIDs = enumerateGlobals(_module); + map const functionIDs = enumerateFunctions(_module); + map const functionTypes = enumerateFunctionTypes(types); - size_t funID = 0; - for (FunctionImport const& fun: _module.imports) - bt.m_functions[fun.internalName] = funID++; - for (FunctionDefinition const& fun: _module.functions) - bt.m_functions[fun.name] = funID++; + yulAssert(globalIDs.size() == _module.globals.size(), ""); + yulAssert(functionIDs.size() == _module.imports.size() + _module.functions.size(), ""); + yulAssert(functionTypes.size() == functionIDs.size(), ""); + yulAssert(functionTypes.size() >= types.size(), ""); bytes ret{0, 'a', 's', 'm'}; // version ret += bytes{1, 0, 0, 0}; - ret += bt.typeSection(_module.imports, _module.functions); - ret += bt.importSection(_module.imports); - ret += bt.functionSection(_module.functions); - ret += bt.memorySection(); - ret += bt.globalSection(); - ret += bt.exportSection(); + ret += typeSection(types); + ret += importSection(_module.imports, functionTypes); + ret += functionSection(_module.functions, functionTypes); + ret += memorySection(); + ret += globalSection(_module.globals); + ret += exportSection(functionIDs); + + map> subModulePosAndSize; for (auto const& sub: _module.subModules) { // TODO should we prefix and / or shorten the name? bytes data = BinaryTransform::run(sub.second); size_t length = data.size(); - ret += bt.customSection(sub.first, std::move(data)); - bt.m_subModulePosAndSize[sub.first] = {ret.size() - length, length}; + ret += customSection(sub.first, move(data)); + subModulePosAndSize[sub.first] = {ret.size() - length, length}; } + + BinaryTransform bt( + move(globalIDs), + move(functionIDs), + move(functionTypes), + move(subModulePosAndSize) + ); + ret += bt.codeSection(_module.functions); return ret; } @@ -302,7 +313,7 @@ bytes BinaryTransform::operator()(LocalVariable 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) @@ -311,12 +322,12 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call) // they are references to object names that should not end up in the code. if (_call.functionName == "dataoffset") { - string name = std::get(_call.arguments.at(0)).value; + string name = get(_call.arguments.at(0)).value; return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).first); } else if (_call.functionName == "datasize") { - string name = std::get(_call.arguments.at(0)).value; + string name = get(_call.arguments.at(0)).value; return toBytes(Opcode::I64Const) + lebEncodeSigned(m_subModulePosAndSize.at(name).second); } @@ -331,20 +342,24 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call) else { 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 ( _call.functionName.find(".load") != string::npos || _call.functionName.find(".store") != string::npos ) - // alignment and offset - ret += bytes{{3, 0}}; + // Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it + // 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; } } 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) @@ -360,7 +375,7 @@ bytes BinaryTransform::operator()(GlobalAssignment const& _assignment) return std::visit(*this, *_assignment.value) + toBytes(Opcode::GlobalSet) + - lebEncode(m_globals.at(_assignment.variableName)); + lebEncode(m_globalIDs.at(_assignment.variableName)); } 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 // we have locals of different types. - ret += lebEncode(1); // number of locals groups - ret += lebEncode(_function.locals.size()); - ret += toBytes(ValueType::I64); + if (_function.locals.size() == 0) + ret += lebEncode(0); // number of locals groups + else + { + ret += lebEncode(1); // number of locals groups + ret += lebEncode(_function.locals.size()); + ret += toBytes(ValueType::I64); + } m_locals.clear(); size_t varIdx = 0; @@ -448,7 +468,7 @@ bytes BinaryTransform::operator()(FunctionDefinition const& _function) yulAssert(m_labels.empty(), "Stray labels."); - return prefixSize(std::move(ret)); + return prefixSize(move(ret)); } BinaryTransform::Type BinaryTransform::typeOf(FunctionImport const& _import) @@ -487,9 +507,9 @@ vector BinaryTransform::encodeTypes(vector const& _typeNames) return result; } -bytes BinaryTransform::typeSection( - vector const& _imports, - vector const& _functions +map> BinaryTransform::typeToFunctionMap( + vector const& _imports, + vector const& _functions ) { map> types; @@ -498,12 +518,50 @@ bytes BinaryTransform::typeSection( for (auto const& fun: _functions) types[typeOf(fun)].emplace_back(fun.name); - bytes result; - size_t index = 0; - for (auto const& [type, funNames]: types) + return types; +} + +map BinaryTransform::enumerateGlobals(Module const& _module) +{ + map globals; + for (size_t i = 0; i < _module.globals.size(); ++i) + globals[_module.globals[i].variableName] = i; + + return globals; +} + +map BinaryTransform::enumerateFunctions(Module const& _module) +{ + map 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 BinaryTransform::enumerateFunctionTypes(map> const& _typeToFunctionMap) +{ + map functionTypes; + size_t typeID = 0; + for (vector const& funNames: _typeToFunctionMap | boost::adaptors::map_values) { for (string const& name: funNames) - m_functionTypes[name] = index; + functionTypes[name] = typeID; + ++typeID; + } + + return functionTypes; +} + +bytes BinaryTransform::typeSection(map> const& _typeToFunctionMap) +{ + bytes result; + size_t index = 0; + for (Type const& type: _typeToFunctionMap | boost::adaptors::map_keys) + { result += toBytes(ValueType::Function); result += lebEncode(type.first.size()) + type.first; result += lebEncode(type.second.size()) + type.second; @@ -511,11 +569,12 @@ bytes BinaryTransform::typeSection( index++; } - return makeSection(Section::TYPE, lebEncode(index) + std::move(result)); + return makeSection(Section::TYPE, lebEncode(index) + move(result)); } bytes BinaryTransform::importSection( - vector const& _imports + vector const& _imports, + map const& _functionTypes ) { bytes result = lebEncode(_imports.size()); @@ -526,17 +585,20 @@ bytes BinaryTransform::importSection( encodeName(import.module) + encodeName(import.externalName) + 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 const& _functions) +bytes BinaryTransform::functionSection( + vector const& _functions, + map const& _functionTypes +) { bytes result = lebEncode(_functions.size()); for (auto const& fun: _functions) - result += lebEncode(m_functionTypes.at(fun.name)); - return makeSection(Section::FUNCTION, std::move(result)); + result += lebEncode(_functionTypes.at(fun.name)); + return makeSection(Section::FUNCTION, move(result)); } bytes BinaryTransform::memorySection() @@ -544,13 +606,13 @@ bytes BinaryTransform::memorySection() bytes result = lebEncode(1); result.push_back(static_cast(LimitsKind::Min)); 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 const& _globals) { - bytes result = lebEncode(m_globals.size()); - for (size_t i = 0; i < m_globals.size(); ++i) + bytes result = lebEncode(_globals.size()); + for (size_t i = 0; i < _globals.size(); ++i) result += toBytes(ValueType::I64) + lebEncode(static_cast(Mutability::Var)) + @@ -558,21 +620,21 @@ bytes BinaryTransform::globalSection() lebEncodeSigned(0) + toBytes(Opcode::End); - return makeSection(Section::GLOBAL, std::move(result)); + return makeSection(Section::GLOBAL, move(result)); } -bytes BinaryTransform::exportSection() +bytes BinaryTransform::exportSection(map const& _functionIDs) { bytes result = lebEncode(2); result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0); - result += encodeName("main") + toBytes(Export::Function) + lebEncode(m_functions.at("main")); - return makeSection(Section::EXPORT, std::move(result)); + result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main")); + return makeSection(Section::EXPORT, move(result)); } bytes BinaryTransform::customSection(string const& _name, bytes _data) { - bytes result = encodeName(_name) + std::move(_data); - return makeSection(Section::CUSTOM, std::move(result)); + bytes result = encodeName(_name) + move(_data); + return makeSection(Section::CUSTOM, move(result)); } bytes BinaryTransform::codeSection(vector const& _functions) @@ -580,7 +642,7 @@ bytes BinaryTransform::codeSection(vector const& _func bytes result = lebEncode(_functions.size()); for (FunctionDefinition const& fun: _functions) result += (*this)(fun); - return makeSection(Section::CODE, std::move(result)); + return makeSection(Section::CODE, move(result)); } bytes BinaryTransform::visit(vector const& _expressions) @@ -611,7 +673,7 @@ bytes BinaryTransform::encodeLabelIdx(string const& _label) const 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 // Solidity or Yul identifiers or similar, non-ascii characters ending up here diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h index c1c924dcc..f4444b2fd 100644 --- a/libyul/backends/wasm/BinaryTransform.h +++ b/libyul/backends/wasm/BinaryTransform.h @@ -55,23 +55,49 @@ public: bytes operator()(wasm::FunctionDefinition const& _function); private: + BinaryTransform( + std::map _globalIDs, + std::map _functionIDs, + std::map _functionTypes, + std::map> _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>; static Type typeOf(wasm::FunctionImport const& _import); static Type typeOf(wasm::FunctionDefinition const& _funDef); static uint8_t encodeType(std::string const& _typeName); static std::vector encodeTypes(std::vector const& _typeNames); - bytes typeSection( + + static std::map> typeToFunctionMap( std::vector const& _imports, std::vector const& _functions ); - bytes importSection(std::vector const& _imports); - bytes functionSection(std::vector const& _functions); - bytes memorySection(); - bytes globalSection(); - bytes exportSection(); - bytes customSection(std::string const& _name, bytes _data); + static std::map enumerateGlobals(Module const& _module); + static std::map enumerateFunctions(Module const& _module); + static std::map enumerateFunctionTypes( + std::map> const& _typeToFunctionMap + ); + + static bytes typeSection(std::map> const& _typeToFunctionMap); + static bytes importSection( + std::vector const& _imports, + std::map const& _functionTypes + ); + static bytes functionSection( + std::vector const& _functions, + std::map const& _functionTypes + ); + static bytes memorySection(); + static bytes globalSection(std::vector const& _globals); + static bytes exportSection(std::map const& _functionIDs); + static bytes customSection(std::string const& _name, bytes _data); bytes codeSection(std::vector const& _functions); bytes visit(std::vector const& _expressions); @@ -81,12 +107,13 @@ private: static bytes encodeName(std::string const& _name); + std::map const m_globalIDs; + std::map const m_functionIDs; + std::map const m_functionTypes; + std::map> const m_subModulePosAndSize; + std::map m_locals; - std::map m_globals; - std::map m_functions; - std::map m_functionTypes; std::vector m_labels; - std::map> m_subModulePosAndSize; }; } diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index 2d7cae6c6..f9b9e7899 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -33,16 +33,16 @@ using namespace solidity::yul; VarNameCleaner::VarNameCleaner( Block const& _ast, Dialect const& _dialect, - set _blacklist + set _namesToKeep ): m_dialect{_dialect}, - m_blacklist{std::move(_blacklist)}, + m_namesToKeep{std::move(_namesToKeep)}, m_translatedNames{} { for (auto const& statement: _ast.statements) if (holds_alternative(statement)) - m_blacklist.insert(std::get(statement).name); - m_usedNames = m_blacklist; + m_namesToKeep.insert(std::get(statement).name); + m_usedNames = m_namesToKeep; } void VarNameCleaner::operator()(FunctionDefinition& _funDef) @@ -51,7 +51,7 @@ void VarNameCleaner::operator()(FunctionDefinition& _funDef) m_insideFunction = true; set globalUsedNames = std::move(m_usedNames); - m_usedNames = m_blacklist; + m_usedNames = m_namesToKeep; map globalTranslatedNames; swap(globalTranslatedNames, m_translatedNames); diff --git a/libyul/optimiser/VarNameCleaner.h b/libyul/optimiser/VarNameCleaner.h index 4bd4ca100..911f67ca6 100644 --- a/libyul/optimiser/VarNameCleaner.h +++ b/libyul/optimiser/VarNameCleaner.h @@ -63,7 +63,7 @@ private: VarNameCleaner( Block const& _ast, Dialect const& _dialect, - std::set _blacklist = {} + std::set _namesToKeep = {} ); /// Tries to rename a list of variables. @@ -77,11 +77,13 @@ private: YulString findCleanName(YulString const& name) const; /// 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; Dialect const& m_dialect; - std::set m_blacklist; + + /// These names will not be modified. + std::set m_namesToKeep; /// Set of names that are in use. std::set m_usedNames; diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index c338a1977..5c7e3f052 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -54,6 +54,10 @@ cmake \ -DTESTS=0 \ .. 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 .. mkdir -p upload diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 3e9b573e9..eec91ff06 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -740,7 +740,7 @@ remap paths using the context:prefix=path syntax. Example: 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 - 23 ); @@ -748,47 +748,67 @@ Allowed options)").c_str(), (g_argHelp.c_str(), "Show help message and exit.") (g_argVersion.c_str(), "Show version 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()->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()->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()->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(), po::value()->value_name("version"), "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " "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>()->value_name("libs"), - "Direct string or file containing library addresses. Syntax: " - ":
[, or whitespace] ...\n" - "Address is interpreted as a hex string optionally prefixed by 0x." - ) ( g_strRevertStrings.c_str(), po::value()->value_name(boost::join(g_revertStringsArgs, ",")), "Strip revert (and require) reason strings or add additional debugging information." ) - ( - (g_argOutputDir + ",o").c_str(), - po::value()->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_argCombinedJson.c_str(), - po::value()->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.") + ; + desc.add(outputOptions); + + po::options_description alternativeInputModes("Alternative Input Modes"); + alternativeInputModes.add_options() ( g_argStandardJSON.c_str(), "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." ) ( - g_argImportAst.c_str(), - ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " - "Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by " - "--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() + g_argLink.c_str(), + ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " + "and modify binaries in place.").c_str() ) ( g_argAssemble.c_str(), @@ -809,58 +829,62 @@ Allowed options)").c_str(), "and assumes input is strict assembly.").c_str() ) ( - g_strYulDialect.c_str(), - po::value()->value_name(boost::join(g_yulDialectArgs, ",")), - "Input dialect to use in assembly or yul mode." + g_argImportAst.c_str(), + ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " + "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(), po::value()->value_name(boost::join(g_machineArgs, ",")), "Target machine in assembly or Yul mode." ) ( - g_argLink.c_str(), - ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " - "and modify binaries in place.").c_str() + g_strYulDialect.c_str(), + po::value()->value_name(boost::join(g_yulDialectArgs, ",")), + "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>()->value_name("libs"), + "Direct string or file containing library addresses. Syntax: " + ":
[, 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(), - po::value()->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.") - ( - g_argAllowPaths.c_str(), - po::value()->value_name("path(s)"), - "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." + g_argColor.c_str(), + "Force colored output." ) ( - g_argBasePath.c_str(), - po::value()->value_name("path"), - "Use the given path as the root of the source tree instead of the root of the filesystem." + g_argNoColor.c_str(), + "Explicitly disable colored output, disabling terminal auto-detection." ) - (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(), - po::value()->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_argOldReporter.c_str(), + "Enables old diagnostics reporter (legacy option, will be removed)." ) - (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()->value_name("steps"), - "Forces yul optimizer to use the specified sequence of optimization steps instead of the built-in one." - ); - desc.add(optimizerOptions); + ; + desc.add(outputFormatting); + po::options_description outputComponents("Output Components"); outputComponents.add_options() (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_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") (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); + 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()->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()->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()->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()->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; allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 31b982d0d..7e8bd21cc 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -45,7 +45,7 @@ object "object" { Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acd01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b4101007e02402000a7200110043703002000a74208a76aada7200210043703002000a74210a76aada7200310043703002000a74218a76aada7200410043703000b0b +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b Text representation: (module diff --git a/test/cmdlineTests/evm_to_wasm_break/output b/test/cmdlineTests/evm_to_wasm_break/output index 2325fa737..4d5e10921 100644 --- a/test/cmdlineTests/evm_to_wasm_break/output +++ b/test/cmdlineTests/evm_to_wasm_break/output @@ -154,7 +154,7 @@ object "object" { Binary representation: -0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa9090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290300100c21084200a74208a76aada7290300100c21094200a74210a76aada7290300100c210a4200a74218a76aada7290300100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b4101007e02402000a72001100c3703002000a74208a76aada72002100c3703002000a74210a76aada72003100c3703002000a74218a76aada72004100c3703000b0b2701007e024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b +0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b Text representation: (module diff --git a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/args b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/args new file mode 100644 index 000000000..04cd5f05b --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/args @@ -0,0 +1 @@ +--yul --yul-dialect ewasm --machine ewasm diff --git a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/err b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/input.yul b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/input.yul new file mode 100644 index 000000000..731e6c9bb --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/input.yul @@ -0,0 +1,8 @@ +object "object" { + code { + function main() + { + i64.store8(0x01:i32, 42:i64) + } + } +} diff --git a/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output new file mode 100644 index 000000000..ef0b409fb --- /dev/null +++ b/test/cmdlineTests/wasm_to_wasm_memory_instructions_alignment/output @@ -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)) + ) +) + +) diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.json b/test/libsolidity/ASTJSON/assembly/switch_default.json index 4cb7b9387..4226a8ea3 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default.json @@ -31,14 +31,14 @@ { "id": 4, "nodeType": "Block", - "src": "42:48:1", + "src": "42:58:1", "statements": [ { "AST": { "nodeType": "YulBlock", - "src": "61:23:1", + "src": "61:33:1", "statements": [ { @@ -48,11 +48,29 @@ "body": { "nodeType": "YulBlock", - "src": "80:2:1", + "src": "79:2:1", "statements": [] }, "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" } ], @@ -65,7 +83,7 @@ "value": "0" }, "nodeType": "YulSwitch", - "src": "63:19:1" + "src": "63:29:1" } ] }, @@ -73,7 +91,7 @@ "externalReferences": [], "id": 3, "nodeType": "InlineAssembly", - "src": "52:32:1" + "src": "52:42:1" } ] }, @@ -99,15 +117,15 @@ "src": "42:0:1" }, "scope": 6, - "src": "17:73:1", + "src": "17:83:1", "stateMutability": "view", "virtual": false, "visibility": "public" } ], "scope": 7, - "src": "0:92:1" + "src": "0:102:1" } ], - "src": "0:93:1" + "src": "0:103:1" } diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.sol b/test/libsolidity/ASTJSON/assembly/switch_default.sol index 8bfba2c11..1f61896fe 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default.sol +++ b/test/libsolidity/ASTJSON/assembly/switch_default.sol @@ -1,6 +1,6 @@ contract C { function g() view public { - assembly { switch 0 default {} } + assembly { switch 0 case 0 {} default {} } } } diff --git a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json index 8c5ab802b..324330f86 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default_legacy.json @@ -91,30 +91,30 @@ [ null ], - "operations": "{\n switch 0\n default { }\n}" + "operations": "{\n switch 0\n case 0 { }\n default { }\n}" }, "children": [], "id": 3, "name": "InlineAssembly", - "src": "52:32:1" + "src": "52:42:1" } ], "id": 4, "name": "Block", - "src": "42:48:1" + "src": "42:58:1" } ], "id": 5, "name": "FunctionDefinition", - "src": "17:73:1" + "src": "17:83:1" } ], "id": 6, "name": "ContractDefinition", - "src": "0:92:1" + "src": "0:102:1" } ], "id": 7, "name": "SourceUnit", - "src": "0:93:1" + "src": "0:103:1" } diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 2a1b72e50..1d61484a9 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -314,9 +314,17 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) BOOST_AUTO_TEST_CASE(switch_invalid_expression) { - CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected."); - CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'"); - CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); + CHECK_PARSE_ERROR("{ switch {} case 1 {} default {} }", ParserError, "Literal or identifier expected."); + CHECK_PARSE_ERROR( + "{ 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) diff --git a/test/libsolidity/semanticTests/array/index_access.sol b/test/libsolidity/semanticTests/array/index_access.sol new file mode 100644 index 000000000..acb33fc46 --- /dev/null +++ b/test/libsolidity/semanticTests/array/index_access.sol @@ -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 diff --git a/test/libsolidity/semanticTests/getters/array_mapping_struct.sol b/test/libsolidity/semanticTests/getters/array_mapping_struct.sol new file mode 100644 index 000000000..534dcd8a6 --- /dev/null +++ b/test/libsolidity/semanticTests/getters/array_mapping_struct.sol @@ -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 diff --git a/test/libsolidity/semanticTests/getters/mapping_array_struct.sol b/test/libsolidity/semanticTests/getters/mapping_array_struct.sol new file mode 100644 index 000000000..2b237c4df --- /dev/null +++ b/test/libsolidity/semanticTests/getters/mapping_array_struct.sol @@ -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 diff --git a/test/libsolidity/semanticTests/getters/mapping_of_string.sol b/test/libsolidity/semanticTests/getters/mapping_of_string.sol new file mode 100644 index 000000000..ea0a8b8f8 --- /dev/null +++ b/test/libsolidity/semanticTests/getters/mapping_of_string.sol @@ -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 diff --git a/test/libsolidity/semanticTests/getters/struct_with_bytes.sol b/test/libsolidity/semanticTests/getters/struct_with_bytes.sol new file mode 100644 index 000000000..2bbd7e8b8 --- /dev/null +++ b/test/libsolidity/semanticTests/getters/struct_with_bytes.sol @@ -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 diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol new file mode 100644 index 000000000..70f8cb9fc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol @@ -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 diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol new file mode 100644 index 000000000..23a05e619 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol @@ -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 diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol new file mode 100644 index 000000000..d4cbbc696 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol @@ -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 diff --git a/test/libsolidity/smtCheckerTests/types/tuple_tuple.sol b/test/libsolidity/smtCheckerTests/types/tuple_tuple.sol new file mode 100644 index 000000000..c12e56a2d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_tuple.sol @@ -0,0 +1,6 @@ +pragma experimental SMTChecker; +contract C { + function f3() public pure { + ((, ), ) = ((7, 8), 9); + } +} diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol index d72310924..a32599d7b 100644 --- a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol @@ -1,15 +1,7 @@ contract C { struct S { bool f; } S s; - function f(uint256 a) internal pure { - S storage c; - assembly { - switch a - default { c_slot := s_slot } - } - c; - } - function g(bool flag) internal pure { + function f(bool flag) internal pure { S storage c; assembly { switch flag @@ -18,7 +10,7 @@ contract C { } c; } - function h(uint256 a) internal pure { + function g(uint256 a) internal pure { S storage c; assembly { switch a diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol index 9e73ecff1..8d0b564fc 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_fine.sol @@ -1,20 +1,14 @@ 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 } - } - } - function g(bool flag) internal pure returns (S storage c) { + function f(bool flag) internal pure returns (S storage c) { assembly { switch flag case 0 { 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 { switch a case 0 { revert(0, 0) } diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_only_default_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_only_default_warn.sol new file mode 100644 index 000000000..fff82c20d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_only_default_warn.sol @@ -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. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index ee6d77dab..57ee7ffa6 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -87,12 +87,12 @@ pair, shared_ptr> yul::test::parse( shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); if (!parserResult) return {}; - if (!parserResult->code || !errorReporter.errors().empty()) + if (!parserResult->code || errorReporter.hasErrors()) return {}; shared_ptr analysisInfo = make_shared(); AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames()); // TODO this should be done recursively. - if (!analyzer.analyze(*parserResult->code) || !errorReporter.errors().empty()) + if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors()) return {}; return {std::move(parserResult->code), std::move(analysisInfo)}; } diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index 2d1cc1c4e..7ae9e44f5 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -332,7 +332,7 @@ BOOST_FIXTURE_TEST_CASE(if_statement_custom_weights, CustomWeightFixture) BOOST_AUTO_TEST_CASE(switch_statement_tiny) { BOOST_CHECK_EQUAL(codeSize( - "{ switch calldatasize() default {} }" + "{ switch calldatasize() case 0 {} }" ), 4); } diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index bda7c6514..a4507e180 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -362,7 +362,7 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c ErrorList errors; soltestAssert(m_dialect, ""); 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; printErrors(_stream, errors); diff --git a/test/yulPhaser/Mutations.cpp b/test/yulPhaser/Mutations.cpp index 27e57be08..b20cb8e4c 100644 --- a/test/yulPhaser/Mutations.cpp +++ b/test/yulPhaser/Mutations.cpp @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position BOOST_TEST(mutatedChromosome.length() > chromosome.length()); vector suffix( - mutatedChromosome.optimisationSteps().end() - chromosome.length(), + mutatedChromosome.optimisationSteps().end() - static_cast(chromosome.length()), mutatedChromosome.optimisationSteps().end() ); BOOST_TEST(suffix == chromosome.optimisationSteps()); @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) vector prefix( mutatedChromosome.optimisationSteps().begin(), - mutatedChromosome.optimisationSteps().begin() + chromosome.length() + mutatedChromosome.optimisationSteps().begin() + static_cast(chromosome.length()) ); 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) { Chromosome mutatedChromosome = mutation(chromosome); - cCount += static_cast(mutatedChromosome == Chromosome("c")); - fCount += static_cast(mutatedChromosome == Chromosome("f")); + cCount += (mutatedChromosome == Chromosome("c") ? 1 : 0); + fCount += (mutatedChromosome == Chromosome("f") ? 1 : 0); } // This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance. diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 1657e97c9..5fd8c2073 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -135,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_get_chromosome_lengths_from_specified_ size_t maxLength = 5; 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); // We can't rely on the order since the population sorts its chromosomes immediately but diff --git a/test/yulPhaser/TestHelpers.cpp b/test/yulPhaser/TestHelpers.cpp index 2379016fe..2f4b40b02 100644 --- a/test/yulPhaser/TestHelpers.cpp +++ b/test/yulPhaser/TestHelpers.cpp @@ -72,9 +72,13 @@ size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome { size_t count = 0; for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i) - count += static_cast(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]); + if (_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]) + ++count; - return count + abs(static_cast(_chromosome1.length() - _chromosome2.length())); + return count + static_cast(abs( + static_cast(_chromosome1.length()) - + static_cast(_chromosome2.length()) + )); } TemporaryDirectory::TemporaryDirectory(std::string const& _prefix): diff --git a/tools/yulPhaser/GeneticAlgorithms.cpp b/tools/yulPhaser/GeneticAlgorithms.cpp index 1da31645f..4a90503aa 100644 --- a/tools/yulPhaser/GeneticAlgorithms.cpp +++ b/tools/yulPhaser/GeneticAlgorithms.cpp @@ -155,7 +155,7 @@ Population ClassicGeneticAlgorithm::select(Population _population, size_t _selec vector selectedIndividuals; 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; for (auto const& individual: _population.individuals()) diff --git a/tools/yulPhaser/Mutations.cpp b/tools/yulPhaser/Mutations.cpp index 2b988e622..f2876e395 100644 --- a/tools/yulPhaser/Mutations.cpp +++ b/tools/yulPhaser/Mutations.cpp @@ -126,12 +126,12 @@ ChromosomePair fixedPointSwap( return { Chromosome( - vector(begin1, begin1 + _crossoverPoint) + - vector(begin2 + _crossoverPoint, end2) + vector(begin1, begin1 + static_cast(_crossoverPoint)) + + vector(begin2 + static_cast(_crossoverPoint), end2) ), Chromosome( - vector(begin2, begin2 + _crossoverPoint) + - vector(begin1 + _crossoverPoint, end1) + vector(begin2, begin2 + static_cast(_crossoverPoint)) + + vector(begin1 + static_cast(_crossoverPoint), end1) ), }; } @@ -196,8 +196,8 @@ ChromosomePair fixedTwoPointSwap( assert(_crossoverPoint2 <= _chromosome1.length()); assert(_crossoverPoint2 <= _chromosome2.length()); - size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2); - size_t highPoint = max(_crossoverPoint1, _crossoverPoint2); + auto lowPoint = static_cast(min(_crossoverPoint1, _crossoverPoint2)); + auto highPoint = static_cast(max(_crossoverPoint1, _crossoverPoint2)); auto begin1 = _chromosome1.optimisationSteps().begin(); auto begin2 = _chromosome2.optimisationSteps().begin(); @@ -282,17 +282,17 @@ ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _ch if (_chromosome1.length() > minLength) { if (swapTail) - steps2.insert(steps2.end(), begin1 + minLength, end1); + steps2.insert(steps2.end(), begin1 + static_cast(minLength), end1); else - steps1.insert(steps1.end(), begin1 + minLength, end1); + steps1.insert(steps1.end(), begin1 + static_cast(minLength), end1); } if (_chromosome2.length() > minLength) { if (swapTail) - steps1.insert(steps1.end(), begin2 + minLength, end2); + steps1.insert(steps1.end(), begin2 + static_cast(minLength), end2); else - steps2.insert(steps2.end(), begin2 + minLength, end2); + steps2.insert(steps2.end(), begin2 + static_cast(minLength), end2); } return {Chromosome(steps1), Chromosome(steps2)}; diff --git a/tools/yulPhaser/PairSelections.cpp b/tools/yulPhaser/PairSelections.cpp index 7e744ff70..8934c3e17 100644 --- a/tools/yulPhaser/PairSelections.cpp +++ b/tools/yulPhaser/PairSelections.cpp @@ -30,7 +30,7 @@ vector> RandomPairSelection::materialise(size_t _poolSize) if (_poolSize < 2) return {}; - size_t count = static_cast(round(_poolSize * m_selectionSize)); + auto count = static_cast(round(_poolSize * m_selectionSize)); vector> selection; for (size_t i = 0; i < count; ++i) @@ -64,7 +64,10 @@ vector> PairsFromRandomSubset::materialise(size_t _poolSiz } while (selectedIndices.size() % 2 != 0); } else - selectedIndices.erase(selectedIndices.begin() + SimulationRNG::uniformInt(0, selectedIndices.size() - 1)); + selectedIndices.erase( + selectedIndices.begin() + + static_cast(SimulationRNG::uniformInt(0, selectedIndices.size() - 1)) + ); } assert(selectedIndices.size() % 2 == 0); @@ -73,14 +76,14 @@ vector> PairsFromRandomSubset::materialise(size_t _poolSiz { size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1); size_t value1 = selectedIndices[position1]; - selectedIndices.erase(selectedIndices.begin() + position1); + selectedIndices.erase(selectedIndices.begin() + static_cast(position1)); size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1); size_t value2 = selectedIndices[position2]; - selectedIndices.erase(selectedIndices.begin() + position2); + selectedIndices.erase(selectedIndices.begin() + static_cast(position2)); - selectedPairs.push_back({value1, value2}); + selectedPairs.emplace_back(value1, value2); } - assert(selectedIndices.size() == 0); + assert(selectedIndices.empty()); return selectedPairs; } diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 2b596f4ed..26f438909 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -17,12 +17,17 @@ #include +// 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 #include #include #include +#include +using namespace std; using namespace solidity; using namespace solidity::phaser; @@ -30,23 +35,27 @@ thread_local boost::random::mt19937 SimulationRNG::s_generator(SimulationRNG::ge bool SimulationRNG::bernoulliTrial(double _successProbability) { - boost::random::bernoulli_distribution<> distribution(_successProbability); + boost::random::bernoulli_distribution distribution(_successProbability); - return static_cast(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); } -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 distribution(_min, _max); return distribution(s_generator); } +size_t SimulationRNG::binomialInt(size_t _numTrials, double _successProbability) +{ + // NOTE: binomial_distribution would not work because it internally tries to use abs() + // and fails to compile due to ambiguous conversion. + assert(_numTrials <= static_cast(numeric_limits::max())); + + boost::random::binomial_distribution distribution(static_cast(_numTrials), _successProbability); + return static_cast(distribution(s_generator)); +} + uint32_t SimulationRNG::generateSeed() { // This is not a secure way to seed the generator but it's good enough for simulation purposes. diff --git a/tools/yulPhaser/SimulationRNG.h b/tools/yulPhaser/SimulationRNG.h index 1e8a1da18..d16189f9e 100644 --- a/tools/yulPhaser/SimulationRNG.h +++ b/tools/yulPhaser/SimulationRNG.h @@ -38,8 +38,8 @@ class SimulationRNG { public: static bool bernoulliTrial(double _successProbability); - static uint32_t uniformInt(uint32_t _min, uint32_t _max); - static uint32_t binomialInt(uint32_t _numTrials, double _successProbability); + static size_t uniformInt(size_t _min, size_t _max); + 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 /// sequence of calls to the members generating random values is guaranteed to produce the