From 38486f47a760a9541f646fef1e9a2be8fd1db2a5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 16 Apr 2020 11:57:07 +0200 Subject: [PATCH 01/65] Example ERC20 contract in Yul. --- docs/yul.rst | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/docs/yul.rst b/docs/yul.rst index b99dd18c1..c200558b0 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -104,6 +104,8 @@ less-than comparison. } } +At the :ref:`end of the section `, a complete implementation of +the ERC-20 standard can be found. @@ -1087,3 +1089,198 @@ d `VarDeclInitializer` Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`. For this reason the Yul optimizer always applies them before applying any steps supplied by the user. + + +.. _erc20yul: + +Complete ERC20 Example +====================== + +.. code-block:: yul + + object "Token" { + code { + // Store the creator in slot zero. + sstore(0, caller()) + + // Deploy the contract + datacopy(0, dataoffset("runtime"), datasize("runtime")) + return(0, datasize("runtime")) + } + object "runtime" { + code { + // Protection against sending Ether + require(iszero(callvalue())) + + // Dispatcher + switch selector() + case 0x70a08231 /* "balanceOf(address)" */ { + returnUint(balanceOf(decodeAsAddress(0))) + } + case 0x18160ddd /* "totalSupply()" */ { + returnUint(totalSupply()) + } + case 0xa9059cbb /* "transfer(address,uint256)" */ { + transfer(decodeAsAddress(0), decodeAsUint(1)) + returnTrue() + } + case 0x23b872dd /* "transferFrom(address,address,uint256)" */ { + transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2)) + returnTrue() + } + case 0x095ea7b3 /* "approve(address,uint256)" */ { + approve(decodeAsAddress(0), decodeAsUint(1)) + returnTrue() + } + case 0xdd62ed3e /* "allowance(address,address)" */ { + returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1))) + } + case 0x40c10f19 /* "mint(address,uint256)" */ { + mint(decodeAsAddress(0), decodeAsUint(1)) + returnTrue() + } + default { + revert(0, 0) + } + + function mint(account, amount) { + require(calledByOwner()) + + mintTokens(amount) + addToBalance(account, amount) + emitTransfer(0, account, amount) + } + function transfer(to, amount) { + executeTransfer(caller(), to, amount) + } + function approve(spender, amount) { + revertIfZeroAddress(spender) + setAllowance(caller(), spender, amount) + emitApproval(caller(), spender, amount) + } + function transferFrom(from, to, amount) { + decreaseAllowanceBy(from, caller(), amount) + executeTransfer(from, to, amount) + } + + function executeTransfer(from, to, amount) { + revertIfZeroAddress(to) + deductFromBalance(from, amount) + addToBalance(to, amount) + emitTransfer(from, to, amount) + } + + + /* ---------- calldata decoding functions ----------- */ + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + function decodeAsAddress(offset) -> v { + v := decodeAsUint(offset) + if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) { + revert(0, 0) + } + } + function decodeAsUint(offset) -> v { + let pos := add(4, mul(offset, 0x20)) + if lt(calldatasize(), add(pos, 0x20)) { + revert(0, 0) + } + v := calldataload(pos) + } + /* ---------- calldata encoding functions ---------- */ + function returnUint(v) { + mstore(0, v) + return(0, 0x20) + } + function returnTrue() { + returnUint(1) + } + + /* -------- events ---------- */ + function emitTransfer(from, to, amount) { + let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + emitEvent(signatureHash, from, to, amount) + } + function emitApproval(from, spender, amount) { + let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925 + emitEvent(signatureHash, from, spender, amount) + } + function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) { + mstore(0, nonIndexed) + log3(0, 0x20, signatureHash, indexed1, indexed2) + } + + /* -------- storage layout ---------- */ + function ownerPos() -> p { p := 0 } + function totalSupplyPos() -> p { p := 1 } + function accountToStorageOffset(account) -> offset { + offset := add(0x1000, account) + } + function allowanceStorageOffset(account, spender) -> offset { + offset := accountToStorageOffset(account) + mstore(0, offset) + mstore(0x20, spender) + offset := keccak256(0, 0x40) + } + + /* -------- storage access ---------- */ + function owner() -> o { + o := sload(ownerPos()) + } + function totalSupply() -> supply { + supply := sload(totalSupplyPos()) + } + function mintTokens(amount) { + sstore(totalSupplyPos(), safeAdd(totalSupply(), amount)) + } + function balanceOf(account) -> bal { + bal := sload(accountToStorageOffset(account)) + } + function addToBalance(account, amount) { + let offset := accountToStorageOffset(account) + sstore(offset, safeAdd(sload(offset), amount)) + } + function deductFromBalance(account, amount) { + let offset := accountToStorageOffset(account) + let bal := sload(offset) + require(lte(amount, bal)) + sstore(offset, sub(bal, amount)) + } + function allowance(account, spender) -> amount { + amount := sload(allowanceStorageOffset(account, spender)) + } + function setAllowance(account, spender, amount) { + sstore(allowanceStorageOffset(account, spender), amount) + } + function decreaseAllowanceBy(account, spender, amount) { + let offset := allowanceStorageOffset(account, spender) + let currentAllowance := sload(offset) + require(lte(amount, currentAllowance)) + sstore(offset, sub(currentAllowance, amount)) + } + + /* ---------- utility functions ---------- */ + function lte(a, b) -> r { + r := iszero(gt(a, b)) + } + function gte(a, b) -> r { + r := iszero(lt(a, b)) + } + function safeAdd(a, b) -> r { + r := add(a, b) + if or(lt(r, a), lt(r, b)) { revert(0, 0) } + } + function calledByOwner() -> cbo { + cbo := eq(owner(), caller()) + } + function revertIfZeroAddress(addr) { + require(addr) + } + function require(condition) { + if iszero(condition) { revert(0, 0) } + } + } + } + } From cf607aa8870695ac96914cee9c274693ea3b9432 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 4 May 2020 20:38:22 +0200 Subject: [PATCH 02/65] [Sol->Yul] Checking if there is base contract when calling base constructror via modifier invocation. --- libsolidity/codegen/ir/IRGenerator.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index fb466f3ba..620daa86e 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -302,22 +302,26 @@ pair> IRGenerator::evaluateConstr for (ASTPointer const& base: _contract.baseContracts()) if (FunctionDefinition const* baseConstructor = dynamic_cast( - base->name().annotation().referencedDeclaration + base->name().annotation().referencedDeclaration )->constructor(); baseConstructor && base->arguments()) baseConstructorArguments.emplace_back( - dynamic_cast(baseConstructor->scope()), - base->arguments() + dynamic_cast(baseConstructor->scope()), + base->arguments() ); if (FunctionDefinition const* constructor = _contract.constructor()) - for (auto const& modifier: constructor->modifiers()) - if (FunctionDefinition const* baseConstructor = dynamic_cast( + for (ASTPointer const& modifier: constructor->modifiers()) + if (auto const* baseContract = dynamic_cast( modifier->name()->annotation().referencedDeclaration - )->constructor(); baseConstructor && modifier->arguments()) - baseConstructorArguments.emplace_back( - dynamic_cast(baseConstructor->scope()), - modifier->arguments() - ); + )) + if ( + FunctionDefinition const* baseConstructor = baseContract->constructor(); + baseConstructor && modifier->arguments() + ) + baseConstructorArguments.emplace_back( + dynamic_cast(baseConstructor->scope()), + modifier->arguments() + ); IRGeneratorForStatements generator{m_context, m_utils}; for (auto&& [baseContract, arguments]: baseConstructorArguments) From 8b7f87eed5710e19116acd665dda07e6048c8bb6 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Fri, 17 Apr 2020 22:24:33 +0200 Subject: [PATCH 03/65] [isoltest] Enforcing compileViaYul to be set if test can pass via yul --- .circleci/config.yml | 18 ++ test/Common.cpp | 1 + test/Common.h | 1 + test/TestCase.cpp | 5 + test/TestCase.h | 3 + test/boostTest.cpp | 11 +- test/libsolidity/SemanticTest.cpp | 236 +++++++++++------- test/libsolidity/SemanticTest.h | 7 +- ...data_array_dynamic_static_short_decode.sol | 2 + .../abiEncoderV2/cleanup/address.sol | 2 + .../abiEncoderV2/cleanup/bool.sol | 2 + .../abiEncoderV2/cleanup/bytesx.sol | 2 + .../abiEncoderV2/cleanup/intx.sol | 2 + .../abiEncoderV2/cleanup/uintx.sol | 2 + .../arithmetics/divisiod_by_zero.sol | 3 +- .../semanticTests/array/array_pop.sol | 3 +- .../array/array_pop_empty_exception.sol | 3 +- .../array/array_pop_isolated.sol | 3 +- .../semanticTests/array/array_push.sol | 3 +- .../array/byte_array_pop_empty_exception.sol | 1 - .../array/byte_array_pop_isolated.sol | 3 +- .../semanticTests/array/calldata_array.sol | 3 +- .../array/calldata_array_dynamic_invalid.sol | 3 +- ...ta_array_dynamic_invalid_static_middle.sol | 3 +- .../create_dynamic_array_with_zero_length.sol | 3 +- .../array/create_multiple_dynamic_arrays.sol | 3 +- .../evm_exceptions_out_of_band_access.sol | 3 +- .../array/fixed_arrays_in_constructors.sol | 3 +- .../array/fixed_bytes_length_access.sol | 3 +- .../memory_arrays_index_access_write.sol | 2 + .../array/memory_arrays_of_various_sizes.sol | 3 +- .../semanticTests/array/push_no_args_1d.sol | 2 + .../cleanup/bool_conversion_v2.sol | 2 + .../cleanup/cleanup_address_types_v2.sol | 2 + .../cleanup/cleanup_bytes_types_v2.sol | 2 + .../cleanup/cleanup_in_compound_assign.sol | 3 +- .../base_constructor_arguments.sol | 2 + .../constructor_arguments_external.sol | 3 +- ...unction_usage_in_constructor_arguments.sol | 2 + ...r_init_inheritence_without_constructor.sol | 3 +- ...conditional_expression_different_types.sol | 2 + .../conditional_expression_false_literal.sol | 2 + .../conditional_expression_functions.sol | 2 + .../conditional_expression_multiple.sol | 2 + .../conditional_expression_true_literal.sol | 2 + .../conditional_expression_tuples.sol | 2 + ...ditional_expression_with_return_values.sol | 2 + .../fallback/fallback_or_receive.sol | 2 + .../semanticTests/fallback/inherited.sol | 2 + .../fallback/short_data_calls_fallback.sol | 2 + .../array_multiple_local_vars.sol | 2 + .../functionCall/call_unimplemented_base.sol | 2 + .../calling_uninitialized_function.sol | 3 +- ...g_uninitialized_function_through_array.sol | 3 +- .../mapping_array_internal_argument.sol | 2 + .../mapping_internal_argument.sol | 2 + .../functionCall/multiple_functions.sol | 1 + .../semanticTests/functionCall/named_args.sol | 2 + .../functionCall/send_zero_ether.sol | 2 + .../functionCall/transaction_status.sol | 2 + .../semanticTests/functionCall/value_test.sol | 2 + .../pass_function_types_internally.sol | 3 +- ...e_function_in_construction_and_runtime.sol | 3 +- .../immutable/multi_creation.sol | 2 + .../inline_assembly_storage_access.sol | 3 +- ...ssembly_storage_access_inside_function.sol | 3 +- .../semanticTests/inlineAssembly/leave.sol | 2 + .../interfaceID/homer_interfaceId.sol | 2 + .../interfaceID/lisa_interfaceId.sol | 2 + .../inherited_function_calldata_memory.sol | 3 +- .../pass_dynamic_arguments_to_the_base.sol | 2 + ...ass_dynamic_arguments_to_the_base_base.sol | 2 + ...ic_arguments_to_the_base_base_with_gap.sol | 2 + .../library_enum_as_an_expression.sol | 2 + .../library_struct_as_an_expression.sol | 2 + .../literals/hex_string_with_underscore.sol | 2 + .../modifiers/break_in_modifier.sol | 3 +- .../stacked_return_with_modifiers.sol | 3 +- .../receive/empty_calldata_calls_receive.sol | 2 + .../semanticTests/receive/ether_and_data.sol | 2 + .../semanticTests/receive/inherited.sol | 2 + .../revertStrings/array_slices.sol | 1 + .../calldata_array_dynamic_invalid.sol | 1 + ...data_array_dynamic_static_short_decode.sol | 1 + ...ta_array_dynamic_static_short_reencode.sol | 1 + .../calldata_array_invalid_length.sol | 1 + .../calldata_arrays_too_large.sol | 1 + .../revertStrings/calldata_tail_short.sol | 1 + .../revertStrings/short_input_array.sol | 1 + .../revertStrings/short_input_bytes.sol | 1 + .../reverts/invalid_enum_as_external_arg.sol | 3 +- .../semanticTests/reverts/revert.sol | 2 + .../semanticTests/reverts/simple_throw.sol | 2 + .../salted_create_with_value.sol | 1 + .../shifts/shift_constant_left.sol | 3 +- .../shifts/shift_constant_right.sol | 3 +- .../shifts/shift_negative_constant_left.sol | 3 +- .../shifts/shift_negative_constant_right.sol | 3 +- .../semanticTests/smoke/bytes_and_strings.sol | 2 + .../semanticTests/smoke/constructor.sol | 2 + .../abi_functions_member_access.sol | 2 + .../storage/complex_accessors.sol | 2 + .../semanticTests/storage/simple_accessor.sol | 2 + .../semanticTests/tryCatch/assert.sol | 2 + .../semanticTests/tryCatch/trivial.sol | 2 + .../types/assign_calldata_value_type.sol | 2 + .../types/external_function_to_address.sol | 2 + .../types/mapping_contract_key_getter.sol | 2 + .../types/mapping_enum_key_getter_v2.sol | 2 + .../types/packing_signed_types.sol | 2 + .../types/packing_unpacking_types.sol | 2 + .../variables/mapping_local_assignment.sol | 2 + .../mapping_local_compound_assignment.sol | 2 + .../variables/public_state_overridding.sol | 4 +- .../semanticTests/various/decayed_tuple.sol | 3 +- .../various/inline_member_init.sol | 3 +- .../inline_member_init_inheritence.sol | 3 +- .../various/literal_empty_string.sol | 2 + .../various/multi_variable_declaration.sol | 3 +- .../various/positive_integers_to_signed.sol | 3 +- .../various/skip_dynamic_types.sol | 3 +- ...string_as_mapping_key_without_variable.sol | 3 +- .../semanticTests/various/string_tuples.sol | 3 +- .../various/test_underscore_in_hex.sol | 2 + .../semanticTests/various/tuples.sol | 3 +- ...unction_usage_in_constructor_arguments.sol | 2 + test/tools/isoltest.cpp | 8 +- 127 files changed, 412 insertions(+), 145 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 063e11bb5..e1161e432 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -617,6 +617,23 @@ jobs: t_ubu_soltest: &t_ubu_soltest <<: *test_ubuntu1904 + t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + environment: + EVM: constantinople + SOLTEST_FLAGS: --enforce-via-yul + OPTIMIZE: 0 + TERM: xterm + steps: + - checkout + - attach_workspace: + at: build + - run: *run_soltest + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results + + t_ubu_clang_soltest: &t_ubu_clang_soltest <<: *test_ubuntu1904_clang environment: @@ -818,6 +835,7 @@ workflows: - b_ubu18: *workflow_trigger_on_tags - t_ubu_cli: *workflow_ubuntu1904 - t_ubu_soltest: *workflow_ubuntu1904 + - t_ubu_soltest_enforce_yul: *workflow_ubuntu1904 - b_ubu_clang: *workflow_trigger_on_tags - t_ubu_clang_soltest: *workflow_ubuntu1904_clang diff --git a/test/Common.cpp b/test/Common.cpp index 0ed7c9881..b65555ba1 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -95,6 +95,7 @@ CommonOptions::CommonOptions(std::string _caption): ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("optimize", po::bool_switch(&optimize), "enables optimization") ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") + ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") ("show-messages", po::bool_switch(&showMessages), "enables message output") ("show-metadata", po::bool_switch(&showMetadata), "enables metadata output"); diff --git a/test/Common.h b/test/Common.h index 513e7154b..128d23ee5 100644 --- a/test/Common.h +++ b/test/Common.h @@ -47,6 +47,7 @@ struct CommonOptions: boost::noncopyable boost::filesystem::path testPath; bool optimize = false; bool optimizeYul = false; + bool enforceViaYul = false; bool disableSMT = false; bool useABIEncoderV2 = false; bool showMessages = false; diff --git a/test/TestCase.cpp b/test/TestCase.cpp index e0897ae47..23f23ac73 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -40,6 +40,11 @@ void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; } +void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix) +{ + printSettings(_stream, _linePrefix); +} + bool TestCase::isTestFilename(boost::filesystem::path const& _filename) { string extension = _filename.extension().string(); diff --git a/test/TestCase.h b/test/TestCase.h index 8ec3086fe..442b4fd5f 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -38,6 +38,7 @@ public: { std::string filename; langutil::EVMVersion evmVersion; + bool enforceCompileViaYul; }; enum class TestResult { Success, Failure, FatalError }; @@ -59,6 +60,8 @@ public: virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0; /// Outputs settings. virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); + /// Outputs updated settings + virtual void printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix = ""); /// Outputs test expectations to @arg _stream that match the actual results of the test. /// Each line of output is prefixed with @arg _linePrefix. virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index fd7bca132..eb55ead89 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -64,12 +64,13 @@ int registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, boost::filesystem::path const& _path, + bool _enforceViaYul, TestCase::TestCaseCreator _testCaseCreator ) { int numTestsAdded = 0; fs::path fullpath = _basepath / _path; - TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion()}; + TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul}; if (fs::is_directory(fullpath)) { test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); @@ -78,7 +79,12 @@ int registerTests( fs::directory_iterator() )) if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator); + numTestsAdded += registerTests( + *sub_suite, + _basepath, _path / entry.path().filename(), + _enforceViaYul, + _testCaseCreator + ); _suite.add(sub_suite); } else @@ -164,6 +170,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) master, options.testPath / ts.path, ts.subpath, + options.enforceViaYul, ts.testCaseCreator ) > 0, std::string("no ") + ts.title + " tests found"); } diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index ded01f8dd..ab454cb28 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -36,9 +36,10 @@ using namespace boost::unit_test; namespace fs = boost::filesystem; -SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion): +SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul): SolidityExecutionFramework(_evmVersion), - EVMVersionRestrictedTestCase(_filename) + EVMVersionRestrictedTestCase(_filename), + m_enforceViaYul(enforceViaYul) { m_source = m_reader.source(); m_lineOffset = m_reader.lineNumber(); @@ -78,112 +79,145 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { - for(bool compileViaYul: set{!m_runWithoutYul, m_runWithYul}) + + for (bool compileViaYul: set{!m_runWithoutYul, m_runWithYul || m_enforceViaYul}) { - reset(); - bool success = true; - - m_compileViaYul = compileViaYul; - if (compileViaYul) - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; - - for (auto& test: m_tests) - test.reset(); - - map libraries; - - bool constructed = false; - - for (auto& test: m_tests) + try { - if (constructed) - { - soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); - } - else if (test.call().isLibrary) - { - soltestAssert( - deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, - "Failed to deploy library " + test.call().signature - ); - libraries[test.call().signature] = m_contractAddress; - continue; - } - else - { - if (test.call().isConstructor) - deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); - else - soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); - constructed = true; - } + reset(); + bool success = true; - if (test.call().isConstructor) - { - if (m_transactionSuccessful == test.call().expectations.failure) - success = false; + m_compileViaYul = compileViaYul; + m_compileViaYulCanBeSet = false; - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(bytes()); - } - else + if (compileViaYul) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; + + for (auto& test: m_tests) + test.reset(); + + map libraries; + + bool constructed = false; + + for (auto& test: m_tests) { - bytes output; - if (test.call().useCallWithoutSignature) - output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); - else + if (constructed) + { + soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); + soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().isLibrary) { soltestAssert( - m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), - "The function " + test.call().signature + " is not known to the compiler" - ); - - output = callContractFunctionWithValueNoEncoding( - test.call().signature, - test.call().value.value, - test.call().arguments.rawBytes() + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature ); + libraries[test.call().signature] = m_contractAddress; + continue; + } + else + { + if (test.call().isConstructor) + deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); + else + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; } - if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) - success = false; + if (test.call().isConstructor) + { + if (m_transactionSuccessful == test.call().expectations.failure) + success = false; - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(std::move(output)); - test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(bytes()); + } + else + { + bytes output; + if (test.call().useCallWithoutSignature) + output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); + else + { + soltestAssert( + m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), + "The function " + test.call().signature + " is not known to the compiler" + ); + + output = callContractFunctionWithValueNoEncoding( + test.call().signature, + test.call().value.value, + test.call().arguments.rawBytes() + ); + } + + if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) + success = false; + + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(std::move(output)); + test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); + } + } + + if (success && !m_runWithYul && compileViaYul) + { + m_compileViaYulCanBeSet = true; + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix + << "Test can pass via Yul and marked with compileViaYul: false." << endl; + return TestResult::Failure; + } + + if (!success && (m_runWithYul || !compileViaYul)) + { + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + for (auto const& test: m_tests) + { + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); + } + _stream << endl; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + for (auto const& test: m_tests) + { + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); + } + AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix + << "Attention: Updates on the test will apply the detected format displayed." << endl; + if (compileViaYul && m_runWithoutYul) + { + _stream << _linePrefix << endl << _linePrefix; + AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) + << "Note that the test passed without Yul."; + _stream << endl; + } + else if (!compileViaYul && m_runWithYul) + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix + << "Note that the test also has to pass via Yul." << endl; + return TestResult::Failure; } } - - if (!success) + catch (boost::exception const&) { - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } - AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix - << "Attention: Updates on the test will apply the detected format displayed." << endl; - if (compileViaYul && m_runWithoutYul) - { - _stream << _linePrefix << endl << _linePrefix; - AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul."; - _stream << endl; - } - else if (!compileViaYul && m_runWithYul) - AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix - << "Note that the test also has to pass via Yul." << endl; - return TestResult::Failure; + if (compileViaYul && !m_runWithYul) + continue; + throw; + } + catch (std::exception const&) + { + if (compileViaYul && !m_runWithYul) + continue; + throw; + } + catch (...) + { + if (compileViaYul && !m_runWithYul) + continue; + throw; } } @@ -204,6 +238,20 @@ void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) con _stream << test.format("", true, false) << endl; } +void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePrefix) +{ + auto& settings = m_reader.settings(); + if (settings.empty() && !m_compileViaYulCanBeSet) + return; + + _stream << _linePrefix << "// ====" << endl; + if (m_compileViaYulCanBeSet) + _stream << _linePrefix << "// compileViaYul: also\n"; + for (auto const& setting: settings) + if (!m_compileViaYulCanBeSet || setting.first != "compileViaYul") + _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; +} + void SemanticTest::parseExpectations(istream& _stream) { TestFileParser parser{_stream}; diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 94c29e193..870d61d56 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -40,13 +40,14 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict { public: static std::unique_ptr create(Config const& _options) - { return std::make_unique(_options.filename, _options.evmVersion); } + { return std::make_unique(_options.filename, _options.evmVersion, _options.enforceCompileViaYul); } - explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion); + explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _enforceViaYul = false); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix = "") const override; + void printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix = "") override; /// Instantiates a test file parser that parses the additional comment section at the end of /// the input stream \param _stream. Each function call is represented using a `FunctionCallTest` @@ -64,8 +65,10 @@ private: std::vector m_tests; bool m_runWithYul = false; bool m_runWithoutYul = true; + bool m_enforceViaYul = false; bool m_runWithABIEncoderV1Only = false; bool m_allowNonExistingFunctions = false; + bool m_compileViaYulCanBeSet = false; }; } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol index a89b6336a..0ded29feb 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol @@ -5,6 +5,8 @@ contract C { return 23; } } +// ==== +// compileViaYul: also // ---- // f(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 23 # this is the common encoding for x.length == 1 && x[0][0].length == 0 && x[0][1].length == 0 # // f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 23 # exotic, but still valid encoding # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol index 4bd9c2f77..efb5045fc 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol @@ -10,6 +10,8 @@ contract C { return this.g(x); } } +// ==== +// compileViaYul: also // ---- // f(uint256): 0 -> 0 // g(address): 0 -> 0 # test validation as well as sanity check # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol index 94d4dbb11..eb9ce5962 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol @@ -10,6 +10,8 @@ contract C { return this.gggg(x); } } +// ==== +// compileViaYul: also // ---- // f(uint256): 0 -> false // gggg(bool): 0 -> false # test validation as well as sanity check # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol index f8824e271..294a666e9 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol @@ -42,6 +42,8 @@ contract C { return this.g16(x); } } +// ==== +// compileViaYul: also // ---- // f1(bytes32): left(0) -> left(0) // gg1(bytes1): left(0) -> left(0) # test validation as well as sanity check # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol index 51ecd3432..15cd07d49 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol @@ -42,6 +42,8 @@ contract C { return this.g128(x); } } +// ==== +// compileViaYul: also // ---- // f8(int256): 0 -> 0 // ggg8(int8): 0 -> 0 # test validation as well as sanity check # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol index ee0ec362e..9788431e1 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol @@ -42,6 +42,8 @@ contract C { return this.g128(x); } } +// ==== +// compileViaYul: also // ---- // f8(uint256): 0 -> 0 // ggg8(uint8): 0 -> 0 # test validation as well as sanity check # diff --git a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol index 127320fc6..0ccc0acb5 100644 --- a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol +++ b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol @@ -7,7 +7,8 @@ contract C { return a % b; } } - +// ==== +// compileViaYul: also // ---- // div(uint256,uint256): 7, 2 -> 3 // div(uint256,uint256): 7, 0 -> FAILURE # throws # diff --git a/test/libsolidity/semanticTests/array/array_pop.sol b/test/libsolidity/semanticTests/array/array_pop.sol index 5667b61d4..9e64119e9 100644 --- a/test/libsolidity/semanticTests/array/array_pop.sol +++ b/test/libsolidity/semanticTests/array/array_pop.sol @@ -11,6 +11,7 @@ contract c { l = data.length; } } - +// ==== +// compileViaYul: also // ---- // test() -> 1, 0 diff --git a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol index 8fc018d72..9f436d2d2 100644 --- a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol +++ b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol @@ -6,6 +6,7 @@ contract c { return true; } } - +// ==== +// compileViaYul: also // ---- // test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/array_pop_isolated.sol b/test/libsolidity/semanticTests/array/array_pop_isolated.sol index 2e6eac83e..58c56adc9 100644 --- a/test/libsolidity/semanticTests/array/array_pop_isolated.sol +++ b/test/libsolidity/semanticTests/array/array_pop_isolated.sol @@ -8,6 +8,7 @@ contract c { x = 3; } } - +// ==== +// compileViaYul: also // ---- // test() -> 3 diff --git a/test/libsolidity/semanticTests/array/array_push.sol b/test/libsolidity/semanticTests/array/array_push.sol index bd8200a37..5923580fa 100644 --- a/test/libsolidity/semanticTests/array/array_push.sol +++ b/test/libsolidity/semanticTests/array/array_push.sol @@ -14,6 +14,7 @@ contract c { z = data[2]; } } - +// ==== +// compileViaYul: also // ---- // test() -> 5, 4, 3, 3 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol index 30ffa3a4c..08aea4254 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol +++ b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol @@ -9,6 +9,5 @@ contract c { return true; } } - // ---- // test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol index a9e3fd383..1635071c5 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol +++ b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol @@ -8,6 +8,7 @@ contract c { x = 3; } } - +// ==== +// compileViaYul: also // ---- // test() -> 3 diff --git a/test/libsolidity/semanticTests/array/calldata_array.sol b/test/libsolidity/semanticTests/array/calldata_array.sol index c9c6dbda0..9fdd8b44b 100644 --- a/test/libsolidity/semanticTests/array/calldata_array.sol +++ b/test/libsolidity/semanticTests/array/calldata_array.sol @@ -11,6 +11,7 @@ contract C { b = s[1]; } } - +// ==== +// compileViaYul: also // ---- // f(uint256[2]): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol index d643d3973..cab938d6c 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol @@ -11,7 +11,8 @@ contract C { return 42; } } - +// ==== +// compileViaYul: also // ---- // f(uint256[][]): 0x20, 0x0 -> 42 # valid access stub # // f(uint256[][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol index 3efd177b2..d4c02df5e 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol @@ -16,7 +16,8 @@ contract C { return 42; } } - +// ==== +// compileViaYul: also // ---- // f(uint256[][1][]): 0x20, 0x0 -> 42 # valid access stub # // f(uint256[][1][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # diff --git a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol index 7c2ccde6e..a529a767d 100644 --- a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol +++ b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol @@ -4,6 +4,7 @@ contract C { return 7; } } - +// ==== +// compileViaYul: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol index 731fb4312..9e57f251c 100644 --- a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol +++ b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol @@ -29,6 +29,7 @@ contract C { return 0; } } - +// ==== +// compileViaYul: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol index af14b48b4..2ac334c17 100644 --- a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol +++ b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol @@ -12,7 +12,8 @@ contract A { return true; } } - +// ==== +// compileViaYul: also // ---- // test() -> false // testIt() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol index ff13db5be..e85cb599e 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -7,7 +7,8 @@ contract Creator { ch = s[2]; } } - +// ==== +// compileViaYul: also // ---- // constructor(): 1, 2, 3, 4 -> // r() -> 4 diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol index 7a6afbae7..ffeab647f 100644 --- a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol +++ b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol @@ -5,6 +5,7 @@ contract C { return (x.length, bytes16(uint128(2)).length, a.length + 7); } } - +// ==== +// compileViaYul: also // ---- // f(bytes32): "789" -> 32, 16, 8 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol index 7a8a18670..7de7f49be 100644 --- a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol +++ b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol @@ -10,5 +10,7 @@ contract Test { return data; } } +// ==== +// compileViaYul: also // ---- // f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol index b641ec1da..fd1fba0a1 100644 --- a/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol +++ b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol @@ -11,7 +11,8 @@ contract C { return rows[n][k - 1]; } } - +// ==== +// compileViaYul: also // ---- // f(uint256,uint256): 3, 1 -> 1 // f(uint256,uint256): 9, 5 -> 70 diff --git a/test/libsolidity/semanticTests/array/push_no_args_1d.sol b/test/libsolidity/semanticTests/array/push_no_args_1d.sol index cf6f0687d..daf8d43b1 100644 --- a/test/libsolidity/semanticTests/array/push_no_args_1d.sol +++ b/test/libsolidity/semanticTests/array/push_no_args_1d.sol @@ -19,6 +19,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // l() -> 0 // lv(uint256): 42 -> diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol index a8fd89833..69b590828 100644 --- a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol @@ -11,6 +11,8 @@ contract C { _out = _in; } } +// ==== +// compileViaYul: also // ---- // f(bool): 0x0 -> 0x0 // f(bool): 0x1 -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol index beff156f7..ce6ff4fe2 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol @@ -13,6 +13,8 @@ contract C { return 0; } } +// ==== +// compileViaYul: also // ---- // f(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE # We input longer data on purpose.# // g(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol index 5adc97378..2ca882c9f 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol @@ -10,5 +10,7 @@ contract C { return 0; } } +// ==== +// compileViaYul: also // ---- // f(bytes2,uint16): "abc", 0x40102 -> FAILURE # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol index c1901c738..7cd496818 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol @@ -8,6 +8,7 @@ contract C { return (x, y); } } - +// ==== +// compileViaYul: also // ---- // test() -> 0xff, 0xff diff --git a/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol index 2384c061f..ffb98ada1 100644 --- a/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol +++ b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol @@ -20,5 +20,7 @@ contract Derived is Base { } } +// ==== +// compileViaYul: also // ---- // getA() -> 49 diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol index ec30bacf6..fe6a4c59b 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -15,7 +15,8 @@ contract Main { return flag; } } - +// ==== +// compileViaYul: also // ---- // constructor(): "abc", true // getFlag() -> true diff --git a/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol index e92a37b19..6f2f41428 100644 --- a/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol +++ b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol @@ -20,5 +20,7 @@ contract Derived is Base { } } +// ==== +// compileViaYul: also // ---- // getA() -> 2 diff --git a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol index 0aea44e6a..1e8f9bc78 100644 --- a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol @@ -14,7 +14,8 @@ contract Derived is Base { return m_derived; } } - +// ==== +// compileViaYul: also // ---- // getBMember() -> 5 // getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol index df07364b3..4f3828f3a 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol @@ -5,6 +5,8 @@ contract test { return cond ? x : y; } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 0xcd // f(bool): false -> 0xabab diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol index 3915c7b8d..456b1902c 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol @@ -3,5 +3,7 @@ contract test { return false ? 5 : 10; } } +// ==== +// compileViaYul: also // ---- // f() -> 10 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol index bff9122b1..482849648 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol @@ -7,6 +7,8 @@ contract test { return z(); } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol index 2a9d6de80..c8a335384 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol @@ -6,6 +6,8 @@ contract test { x > 50 ? 50 : 10; } } +// ==== +// compileViaYul: also // ---- // f(uint256): 1001 -> 1000 // f(uint256): 500 -> 100 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol index 06247f0dc..651455079 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol @@ -3,5 +3,7 @@ contract test { return true ? 5 : 10; } } +// ==== +// compileViaYul: also // ---- // f() -> 5 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol index d27d06bac..53eba3f1d 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol @@ -3,6 +3,8 @@ contract test { return cond ? (1, 2) : (3, 4); } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1, 2 // f(bool): false -> 3, 4 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol index bbaf051bd..df6cdb344 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol @@ -3,6 +3,8 @@ contract test { cond ? a = v : b = v; } } +// ==== +// compileViaYul: also // ---- // f(bool,uint256): true, 20 -> 20, 0 // f(bool,uint256): false, 20 -> 0, 20 diff --git a/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol b/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol index 9b5319ae0..90efa8fda 100644 --- a/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol +++ b/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol @@ -5,6 +5,8 @@ contract C { receive () payable external { ++y; } function f() external returns (uint, uint) { return (x, y); } } +// ==== +// compileViaYul: also // ---- // f() -> 0, 0 // () -> diff --git a/test/libsolidity/semanticTests/fallback/inherited.sol b/test/libsolidity/semanticTests/fallback/inherited.sol index a4ee55419..1e0cdc407 100644 --- a/test/libsolidity/semanticTests/fallback/inherited.sol +++ b/test/libsolidity/semanticTests/fallback/inherited.sol @@ -4,6 +4,8 @@ contract A { function getData() public returns (uint r) { return data; } } contract B is A {} +// ==== +// compileViaYul: also // ---- // getData() -> 0 // (): 42 -> diff --git a/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol b/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol index dd97f4c03..57dbb37bb 100644 --- a/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol +++ b/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol @@ -4,6 +4,8 @@ contract A { function fow() public { x = 3; } fallback () external { x = 2; } } +// ==== +// compileViaYul: also // ---- // (): hex"d88e0b" // x() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol b/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol index 4dbfe91f1..52ddfff98 100644 --- a/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol +++ b/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol @@ -22,6 +22,8 @@ contract test { return sum; } } +// ==== +// compileViaYul: also // ---- // f(uint256[]): 32, 3, 1000, 1, 2 -> 3 // f(uint256[]): 32, 3, 100, 500, 300 -> 600 diff --git a/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol b/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol index 3041f0e4b..862f37f8d 100644 --- a/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol +++ b/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol @@ -10,5 +10,7 @@ contract C is V { function a() internal view override returns (uint256) { return 42;} } +// ==== +// compileViaYul: also // ---- // b() -> 42 diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol index e1cab8fe8..c074d8598 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol @@ -11,7 +11,8 @@ contract C { return 7; } } - +// ==== +// compileViaYul: also // ---- // intern() -> FAILURE # This should throw exceptions # // extern() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol index 26a2a79f4..44af5be40 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol @@ -15,6 +15,7 @@ contract C { return 2; } } - +// ==== +// compileViaYul: also // ---- // t() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol b/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol index 54baa4572..4fcc640f6 100644 --- a/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol +++ b/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol @@ -16,6 +16,8 @@ contract test { return (a[0][key], a[1][key], b[0][key], b[1][key]); } } +// ==== +// compileViaYul: also // ---- // set(uint8,uint8,uint8,uint8,uint8): 1, 21, 22, 42, 43 -> 0, 0, 0, 0 // get(uint8): 1 -> 21, 22, 42, 43 diff --git a/test/libsolidity/semanticTests/functionCall/mapping_internal_argument.sol b/test/libsolidity/semanticTests/functionCall/mapping_internal_argument.sol index 728be2c09..4154baa9b 100644 --- a/test/libsolidity/semanticTests/functionCall/mapping_internal_argument.sol +++ b/test/libsolidity/semanticTests/functionCall/mapping_internal_argument.sol @@ -14,6 +14,8 @@ contract test { return (a[key], b[key]); } } +// ==== +// compileViaYul: also // ---- // set(uint8,uint8,uint8): 1, 21, 42 -> 0, 0 // get(uint8): 1 -> 21, 42 diff --git a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol index 3c60a13f1..e72922d7b 100644 --- a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol +++ b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol @@ -6,6 +6,7 @@ contract test { } // ==== // allowNonExistingFunctions: true +// compileViaYul: also // ---- // a() -> 0 // b() -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/named_args.sol b/test/libsolidity/semanticTests/functionCall/named_args.sol index 132139b99..e959eba44 100644 --- a/test/libsolidity/semanticTests/functionCall/named_args.sol +++ b/test/libsolidity/semanticTests/functionCall/named_args.sol @@ -2,5 +2,7 @@ contract test { function a(uint a, uint b, uint c) public returns (uint r) { r = a * 100 + b * 10 + c * 1; } function b() public returns (uint r) { r = a({a: 1, b: 2, c: 3}); } } +// ==== +// compileViaYul: also // ---- // b() -> 123 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index e0c6ff5e0..07f6ff51d 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -14,6 +14,8 @@ contract Main { } } +// ==== +// compileViaYul: also // ---- // constructor(), 20 wei -> // s() -> true diff --git a/test/libsolidity/semanticTests/functionCall/transaction_status.sol b/test/libsolidity/semanticTests/functionCall/transaction_status.sol index 6651630d5..449ebacbe 100644 --- a/test/libsolidity/semanticTests/functionCall/transaction_status.sol +++ b/test/libsolidity/semanticTests/functionCall/transaction_status.sol @@ -3,6 +3,8 @@ contract test { function g() public { revert(); } function h() public { assert(false); } } +// ==== +// compileViaYul: also // ---- // f() -> // g() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/value_test.sol b/test/libsolidity/semanticTests/functionCall/value_test.sol index 582cda9f6..9cd1fd9ef 100644 --- a/test/libsolidity/semanticTests/functionCall/value_test.sol +++ b/test/libsolidity/semanticTests/functionCall/value_test.sol @@ -3,6 +3,8 @@ contract C { return msg.value; } } +// ==== +// compileViaYul: also // ---- // f(), 1 ether -> 1000000000000000000 // f(), 1 wei -> 1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol index 6fb4f5f6e..0acbcc585 100644 --- a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol @@ -7,10 +7,9 @@ contract C { return x(a); } - function g(uint256 x) public returns (uint256) { + function g(uint256 x) public pure returns (uint256) { return x + 1; } } - // ---- // f(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol index a79cb2d76..d829c1554 100644 --- a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol @@ -13,7 +13,8 @@ contract C { return double(_arg); } } - +// ==== +// compileViaYul: also // ---- // runtime(uint256): 3 -> 6 // initial() -> 4 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index b9e362dbd..970ea9155 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -25,6 +25,8 @@ contract C { return (a, (new A()).f(), (new B()).f()); } } +// ==== +// compileViaYul: also // ---- // f() -> 3, 7, 5 // x() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol index 823afe2bd..8e42595d4 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol @@ -16,7 +16,8 @@ contract C { return true; } } - +// ==== +// compileViaYul: also // ---- // f() -> true // z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol index ce56dbaa1..8f48f2f52 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol @@ -17,7 +17,8 @@ contract C { return true; } } - +// ==== +// compileViaYul: also // ---- // f() -> true // z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/leave.sol b/test/libsolidity/semanticTests/inlineAssembly/leave.sol index 40add273e..b8c0ccda2 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/leave.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/leave.sol @@ -10,5 +10,7 @@ contract C { } } } +// ==== +// compileViaYul: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol b/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol index d2fa2e821..855ca4d40 100644 --- a/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol +++ b/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol @@ -29,6 +29,8 @@ contract Homer is ERC165, Simpson { } } +// ==== +// compileViaYul: also // ---- // supportsInterface(bytes4): left(0x01ffc9a0) -> false // supportsInterface(bytes4): left(0x01ffc9a7) -> true diff --git a/test/libsolidity/semanticTests/interfaceID/lisa_interfaceId.sol b/test/libsolidity/semanticTests/interfaceID/lisa_interfaceId.sol index 9c9dd5eb7..96f1a4742 100644 --- a/test/libsolidity/semanticTests/interfaceID/lisa_interfaceId.sol +++ b/test/libsolidity/semanticTests/interfaceID/lisa_interfaceId.sol @@ -40,6 +40,8 @@ contract Lisa is ERC165MappingImplementation, Simpson { } } +// ==== +// compileViaYul: also // ---- // supportsInterface(bytes4): left(0x01ffc9a0) -> false // supportsInterface(bytes4): left(0x01ffc9a7) -> true diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol index 0fd02e8f3..a9956cc80 100644 --- a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol @@ -17,6 +17,7 @@ contract B is A { return A(this).f(m); } } - +// ==== +// compileViaYul: also // ---- // g() -> 23 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol index 26de9ce70..98922c389 100644 --- a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol @@ -14,5 +14,7 @@ contract Derived is Base { contract Final is Derived(4) {} +// ==== +// compileViaYul: also // ---- // m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol index 6349bd814..cab918567 100644 --- a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol @@ -19,5 +19,7 @@ contract Derived is Base, Base1 { contract Final is Derived(4) {} +// ==== +// compileViaYul: also // ---- // m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol index 4556cf5c4..ab7bada38 100644 --- a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol @@ -19,5 +19,7 @@ contract Derived is Base, Base1 { contract Final is Derived(4) {} +// ==== +// compileViaYul: also // ---- // m_i() -> 4 diff --git a/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol index f24d93c2d..31a64ce20 100644 --- a/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol +++ b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol @@ -10,5 +10,7 @@ contract Tsra { } } +// ==== +// compileViaYul: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol index d7df52434..9965a3815 100644 --- a/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol +++ b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol @@ -13,5 +13,7 @@ contract Tsra { } } +// ==== +// compileViaYul: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol b/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol index db7f70f83..3194d7ee9 100644 --- a/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol +++ b/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol @@ -3,5 +3,7 @@ contract C { return hex"12_34_5678_9A"; } } +// ==== +// compileViaYul: also // ---- // f() -> 32, 5, left(0x123456789A) diff --git a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol index 1d1036c2c..27a9550cf 100644 --- a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol +++ b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol @@ -13,7 +13,8 @@ contract C { x = t; } } - +// ==== +// compileViaYul: also // ---- // x() -> 0 // f() -> diff --git a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol index 1d1036c2c..27a9550cf 100644 --- a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol +++ b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol @@ -13,7 +13,8 @@ contract C { x = t; } } - +// ==== +// compileViaYul: also // ---- // x() -> 0 // f() -> diff --git a/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol b/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol index ce04cd737..b6e9416a7 100644 --- a/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol +++ b/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol @@ -2,6 +2,8 @@ contract A { uint public x; receive () external payable { ++x; } } +// ==== +// compileViaYul: also // ---- // x() -> 0 // () diff --git a/test/libsolidity/semanticTests/receive/ether_and_data.sol b/test/libsolidity/semanticTests/receive/ether_and_data.sol index 4c48df8d4..44af7ab91 100644 --- a/test/libsolidity/semanticTests/receive/ether_and_data.sol +++ b/test/libsolidity/semanticTests/receive/ether_and_data.sol @@ -1,6 +1,8 @@ contract C { receive () payable external { } } +// ==== +// compileViaYul: also // ---- // (), 1 ether // (), 1 ether: 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/receive/inherited.sol b/test/libsolidity/semanticTests/receive/inherited.sol index 36273068a..a71322983 100644 --- a/test/libsolidity/semanticTests/receive/inherited.sol +++ b/test/libsolidity/semanticTests/receive/inherited.sol @@ -4,6 +4,8 @@ contract A { function getData() public returns (uint r) { return data; } } contract B is A {} +// ==== +// compileViaYul: also // ---- // getData() -> 0 // () -> diff --git a/test/libsolidity/semanticTests/revertStrings/array_slices.sol b/test/libsolidity/semanticTests/revertStrings/array_slices.sol index dec3e916d..5d2cebb87 100644 --- a/test/libsolidity/semanticTests/revertStrings/array_slices.sol +++ b/test/libsolidity/semanticTests/revertStrings/array_slices.sol @@ -6,6 +6,7 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256,uint256,uint256[]): 2, 1, 0x80, 3, 1, 2, 3 -> FAILURE, hex"08c379a0", 0x20, 22, "Slice starts after end" // f(uint256,uint256,uint256[]): 1, 5, 0x80, 3, 1, 2, 3 -> FAILURE, hex"08c379a0", 0x20, 28, "Slice is greater than length" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol index 2538a2175..e91f08a03 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol @@ -7,5 +7,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256[][]): 0x20, 1 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray stride" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol index 9f9296d2a..d39b3f4fb 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol @@ -8,5 +8,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE, hex"08c379a0", 0x20, 28, "Invalid calldata tail offset" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol index 7aac0f7f8..0e2ba94b1 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol @@ -10,5 +10,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00 -> FAILURE, hex"08c379a0", 0x20, 30, "Invalid calldata access offset" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol index 3b68bacea..f54a47d73 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol @@ -7,5 +7,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256[][]): 0x20, 1, 0x20, 0x0100000000000000000000 -> FAILURE, hex"08c379a0", 0x20, 28, "Invalid calldata tail length" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol index 52809a0b2..c19538d0a 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol @@ -7,5 +7,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256,uint256[],uint256): 6, 0x60, 9, 0x1000000000000000000000000000000000000000000000000000000000000002, 1, 2 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray length" diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol index b6cc5800d..c3fcece1b 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol @@ -5,5 +5,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256[][]): 0x20, 1, 0x20, 2, 0x42 -> FAILURE, hex"08c379a0", 0x20, 23, "Calldata tail too short" diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_array.sol b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol index 1a80acc26..4c83b79ff 100644 --- a/test/libsolidity/semanticTests/revertStrings/short_input_array.sol +++ b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol @@ -5,5 +5,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // f(uint256[]): 0x20, 1 -> FAILURE, hex"08c379a0", 0x20, 43, "ABI decoding: invalid calldata a", "rray stride" diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol index f361da7b6..b8bb0493a 100644 --- a/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol +++ b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol @@ -5,5 +5,6 @@ contract C { // ==== // EVMVersion: >=byzantium // revertStrings: debug +// compileViaYul: also // ---- // e(bytes): 0x20, 7 -> FAILURE, hex"08c379a0", 0x20, 39, "ABI decoding: invalid byte array", " length" diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol index d0adf2785..1d240813a 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol @@ -15,6 +15,7 @@ contract C { return this.tested(garbled); } } - +// ==== +// compileViaYul: also // ---- // test() -> FAILURE # should throw # diff --git a/test/libsolidity/semanticTests/reverts/revert.sol b/test/libsolidity/semanticTests/reverts/revert.sol index eae49c799..02496ef94 100644 --- a/test/libsolidity/semanticTests/reverts/revert.sol +++ b/test/libsolidity/semanticTests/reverts/revert.sol @@ -14,6 +14,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> FAILURE // a() -> 42 diff --git a/test/libsolidity/semanticTests/reverts/simple_throw.sol b/test/libsolidity/semanticTests/reverts/simple_throw.sol index ad88deca6..bf9df114c 100644 --- a/test/libsolidity/semanticTests/reverts/simple_throw.sol +++ b/test/libsolidity/semanticTests/reverts/simple_throw.sol @@ -6,6 +6,8 @@ contract Test { } } +// ==== +// compileViaYul: also // ---- // f(uint256): 11 -> 21 // f(uint256): 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index f8b02693d..4e62acc5e 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -18,6 +18,7 @@ contract A { } } // ==== +// compileViaYul: also // EVMVersion: >=constantinople // ---- // f(), 10 ether -> 3007, 3008, 3009 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol index 4c6f3737f..4e43cae37 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol @@ -1,6 +1,7 @@ contract C { uint256 public a = 0x42 << 8; } - +// ==== +// compileViaYul: also // ---- // a() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol index 766a8522e..8278e045c 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol @@ -1,6 +1,7 @@ contract C { uint256 public a = 0x4200 >> 8; } - +// ==== +// compileViaYul: also // ---- // a() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol index b92fb2229..964b6543b 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol @@ -1,6 +1,7 @@ contract C { int256 public a = -0x42 << 8; } - +// ==== +// compileViaYul: also // ---- // a() -> -16896 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol index b08463333..993fae441 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol @@ -1,6 +1,7 @@ contract C { int256 public a = -0x4200 >> 8; } - +// ==== +// compileViaYul: also // ---- // a() -> -66 diff --git a/test/libsolidity/semanticTests/smoke/bytes_and_strings.sol b/test/libsolidity/semanticTests/smoke/bytes_and_strings.sol index db6f35497..6bfc1da0a 100644 --- a/test/libsolidity/semanticTests/smoke/bytes_and_strings.sol +++ b/test/libsolidity/semanticTests/smoke/bytes_and_strings.sol @@ -12,6 +12,8 @@ contract C { return "any"; } } +// ==== +// compileViaYul: also // ---- // e(bytes): 32, 3, hex"AB33BB" -> 32, 3, left(0xAB33BB) // e(bytes): 32, 32, 0x20 -> 32, 32, 0x20 diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index e3bee950f..27e26f1eb 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -10,6 +10,8 @@ contract C { state = _state; } } +// ==== +// compileViaYul: also // ---- // constructor(), 2 wei: 3 -> // state() -> 3 diff --git a/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol b/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol index b255b1880..5d18cf32d 100644 --- a/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol +++ b/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol @@ -7,5 +7,7 @@ contract C { abi.decode; } } +// ==== +// compileViaYul: also // ---- // f() -> diff --git a/test/libsolidity/semanticTests/storage/complex_accessors.sol b/test/libsolidity/semanticTests/storage/complex_accessors.sol index 1169e4ea5..9b3fa7a3c 100644 --- a/test/libsolidity/semanticTests/storage/complex_accessors.sol +++ b/test/libsolidity/semanticTests/storage/complex_accessors.sol @@ -10,6 +10,8 @@ contract test { to_multiple_map[42][23] = 31; } } +// ==== +// compileViaYul: also // ---- // to_string_map(uint256): 42 -> "24" // to_bool_map(uint256): 42 -> false diff --git a/test/libsolidity/semanticTests/storage/simple_accessor.sol b/test/libsolidity/semanticTests/storage/simple_accessor.sol index 23bcfbfac..aacfb93a0 100644 --- a/test/libsolidity/semanticTests/storage/simple_accessor.sol +++ b/test/libsolidity/semanticTests/storage/simple_accessor.sol @@ -4,5 +4,7 @@ contract test { data = 8; } } +// ==== +// compileViaYul: also // ---- // data() -> 8 diff --git a/test/libsolidity/semanticTests/tryCatch/assert.sol b/test/libsolidity/semanticTests/tryCatch/assert.sol index 8b6a7b996..be475a3f2 100644 --- a/test/libsolidity/semanticTests/tryCatch/assert.sol +++ b/test/libsolidity/semanticTests/tryCatch/assert.sol @@ -11,6 +11,8 @@ contract C { } } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/tryCatch/trivial.sol b/test/libsolidity/semanticTests/tryCatch/trivial.sol index d43477e99..567b4b714 100644 --- a/test/libsolidity/semanticTests/tryCatch/trivial.sol +++ b/test/libsolidity/semanticTests/tryCatch/trivial.sol @@ -11,6 +11,8 @@ contract C { } } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol b/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol index 8350628e4..c7d2d8bf8 100644 --- a/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol +++ b/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol @@ -5,5 +5,7 @@ contract C { return (x, b); } } +// ==== +// compileViaYul: also // ---- // f(uint256): 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/types/external_function_to_address.sol b/test/libsolidity/semanticTests/types/external_function_to_address.sol index fb938d37a..8ae544855 100644 --- a/test/libsolidity/semanticTests/types/external_function_to_address.sol +++ b/test/libsolidity/semanticTests/types/external_function_to_address.sol @@ -6,6 +6,8 @@ contract C { return cb.address; } } +// ==== +// compileViaYul: also // ---- // f() -> true // g(function): hex"00000000000000000000000000000000000004226121ff00000000000000000" -> 0x42 diff --git a/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol index ee916a63d..8e29ad63e 100644 --- a/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol +++ b/test/libsolidity/semanticTests/types/mapping_contract_key_getter.sol @@ -8,6 +8,8 @@ contract test { return this.table(k); } } +// ==== +// compileViaYul: also // ---- // table(address): 0 -> 0 // table(address): 0x01 -> 0 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol index f1fd184ed..f822a9395 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol @@ -9,6 +9,8 @@ contract test { return this.table(k); } } +// ==== +// compileViaYul: also // ---- // table(uint8): 0 -> 0 // table(uint8): 0x01 -> 0 diff --git a/test/libsolidity/semanticTests/types/packing_signed_types.sol b/test/libsolidity/semanticTests/types/packing_signed_types.sol index 2d2e0e16e..3a10fd336 100644 --- a/test/libsolidity/semanticTests/types/packing_signed_types.sol +++ b/test/libsolidity/semanticTests/types/packing_signed_types.sol @@ -4,5 +4,7 @@ contract test { return int8(x); } } +// ==== +// compileViaYul: also // ---- // run() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa diff --git a/test/libsolidity/semanticTests/types/packing_unpacking_types.sol b/test/libsolidity/semanticTests/types/packing_unpacking_types.sol index 8b500cca1..ea2ed58f2 100644 --- a/test/libsolidity/semanticTests/types/packing_unpacking_types.sol +++ b/test/libsolidity/semanticTests/types/packing_unpacking_types.sol @@ -5,6 +5,8 @@ contract test { y = y * 0x10000000000000000 | ~c; } } +// ==== +// compileViaYul: also // ---- // run(bool,uint32,uint64): true, 0x0f0f0f0f, 0xf0f0f0f0f0f0f0f0 // -> 0x0000000000000000000000000000000000000001f0f0f0f00f0f0f0f0f0f0f0f diff --git a/test/libsolidity/semanticTests/variables/mapping_local_assignment.sol b/test/libsolidity/semanticTests/variables/mapping_local_assignment.sol index b99385823..b4b158f62 100644 --- a/test/libsolidity/semanticTests/variables/mapping_local_assignment.sol +++ b/test/libsolidity/semanticTests/variables/mapping_local_assignment.sol @@ -11,5 +11,7 @@ contract test { return (m1[1], m1[2], m2[1], m2[2]); } } +// ==== +// compileViaYul: also // ---- // f() -> 42, 0, 0, 21 diff --git a/test/libsolidity/semanticTests/variables/mapping_local_compound_assignment.sol b/test/libsolidity/semanticTests/variables/mapping_local_compound_assignment.sol index df3ee2f54..5e237ae17 100644 --- a/test/libsolidity/semanticTests/variables/mapping_local_compound_assignment.sol +++ b/test/libsolidity/semanticTests/variables/mapping_local_compound_assignment.sol @@ -10,5 +10,7 @@ contract test { return (m1[1], m1[2], m2[1], m2[2]); } } +// ==== +// compileViaYul: also // ---- // f() -> 42, 0, 0, 21 diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding.sol b/test/libsolidity/semanticTests/variables/public_state_overridding.sol index 2921f1a4a..bff486eaa 100644 --- a/test/libsolidity/semanticTests/variables/public_state_overridding.sol +++ b/test/libsolidity/semanticTests/variables/public_state_overridding.sol @@ -11,8 +11,8 @@ contract X is A function set() public { test = 2; } } - - +// ==== +// compileViaYul: also // ---- // test() -> 0 // set() -> diff --git a/test/libsolidity/semanticTests/various/decayed_tuple.sol b/test/libsolidity/semanticTests/various/decayed_tuple.sol index b00942cb3..2f1122f6a 100644 --- a/test/libsolidity/semanticTests/various/decayed_tuple.sol +++ b/test/libsolidity/semanticTests/various/decayed_tuple.sol @@ -5,6 +5,7 @@ contract C { return x; } } - +// ==== +// compileViaYul: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/various/inline_member_init.sol b/test/libsolidity/semanticTests/various/inline_member_init.sol index 5dca66d80..cac10c786 100644 --- a/test/libsolidity/semanticTests/various/inline_member_init.sol +++ b/test/libsolidity/semanticTests/various/inline_member_init.sol @@ -14,6 +14,7 @@ contract test { c = m_c; } } - +// ==== +// compileViaYul: also // ---- // get() -> 5, 6, 8 diff --git a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol index 53f5b3718..7ee1028c9 100644 --- a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol +++ b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol @@ -18,7 +18,8 @@ contract Derived is Base { return m_derived; } } - +// ==== +// compileViaYul: also // ---- // getBMember() -> 5 // getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/various/literal_empty_string.sol b/test/libsolidity/semanticTests/various/literal_empty_string.sol index bf4da5409..ab916ae8c 100644 --- a/test/libsolidity/semanticTests/various/literal_empty_string.sol +++ b/test/libsolidity/semanticTests/various/literal_empty_string.sol @@ -12,6 +12,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // x() -> 0 // a() -> 0 diff --git a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol index 6f79442e7..75f3fa724 100644 --- a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol +++ b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol @@ -42,6 +42,7 @@ contract C { return f1() && f2(); } } - +// ==== +// compileViaYul: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol index 9ba67b198..bc5e546ca 100644 --- a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol +++ b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol @@ -3,7 +3,8 @@ contract test { int8 public y = 127; int16 public q = 250; } - +// ==== +// compileViaYul: also // ---- // x() -> 2 // y() -> 127 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol index 350ea2622..a0231678f 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol @@ -10,6 +10,7 @@ contract C { return (a, b); } } - +// ==== +// compileViaYul: also // ---- // g() -> 7, 8 diff --git a/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol index 7a8a2c276..d5f53c120 100644 --- a/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol +++ b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol @@ -6,6 +6,7 @@ contract Test { return data["abc"]; } } - +// ==== +// compileViaYul: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/various/string_tuples.sol b/test/libsolidity/semanticTests/various/string_tuples.sol index 3269d97c0..0a7a38b8b 100644 --- a/test/libsolidity/semanticTests/various/string_tuples.sol +++ b/test/libsolidity/semanticTests/various/string_tuples.sol @@ -11,7 +11,8 @@ contract C { return ("abc"); } } - +// ==== +// compileViaYul: also // ---- // f() -> 0x40, 0x8, 0x3, "abc" // g() -> 0x40, 0x80, 0x3, "abc", 0x3, "def" diff --git a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol index 1f12ba390..fae85cd34 100644 --- a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol +++ b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol @@ -5,6 +5,8 @@ contract test { return cond ? x : y; } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 0x1234ab // f(bool): false -> 0x1234abcd1234 diff --git a/test/libsolidity/semanticTests/various/tuples.sol b/test/libsolidity/semanticTests/various/tuples.sol index 00fcd6f9a..b89928a72 100644 --- a/test/libsolidity/semanticTests/various/tuples.sol +++ b/test/libsolidity/semanticTests/various/tuples.sol @@ -25,6 +25,7 @@ contract C { if (a != 8 || b != 10) return 4; } } - +// ==== +// compileViaYul: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol index 590294f15..e66b8ad4f 100644 --- a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol @@ -28,5 +28,7 @@ contract Derived is Base { } } +// ==== +// compileViaYul: also // ---- // getA() -> 2 diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 7a3c63cff..846a54415 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,7 +161,11 @@ TestTool::Result TestTool::process() { (AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush(); - m_test = m_testCaseCreator(TestCase::Config{m_path.string(), m_options.evmVersion()}); + m_test = m_testCaseCreator(TestCase::Config{ + m_path.string(), + m_options.evmVersion(), + m_options.enforceViaYul + }); if (m_test->shouldRun()) switch (TestCase::TestResult result = m_test->run(outputMessages, " ", formatted)) { @@ -232,7 +236,7 @@ TestTool::Request TestTool::handleResponse(bool _exception) cout << endl; ofstream file(m_path.string(), ios::trunc); m_test->printSource(file); - m_test->printSettings(file); + m_test->printUpdatedSettings(file); file << "// ----" << endl; m_test->printUpdatedExpectations(file, "// "); return Request::Rerun; From e8f6f63e77c829066c20a754d2919d10c3f0e6c9 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 4 May 2020 23:04:03 +0200 Subject: [PATCH 04/65] [Sol2Yul] Adding support for builtin revert(string) --- .../codegen/ir/IRGeneratorForStatements.cpp | 36 ++++++++++++++++++- .../semanticTests/revertStrings/bubble.sol | 1 + .../semanticTests/revertStrings/transfer.sol | 1 + .../semanticTests/smoke/failure.sol | 5 +-- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 572ef925f..f8c8b2956 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -737,7 +737,41 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) if (arguments.empty()) m_code << "revert(0, 0)\n"; else - solUnimplementedAssert(false, ""); + { + solAssert(arguments.size() == 1, ""); + + if (m_context.revertStrings() == RevertStrings::Strip) + m_code << "revert(0, 0)\n"; + else + { + solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),""); + + Whiskers templ(R"({ + let := () + mstore(, ) + let := (add(, 4) ) + revert(, sub(, )) + })"); + templ("pos", m_context.newYulVariable()); + templ("end", m_context.newYulVariable()); + templ( + "hash", + (u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str() + ); + templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); + templ( + "argumentVars", + (type(*arguments.front()).sizeOnStack() > 0 ? ", " : "") + + IRVariable{*arguments.front()}.commaSeparatedList() + ); + templ("encode", m_context.abiFunctions().tupleEncoder( + {&type(*arguments.front())}, + {TypeProvider::stringMemory()} + )); + + m_code << templ.render(); + } + } break; } diff --git a/test/libsolidity/semanticTests/revertStrings/bubble.sol b/test/libsolidity/semanticTests/revertStrings/bubble.sol index 5e9a0730d..24eceaba3 100644 --- a/test/libsolidity/semanticTests/revertStrings/bubble.sol +++ b/test/libsolidity/semanticTests/revertStrings/bubble.sol @@ -9,6 +9,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >=byzantium // revertStrings: debug // ---- diff --git a/test/libsolidity/semanticTests/revertStrings/transfer.sol b/test/libsolidity/semanticTests/revertStrings/transfer.sol index 0d766bd1e..5d13f7d72 100644 --- a/test/libsolidity/semanticTests/revertStrings/transfer.sol +++ b/test/libsolidity/semanticTests/revertStrings/transfer.sol @@ -18,6 +18,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >=byzantium // revertStrings: debug // ---- diff --git a/test/libsolidity/semanticTests/smoke/failure.sol b/test/libsolidity/semanticTests/smoke/failure.sol index e693a8459..95b8c9109 100644 --- a/test/libsolidity/semanticTests/smoke/failure.sol +++ b/test/libsolidity/semanticTests/smoke/failure.sol @@ -14,11 +14,12 @@ contract C { } } // ==== -// allowNonExistingFunctions: true +// compileViaYul: also // EVMVersion: >homestead +// allowNonExistingFunctions: true // ---- // _() -> FAILURE // e() -> FAILURE, hex"08c379a0", 0x20, 19, "Transaction failed." // f(bool): false -> FAILURE, hex"08c379a0", 0x20, 0 // g(bool): false -> FAILURE, hex"08c379a0", 0x20, 15, "Value is false." -// h() -> FAILURE \ No newline at end of file +// h() -> FAILURE From 7ad319687d3465c5032bf691f5aa0b4700c44491 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 5 May 2020 13:06:01 +0200 Subject: [PATCH 05/65] Sol->Yul: Add cleanup to operations. --- libsolidity/codegen/YulUtilFunctions.cpp | 51 +++++++++++++------ .../codegen/ir/IRGeneratorForStatements.cpp | 13 +++-- .../codegen/ir/IRGeneratorForStatements.h | 3 +- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 75d297b34..481fbc479 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -393,6 +393,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) return Whiskers(R"( function (x, y) -> sum { + x := (x) + y := (y) // overflow, if x >= 0 and y > (maxValue - x) if and(iszero(slt(x, 0)), sgt(y, sub(, x))) { revert(0, 0) } @@ -409,6 +411,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) ("signed", _type.isSigned()) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -421,6 +424,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) // Multiplication by zero could be treated separately and directly return zero. Whiskers(R"( function (x, y) -> product { + x := (x) + y := (y) // overflow, if x > 0, y > 0 and x > (maxValue / y) if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(, y))) { revert(0, 0) } @@ -441,6 +446,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) ("signed", _type.isSigned()) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -452,6 +458,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) return Whiskers(R"( function (x, y) -> r { + x := (x) + y := (y) if iszero(y) { revert(0, 0) } // overflow for minVal / -1 @@ -466,6 +474,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) ("functionName", functionName) ("signed", _type.isSigned()) ("minVal", toCompactHexWithPrefix(u256(_type.minValue()))) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -477,12 +486,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type) return Whiskers(R"( function (x, y) -> r { + x := (x) + y := (y) if iszero(y) { revert(0, 0) } r := smod(x, y) } )") ("functionName", functionName) ("signed", _type.isSigned()) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -494,6 +506,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) return Whiskers(R"( function (x, y) -> diff { + x := (x) + y := (y) // underflow, if y >= 0 and x < (minValue + y) if and(iszero(slt(y, 0)), slt(x, add(, y))) { revert(0, 0) } @@ -509,6 +523,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) ("signed", _type.isSigned()) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -1938,14 +1953,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type) return Whiskers(R"( function (value) -> ret { + value := (value) if (value, ) { revert(0,0) } ret := sub(value, 1) } )") - ("functionName", functionName) - ("minval", toCompactHexWithPrefix(minintval)) - ("lt", type.isSigned() ? "slt" : "lt") - .render(); + ("functionName", functionName) + ("minval", toCompactHexWithPrefix(minintval)) + ("lt", type.isSigned() ? "slt" : "lt") + ("cleanupFunction", cleanupFunction(_type)) + .render(); }); } @@ -1966,14 +1983,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type) return Whiskers(R"( function (value) -> ret { + value := (value) if (value, ) { revert(0,0) } ret := add(value, 1) } )") - ("functionName", functionName) - ("maxval", toCompactHexWithPrefix(maxintval)) - ("gt", type.isSigned() ? "sgt" : "gt") - .render(); + ("functionName", functionName) + ("maxval", toCompactHexWithPrefix(maxintval)) + ("gt", type.isSigned() ? "sgt" : "gt") + ("cleanupFunction", cleanupFunction(_type)) + .render(); }); } @@ -1988,15 +2007,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (_value) -> ret { - if slt(_value, ) { revert(0,0) } - ret := sub(0, _value) + function (value) -> ret { + value := (value) + if slt(value, ) { revert(0,0) } + ret := sub(0, value) } )") - ("functionName", functionName) - ("minval", toCompactHexWithPrefix(minintval)) - .render(); - }); + ("functionName", functionName) + ("minval", toCompactHexWithPrefix(minintval)) + ("cleanupFunction", cleanupFunction(_type)) + .render(); + }); } string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index f8c8b2956..f795e0e98 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -496,9 +496,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) isSigned = type->isSigned(); string args = - expressionAsType(_binOp.leftExpression(), *commonType) + + expressionAsType(_binOp.leftExpression(), *commonType, true) + ", " + - expressionAsType(_binOp.rightExpression(), *commonType); + expressionAsType(_binOp.rightExpression(), *commonType, true); string expr; if (op == Token::Equal) @@ -1811,11 +1811,16 @@ IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const } } -std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to) +std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup) { IRVariable from(_expression); if (from.type() == _to) - return from.commaSeparatedList(); + { + if (_forceCleanup) + return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")"; + else + return from.commaSeparatedList(); + } else return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")"; } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index b8fafd8b6..e4282c2b3 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -112,7 +112,8 @@ private: /// @returns a Yul expression representing the current value of @a _expression, /// converted to type @a _to if it does not yet have that type. - std::string expressionAsType(Expression const& _expression, Type const& _to); + /// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to. + std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false); /// @returns an output stream that can be used to define @a _var using a function call or /// single stack slot expression. From 4d739b16a45347dc3e825846ad3a38857944c920 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 5 May 2020 13:07:10 +0200 Subject: [PATCH 06/65] Cleanup function for internal function pointers. --- libsolidity/codegen/YulUtilFunctions.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 481fbc479..78af6306e 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1779,8 +1779,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type) solUnimplemented("Fixed point types not implemented."); break; case Type::Category::Function: - solAssert(dynamic_cast(_type).kind() == FunctionType::Kind::External, ""); - templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)"); + switch (dynamic_cast(_type).kind()) + { + case FunctionType::Kind::External: + templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)"); + break; + case FunctionType::Kind::Internal: + templ("body", "cleaned := value"); + break; + default: + solAssert(false, ""); + break; + } break; case Type::Category::Array: case Type::Category::Struct: From 28d25afab1a27618320a33438fca36ad1f6c2dea Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 5 May 2020 13:25:53 +0200 Subject: [PATCH 07/65] Cleanup tests for Sol->Yul arithmetic and comparison. --- .../viaYul/cleanup/checked_arithmetic.sol | 65 +++++++++++++++++++ .../viaYul/cleanup/comparison.sol | 41 ++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol create mode 100644 test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol diff --git a/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol b/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol new file mode 100644 index 000000000..cc9e44938 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol @@ -0,0 +1,65 @@ +contract C { + function add() public pure returns (uint8, uint8) { + uint8 x; uint8 y = 0; + assembly { x := 0x0101 } + return (x + y, y + x); + } + function sub() public pure returns (uint8, uint8) { + uint8 x; uint8 y = 1; + assembly { x := 0x0101 } + return (x - y, y - x); + } + function mul() public pure returns (uint8, uint8) { + uint8 x; uint8 y = 1; + assembly { x := 0x0101 } + return (x * y, y * x); + } + function div() public pure returns (uint8, uint8) { + uint8 x; uint8 y = 1; + assembly { x := 0x0101 } + return (x / y, y / x); + } + function mod() public pure returns (uint8, uint8) { + uint8 x; uint8 y = 2; + assembly { x := 0x0101 } + return (x % y, y % x); + } + function inc_pre() public pure returns (uint8) { + uint8 x; + assembly { x := 0x0100 } + return ++x; + } + function inc_post() public pure returns (uint8) { + uint8 x; + assembly { x := 0x0100 } + return x++; + } + function dec_pre() public pure returns (uint8) { + uint8 x; + assembly { x := not(0xFF) } + return --x; + } + function dec_post() public pure returns (uint8) { + uint8 x; + assembly { x := not(0xFF) } + return x--; + } + function neg() public pure returns (int8) { + int8 x; + assembly { x := 0x80 } + return -x; + } +} +// ==== +// compileViaYul: true +// ---- +// add() -> 1, 1 +// sub() -> 0, 0 +// mul() -> 1, 1 +// div() -> 1, 1 +// mod() -> 1, 0 +// inc_pre() -> 1 +// inc_post() -> 0 +// dec_pre() -> FAILURE +// dec_post() -> FAILURE +// neg() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol b/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol new file mode 100644 index 000000000..86bd8ede7 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol @@ -0,0 +1,41 @@ +contract C { + function eq() public pure returns (bool) { + uint8 x = 1; uint8 y; + assembly { y := 0x0101 } + return (x == y); + } + function neq() public pure returns (bool) { + uint8 x = 1; uint8 y; + assembly { y := 0x0101 } + return (x != y); + } + function geq() public pure returns (bool) { + uint8 x = 1; uint8 y; + assembly { y := 0x0101 } + return (x >= y); + } + function leq() public pure returns (bool) { + uint8 x = 2; uint8 y; + assembly { y := 0x0101 } + return (x <= y); + } + function gt() public pure returns (bool) { + uint8 x = 2; uint8 y; + assembly { y := 0x0101 } + return (x > y); + } + function lt() public pure returns (bool) { + uint8 x = 1; uint8 y; + assembly { y := 0x0101 } + return (x < y); + } +} +// ==== +// compileViaYul: also +// ---- +// eq() -> true +// neq() -> false +// geq() -> true +// leq() -> false +// gt() -> true +// lt() -> false From 7b48c120b590f94006c907722bb4ee2a486a02b5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 5 May 2020 14:05:11 +0200 Subject: [PATCH 08/65] Always enforce via yul for isoltest runs. --- test/tools/IsolTestOptions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tools/IsolTestOptions.cpp b/test/tools/IsolTestOptions.cpp index b8ae70a63..28552a86a 100644 --- a/test/tools/IsolTestOptions.cpp +++ b/test/tools/IsolTestOptions.cpp @@ -69,6 +69,7 @@ bool IsolTestOptions::parse(int _argc, char const* const* _argv) std::cout << options << std::endl; return false; } + enforceViaYul = true; return res; } From 02c20698c9fa50c31d008ed8af7af3c330053e20 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 20 Apr 2020 23:05:14 +0200 Subject: [PATCH 09/65] IR generation for constants. --- libsolidity/codegen/ir/IRGenerator.cpp | 12 +++++++- .../codegen/ir/IRGeneratorForStatements.cpp | 30 +++++++++++++++---- .../codegen/ir/IRGeneratorForStatements.h | 4 +++ .../accessor_for_const_state_variable.sol | 3 +- ...signment_to_const_var_involving_keccak.sol | 2 ++ .../constants/constant_string.sol | 2 ++ .../simple_constant_variables_test.sol | 2 ++ .../inherited_constant_state_var.sol | 2 ++ ...ment_to_const_var_involving_expression.sol | 2 ++ 9 files changed, 52 insertions(+), 7 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 620daa86e..bac4fea66 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -202,11 +202,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) Type const* type = _varDecl.annotation().type; - solAssert(!_varDecl.isConstant(), ""); solAssert(_varDecl.isStateVariable(), ""); if (auto const* mappingType = dynamic_cast(type)) return m_context.functionCollector().createFunction(functionName, [&]() { + solAssert(!_varDecl.isConstant(), ""); pair slot_offset = m_context.storageLocationOfVariable(_varDecl); solAssert(slot_offset.second == 0, ""); FunctionType funType(_varDecl); @@ -268,6 +268,16 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) ("id", to_string(_varDecl.id())) .render(); } + else if (_varDecl.isConstant()) + return Whiskers(R"( + function () -> { + := () + } + )") + ("functionName", functionName) + ("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl)) + ("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack())) + .render(); else { pair slot_offset = m_context.storageLocationOfVariable(_varDecl); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index f8c8b2956..cdb5f3c26 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -178,6 +178,28 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre return variable; } +string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) +{ + string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id()); + return m_context.functionCollector().createFunction(functionName, [&] { + Whiskers templ(R"( + function () -> { + + := + } + )"); + templ("functionName", functionName); + IRGeneratorForStatements generator(m_context, m_utils); + solAssert(_constant.value(), ""); + Type const& constantType = *_constant.type(); + templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); + templ("code", generator.code()); + templ("ret", IRVariable("ret", constantType).commaSeparatedList()); + + return templ.render(); + }); +} + void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { if (Expression const* expression = _varDeclStatement.initialValue()) @@ -1576,11 +1598,9 @@ void IRGeneratorForStatements::handleVariableReference( Expression const& _referencingExpression ) { - // TODO for the constant case, we have to be careful: - // If the value is visited twice, `defineExpression` is called twice on - // the same expression. - solUnimplementedAssert(!_variable.isConstant(), ""); - if (_variable.isStateVariable() && _variable.immutable()) + if (_variable.isStateVariable() && _variable.isConstant()) + define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; + else if (_variable.isStateVariable() && _variable.immutable()) setLValue(_referencingExpression, IRLValue{ *_variable.annotation().type, IRLValue::Immutable{&_variable} diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index b8fafd8b6..a35c09fbb 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -54,6 +54,10 @@ public: /// Calculates expression's value and returns variable where it was stored IRVariable evaluateExpression(Expression const& _expression, Type const& _to); + /// @returns the name of a function that computes the value of the given constant + /// and also generates the function. + std::string constantValueFunction(VariableDeclaration const& _constant); + void endVisit(VariableDeclarationStatement const& _variableDeclaration) override; bool visit(Conditional const& _conditional) override; bool visit(Assignment const& _assignment) override; diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol index a9402aa86..a56956a90 100644 --- a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol +++ b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol @@ -1,6 +1,7 @@ contract Lotto { uint256 public constant ticketPrice = 555; } - +// ==== +// compileViaYul: also // ---- // ticketPrice() -> 555 diff --git a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol index 2fc479d0e..a1bdf3236 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol @@ -5,5 +5,7 @@ contract C { return x; } } +// ==== +// compileViaYul: also // ---- // f() -> 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 diff --git a/test/libsolidity/semanticTests/constants/constant_string.sol b/test/libsolidity/semanticTests/constants/constant_string.sol index 56cbf982d..6b588571d 100644 --- a/test/libsolidity/semanticTests/constants/constant_string.sol +++ b/test/libsolidity/semanticTests/constants/constant_string.sol @@ -16,6 +16,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 3, "\x03\x01\x02" // g() -> 0x20, 3, "\x03\x01\x02" diff --git a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol index c05060b7e..613cf62bb 100644 --- a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol +++ b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol @@ -6,5 +6,7 @@ contract Foo { uint256 constant x = 56; } +// ==== +// compileViaYul: also // ---- // getX() -> 56 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol index e25db0dcb..ab7a097cc 100644 --- a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol +++ b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol @@ -9,5 +9,7 @@ contract B is A { } } +// ==== +// compileViaYul: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol index b83b1c598..4f849be89 100644 --- a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol +++ b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol @@ -6,5 +6,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x57a From b580106c806aea796594edfa35a784955cc08f42 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 5 May 2020 17:22:54 +0200 Subject: [PATCH 10/65] Yul parser hack and more yul tests. --- libyul/AsmParser.cpp | 2 ++ test/libsolidity/semanticTests/array/function_memory_array.sol | 3 ++- .../semanticTests/cleanup/cleanup_bytes_types_shortening.sol | 3 ++- .../semanticTests/reverts/invalid_enum_compared.sol | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 758b11d92..99a3a6e21 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -325,6 +325,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() case Token::Bool: case Token::Address: case Token::Var: + case Token::In: { YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) @@ -515,6 +516,7 @@ YulString Parser::expectAsmIdentifier() case Token::Bool: case Token::Identifier: case Token::Var: + case Token::In: break; default: expectToken(Token::Identifier); diff --git a/test/libsolidity/semanticTests/array/function_memory_array.sol b/test/libsolidity/semanticTests/array/function_memory_array.sol index cc6b3cf46..9ff4cad33 100644 --- a/test/libsolidity/semanticTests/array/function_memory_array.sol +++ b/test/libsolidity/semanticTests/array/function_memory_array.sol @@ -30,7 +30,8 @@ contract C { return arr[i](x); } } - +// ==== +// compileViaYul: also // ---- // test(uint256,uint256): 10, 0 -> 11 // test(uint256,uint256): 10, 1 -> 12 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol index dc7c7f890..073e015b4 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol @@ -10,6 +10,7 @@ contract C { require(y == bytes2(0xffff)); } } - +// ==== +// compileViaYul: also // ---- // f() -> "\xff\xff\xff\xff" diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol index 17ee32bb8..3c082a363 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol @@ -22,7 +22,8 @@ contract C { return garbled != garbled; } } - +// ==== +// compileViaYul: also // ---- // test_eq_ok() -> 1 // test_eq() -> FAILURE # both should throw # From c25a3eba14697b011411e105b019f626ba721ef1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 4 May 2020 14:21:48 +0200 Subject: [PATCH 11/65] Fix accessing memory reference types in yul codegen and clean up on memory reads. --- libsolidity/codegen/YulUtilFunctions.cpp | 20 +++++++++++-------- libsolidity/codegen/YulUtilFunctions.h | 2 +- .../codegen/ir/IRGeneratorForStatements.cpp | 12 ++++------- .../viaYul/dirty_memory_read.sol | 15 ++++++++++++++ 4 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 78af6306e..540086756 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2245,7 +2245,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC } solAssert(_type.isValueType(), ""); - if (auto const* funType = dynamic_cast(&_type)) if (funType->kind() == FunctionType::Kind::External) return Whiskers(R"( @@ -2259,18 +2258,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC ("splitFunction", splitExternalFunctionIdFunction()) .render(); + return Whiskers(R"( - function (memPtr) -> value { - value := (memPtr) - + function (ptr) -> value { + + value := calldataload(ptr) (value) - + + value := (mload(ptr)) + } )") ("functionName", functionName) - ("load", _fromCalldata ? "calldataload" : "mload") - ("needsValidation", _fromCalldata) - ("validate", _fromCalldata ? validatorFunction(_type) : "") + ("fromCalldata", _fromCalldata) + ("validate", validatorFunction(_type)) + // Byte array elements generally need cleanup. + // Other types are cleaned as well to account for dirty memory e.g. due to inline assembly. + ("cleanup", cleanupFunction(_type)) .render(); }); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 4f7556f5f..9f21a7d39 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -204,7 +204,7 @@ public: std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes); std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes); - /// @returns a function that reads a value type from memory. + /// @returns a function that reads a value type from memory. Performs cleanup. /// signature: (addr) -> value std::string readFromMemory(Type const& _type); /// @returns a function that reads a value type from calldata. diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index f795e0e98..aee1ec34b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -117,10 +117,12 @@ struct CopyTranslate: public yul::ASTCopier reference.isOffset == false && reference.isSlot == false, "Should not be called for offset/slot" ); + auto const& var = m_context.localVariable(*varDecl); + solAssert(var.type().sizeOnStack() == 1, ""); return yul::Identifier{ _identifier.location, - yul::YulString{m_context.localVariable(*varDecl).name()} + yul::YulString{var.commaSeparatedList()} }; } @@ -2060,13 +2062,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) ")\n"; }, [&](IRLValue::Memory const& _memory) { - if (_memory.byteArrayElement) - define(result) << - m_utils.cleanupFunction(_lvalue.type) << - "(mload(" << - _memory.address << - "))\n"; - else if (_lvalue.type.isValueType()) + if (_lvalue.type.isValueType()) define(result) << m_utils.readFromMemory(_lvalue.type) << "(" << diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol new file mode 100644 index 000000000..d03aa489f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_read.sol @@ -0,0 +1,15 @@ +contract C { + function f() public pure returns (uint8 x, bool a, bool b) { + uint8[1] memory m; + assembly { + mstore(m, 257) + } + x = m[0]; + a = (m[0] == 0x01); + b = (m[0] == 0x0101); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, true, false From 02c5f1f7488f43ce56668982ecad5de596ac6924 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 6 May 2020 11:49:05 +0200 Subject: [PATCH 12/65] Remove --optimize-yul flag for soltest --- test/Common.cpp | 1 - test/Common.h | 1 - test/ExecutionFramework.cpp | 4 +--- test/libsolidity/GasCosts.cpp | 4 ++-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/Common.cpp b/test/Common.cpp index b65555ba1..69bfd21ee 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -94,7 +94,6 @@ CommonOptions::CommonOptions(std::string _caption): ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("optimize", po::bool_switch(&optimize), "enables optimization") - ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") ("show-messages", po::bool_switch(&showMessages), "enables message output") diff --git a/test/Common.h b/test/Common.h index 128d23ee5..119050f39 100644 --- a/test/Common.h +++ b/test/Common.h @@ -46,7 +46,6 @@ struct CommonOptions: boost::noncopyable boost::filesystem::path evmonePath; boost::filesystem::path testPath; bool optimize = false; - bool optimizeYul = false; bool enforceViaYul = false; bool disableSMT = false; bool useABIEncoderV2 = false; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 170f5638c..ab11b2e98 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -49,9 +49,7 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): m_showMessages(solidity::test::CommonOptions::get().showMessages), m_evmHost(make_shared(m_evmVersion)) { - if (solidity::test::CommonOptions::get().optimizeYul) - m_optimiserSettings = solidity::frontend::OptimiserSettings::full(); - else if (solidity::test::CommonOptions::get().optimize) + if (solidity::test::CommonOptions::get().optimize) m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); reset(); diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 1fa0bf712..252b7759b 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(string_storage) // This is only correct on >=Constantinople. else if (CommonOptions::get().useABIEncoderV2) { - if (CommonOptions::get().optimizeYul) + if (CommonOptions::get().optimize) { // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(string_storage) // This is only correct on >=Constantinople. else if (CommonOptions::get().useABIEncoderV2) { - if (CommonOptions::get().optimizeYul) + if (CommonOptions::get().optimize) { if (evmVersion < EVMVersion::istanbul()) CHECK_GAS(0, 21567, 20); From a93d79ff28d80b1b101f65e5ad5c605e6f33031f Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Mon, 4 May 2020 11:28:27 +0200 Subject: [PATCH 13/65] Remove obsolete flag --optimize-yul from scripts --- .circleci/soltest.sh | 2 +- scripts/tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/soltest.sh b/.circleci/soltest.sh index 948c79dee..f55239e60 100755 --- a/.circleci/soltest.sh +++ b/.circleci/soltest.sh @@ -57,7 +57,7 @@ get_logfile_basename() { BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml" SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS" test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize" -test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul" +test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2" echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}" diff --git a/scripts/tests.sh b/scripts/tests.sh index c0601a12d..00b4a1641 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -100,7 +100,7 @@ do force_abiv2_flag="" if [[ "$abiv2" == "yes" ]] then - force_abiv2_flag="--abiencoderv2 --optimize-yul" + force_abiv2_flag="--abiencoderv2" fi printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..." From e3641b88ec59895762dbb649830da5aea4a94a5e Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 18 Apr 2020 03:00:22 +0200 Subject: [PATCH 14/65] Groundwork. Prepare for automatic tagging [Not compilable until the next commit] --- liblangutil/ErrorReporter.cpp | 65 +++++++++++++-------- liblangutil/ErrorReporter.h | 46 ++++++++++----- libsolidity/analysis/ConstantEvaluator.cpp | 1 + libsolidity/analysis/ImmutableValidator.cpp | 1 + libsolidity/analysis/SyntaxChecker.cpp | 2 +- libsolidity/parsing/Parser.cpp | 2 +- 6 files changed, 76 insertions(+), 41 deletions(-) diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index c6c059bc7..d06fbc65b 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -28,6 +28,8 @@ using namespace std; using namespace solidity; using namespace solidity::langutil; +ErrorId solidity::langutil::operator"" _error(unsigned long long _error) { return ErrorId{ _error }; } + ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) { if (&_errorReporter == this) @@ -36,30 +38,31 @@ ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) return *this; } - -void ErrorReporter::warning(string const& _description) +void ErrorReporter::warning(ErrorId _error, string const& _description) { - error(Error::Type::Warning, SourceLocation(), _description); + error(_error, Error::Type::Warning, SourceLocation(), _description); } void ErrorReporter::warning( + ErrorId _error, SourceLocation const& _location, string const& _description ) { - error(Error::Type::Warning, _location, _description); + error(_error, Error::Type::Warning, _location, _description); } void ErrorReporter::warning( + ErrorId _error, SourceLocation const& _location, string const& _description, SecondarySourceLocation const& _secondaryLocation ) { - error(Error::Type::Warning, _location, _secondaryLocation, _description); + error(_error, Error::Type::Warning, _location, _secondaryLocation, _description); } -void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) +void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, string const& _description) { if (checkForExcessiveErrors(_type)) return; @@ -72,7 +75,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st m_errorList.push_back(err); } -void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { if (checkForExcessiveErrors(_type)) return; @@ -123,15 +126,15 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) return false; } -void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { - error(_type, _location, _secondaryLocation, _description); + error(_error, _type, _location, _secondaryLocation, _description); BOOST_THROW_EXCEPTION(FatalError()); } -void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description) +void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, string const& _description) { - error(_type, _location, _description); + error(_error, _type, _location, _description); BOOST_THROW_EXCEPTION(FatalError()); } @@ -145,9 +148,10 @@ void ErrorReporter::clear() m_errorList.clear(); } -void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { error( + _error, Error::Type::DeclarationError, _location, _secondaryLocation, @@ -155,53 +159,59 @@ void ErrorReporter::declarationError(SourceLocation const& _location, SecondaryS ); } -void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description) +void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, string const& _description) { error( + _error, Error::Type::DeclarationError, _location, _description ); } -void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description) +void ErrorReporter::fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description) { fatalError( + _error, Error::Type::DeclarationError, _location, _description); } -void ErrorReporter::parserError(SourceLocation const& _location, string const& _description) +void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, string const& _description) { error( + _error, Error::Type::ParserError, _location, _description ); } -void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description) +void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description) { fatalError( + _error, Error::Type::ParserError, _location, _description ); } -void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description) +void ErrorReporter::syntaxError(ErrorId _error, SourceLocation const& _location, string const& _description) { error( + _error, Error::Type::SyntaxError, _location, _description ); } -void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { error( + _error, Error::Type::TypeError, _location, _secondaryLocation, @@ -209,18 +219,21 @@ void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLo ); } -void ErrorReporter::typeError(SourceLocation const& _location, string const& _description) +void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, string const& _description) { error( + _error, Error::Type::TypeError, _location, _description ); } -void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) + +void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { fatalError( + _error, Error::Type::TypeError, _location, _secondaryLocation, @@ -228,26 +241,30 @@ void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySou ); } -void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description) +void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, string const& _description) { - fatalError(Error::Type::TypeError, + fatalError( + _error, + Error::Type::TypeError, _location, _description ); } -void ErrorReporter::docstringParsingError(string const& _description) +void ErrorReporter::docstringParsingError(ErrorId _error, string const& _description) { error( + _error, Error::Type::DocstringParsingError, SourceLocation(), _description ); } -void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description) +void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _location, string const& _description) { error( + _error, Error::Type::DocstringParsingError, _location, _description diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index 5aca4beb9..17e236e61 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -33,6 +33,15 @@ namespace solidity::langutil { +/** + * Unique identifiers are used to tag and track individual error cases. + * They are passed as the first parameter of error reporting functions. + * Suffix _error helps to find them in the sources. + * The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error). + */ +struct ErrorId { unsigned long long error = 0; }; +ErrorId operator"" _error(unsigned long long error); + class ErrorReporter { public: @@ -50,64 +59,68 @@ public: m_errorList += _errorList; } - void warning(std::string const& _description); + void warning(ErrorId _error, std::string const& _description); - void warning(SourceLocation const& _location, std::string const& _description); + void warning(ErrorId _error, SourceLocation const& _location, std::string const& _description); void warning( + ErrorId _error, SourceLocation const& _location, std::string const& _description, SecondarySourceLocation const& _secondaryLocation ); void error( + ErrorId _error, Error::Type _type, SourceLocation const& _location, std::string const& _description ); void declarationError( + ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description ); - void declarationError(SourceLocation const& _location, std::string const& _description); + void declarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description); - void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); + void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description); - void parserError(SourceLocation const& _location, std::string const& _description); + void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description); - void fatalParserError(SourceLocation const& _location, std::string const& _description); + void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description); - void syntaxError(SourceLocation const& _location, std::string const& _description); + void syntaxError(ErrorId _error, SourceLocation const& _location, std::string const& _description); void typeError( + ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(), std::string const& _description = std::string() ); - void typeError(SourceLocation const& _location, std::string const& _description); + void typeError(ErrorId _error, SourceLocation const& _location, std::string const& _description); template - void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions) + void typeErrorConcatenateDescriptions(ErrorId _error, SourceLocation const& _location, Strings const&... _descriptions) { - std::initializer_list const descs = {_descriptions...}; + std::initializer_list const descs = { _descriptions... }; solAssert(descs.size() > 0, "Need error descriptions!"); auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); }); std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " "); - error(Error::Type::TypeError, _location, errorStr); + error(_error, Error::Type::TypeError, _location, errorStr); } - void fatalTypeError(SourceLocation const& _location, std::string const& _description); - void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description); + void fatalTypeError(ErrorId _error, SourceLocation const& _location, std::string const& _description); + void fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description); - void docstringParsingError(std::string const& _description); - void docstringParsingError(SourceLocation const& _location, std::string const& _description); + void docstringParsingError(ErrorId _error, std::string const& _description); + void docstringParsingError(ErrorId _error, SourceLocation const& _location, std::string const& _description); ErrorList const& errors() const; @@ -124,18 +137,21 @@ public: private: void error( + ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description = std::string()); void fatalError( + ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, std::string const& _description = std::string()); void fatalError( + ErrorId _error, Error::Type _type, SourceLocation const& _location = SourceLocation(), std::string const& _description = std::string()); diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index d48d640ce..7617cc518 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -29,6 +29,7 @@ using namespace std; using namespace solidity; using namespace solidity::frontend; +using namespace solidity::langutil; void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 598f43e50..a63d38ccf 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -22,6 +22,7 @@ #include using namespace solidity::frontend; +using namespace solidity::langutil; void ImmutableValidator::analyze() { diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c9ad29d33..bd2060d6a 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -123,7 +123,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError( _pragma.location(), "Source file requires different compiler version (current compiler is " + - string(VersionString) + " - note that nightly builds are considered to be " + string(VersionString) + ") - note that nightly builds are considered to be " "strictly less than the released version" ); m_versionPragmaFound = true; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index c6da9a55b..d3c4a2cdd 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -130,7 +130,7 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector c m_errorReporter.fatalParserError( _location, "Source file requires different compiler version (current compiler is " + - string(VersionString) + " - note that nightly builds are considered to be " + string(VersionString) + ") - note that nightly builds are considered to be " "strictly less than the released version" ); } From 8f68c043583247e56ff848d5193d01e49d4c2e9b Mon Sep 17 00:00:00 2001 From: a3d4 Date: Wed, 6 May 2020 00:38:28 +0200 Subject: [PATCH 15/65] Add unique IDs to error reporting calls --- liblangutil/ParserBase.cpp | 6 +- libsolidity/analysis/ConstantEvaluator.cpp | 3 +- libsolidity/analysis/ContractLevelChecker.cpp | 21 +- libsolidity/analysis/ControlFlowAnalyzer.cpp | 3 +- .../analysis/DeclarationTypeChecker.cpp | 12 +- libsolidity/analysis/DocStringAnalyser.cpp | 2 +- libsolidity/analysis/ImmutableValidator.cpp | 7 + libsolidity/analysis/NameAndTypeResolver.cpp | 13 +- libsolidity/analysis/OverrideChecker.cpp | 13 +- libsolidity/analysis/PostTypeChecker.cpp | 8 +- libsolidity/analysis/ReferencesResolver.cpp | 6 +- libsolidity/analysis/StaticAnalyzer.cpp | 13 +- libsolidity/analysis/SyntaxChecker.cpp | 56 +-- libsolidity/analysis/TypeChecker.cpp | 330 +++++++++++------- libsolidity/analysis/ViewPureChecker.cpp | 5 + libsolidity/formal/BMC.cpp | 21 +- libsolidity/formal/CHC.cpp | 4 +- libsolidity/formal/SMTEncoder.cpp | 29 +- libsolidity/interface/CompilerStack.cpp | 4 +- libsolidity/parsing/DocStringParser.cpp | 2 +- libsolidity/parsing/Parser.cpp | 1 + libyul/AsmAnalysis.cpp | 5 +- libyul/AsmParser.cpp | 9 +- libyul/AsmScopeFiller.cpp | 2 + 24 files changed, 382 insertions(+), 193 deletions(-) diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index 747cb37cf..c3a15794b 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -145,12 +145,12 @@ void ParserBase::decreaseRecursionDepth() void ParserBase::parserWarning(string const& _description) { - m_errorReporter.warning(currentLocation(), _description); + m_errorReporter.warning(6635_error, currentLocation(), _description); } void ParserBase::parserError(SourceLocation const& _location, string const& _description) { - m_errorReporter.parserError(_location, _description); + m_errorReporter.parserError(2314_error, _location, _description); } void ParserBase::parserError(string const& _description) @@ -165,5 +165,5 @@ void ParserBase::fatalParserError(string const& _description) void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description) { - m_errorReporter.fatalParserError(_location, _description); + m_errorReporter.fatalParserError(1957_error, _location, _description); } diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 7617cc518..4bd8b1389 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -47,6 +47,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right); if (!commonType) m_errorReporter.fatalTypeError( + 6020_error, _operation.location(), "Operator " + string(TokenTraits::toString(_operation.getOperator())) + @@ -83,7 +84,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) else if (!m_types->count(value.get())) { if (m_depth > 32) - m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); + m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value); } diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 18051f99b..1327e89f3 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -78,6 +78,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co { if (constructor) m_errorReporter.declarationError( + 7997_error, function->location(), SecondarySourceLocation().append("Another declaration is here:", constructor->location()), "More than one constructor defined." @@ -88,6 +89,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co { if (fallback) m_errorReporter.declarationError( + 7301_error, function->location(), SecondarySourceLocation().append("Another declaration is here:", fallback->location()), "Only one fallback function is allowed." @@ -98,6 +100,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co { if (receive) m_errorReporter.declarationError( + 4046_error, function->location(), SecondarySourceLocation().append("Another declaration is here:", receive->location()), "Only one receive function is allowed." @@ -147,6 +150,7 @@ void ContractLevelChecker::findDuplicateDefinitions(map> const ssl.limitSize(_message); m_errorReporter.declarationError( + 1686_error, overloads[i]->location(), ssl, _message @@ -197,9 +201,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c if (_contract.abstract()) { if (_contract.contractKind() == ContractKind::Interface) - m_errorReporter.typeError(_contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly."); + m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly."); else if (_contract.contractKind() == ContractKind::Library) - m_errorReporter.typeError(_contract.location(), "Libraries cannot be abstract."); + m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract."); else solAssert(_contract.contractKind() == ContractKind::Contract, ""); } @@ -215,7 +219,8 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c SecondarySourceLocation ssl; for (auto declaration: _contract.annotation().unimplementedDeclarations) ssl.append("Missing implementation: ", declaration->location()); - m_errorReporter.typeError(_contract.location(), ssl, + m_errorReporter.typeError( + 3656_error,_contract.location(), ssl, "Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."); @@ -243,6 +248,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons } else m_errorReporter.declarationError( + 1563_error, modifier->location(), "Modifier-style base constructor call without arguments." ); @@ -304,6 +310,7 @@ void ContractLevelChecker::annotateBaseConstructorArguments( } m_errorReporter.declarationError( + 3364_error, *mainLocation, ssl, "Base constructor arguments given twice." @@ -343,6 +350,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c for (size_t j = i + 1; j < it.second.size(); ++j) if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) m_errorReporter.typeError( + 9914_error, it.second[j].first->location(), "Function overload clash during conversion to external types for arguments." ); @@ -356,6 +364,7 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra util::FixedHash<4> const& hash = it.first; if (hashes.count(hash)) m_errorReporter.typeError( + 1860_error, _contract.location(), string("Function signature hash collision for ") + it.second->externalSignature() ); @@ -369,11 +378,11 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c return; if (!_contract.baseContracts().empty()) - m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit."); + m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit."); for (auto const& var: _contract.stateVariables()) if (!var->isConstant()) - m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables"); + m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables"); } void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract) @@ -412,6 +421,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _ if (!errors.infos.empty()) m_errorReporter.fatalTypeError( + 6594_error, _contract.location(), errors, std::string("Contract \"") + @@ -428,6 +438,7 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition if (auto const* fallback = _contract.fallbackFunction()) if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction()) m_errorReporter.warning( + 3628_error, _contract.location(), "This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.", SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location()) diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 1a001eb1e..84979082f 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -136,6 +136,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod ssl.append("The variable was declared here.", variableOccurrence->declaration().location()); m_errorReporter.typeError( + 3464_error, variableOccurrence->occurrence() ? *variableOccurrence->occurrence() : variableOccurrence->declaration().location(), @@ -176,6 +177,6 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const* // Extend the location, as long as the next location overlaps (unreachable is sorted). for (; it != unreachable.end() && it->start <= location.end; ++it) location.end = std::max(location.end, it->end); - m_errorReporter.warning(location, "Unreachable code."); + m_errorReporter.warning(5740_error, location, "Unreachable code."); } } diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 0c53732d6..4e55151e9 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -188,12 +188,14 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping) { if (contractType->contractDefinition().isLibrary()) m_errorReporter.fatalTypeError( + 1665_error, typeName->location(), "Library types cannot be used as mapping keys." ); } else if (typeName->annotation().type->category() != Type::Category::Enum) m_errorReporter.fatalTypeError( + 7804_error, typeName->location(), "Only elementary types, contract types or enums are allowed as mapping keys." ); @@ -253,9 +255,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) return; if (_variable.isConstant() && !_variable.isStateVariable()) - m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables."); if (_variable.immutable() && !_variable.isStateVariable()) - m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables."); + m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables."); if (!_variable.typeName()) { @@ -360,19 +362,19 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; - m_errorReporter.typeError(_location, _description); + m_errorReporter.typeError(2311_error, _location, _description); } void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; - m_errorReporter.fatalTypeError(_location, _description); + m_errorReporter.fatalTypeError(5651_error, _location, _description); } void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; - m_errorReporter.fatalDeclarationError(_location, _description); + m_errorReporter.fatalDeclarationError(2046_error, _location, _description); } bool DeclarationTypeChecker::check(ASTNode const& _node) diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index ee0a39163..53edc8cdf 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings( void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description) { m_errorOccured = true; - m_errorReporter.docstringParsingError(_location, _description); + m_errorReporter.docstringParsingError(7816_error, _location, _description); } diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index a63d38ccf..0fad0a66d 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -166,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va { if (!m_currentConstructor) m_errorReporter.typeError( + 1581_error, _expression.location(), "Immutable variables can only be initialized inline or assigned directly in the constructor." ); else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) m_errorReporter.typeError( + 7484_error, _expression.location(), "Immutable variables must be initialized in the constructor of the contract they are defined in." ); else if (m_inLoop) m_errorReporter.typeError( + 6672_error, _expression.location(), "Immutable variables can only be initialized once, not in a while statement." ); else if (m_inBranch) m_errorReporter.typeError( + 4599_error, _expression.location(), "Immutable variables must be initialized unconditionally, not in an if statement." ); if (!m_initializedStateVariables.emplace(&_variableReference).second) m_errorReporter.typeError( + 1574_error, _expression.location(), "Immutable state variable already initialized." ); } else if (m_inConstructionContext) m_errorReporter.typeError( + 7733_error, _expression.location(), "Immutable variables cannot be read during contract creation time, which means " "they cannot be read in the constructor or any function or modifier called from it." @@ -206,6 +212,7 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source if (varDecl->immutable()) if (!util::contains(m_initializedStateVariables, varDecl)) m_errorReporter.typeError( + 2658_error, _location, solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), "Construction control flow ends without initializing all immutable state variables." diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 65831ec86..87482b78c 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -80,6 +80,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), "Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found." ); @@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), "Declaration \"" + alias.symbol->name() + @@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() // Don't warn the user for what the user did not. continue; m_errorReporter.warning( + 8261_error, declaration->location(), "Variable is shadowed in inline assembly by an instruction of the same name" ); @@ -326,6 +329,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) } m_errorReporter.declarationError( + 9097_error, secondDeclarationLocation, SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), "Identifier already declared." @@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) UserDefinedTypeName const& baseName = baseSpecifier->name(); auto base = dynamic_cast(baseName.annotation().referencedDeclaration); if (!base) - m_errorReporter.fatalTypeError(baseName.location(), "Contract expected."); + m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected."); // "push_front" has the effect that bases mentioned later can overwrite members of bases // mentioned earlier input.back().push_front(base); vector const& basesBases = base->annotation().linearizedBaseContracts; if (basesBases.empty()) - m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract"); + m_errorReporter.fatalTypeError(2449_error, baseName.location(), "Definition of base has to precede definition of derived contract"); input.push_front(list(basesBases.begin(), basesBases.end())); } input.back().push_front(&_contract); vector result = cThreeMerge(input); if (result.empty()) - m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible"); + m_errorReporter.fatalTypeError(5005_error, _contract.location(), "Linearization of inheritance graph impossible"); _contract.annotation().linearizedBaseContracts = result; _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end()); } @@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( } _errorReporter.declarationError( + 2333_error, secondDeclarationLocation, SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), "Identifier already declared." @@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( { if (dynamic_cast(shadowedDeclaration)) _errorReporter.warning( + 2319_error, *_errorLocation, "This declaration shadows a builtin symbol." ); @@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( { auto shadowedLocation = shadowedDeclaration->location(); _errorReporter.warning( + 2519_error, _declaration.location(), "This declaration shadows an existing declaration.", SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation) diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index 91b45d33c..fbff807f9 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -461,6 +461,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract) { if (contains_if(inheritedFuncs, MatchByName{modifier->name()})) m_errorReporter.typeError( + 5631_error, modifier->location(), "Override changes function or public state variable to modifier." ); @@ -474,7 +475,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract) continue; if (contains_if(inheritedMods, MatchByName{function->name()})) - m_errorReporter.typeError(function->location(), "Override changes modifier to function."); + m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function."); checkOverrideList(OverrideProxy{function}, inheritedFuncs); } @@ -484,7 +485,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract) continue; if (contains_if(inheritedMods, MatchByName{stateVar->name()})) - m_errorReporter.typeError(stateVar->location(), "Override changes modifier to public state variable."); + m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable."); checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs); } @@ -500,6 +501,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType()) m_errorReporter.typeError( + 1078_error, _overriding.location(), "Override changes modifier signature." ); @@ -593,6 +595,7 @@ void OverrideChecker::overrideListError( contractSingularPlural = "contracts "; m_errorReporter.typeError( + 5883_error, _item.overrides() ? _item.overrides()->location() : _item.location(), ssl, _message1 + @@ -606,6 +609,7 @@ void OverrideChecker::overrideListError( void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg) { m_errorReporter.typeError( + 9456_error, _overriding.location(), SecondarySourceLocation().append(_secondaryMsg, _super.location()), _message @@ -616,6 +620,7 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg) { m_errorReporter.typeError( + 1452_error, _overriding.location(), SecondarySourceLocation().append(_secondaryMsg, _super.location()), _message @@ -718,7 +723,7 @@ void OverrideChecker::checkAmbiguousOverridesInternal(set _baseCa " Since one of the bases defines a public state variable which cannot be overridden, " "you have to change the inheritance layout or the names of the functions."; - m_errorReporter.typeError(_location, ssl, message); + m_errorReporter.typeError(6480_error, _location, ssl, message); } set OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const @@ -766,6 +771,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign SecondarySourceLocation ssl; ssl.append("First occurrence here: ", list[i-1]->location()); m_errorReporter.typeError( + 4520_error, list[i]->location(), ssl, "Duplicate contract \"" + @@ -791,6 +797,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign if (_item.overrides() && expectedContracts.empty()) m_errorReporter.typeError( + 7792_error, _item.overrides()->location(), _item.astNodeNameCapitalized() + " has override specified but does not override anything." ); diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index 62317587a..1b5b5b7c3 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -122,6 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker for (auto declaration: m_constVariables) if (auto identifier = findCycle(*declaration)) m_errorReporter.typeError( + 6161_error, declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + "." @@ -165,7 +166,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector& _cycleDetector, size_t _depth) { if (_depth >= 256) - m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator."); + m_errorReporter.fatalDeclarationError(7380_error, _variable.location(), "Variable definition exhausting cyclic dependency validator."); // Iterating through the dependencies needs to be deterministic and thus cannot // depend on the memory layout. @@ -209,6 +210,7 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker TypeType const* actualTypeType = dynamic_cast(decl->type()); m_errorReporter.typeError( + 9301_error, override->location(), "Expected contract but got " + actualTypeType->actualType()->toString(true) + @@ -243,6 +245,7 @@ struct ModifierContextChecker: public PostTypeChecker::Checker if (ModifierType const* type = dynamic_cast(_identifier.annotation().type)) { m_errorReporter.typeError( + 3112_error, _identifier.location(), "Modifier can only be referenced in function headers." ); @@ -280,6 +283,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker // Check for event outside of emit statement if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) m_errorReporter.typeError( + 3132_error, _functionCall.location(), "Event invocations have to be prefixed by \"emit\"." ); @@ -308,7 +312,7 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker && !_variable.isCallableOrCatchParameter() && !m_insideStruct ) - m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); + m_errorReporter.typeError(8274_error, _variable.location(), "Variables cannot be declared in interfaces."); return true; } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f72253c1f..3415dee22 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; - m_errorReporter.declarationError(_location, _description); + m_errorReporter.declarationError(8532_error, _location, _description); } void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description) { m_errorOccurred = true; - m_errorReporter.declarationError(_location, _ssl, _description); + m_errorReporter.declarationError(3881_error, _location, _ssl, _description); } void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description) { m_errorOccurred = true; - m_errorReporter.fatalDeclarationError(_location, _description); + m_errorReporter.fatalDeclarationError(6546_error, _location, _description); } } diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index c53bc03fa..3cbfa4db4 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -120,13 +120,14 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) { if (var.first.second->isCallableOrCatchParameter()) m_errorReporter.warning( + 5667_error, var.first.second->location(), "Unused " + string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") + " parameter. Remove or comment out the variable name to silence this warning." ); else - m_errorReporter.warning(var.first.second->location(), "Unused local variable."); + m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable."); } m_localVarUseCount.clear(); m_constructor = false; @@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) set structsSeen; if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64) m_errorReporter.warning( + 3408_error, _variable.location(), "Variable covers a large part of storage and thus makes collisions likely. " "Either use mappings or dynamic arrays and allow their size to be increased only " @@ -183,6 +185,7 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement) { if (_statement.expression().annotation().isPure) m_errorReporter.warning( + 6133_error, _statement.location(), "Statement has no effect." ); @@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) { if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") m_errorReporter.typeError( + 1400_error, _memberAccess.location(), "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" ); else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") m_errorReporter.typeError( + 8113_error, _memberAccess.location(), "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" ); @@ -211,6 +216,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) ContractType const& contract = dynamic_cast(*type->typeArgument()); if (m_constructorUsesAssembly->check(contract.contractDefinition())) m_errorReporter.warning( + 6417_error, _memberAccess.location(), "The constructor of the contract (or its base) uses inline assembly. " "Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode." @@ -222,6 +228,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (auto const* type = dynamic_cast(_memberAccess.annotation().type)) if (type->kind() == FunctionType::Kind::BareCallCode) m_errorReporter.typeError( + 2256_error, _memberAccess.location(), "\"callcode\" has been deprecated in favour of \"delegatecall\"." ); @@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) { if (id->name() == "this") m_errorReporter.warning( + 5805_error, id->location(), "\"this\" used in constructor. " "Note that external functions of a contract " @@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) )) if (rhs->isZero()) m_errorReporter.typeError( + 1211_error, _operation.location(), (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." ); @@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) )) if (lastArg->isZero()) m_errorReporter.typeError( + 4195_error, _functionCall.location(), "Arithmetic modulo zero." ); @@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) functionType->declaration().scope() == m_currentContract ) m_errorReporter.typeError( + 6700_error, _functionCall.location(), SecondarySourceLocation().append( "The function declaration is here:", diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index bd2060d6a..c3267c37f 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -69,7 +69,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) string(";\""); // when reporting the warning, print the source name only - m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString); + m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); } m_sourceUnit = nullptr; } @@ -79,18 +79,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); if (_pragma.tokens()[0] != Token::Identifier) - m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); + m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); else if (_pragma.literals()[0] == "experimental") { solAssert(m_sourceUnit, ""); vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); if (literals.empty()) m_errorReporter.syntaxError( + 9679_error, _pragma.location(), "Experimental feature name is missing." ); else if (literals.size() > 1) m_errorReporter.syntaxError( + 6022_error, _pragma.location(), "Stray arguments." ); @@ -98,17 +100,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { string const literal = literals[0]; if (literal.empty()) - m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid."); + m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid."); else if (!ExperimentalFeatureNames.count(literal)) - m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name."); + m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name."); else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal))) - m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); + m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name."); else { auto feature = ExperimentalFeatureNames.at(literal); m_sourceUnit->annotation().experimentalFeatures.insert(feature); if (!ExperimentalFeatureWithoutWarning.count(feature)) - m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); } } } @@ -121,6 +123,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) static SemVerVersion const currentVersion{string(VersionString)}; if (!matchExpression.matches(currentVersion)) m_errorReporter.syntaxError( + 3997_error, _pragma.location(), "Source file requires different compiler version (current compiler is " + string(VersionString) + ") - note that nightly builds are considered to be " @@ -129,7 +132,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_versionPragmaFound = true; } else - m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); return true; } @@ -142,7 +145,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&) void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) { if (_modifier.isImplemented() && !m_placeholderFound) - m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'."); + m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'."); m_placeholderFound = false; } @@ -150,7 +153,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat { auto varDecl = dynamic_cast(&_statement); if (varDecl) - m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks."); + m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks."); } bool SyntaxChecker::visit(IfStatement const& _ifStatement) @@ -189,7 +192,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement) { if (m_inLoopDepth <= 0) // we're not in a for/while loop, report syntax error - m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop."); + m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop."); return true; } @@ -197,13 +200,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement) { if (m_inLoopDepth <= 0) // we're not in a for/while loop, report syntax error - m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop."); + m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop."); return true; } bool SyntaxChecker::visit(Throw const& _throwStatement) { m_errorReporter.syntaxError( + 4538_error, _throwStatement.location(), "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." ); @@ -222,29 +226,29 @@ bool SyntaxChecker::visit(Literal const& _literal) // Generic checks no matter what base this number literal is of: if (value.back() == '_') { - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); + m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); return true; } if (value.find("__") != ASTString::npos) { - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed."); + m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed."); return true; } if (!_literal.isHexNumber()) // decimal literal { if (value.find("._") != ASTString::npos) - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); if (value.find("_.") != ASTString::npos) - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); if (value.find("_e") != ASTString::npos) - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed."); + m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed."); if (value.find("e_") != ASTString::npos) - m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed."); + m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed."); } return true; @@ -253,7 +257,7 @@ bool SyntaxChecker::visit(Literal const& _literal) bool SyntaxChecker::visit(UnaryOperation const& _operation) { if (_operation.getOperator() == Token::Add) - m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed."); + m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed."); return true; } @@ -265,6 +269,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly) if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations())) m_errorReporter.syntaxError( + 6553_error, _inlineAssembly.location(), "The msize instruction cannot be used when the Yul optimizer is activated because " "it can change its semantics. Either disable the Yul optimizer or do not use the instruction." @@ -285,7 +290,8 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) if (function->name() == contractName) - m_errorReporter.syntaxError(function->location(), + m_errorReporter.syntaxError( + 5796_error,function->location(), "Functions are not allowed to have the same name as the contract. " "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." ); @@ -298,15 +304,16 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) { string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; m_errorReporter.syntaxError( + 4937_error, _function.location(), "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); } if (m_isInterface && !_function.modifiers().empty()) - m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers."); + m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); else if (!_function.isImplemented() && !_function.modifiers().empty()) - m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); + m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); return true; } @@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) { for (auto const& decl: _node.parameterTypeList()->parameters()) if (!decl->name().empty()) - m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated."); + m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated."); for (auto const& decl: _node.returnParameterTypeList()->parameters()) if (!decl->name().empty()) - m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named."); + m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named."); return true; } @@ -329,6 +336,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr)) m_errorReporter.syntaxError( + 3299_error, _statement.location(), "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." ); @@ -339,7 +347,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) bool SyntaxChecker::visit(StructDefinition const& _struct) { if (_struct.members().empty()) - m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed."); return true; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b4030c0c4..7277714b8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -117,6 +117,7 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) } if (storageToStorageCopies >= 1 && toStorageCopies >= 2) m_errorReporter.warning( + 7238_error, _assignment.location(), "This assignment performs two copies to storage. Since storage copies do not first " "copy to a temporary location, one of them might be overwritten before the second " @@ -130,6 +131,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c vector> arguments = _functionCall.arguments(); if (arguments.size() != 2) m_errorReporter.typeError( + 5782_error, _functionCall.location(), "This function takes two arguments, but " + toString(arguments.size()) + @@ -142,6 +144,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c !type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()) ) m_errorReporter.typeError( + 1956_error, arguments.front()->location(), "The first argument to \"abi.decode\" must be implicitly convertible to " "bytes memory or bytes calldata, but is of type " + @@ -158,6 +161,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c if (!tupleExpression) { m_errorReporter.typeError( + 6444_error, arguments[1]->location(), "The second argument to \"abi.decode\" has to be a tuple of types." ); @@ -186,6 +190,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c ); if (!actualType->fullEncodingType(false, _abiEncoderV2, false)) m_errorReporter.typeError( + 9611_error, typeArgument->location(), "Decoding type " + actualType->toString(false) + " not supported." ); @@ -193,7 +198,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c } else { - m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name."); + m_errorReporter.typeError(1039_error, typeArgument->location(), "Argument has to be a type name."); components.push_back(TypeProvider::emptyTuple()); } } @@ -206,6 +211,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio if (arguments.size() != 1) { m_errorReporter.typeError( + 8885_error, _functionCall.location(), "This function takes one argument, but " + toString(arguments.size()) + @@ -220,6 +226,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio ) { m_errorReporter.typeError( + 4259_error, arguments.front()->location(), "Invalid type for argument in function call. " "Contract type required, but " + @@ -238,10 +245,10 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) solAssert(base, "Base contract not available."); if (m_scope->isInterface() && !base->isInterface()) - m_errorReporter.typeError(_inheritance.location(), "Interfaces can only inherit from other interfaces."); + m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); if (base->isLibrary()) - m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); + m_errorReporter.typeError(2571_error, _inheritance.location(), "Libraries cannot be inherited from."); auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes; @@ -254,6 +261,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) if (parameterTypes.size() != arguments->size()) { m_errorReporter.typeError( + 7927_error, _inheritance.location(), "Wrong argument count for constructor call: " + toString(arguments->size()) + @@ -267,6 +275,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) BoolResult result = type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]); if (!result) m_errorReporter.typeErrorConcatenateDescriptions( + 9827_error, (*arguments)[i]->location(), "Invalid type for argument in constructor call. " "Invalid implicit conversion from " + @@ -286,13 +295,13 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) _usingFor.libraryName().annotation().referencedDeclaration ); if (!library || !library->isLibrary()) - m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected."); + m_errorReporter.fatalTypeError(4357_error, _usingFor.libraryName().location(), "Library name expected."); } void TypeChecker::endVisit(ModifierDefinition const& _modifier) { if (!_modifier.isImplemented() && !_modifier.virtualSemantics()) - m_errorReporter.typeError(_modifier.location(), "Modifiers without implementation must be marked virtual."); + m_errorReporter.typeError(8063_error, _modifier.location(), "Modifiers without implementation must be marked virtual."); } bool TypeChecker::visit(FunctionDefinition const& _function) @@ -302,17 +311,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.markedVirtual()) { if (_function.annotation().contract->isInterface()) - m_errorReporter.warning(_function.location(), "Interface functions are implicitly \"virtual\""); + m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); if (_function.visibility() == Visibility::Private) - m_errorReporter.typeError(_function.location(), "\"virtual\" and \"private\" cannot be used together."); + m_errorReporter.typeError(3942_error, _function.location(), "\"virtual\" and \"private\" cannot be used together."); } if (_function.isPayable()) { if (isLibraryFunction) - m_errorReporter.typeError(_function.location(), "Library functions cannot be payable."); + m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) - m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); + m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable."); } auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) { if (type(var)->category() == Type::Category::Mapping) @@ -320,9 +329,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (var.referenceLocation() != VariableDeclaration::Location::Storage) { if (!isLibraryFunction && _function.isPublic()) - m_errorReporter.typeError(var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions."); + m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions."); else - m_errorReporter.typeError(var.location(), "Mapping types can only have a data location of \"storage\"." ); + m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." ); } else { @@ -332,7 +341,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else { if (!type(var)->canLiveOutsideStorage() && _function.isPublic()) - m_errorReporter.typeError(var.location(), "Type is required to live outside storage."); + m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage."); if (_function.isPublic()) { auto iType = type(var)->interfaceType(isLibraryFunction); @@ -340,7 +349,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!iType) { solAssert(!iType.message().empty(), "Expected detailed error message!"); - m_errorReporter.fatalTypeError(var.location(), iType.message()); + m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message()); } } } @@ -350,6 +359,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) !typeSupportedByOldABIEncoder(*type(var), isLibraryFunction) ) m_errorReporter.typeError( + 4957_error, var.location(), "This type is only supported in ABIEncoderV2. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." @@ -380,7 +390,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (modifiers.count(decl)) { if (dynamic_cast(decl)) - m_errorReporter.declarationError(modifier->location(), "Base constructor already provided."); + m_errorReporter.declarationError(1697_error, modifier->location(), "Base constructor already provided."); } else modifiers.insert(decl); @@ -388,25 +398,25 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (m_scope->isInterface()) { if (_function.isImplemented()) - m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); + m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external."); + m_errorReporter.typeError(1560_error, _function.location(), "Functions in interfaces must be declared external."); if (_function.isConstructor()) - m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); + m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); } else if (m_scope->contractKind() == ContractKind::Library) if (_function.isConstructor()) - m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in libraries."); + 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(_function.location(), "Constructor must be implemented if declared."); + m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared."); else if (isLibraryFunction) - m_errorReporter.typeError(_function.location(), "Library functions must be implemented if declared."); + m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); else if (!_function.virtualSemantics()) - m_errorReporter.typeError(_function.location(), "Functions without implementation must be marked virtual."); + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); if (_function.isFallback()) @@ -431,7 +441,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (auto contractType = dynamic_cast(varType)) if (contractType->contractDefinition().isLibrary()) - m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library."); + m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library."); if (_variable.value()) expectType(*_variable.value(), *varType); if (_variable.isConstant()) @@ -442,13 +452,14 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (auto arrayType = dynamic_cast(_variable.type())) allowed = arrayType->isByteArray(); if (!allowed) - m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented."); + m_errorReporter.typeError(9259_error, _variable.location(), "Constants of non-value type not yet implemented."); } if (!_variable.value()) - m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable."); + m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable."); else if (!_variable.value()->annotation().isPure) m_errorReporter.typeError( + 8349_error, _variable.value()->location(), "Initial value for constant variable has to be compile-time constant." ); @@ -456,12 +467,12 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) else if (_variable.immutable()) { if (!_variable.type()->isValueType()) - m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type."); + m_errorReporter.typeError(6377_error, _variable.location(), "Immutable variables cannot have a non-value type."); if ( auto const* functionType = dynamic_cast(_variable.type()); functionType && functionType->kind() == FunctionType::Kind::External ) - m_errorReporter.typeError(_variable.location(), "Immutable variables of external function type are not yet supported."); + m_errorReporter.typeError(3366_error, _variable.location(), "Immutable variables of external function type are not yet supported."); solAssert(_variable.type()->sizeOnStack() == 1 || m_errorReporter.hasErrors(), ""); } @@ -469,7 +480,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (!varType->canLiveOutsideStorage()) - m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage."); + m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage."); } else if (_variable.visibility() >= Visibility::Public) { @@ -481,14 +492,15 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!typeSupportedByOldABIEncoder(*param, false /* isLibrary */)) unsupportedTypes.emplace_back(param->toString()); if (!unsupportedTypes.empty()) - m_errorReporter.typeError(_variable.location(), + m_errorReporter.typeError( + 2763_error,_variable.location(), "The following types are only supported for getters in ABIEncoderV2: " + joinHumanReadable(unsupportedTypes) + ". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); } if (!getter.interfaceFunctionType()) - m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); + m_errorReporter.typeError(6744_error, _variable.location(), "Internal or recursive type is not allowed for public state variables."); } if (auto referenceType = dynamic_cast(varType)) @@ -499,7 +511,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!result) { solAssert(!result.message().empty(), "Expected detailed error message"); - m_errorReporter.typeError(_variable.location(), result.message()); + m_errorReporter.typeError(1534_error, _variable.location(), result.message()); } } @@ -536,12 +548,13 @@ void TypeChecker::visitManually( } if (!parameters) { - m_errorReporter.typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class."); + m_errorReporter.typeError(4659_error, _modifier.location(), "Referenced declaration is neither modifier nor base class."); return; } if (parameters->size() != arguments.size()) { m_errorReporter.typeError( + 2973_error, _modifier.location(), "Wrong argument count for modifier invocation: " + toString(arguments.size()) + @@ -556,6 +569,7 @@ void TypeChecker::visitManually( BoolResult result = type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])); if (!result) m_errorReporter.typeErrorConcatenateDescriptions( + 4649_error, arguments[i]->location(), "Invalid type for argument in modifier invocation. " "Invalid implicit conversion from " + @@ -577,23 +591,24 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (var->isIndexed()) numIndexed++; if (!type(*var)->canLiveOutsideStorage()) - m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); + m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type."); + m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); if ( !_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) ) m_errorReporter.typeError( + 3061_error, var->location(), "This type is only supported in ABIEncoderV2. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); } if (_eventDef.isAnonymous() && numIndexed > 4) - m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); + m_errorReporter.typeError(8598_error, _eventDef.location(), "More than 4 indexed arguments for anonymous event."); else if (!_eventDef.isAnonymous() && numIndexed > 3) - m_errorReporter.typeError(_eventDef.location(), "More than 3 indexed arguments for event."); + m_errorReporter.typeError(7249_error, _eventDef.location(), "More than 3 indexed arguments for event."); return false; } @@ -606,7 +621,7 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType) { solAssert(t->annotation().type, "Type not set for parameter."); if (!t->annotation().type->interfaceType(false).get()) - m_errorReporter.typeError(t->location(), "Internal type cannot be used for external function type."); + m_errorReporter.typeError(2582_error, t->location(), "Internal type cannot be used for external function type."); } solAssert(fun.interfaceType(false), "External function type uses internal types."); } @@ -633,7 +648,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(var->type(), "Expected variable type!"); if (var->immutable()) { - m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported."); + m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported."); return size_t(-1); } if (var->isConstant()) @@ -642,7 +657,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (var && !var->value()) { - m_errorReporter.typeError(_identifier.location, "Constant has no value."); + m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value."); return size_t(-1); } else if (!var || !type(*var)->isValueType() || ( @@ -650,17 +665,17 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) type(*var->value())->category() != Type::Category::RationalNumber )) { - m_errorReporter.typeError(_identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); + m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); return size_t(-1); } else if (_context == yul::IdentifierContext::LValue) { - m_errorReporter.typeError(_identifier.location, "Constant variables cannot be assigned to."); + m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); return size_t(-1); } else if (requiresStorage) { - m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables."); + m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables."); return size_t(-1); } } @@ -669,19 +684,19 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); + m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } else if (_context == yul::IdentifierContext::LValue) { if (var->isStateVariable()) { - m_errorReporter.typeError(_identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); return size_t(-1); } else if (ref->second.isOffset) { - m_errorReporter.typeError(_identifier.location, "Only _slot can be assigned to."); + m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to."); return size_t(-1); } else @@ -690,26 +705,26 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) } else if (!var->isConstant() && var->isStateVariable()) { - m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); + m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); return size_t(-1); } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(_identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables."); + m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables."); return size_t(-1); } else if (var->type()->sizeOnStack() != 1) { if (var->type()->dataStoredIn(DataLocation::CallData)) - m_errorReporter.typeError(_identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes."); + m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes."); else - m_errorReporter.typeError(_identifier.location, "Only types that use one stack slot are supported."); + m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); return size_t(-1); } } else if (requiresStorage) { - m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); + m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } else if (_context == yul::IdentifierContext::LValue) @@ -717,7 +732,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (dynamic_cast(declaration)) return size_t(-1); - m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); + m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } @@ -726,7 +741,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { - m_errorReporter.declarationError(_identifier.location, "Access to functions is not allowed in inline assembly."); + m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly."); return size_t(-1); } else if (dynamic_cast(declaration)) @@ -736,7 +751,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!contract->isLibrary()) { - m_errorReporter.typeError(_identifier.location, "Expected a library."); + m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library."); return size_t(-1); } } @@ -774,6 +789,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall) { m_errorReporter.typeError( + 5347_error, _tryStatement.externalCall().location(), "Try can only be used with external function calls and contract creation calls." ); @@ -788,6 +804,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) ) { m_errorReporter.typeError( + 2536_error, _tryStatement.externalCall().location(), "Try can only be used with external function calls and contract creation calls." ); @@ -810,6 +827,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) successClause.parameters()->parameters(); if (returnTypes.size() != parameters.size()) m_errorReporter.typeError( + 2800_error, successClause.location(), "Function returns " + to_string(functionType.returnParameterTypes().size()) + @@ -823,6 +841,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) solAssert(returnTypes[i], ""); if (parameters[i] && *parameters[i]->annotation().type != *returnTypes[i]) m_errorReporter.typeError( + 6509_error, parameters[i]->location(), "Invalid type, expected " + returnTypes[i]->toString(false) + @@ -842,6 +861,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) { if (lowLevelClause) m_errorReporter.typeError( + 5320_error, clause.location(), SecondarySourceLocation{}.append("The first clause is here:", lowLevelClause->location()), "This try statement already has a low-level catch clause." @@ -853,9 +873,10 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) clause.parameters()->parameters().size() != 1 || *clause.parameters()->parameters().front()->type() != *TypeProvider::bytesMemory() ) - m_errorReporter.typeError(clause.location(), "Expected `catch (bytes memory ...) { ... }` or `catch { ... }`."); + m_errorReporter.typeError(6231_error, clause.location(), "Expected `catch (bytes memory ...) { ... }` or `catch { ... }`."); if (!m_evmVersion.supportsReturndata()) m_errorReporter.typeError( + 9908_error, clause.location(), "This catch clause type cannot be used on the selected EVM version (" + m_evmVersion.name() + @@ -867,6 +888,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) { if (!m_evmVersion.supportsReturndata()) m_errorReporter.typeError( + 1812_error, clause.location(), "This catch clause type cannot be used on the selected EVM version (" + m_evmVersion.name() + @@ -875,6 +897,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) if (errorClause) m_errorReporter.typeError( + 1036_error, clause.location(), SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()), "This try statement already has an \"Error\" catch clause." @@ -885,10 +908,11 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement) clause.parameters()->parameters().size() != 1 || *clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory() ) - m_errorReporter.typeError(clause.location(), "Expected `catch Error(string memory ...) { ... }`."); + m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`."); } else m_errorReporter.typeError( + 3542_error, clause.location(), "Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`." ); @@ -920,12 +944,12 @@ void TypeChecker::endVisit(Return const& _return) if (!_return.expression()) { if (params && !params->parameters().empty()) - m_errorReporter.typeError(_return.location(), "Return arguments required."); + m_errorReporter.typeError(6777_error, _return.location(), "Return arguments required."); return; } if (!params) { - m_errorReporter.typeError(_return.location(), "Return arguments not allowed."); + m_errorReporter.typeError(7552_error, _return.location(), "Return arguments not allowed."); return; } TypePointers returnTypes; @@ -934,12 +958,13 @@ void TypeChecker::endVisit(Return const& _return) if (auto tupleType = dynamic_cast(type(*_return.expression()))) { if (tupleType->components().size() != params->parameters().size()) - m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); + m_errorReporter.typeError(5132_error, _return.location(), "Different number of arguments in return statement than in returns declaration."); else { BoolResult result = tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)); if (!result) m_errorReporter.typeErrorConcatenateDescriptions( + 5992_error, _return.expression()->location(), "Return argument type " + type(*_return.expression())->toString() + @@ -950,13 +975,14 @@ void TypeChecker::endVisit(Return const& _return) } } else if (params->parameters().size() != 1) - m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); + m_errorReporter.typeError(8863_error, _return.location(), "Different number of arguments in return statement than in returns declaration."); else { TypePointer const& expected = type(*params->parameters().front()); BoolResult result = type(*_return.expression())->isImplicitlyConvertibleTo(*expected); if (!result) m_errorReporter.typeErrorConcatenateDescriptions( + 6359_error, _return.expression()->location(), "Return argument type " + type(*_return.expression())->toString() + @@ -974,7 +1000,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit) type(_emit.eventCall().expression())->category() != Type::Category::Function || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) - m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation."); + m_errorReporter.typeError(9292_error, _emit.eventCall().expression().location(), "Expression has to be an event invocation."); } namespace @@ -1042,15 +1068,16 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) } else // Bailing out *fatal* here, as those (untyped) vars may be used later, and diagnostics wouldn't be helpful then. - m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); + m_errorReporter.fatalTypeError(4626_error, _statement.location(), "Use of the \"var\" keyword is disallowed."); } VariableDeclaration const& varDecl = *_statement.declarations().front(); if (!varDecl.annotation().type) - m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); + m_errorReporter.fatalTypeError(6983_error, _statement.location(), "Use of the \"var\" keyword is disallowed."); if (dynamic_cast(type(varDecl))) m_errorReporter.typeError( + 4182_error, varDecl.location(), "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." ); @@ -1074,6 +1101,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) solAssert(m_errorReporter.hasErrors(), ""); else if (valueTypes.size() != variables.size()) m_errorReporter.typeError( + 7364_error, _statement.location(), "Different number of components on the left hand side (" + toString(variables.size()) + @@ -1103,6 +1131,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (valueComponentType->category() == Type::Category::RationalNumber) m_errorReporter.fatalTypeError( + 6963_error, _statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + @@ -1158,11 +1187,13 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (var.annotation().type->operator==(*valueComponentType->mobileType())) m_errorReporter.typeError( + 5107_error, _statement.location(), errorMsg + ", but it can be explicitly converted." ); else m_errorReporter.typeError( + 4486_error, _statement.location(), errorMsg + ". Try converting to type " + @@ -1172,6 +1203,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) } else m_errorReporter.typeErrorConcatenateDescriptions( + 9574_error, _statement.location(), errorMsg + ".", result.message() @@ -1192,12 +1224,14 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (!typeCanBeExpressed(variables)) m_errorReporter.syntaxError( + 3478_error, _statement.location(), "Use of the \"var\" keyword is disallowed. " "Type cannot be expressed in syntax." ); else m_errorReporter.syntaxError( + 1719_error, _statement.location(), "Use of the \"var\" keyword is disallowed. " "Use explicit declaration `" + createTupleDecl(variables) + " = ...´ instead." @@ -1211,7 +1245,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) { if (type(_statement.expression())->category() == Type::Category::RationalNumber) if (!dynamic_cast(*type(_statement.expression())).mobileType()) - m_errorReporter.typeError(_statement.expression().location(), "Invalid rational number."); + m_errorReporter.typeError(3757_error, _statement.expression().location(), "Invalid rational number."); if (auto call = dynamic_cast(&_statement.expression())) { @@ -1224,9 +1258,9 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) kind == FunctionType::Kind::BareDelegateCall || kind == FunctionType::Kind::BareStaticCall ) - m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used."); + m_errorReporter.warning(9302_error, _statement.location(), "Return value of low-level calls not used."); else if (kind == FunctionType::Kind::Send) - m_errorReporter.warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead."); + m_errorReporter.warning(5878_error, _statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead."); } } } @@ -1244,12 +1278,12 @@ bool TypeChecker::visit(Conditional const& _conditional) TypePointer commonType = nullptr; if (!trueType) - m_errorReporter.typeError(_conditional.trueExpression().location(), "Invalid mobile type in true expression."); + m_errorReporter.typeError(9717_error, _conditional.trueExpression().location(), "Invalid mobile type in true expression."); else commonType = trueType; if (!falseType) - m_errorReporter.typeError(_conditional.falseExpression().location(), "Invalid mobile type in false expression."); + m_errorReporter.typeError(3703_error, _conditional.falseExpression().location(), "Invalid mobile type in false expression."); else commonType = falseType; @@ -1262,6 +1296,7 @@ bool TypeChecker::visit(Conditional const& _conditional) if (!commonType) { m_errorReporter.typeError( + 1080_error, _conditional.location(), "True expression's type " + trueType->toString() + @@ -1283,6 +1318,7 @@ bool TypeChecker::visit(Conditional const& _conditional) if (_conditional.annotation().willBeWrittenTo) m_errorReporter.typeError( + 2212_error, _conditional.location(), "Conditional expression as left value is not supported yet." ); @@ -1295,7 +1331,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& if (auto const* tupleExpression = dynamic_cast(&_expression)) { if (tupleExpression->components().empty()) - m_errorReporter.typeError(_expression.location(), "Empty tuple on the left hand side."); + m_errorReporter.typeError(5547_error, _expression.location(), "Empty tuple on the left hand side."); auto const* tupleType = dynamic_cast(&_type); auto const& types = tupleType && tupleExpression->components().size() != 1 ? tupleType->components() : vector { &_type }; @@ -1320,7 +1356,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& if (variableDeclaration->isLocalOrReturn()) isLocalOrReturn = true; if (!isLocalOrReturn) - m_errorReporter.typeError(_expression.location(), "Mappings cannot be assigned to."); + m_errorReporter.typeError(9214_error, _expression.location(), "Mappings cannot be assigned to."); } } @@ -1339,6 +1375,7 @@ bool TypeChecker::visit(Assignment const& _assignment) { if (_assignment.assignmentOperator() != Token::Assign) m_errorReporter.typeError( + 4289_error, _assignment.location(), "Compound assignment is not allowed for tuple types." ); @@ -1363,6 +1400,7 @@ bool TypeChecker::visit(Assignment const& _assignment) ); if (!resultType || *resultType != *t) m_errorReporter.typeError( + 7366_error, _assignment.location(), "Operator " + string(TokenTraits::toString(_assignment.assignmentOperator())) + @@ -1383,7 +1421,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (_tuple.annotation().willBeWrittenTo) { if (_tuple.isInlineArray()) - m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue."); + m_errorReporter.fatalTypeError(3025_error, _tuple.location(), "Inline array type cannot be declared as LValue."); for (auto const& component: components) if (component) { @@ -1410,7 +1448,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) for (size_t i = 0; i < components.size(); ++i) { if (!components[i]) - m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty."); + m_errorReporter.fatalTypeError(8381_error, _tuple.location(), "Tuple component cannot be empty."); components[i]->accept(*this); types.push_back(type(*components[i])); @@ -1419,21 +1457,21 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (dynamic_cast(*types[i]).components().empty()) { if (_tuple.isInlineArray()) - m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty."); - m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty."); + m_errorReporter.fatalTypeError(5604_error, components[i]->location(), "Array component cannot be empty."); + m_errorReporter.typeError(6473_error, components[i]->location(), "Tuple component cannot be empty."); } // Note: code generation will visit each of the expression even if they are not assigned from. if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1) if (!dynamic_cast(*types[i]).mobileType()) - m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number."); + m_errorReporter.fatalTypeError(3390_error, components[i]->location(), "Invalid rational number."); if (_tuple.isInlineArray()) { solAssert(!!types[i], "Inline array cannot have empty components"); if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) - m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type."); + m_errorReporter.fatalTypeError(9563_error, components[i]->location(), "Invalid mobile type."); if (i == 0) inlineArrayType = types[i]->mobileType(); @@ -1447,9 +1485,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { if (!inlineArrayType) - m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements."); + m_errorReporter.fatalTypeError(6378_error, _tuple.location(), "Unable to deduce common type for array elements."); else if (!inlineArrayType->canLiveOutsideStorage()) - m_errorReporter.fatalTypeError(_tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); + m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); _tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size()); } @@ -1482,9 +1520,9 @@ bool TypeChecker::visit(UnaryOperation const& _operation) if (modifying) // Cannot just report the error, ignore the unary operator, and continue, // because the sub-expression was already processed with requireLValue() - m_errorReporter.fatalTypeError(_operation.location(), description); + m_errorReporter.fatalTypeError(9767_error, _operation.location(), description); else - m_errorReporter.typeError(_operation.location(), description); + m_errorReporter.typeError(4907_error, _operation.location(), description); t = subExprType; } _operation.annotation().type = t; @@ -1501,6 +1539,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) if (!commonType) { m_errorReporter.typeError( + 2271_error, _operation.location(), "Operator " + string(TokenTraits::toString(_operation.getOperator())) + @@ -1536,6 +1575,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) dynamic_cast(*commonType).numBits() != 256 )) m_errorReporter.warning( + 9085_error, _operation.location(), "Result of " + operation + " has type " + commonType->toString() + " and thus " "might overflow. Silence this warning by converting the literal to the " @@ -1548,6 +1588,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) dynamic_cast(*rightType).numBits() ) m_errorReporter.warning( + 3149_error, _operation.location(), "The result type of the " + operation + @@ -1574,11 +1615,13 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( TypePointer resultType = dynamic_cast(*expressionType).actualType(); if (arguments.size() != 1) m_errorReporter.typeError( + 2558_error, _functionCall.location(), "Exactly one argument expected for explicit type conversion." ); else if (!isPositionalCall) m_errorReporter.typeError( + 5153_error, _functionCall.location(), "Type conversion cannot allow named arguments." ); @@ -1639,6 +1682,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( variableDeclaration->location() ); m_errorReporter.typeError( + 7398_error, _functionCall.location(), ssl, "Explicit type conversion not allowed from non-payable \"address\" to \"" + @@ -1653,6 +1697,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( resultType->category() == Type::Category::Address ) m_errorReporter.typeError( + 5030_error, _functionCall.location(), "Explicit type conversion not allowed from \"" + argType->toString() + @@ -1663,6 +1708,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( ); else m_errorReporter.typeError( + 9640_error, _functionCall.location(), "Explicit type conversion not allowed from \"" + argType->toString() + @@ -1700,11 +1746,13 @@ void TypeChecker::typeCheckFunctionCall( !dynamic_cast(_functionType->declaration()).isImplemented() ) m_errorReporter.typeError( + 7501_error, _functionCall.location(), "Cannot call unimplemented base function." ); else m_errorReporter.typeError( + 3419_error, _functionCall.location(), "Cannot call function via contract type name." ); @@ -1717,6 +1765,7 @@ void TypeChecker::typeCheckFunctionCall( !m_evmVersion.hasStaticCall() ) m_errorReporter.typeError( + 5052_error, _functionCall.location(), "\"staticcall\" is not supported by the VM version." ); @@ -1730,25 +1779,26 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function) solAssert(_function.isFallback(), ""); if (_function.inContractKind() == ContractKind::Library) - m_errorReporter.typeError(_function.location(), "Libraries cannot have fallback functions."); + m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions."); if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) m_errorReporter.typeError( + 4575_error, _function.location(), "Fallback function must be payable or non-payable, but is \"" + stateMutabilityToString(_function.stateMutability()) + "\"." ); if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(_function.location(), "Fallback function must be defined as \"external\"."); + m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\"."); if (!_function.returnParameters().empty()) { if (_function.returnParameters().size() > 1 || *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory()) - m_errorReporter.typeError(_function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value."); + m_errorReporter.typeError(5570_error, _function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value."); else - m_errorReporter.typeError(_function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented."); + m_errorReporter.typeError(6151_error, _function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented."); } if (!_function.parameters().empty()) - m_errorReporter.typeError(_function.parameterList().location(), "Fallback function cannot take parameters."); + m_errorReporter.typeError(3978_error, _function.parameterList().location(), "Fallback function cannot take parameters."); } void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) @@ -1756,21 +1806,22 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) solAssert(_function.isReceive(), ""); if (_function.inContractKind() == ContractKind::Library) - m_errorReporter.typeError(_function.location(), "Libraries cannot have receive ether functions."); + m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions."); if (_function.stateMutability() != StateMutability::Payable) m_errorReporter.typeError( + 7793_error, _function.location(), "Receive ether function must be payable, but is \"" + stateMutabilityToString(_function.stateMutability()) + "\"." ); if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(_function.location(), "Receive ether function must be defined as \"external\"."); + m_errorReporter.typeError(4095_error, _function.location(), "Receive ether function must be defined as \"external\"."); if (!_function.returnParameters().empty()) - m_errorReporter.typeError(_function.returnParameterList()->location(), "Receive ether function cannot return values."); + m_errorReporter.typeError(6899_error, _function.returnParameterList()->location(), "Receive ether function cannot return values."); if (!_function.parameters().empty()) - m_errorReporter.typeError(_function.parameterList().location(), "Receive ether function cannot take parameters."); + m_errorReporter.typeError(6857_error, _function.parameterList().location(), "Receive ether function cannot take parameters."); } @@ -1778,20 +1829,21 @@ void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) { solAssert(_function.isConstructor(), ""); if (_function.markedVirtual()) - m_errorReporter.typeError(_function.location(), "Constructors cannot be virtual."); + m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); if (_function.overrides()) - m_errorReporter.typeError(_function.location(), "Constructors cannot override."); + m_errorReporter.typeError(1209_error, _function.location(), "Constructors cannot override."); if (!_function.returnParameters().empty()) - m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); + m_errorReporter.typeError(9712_error, _function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) m_errorReporter.typeError( + 1558_error, _function.location(), "Constructor must be payable or non-payable, but is \"" + stateMutabilityToString(_function.stateMutability()) + "\"." ); if (_function.visibility() != Visibility::Public && _function.visibility() != Visibility::Internal) - m_errorReporter.typeError(_function.location(), "Constructor must be public or internal."); + m_errorReporter.typeError(9239_error, _function.location(), "Constructor must be public or internal."); } void TypeChecker::typeCheckABIEncodeFunctions( @@ -1820,6 +1872,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (!_functionCall.names().empty()) { m_errorReporter.typeError( + 2627_error, _functionCall.location(), "Named arguments cannot be used for functions that take arbitrary parameters." ); @@ -1841,6 +1894,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (rationalType.isFractional()) { m_errorReporter.typeError( + 6090_error, arguments[i]->location(), "Fractional numbers cannot yet be encoded." ); @@ -1849,6 +1903,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( else if (!argType->mobileType()) { m_errorReporter.typeError( + 8009_error, arguments[i]->location(), "Invalid rational number (too large or division by zero)." ); @@ -1857,6 +1912,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( else if (isPacked) { m_errorReporter.typeError( + 7279_error, arguments[i]->location(), "Cannot perform packed encoding for a literal." " Please convert it to an explicit type first." @@ -1868,6 +1924,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (isPacked && !typeSupportedByOldABIEncoder(*argType, false /* isLibrary */)) { m_errorReporter.typeError( + 9578_error, arguments[i]->location(), "Type not supported in packed mode." ); @@ -1876,6 +1933,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (!argType->fullEncodingType(false, abiEncoderV2, !_functionType->padArguments())) m_errorReporter.typeError( + 2056_error, arguments[i]->location(), "This type cannot be encoded." ); @@ -1977,7 +2035,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( " This function requires a single bytes argument." " Use abi.encodePacked(...) to obtain the pre-0.5.0" " behaviour or abi.encode(...) to use ABI encoding."; - m_errorReporter.typeError(_functionCall.location(), msg); + m_errorReporter.typeError(1093_error, _functionCall.location(), msg); return; } @@ -2006,6 +2064,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( { duplication = true; m_errorReporter.typeError( + 6995_error, arguments[i]->location(), "Duplicate named argument \"" + *argumentNames[i] + "\"." ); @@ -2032,6 +2091,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( paramArgMap[i] = nullptr; not_all_mapped = true; m_errorReporter.typeError( + 4974_error, _functionCall.location(), "Named argument \"" + *argumentNames[i] + @@ -2077,7 +2137,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( " This function requires a single bytes argument." " Use abi.encodePacked(...) to obtain the pre-0.5.0" " behaviour or abi.encode(...) to use ABI encoding."; - m_errorReporter.typeError(paramArgMap[i]->location(), msg); + m_errorReporter.typeError(6706_error, paramArgMap[i]->location(), msg); } } } @@ -2159,7 +2219,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } default: - m_errorReporter.fatalTypeError(_functionCall.location(), "Type is not callable"); + m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); funcCallAnno.kind = FunctionCallKind::Unset; funcCallAnno.isPure = argumentsArePure; break; @@ -2238,7 +2298,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) auto expressionFunctionType = dynamic_cast(type(_functionCallOptions.expression())); if (!expressionFunctionType) { - m_errorReporter.fatalTypeError(_functionCallOptions.location(), "Expected callable expression before call options."); + m_errorReporter.fatalTypeError(2622_error, _functionCallOptions.location(), "Expected callable expression before call options."); return false; } @@ -2257,6 +2317,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) ) { m_errorReporter.fatalTypeError( + 2193_error, _functionCallOptions.location(), "Function call options can only be set on external function calls or contract creations." ); @@ -2267,6 +2328,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { if (_option || _alreadySet) m_errorReporter.typeError( + 9886_error, _functionCallOptions.location(), _alreadySet ? "Option \"" + std::move(_name) + "\" has already been set." : @@ -2288,6 +2350,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) } else m_errorReporter.typeError( + 2721_error, _functionCallOptions.location(), "Function call option \"salt\" can only be used with \"new\"." ); @@ -2296,16 +2359,19 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { if (kind == FunctionType::Kind::BareDelegateCall) m_errorReporter.typeError( + 6189_error, _functionCallOptions.location(), "Cannot set option \"value\" for delegatecall." ); else if (kind == FunctionType::Kind::BareStaticCall) m_errorReporter.typeError( + 2842_error, _functionCallOptions.location(), "Cannot set option \"value\" for staticcall." ); else if (!expressionFunctionType->isPayable()) m_errorReporter.typeError( + 7006_error, _functionCallOptions.location(), kind == FunctionType::Kind::Creation ? "Cannot set option \"value\", since the constructor of " + @@ -2324,6 +2390,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { if (kind == FunctionType::Kind::Creation) m_errorReporter.typeError( + 9903_error, _functionCallOptions.location(), "Function call option \"gas\" cannot be used with \"new\"." ); @@ -2336,6 +2403,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) } else m_errorReporter.typeError( + 9318_error, _functionCallOptions.location(), "Unknown call option \"" + name + "\". Valid options are \"salt\", \"value\" and \"gas\"." ); @@ -2343,6 +2411,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) if (setSalt && !m_evmVersion.hasCreate2()) m_errorReporter.typeError( + 5189_error, _functionCallOptions.location(), "Unsupported call option \"salt\" (requires Constantinople-compatible VMs)." ); @@ -2361,13 +2430,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) auto contract = dynamic_cast(&dereference(*contractName)); if (!contract) - m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + m_errorReporter.fatalTypeError(5540_error, _newExpression.location(), "Identifier is not a contract."); if (contract->isInterface()) - m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); + m_errorReporter.fatalTypeError(2971_error, _newExpression.location(), "Cannot instantiate an interface."); if (!contract->constructorIsPublic()) - m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); + m_errorReporter.typeError(9054_error, _newExpression.location(), "Contract with internal constructor cannot be created directly."); if (contract->abstract()) - m_errorReporter.typeError(_newExpression.location(), "Cannot instantiate an abstract contract."); + m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); solAssert(!!m_scope, ""); m_scope->annotation().contractDependencies.insert(contract); @@ -2377,6 +2446,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) ); if (contractDependenciesAreCyclic(*m_scope)) m_errorReporter.typeError( + 4579_error, _newExpression.location(), "Circular reference for contract creation (cannot create instance of derived or same contract)." ); @@ -2387,11 +2457,13 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) { if (!type->canLiveOutsideStorage()) m_errorReporter.fatalTypeError( + 1164_error, _newExpression.typeName().location(), "Type cannot live outside storage." ); if (!type->isDynamicallySized()) m_errorReporter.typeError( + 3904_error, _newExpression.typeName().location(), "Length has to be placed in parentheses after the array type for new expression." ); @@ -2408,7 +2480,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) _newExpression.annotation().isPure = true; } else - m_errorReporter.fatalTypeError(_newExpression.location(), "Contract or array type expected."); + m_errorReporter.fatalTypeError(8807_error, _newExpression.location(), "Contract or array type expected."); } bool TypeChecker::visit(MemberAccess const& _memberAccess) @@ -2447,6 +2519,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); if (!storageType->members(m_scope).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( + 4994_error, _memberAccess.location(), "Member \"" + memberName + "\" is not available in " + exprType->toString() + @@ -2505,12 +2578,14 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) } m_errorReporter.fatalTypeError( + 4035_error, _memberAccess.location(), errorMsg ); } else if (possibleMembers.size() > 1) m_errorReporter.fatalTypeError( + 6675_error, _memberAccess.location(), "Member \"" + memberName + "\" not unique " "after argument-dependent lookup in " + exprType->toString() + @@ -2534,6 +2609,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) (memberName == "value" || memberName == "gas") ) m_errorReporter.warning( + 1621_error, _memberAccess.location(), "Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." ); @@ -2592,6 +2668,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); if (contractDependenciesAreCyclic(*m_scope)) m_errorReporter.typeError( + 4224_error, _memberAccess.location(), "Circular reference for contract code access." ); @@ -2619,7 +2696,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { auto const& arrayType = dynamic_cast(*baseType).arrayType(); if (arrayType.location() != DataLocation::CallData || !arrayType.isDynamicallySized()) - m_errorReporter.typeError(_access.location(), "Index access is only implemented for slices of dynamic calldata arrays."); + m_errorReporter.typeError(4802_error, _access.location(), "Index access is only implemented for slices of dynamic calldata arrays."); baseType = &arrayType; [[fallthrough]]; } @@ -2627,10 +2704,10 @@ bool TypeChecker::visit(IndexAccess const& _access) { ArrayType const& actualType = dynamic_cast(*baseType); if (!index) - m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(9689_error, _access.location(), "Index expression cannot be omitted."); else if (actualType.isString()) { - m_errorReporter.typeError(_access.location(), "Index access for string is not possible."); + m_errorReporter.typeError(9961_error, _access.location(), "Index access for string is not possible."); index->accept(*this); } else @@ -2641,7 +2718,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { solAssert(!numberType->isFractional(), ""); if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) - m_errorReporter.typeError(_access.location(), "Out of bounds array access."); + m_errorReporter.typeError(3383_error, _access.location(), "Out of bounds array access."); } } resultType = actualType.baseType(); @@ -2652,7 +2729,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { MappingType const& actualType = dynamic_cast(*baseType); if (!index) - m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(1267_error, _access.location(), "Index expression cannot be omitted."); else expectType(*index, *actualType.keyType()); resultType = actualType.valueType(); @@ -2663,7 +2740,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { TypeType const& typeType = dynamic_cast(*baseType); if (dynamic_cast(typeType.actualType())) - m_errorReporter.typeError(_access.location(), "Index access for contracts or libraries is not possible."); + m_errorReporter.typeError(2876_error, _access.location(), "Index access for contracts or libraries is not possible."); if (!index) resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, typeType.actualType())); else @@ -2674,7 +2751,7 @@ bool TypeChecker::visit(IndexAccess const& _access) if (auto indexValue = dynamic_cast(type(*index))) length = indexValue->literalValue(nullptr); else - m_errorReporter.fatalTypeError(index->location(), "Integer constant expected."); + m_errorReporter.fatalTypeError(3940_error, index->location(), "Integer constant expected."); } else solAssert(m_errorReporter.hasErrors(), "Expected errors as expectType returned false"); @@ -2691,14 +2768,14 @@ bool TypeChecker::visit(IndexAccess const& _access) { FixedBytesType const& bytesType = dynamic_cast(*baseType); if (!index) - m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(8830_error, _access.location(), "Index expression cannot be omitted."); else { if (!expectType(*index, *TypeProvider::uint256())) - m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer."); + m_errorReporter.fatalTypeError(6318_error, _access.location(), "Index expression cannot be represented as an unsigned integer."); if (auto integerType = dynamic_cast(type(*index))) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) - m_errorReporter.typeError(_access.location(), "Out of bounds array access."); + m_errorReporter.typeError(1859_error, _access.location(), "Out of bounds array access."); } resultType = TypeProvider::fixedBytes(1); isLValue = false; // @todo this heavily depends on how it is embedded @@ -2706,6 +2783,7 @@ bool TypeChecker::visit(IndexAccess const& _access) } default: m_errorReporter.fatalTypeError( + 2614_error, _access.baseExpression().location(), "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")" ); @@ -2742,7 +2820,7 @@ bool TypeChecker::visit(IndexRangeAccess const& _access) TypePointer exprType = type(_access.baseExpression()); if (exprType->category() == Type::Category::TypeType) { - m_errorReporter.typeError(_access.location(), "Types cannot be sliced."); + m_errorReporter.typeError(1760_error, _access.location(), "Types cannot be sliced."); _access.annotation().type = exprType; return false; } @@ -2751,11 +2829,11 @@ bool TypeChecker::visit(IndexRangeAccess const& _access) if (auto const* arraySlice = dynamic_cast(exprType)) arrayType = &arraySlice->arrayType(); else if (!(arrayType = dynamic_cast(exprType))) - m_errorReporter.fatalTypeError(_access.location(), "Index range access is only possible for arrays and array slices."); + m_errorReporter.fatalTypeError(4781_error, _access.location(), "Index range access is only possible for arrays and array slices."); if (arrayType->location() != DataLocation::CallData || !arrayType->isDynamicallySized()) - m_errorReporter.typeError(_access.location(), "Index range access is only supported for dynamic calldata arrays."); + m_errorReporter.typeError(1227_error, _access.location(), "Index range access is only supported for dynamic calldata arrays."); _access.annotation().type = TypeProvider::arraySlice(*arrayType); _access.annotation().isLValue = isLValue; _access.annotation().isPure = isPure; @@ -2790,7 +2868,7 @@ vector TypeChecker::cleanOverloadedDeclarations( for (TypePointer parameter: functionType->parameterTypes() + functionType->returnParameterTypes()) if (!parameter) - m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context."); + m_errorReporter.fatalDeclarationError(3893_error, _identifier.location(), "Function type can not be used in this context."); if (uniqueDeclarations.end() == find_if( uniqueDeclarations.begin(), @@ -2825,14 +2903,14 @@ bool TypeChecker::visit(Identifier const& _identifier) candidates.push_back(declaration); } if (candidates.empty()) - m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup."); + m_errorReporter.fatalTypeError(2144_error, _identifier.location(), "No matching declaration found after variable lookup."); else if (candidates.size() == 1) annotation.referencedDeclaration = candidates.front(); else - m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup."); + m_errorReporter.fatalTypeError(7589_error, _identifier.location(), "No unique declaration found after variable lookup."); } else if (annotation.overloadedDeclarations.empty()) - m_errorReporter.fatalTypeError(_identifier.location(), "No candidates for overload resolution found."); + m_errorReporter.fatalTypeError(7593_error, _identifier.location(), "No candidates for overload resolution found."); else if (annotation.overloadedDeclarations.size() == 1) annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin(); else @@ -2866,9 +2944,9 @@ bool TypeChecker::visit(Identifier const& _identifier) else ssl.append("Candidate:", declaration->location()); if (candidates.empty()) - m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup."); + m_errorReporter.fatalTypeError(9322_error, _identifier.location(), ssl, "No matching declaration found after argument-dependent lookup."); else - m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup."); + m_errorReporter.fatalTypeError(4487_error, _identifier.location(), ssl, "No unique declaration found after argument-dependent lookup."); } } } @@ -2896,11 +2974,13 @@ bool TypeChecker::visit(Identifier const& _identifier) { if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256) m_errorReporter.typeError( + 3557_error, _identifier.location(), "\"sha3\" has been deprecated in favour of \"keccak256\"." ); else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.typeError( + 8050_error, _identifier.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\"." ); @@ -2938,6 +3018,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (!msg.empty()) m_errorReporter.syntaxError( + 9429_error, _literal.location(), msg + " If this is not used as an address, please prepend '00'. " + @@ -2947,6 +3028,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) m_errorReporter.fatalTypeError( + 5145_error, _literal.location(), "Hexadecimal numbers cannot be used with unit denominations. " "You can use an expression of the form \"0x1234 * 1 day\" instead." @@ -2954,6 +3036,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.subDenomination() == Literal::SubDenomination::Year) m_errorReporter.typeError( + 4820_error, _literal.location(), "Using \"years\" as a unit denomination is deprecated." ); @@ -2962,7 +3045,7 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().type = TypeProvider::forLiteral(_literal); if (!_literal.annotation().type) - m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); + m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value."); _literal.annotation().isPure = true; } @@ -3012,11 +3095,13 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte { if (_expectedType.operator==(*type(_expression)->mobileType())) m_errorReporter.typeError( + 4426_error, _expression.location(), errorMsg + ", but it can be explicitly converted." ); else m_errorReporter.typeError( + 2326_error, _expression.location(), errorMsg + ". Try converting to type " + @@ -3025,7 +3110,7 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte ); } else - m_errorReporter.typeError(_expression.location(), errorMsg + "."); + m_errorReporter.typeError(7407_error, _expression.location(), errorMsg + "."); return false; } return true; @@ -3040,7 +3125,8 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss if (_expression.annotation().isLValue) return; - return m_errorReporter.typeError(_expression.location(), [&]() { + return m_errorReporter.typeError( + 1123_error,_expression.location(), [&]() { if (_expression.annotation().isConstant) return "Cannot assign to a constant variable."; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 0d87fdbc2..645425107 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -171,6 +171,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) !_funDef.overrides() ) m_errorReporter.warning( + 2018_error, _funDef.location(), "Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability) ); @@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability( )) { m_errorReporter.typeError( + 2527_error, _location, "Function declared as pure, but this expression (potentially) reads from the " "environment or state and thus requires \"view\"." @@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability( else if (_mutability == StateMutability::NonPayable) { m_errorReporter.typeError( + 8961_error, _location, "Function declared as " + stateMutabilityToString(m_currentFunction->stateMutability()) + @@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability( { if (_nestedLocation) m_errorReporter.typeError( + 4006_error, _location, SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation), "This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal." ); else m_errorReporter.typeError( + 5887_error, _location, "\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function " "\"payable\" or use an internal function to avoid this error." diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 13a19694b..e8fc331fa 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -44,6 +44,7 @@ BMC::BMC( if (_enabledSolvers.some()) if (!_smtlib2Responses.empty()) m_errorReporter.warning( + 5622_error, "SMT-LIB2 query responses were given in the auxiliary input, " "but this Solidity binary uses an SMT solver (Z3/CVC4) directly." "These responses will be ignored." @@ -74,6 +75,7 @@ void BMC::analyze(SourceUnit const& _source, set _safeAsserti { m_noSolverWarning = true; m_outerErrorReporter.warning( + 8084_error, SourceLocation(), "BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found." ); @@ -467,6 +469,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall) inlineFunctionCall(_funCall); else if (funType.kind() == FunctionType::Kind::Internal) m_errorReporter.warning( + 5729_error, _funCall.location(), "Assertion checker does not yet implement this type of function call." ); @@ -760,6 +763,7 @@ void BMC::checkCondition( for (auto const& eval: sortedModel) modelMessage << " " << eval.first << " = " << eval.second << "\n"; m_errorReporter.warning( + 4334_error, _location, message.str(), SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) @@ -770,20 +774,20 @@ void BMC::checkCondition( else { message << "."; - m_errorReporter.warning(_location, message.str(), secondaryLocation); + m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation); } break; } case smt::CheckResult::UNSATISFIABLE: break; case smt::CheckResult::UNKNOWN: - m_errorReporter.warning(_location, _description + " might happen here.", secondaryLocation); + m_errorReporter.warning(5225_error, _location, _description + " might happen here.", secondaryLocation); break; case smt::CheckResult::CONFLICTING: - m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); + m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); break; case smt::CheckResult::ERROR: - m_errorReporter.warning(_location, "Error trying to invoke SMT solver."); + m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver."); break; } @@ -813,9 +817,9 @@ void BMC::checkBooleanNotConstant( m_interface->pop(); if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR) - m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver."); + m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver."); else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING) - m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound."); + m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound."); else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE) { // everything fine. @@ -825,7 +829,7 @@ void BMC::checkBooleanNotConstant( // can't do anything. } else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE) - m_errorReporter.warning(_condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack)); + m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack)); else { string value; @@ -841,6 +845,7 @@ void BMC::checkBooleanNotConstant( value = "false"; } m_errorReporter.warning( + 6838_error, _condition.location(), boost::algorithm::replace_all_copy(_description, "$VALUE", value), SMTEncoder::callStackMessage(_callStack) @@ -862,7 +867,7 @@ BMC::checkSatisfiableAndGenerateModel(vector const& _expression string description("Error querying SMT solver"); if (_e.comment()) description += ": " + *_e.comment(); - m_errorReporter.warning(description); + m_errorReporter.warning(8140_error, description); result = smt::CheckResult::ERROR; } diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 59e77d7cd..1453df34f 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -965,10 +965,10 @@ pair> CHC::query(smt::Expression const& _query, case smt::CheckResult::UNKNOWN: break; case smt::CheckResult::CONFLICTING: - m_outerErrorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); + m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); break; case smt::CheckResult::ERROR: - m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver."); + m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); break; } return {result, values}; diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 9003d26ce..8760c7b6b 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -266,6 +266,7 @@ void SMTEncoder::endVisit(FunctionDefinition const&) bool SMTEncoder::visit(InlineAssembly const& _inlineAsm) { m_errorReporter.warning( + 7737_error, _inlineAsm.location(), "Assertion checker does not support inline assembly." ); @@ -325,6 +326,7 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) } else m_errorReporter.warning( + 7186_error, _varDecl.location(), "Assertion checker does not yet implement such variable declarations." ); @@ -350,6 +352,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment) m_context.newValue(*varDecl); m_errorReporter.warning( + 9149_error, _assignment.location(), "Assertion checker does not yet implement this assignment operator." ); @@ -409,6 +412,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) m_errorReporter.warning( + 2177_error, _tuple.location(), "Assertion checker does not yet implement inline arrays." ); @@ -493,6 +497,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) } else m_errorReporter.warning( + 1950_error, _op.location(), "Assertion checker does not yet implement such increments / decrements." ); @@ -522,6 +527,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) arrayIndexAssignment(_op.subExpression(), symbVar->currentValue()); else m_errorReporter.warning( + 2683_error, _op.location(), "Assertion checker does not yet implement \"delete\" for this expression." ); @@ -530,6 +536,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) } default: m_errorReporter.warning( + 3682_error, _op.location(), "Assertion checker does not yet implement this operator." ); @@ -568,6 +575,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op) compareOperation(_op); else m_errorReporter.warning( + 3876_error, _op.location(), "Assertion checker does not yet implement this operator." ); @@ -580,6 +588,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall) { m_errorReporter.warning( + 4639_error, _funCall.location(), "Assertion checker does not yet implement this expression." ); @@ -639,6 +648,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) } default: m_errorReporter.warning( + 4588_error, _funCall.location(), "Assertion checker does not yet implement this type of function call." ); @@ -771,6 +781,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) } m_errorReporter.warning( + 5084_error, _funCall.location(), "Type conversion is not yet fully supported and might yield false positives." ); @@ -800,6 +811,7 @@ void SMTEncoder::endVisit(Literal const& _literal) else { m_errorReporter.warning( + 7885_error, _literal.location(), "Assertion checker does not yet support the type of this literal (" + _literal.annotation().type->toString() + @@ -850,6 +862,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) accessedName = identifier->name(); else m_errorReporter.warning( + 9551_error, _memberAccess.location(), "Assertion checker does not yet support this expression." ); @@ -879,6 +892,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) } else m_errorReporter.warning( + 7650_error, _memberAccess.location(), "Assertion checker does not yet support this expression." ); @@ -903,6 +917,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) if (varDecl->type()->category() == Type::Category::FixedBytes) { m_errorReporter.warning( + 7989_error, _indexAccess.location(), "Assertion checker does not yet support index accessing fixed bytes." ); @@ -917,6 +932,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) else { m_errorReporter.warning( + 9118_error, _indexAccess.location(), "Assertion checker does not yet implement this expression." ); @@ -940,6 +956,7 @@ void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess) { createExpr(_indexRangeAccess); m_errorReporter.warning( + 2923_error, _indexRangeAccess.location(), "Assertion checker does not yet implement this expression." ); @@ -1019,6 +1036,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c else { m_errorReporter.warning( + 9056_error, _expr.location(), "Assertion checker does not yet implement this expression." ); @@ -1034,6 +1052,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex bool abstract = m_context.createGlobalSymbol(_name, _expr); if (abstract) m_errorReporter.warning( + 1695_error, _expr.location(), "Assertion checker does not yet support this global variable." ); @@ -1087,6 +1106,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op) } default: m_errorReporter.warning( + 5188_error, _op.location(), "Assertion checker does not yet implement this operator." ); @@ -1094,6 +1114,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op) } else m_errorReporter.warning( + 9011_error, _op.location(), "Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "." ); @@ -1185,6 +1206,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op) } else m_errorReporter.warning( + 7229_error, _op.location(), "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons" ); @@ -1213,6 +1235,7 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op) } else m_errorReporter.warning( + 3263_error, _op.location(), "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations" ); @@ -1245,6 +1268,7 @@ void SMTEncoder::assignment( m_context.newValue(*varDecl); m_errorReporter.warning( + 6191_error, _location, "Assertion checker does not yet implement type " + _type->toString() ); @@ -1272,6 +1296,7 @@ void SMTEncoder::assignment( } else m_errorReporter.warning( + 8182_error, _location, "Assertion checker does not yet implement such assignments." ); @@ -1482,6 +1507,7 @@ bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl) if (abstract) { m_errorReporter.warning( + 8115_error, _varDecl.location(), "Assertion checker does not yet support the type of this variable." ); @@ -1494,7 +1520,7 @@ smt::Expression SMTEncoder::expr(Expression const& _e, TypePointer _targetType) { if (!m_context.knownExpression(_e)) { - m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." ); + m_errorReporter.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." ); createExpr(_e); } @@ -1506,6 +1532,7 @@ void SMTEncoder::createExpr(Expression const& _e) bool abstract = m_context.createExpression(_e); if (abstract) m_errorReporter.warning( + 8364_error, _e.location(), "Assertion checker does not yet implement type " + _e.annotation().type->toString() ); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 0af68bda7..519eea81a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -239,7 +239,7 @@ bool CompilerStack::parse() m_errorReporter.clear(); if (SemVerVersion{string(VersionString)}.isPrerelease()) - m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production."); + m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production."); Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery}; @@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string else { m_errorReporter.parserError( + 6275_error, import->location(), string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) ); @@ -1110,6 +1111,7 @@ void CompilerStack::compileContract( compiledContract.runtimeObject.bytecode.size() > 0x6000 ) m_errorReporter.warning( + 5574_error, _contract.location(), "Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). " "This contract may not be deployable on mainnet. " diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index fb1468197..b8238554f 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -195,5 +195,5 @@ void DocStringParser::newTag(string const& _tagName) void DocStringParser::appendError(string const& _description) { m_errorsOccurred = true; - m_errorReporter->docstringParsingError(_description); + m_errorReporter->docstringParsingError(9440_error, _description); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d3c4a2cdd..d24b0e69f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -128,6 +128,7 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector c // so we don't need to report anything here. if (!m_parserErrorRecovery) m_errorReporter.fatalParserError( + 5333_error, _location, "Source file requires different compiler version (current compiler is " + string(VersionString) + ") - note that nightly builds are considered to be " diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 7858b70c4..5c6b7c927 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -583,6 +583,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation ) { m_errorReporter.error( + 4316_error, Error::Type::SyntaxError, _location, "Jump instructions and labels are low-level EVM features that can lead to " @@ -599,13 +600,13 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description) { - m_errorReporter.typeError(_location, _description); + m_errorReporter.typeError(7569_error, _location, _description); m_success = false; } void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description) { - m_errorReporter.declarationError(_location, _description); + m_errorReporter.declarationError(9595_error, _location, _description); m_success = false; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 99a3a6e21..13b96839b 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -151,7 +151,7 @@ Statement Parser::parseStatement() { Statement stmt{createWithLocation()}; if (!m_insideFunction) - m_errorReporter.syntaxError(currentLocation(), "Keyword \"leave\" can only be used inside a function."); + m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); m_scanner->next(); return stmt; } @@ -417,6 +417,7 @@ FunctionDefinition Parser::parseFunctionDefinition() if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre) m_errorReporter.syntaxError( + 3441_error, currentLocation(), "Functions cannot be defined inside a for-loop init block." ); @@ -534,13 +535,13 @@ void Parser::checkBreakContinuePosition(string const& _which) switch (m_currentForLoopComponent) { case ForLoopComponent::None: - m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body."); + m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body."); break; case ForLoopComponent::ForLoopPre: - m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed."); + m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed."); break; case ForLoopComponent::ForLoopPost: - m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed."); + m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed."); break; case ForLoopComponent::ForLoopBody: break; diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 4c37bf5ec..62d00435f 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -141,6 +141,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& { //@TODO secondary location m_errorReporter.declarationError( + 1395_error, _location, "Variable name " + _name.name.str() + " already taken in this scope." ); @@ -161,6 +162,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef) { //@TODO secondary location m_errorReporter.declarationError( + 6052_error, _funDef.location, "Function name " + _funDef.name.str() + " already taken in this scope." ); From 2d984b77a10ebeb1028bba6254c549b090e5fb6f Mon Sep 17 00:00:00 2001 From: a3d4 Date: Wed, 6 May 2020 14:25:13 +0200 Subject: [PATCH 16/65] Add a script to correct IDs --- liblangutil/ErrorReporter.h | 2 + scripts/correct_error_ids.py | 158 +++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 scripts/correct_error_ids.py diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index 17e236e61..466bb66cb 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -38,6 +38,8 @@ namespace solidity::langutil * They are passed as the first parameter of error reporting functions. * Suffix _error helps to find them in the sources. * The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error). + * To create a new ID, one can add 0000_error and then run "python ./scripts/correct_error_ids.py" + * from the root of the repo. */ struct ErrorId { unsigned long long error = 0; }; ErrorId operator"" _error(unsigned long long error); diff --git a/scripts/correct_error_ids.py b/scripts/correct_error_ids.py new file mode 100644 index 000000000..d1f1cbf57 --- /dev/null +++ b/scripts/correct_error_ids.py @@ -0,0 +1,158 @@ +import random +import re +import os +from os import path + +ENCODING = "utf-8" +PATTERN = r"\b\d+_error\b" + + +def read_file(file_name): + content = None + try: + with open(file_name, "r", encoding=ENCODING) as f: + content = f.read() + finally: + if content == None: + print(f"Error reading: {file_name}") + return content + + +def write_file(file_name, content): + with open(file_name, "w", encoding=ENCODING) as f: + f.write(content) + + +def in_comment(source, pos): + slash_slash_pos = source.rfind("//", 0, pos) + lf_pos = source.rfind("\n", 0, pos) + if slash_slash_pos > lf_pos: + return True + slash_star_pos = source.rfind("/*", 0, pos) + star_slash_pos = source.rfind("*/", 0, pos) + return slash_star_pos > star_slash_pos + + +def find_ids_in_file(file_name, ids): + source = read_file(file_name) + for m in re.finditer(PATTERN, source): + if in_comment(source, m.start()): + continue + underscore_pos = m.group(0).index("_") + id = m.group(0)[0:underscore_pos] + if id in ids: + ids[id] += 1 + else: + ids[id] = 1 + + +def get_used_ids(file_names): + used_ids = {} + for file_name in file_names: + find_ids_in_file(file_name, used_ids) + return used_ids + + +def get_id(available_ids, used_ids): + while len(available_ids) > 0: + random.seed(len(available_ids)) + k = random.randrange(len(available_ids)) + id = list(available_ids.keys())[k] + del available_ids[id] + if id not in used_ids: + return id + assert False, "Out of IDs" + + +def fix_ids_in_file(file_name, available_ids, used_ids): + source = read_file(file_name) + + k = 0 + destination = [] + for m in re.finditer(PATTERN, source): + destination.extend(source[k:m.start()]) + + underscore_pos = m.group(0).index("_") + id = m.group(0)[0:underscore_pos] + + # incorrect id or id has a duplicate somewhere + if not in_comment(source, m.start()) and (len(id) != 4 or id[0] == "0" or used_ids[id] > 1): + assert id in used_ids + new_id = get_id(available_ids, used_ids) + used_ids[id] -= 1 + else: + new_id = id + + destination.extend(new_id + "_error") + k = m.end() + + destination.extend(source[k:]) + + destination = ''.join(destination) + if source != destination: + write_file(file_name, destination) + print(f"Fixed file: {file_name}") + + +def fix_ids(used_ids, file_names): + available_ids = {str(id): None for id in range(1000, 10000)} + for file_name in file_names: + fix_ids_in_file(file_name, available_ids, used_ids) + + +def find_source_files(top_dir): + """Builds the list of .h and .cpp files in top_dir directory""" + + source_file_names = [] + black_set = { ".circleci", ".git", ".github", "build", "cmake", "CMakeFiles", "deps", "docs" } + + for root, _, file_names in os.walk(top_dir, onerror=lambda e: exit(f"Walk error: {e}")): + path_elements = set(root.split(os.sep)) + if not black_set.isdisjoint(path_elements): + continue + for file_name in file_names: + _, ext = path.splitext(file_name) + if ext in [".h", ".cpp"]: + source_file_names.append(path.join(root, file_name)) + + return source_file_names + + +def main(): + cwd = os.getcwd() + answer = input( + f"This script checks and corrects *_error literals in .h and .cpp files\n" + f"in {cwd}, recursively.\n\n" + f"Please commit current changes first, and review the results when the script finishes.\n\n" + f"Do you want to start [Y/N]? " + ) + while len(answer) == 0 or answer not in "YN": + answer = input("[Y/N]? ") + if answer != "Y": + return + + source_file_names = find_source_files(cwd) + + used_ids = get_used_ids(source_file_names) + + ok = True + for id in sorted(used_ids): + if len(id) != 4: + print(f"ID {id} length != 4") + ok = False + if id[0] == "0": + print(f"ID {id} starts with zero") + ok = False + if used_ids[id] > 1: + print(f"ID {id} appears {used_ids[id]} times") + ok = False + + if ok: + print("No incorrect IDs found") + else: + fix_ids(used_ids, source_file_names) + print("Fixing compteted") + + +if __name__ == "__main__": + main() From 327c75bc1b57c37f6dc6a92d3fbf0f9bb03a57a5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 20 Apr 2020 22:16:42 +0100 Subject: [PATCH 17/65] IR generation for shifts --- libsolidity/codegen/YulUtilFunctions.cpp | 83 +++++++++++++++++- libsolidity/codegen/YulUtilFunctions.h | 8 ++ .../codegen/ir/IRGeneratorForStatements.cpp | 87 ++++++++++++++++--- .../codegen/ir/IRGeneratorForStatements.h | 5 ++ .../shifts/shift_cleanup_garbled.sol | 2 + .../shifts/shift_constant_left_assignment.sol | 2 + .../shift_constant_right_assignment.sol | 2 + .../semanticTests/shifts/shift_left.sol | 2 + .../shifts/shift_left_assignment.sol | 2 + .../shift_left_assignment_different_type.sol | 2 + .../shifts/shift_left_larger_type.sol | 3 +- .../shifts/shift_left_uint32.sol | 2 + .../semanticTests/shifts/shift_left_uint8.sol | 2 + .../shifts/shift_negative_rvalue.sol | 2 + .../shift_negative_rvalue_assignment.sol | 2 + .../semanticTests/shifts/shift_overflow.sol | 2 + .../semanticTests/shifts/shift_right.sol | 2 + .../shifts/shift_right_assignment.sol | 2 + .../shifts/shift_right_assignment_signed.sol | 2 + .../shifts/shift_right_garbled_signed_v2.sol | 2 + .../shifts/shift_right_garbled_v2.sol | 2 + .../shifts/shift_right_negative_lvalue.sol | 2 + ...shift_right_negative_lvalue_assignment.sol | 2 + .../shift_right_negative_lvalue_int16.sol | 2 + .../shift_right_negative_lvalue_int32.sol | 2 + .../shift_right_negative_lvalue_int8.sol | 2 + ...ht_negative_lvalue_signextend_int16_v2.sol | 2 + ...ht_negative_lvalue_signextend_int32_v2.sol | 2 + ...ght_negative_lvalue_signextend_int8_v2.sol | 2 + .../shifts/shift_right_uint32.sol | 2 + .../shifts/shift_right_uint8.sol | 2 + .../shifts/shift_signed_cleanup_amount.sol | 16 ++++ 32 files changed, 236 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/semanticTests/shifts/shift_signed_cleanup_amount.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 540086756..4e0ebb929 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -299,9 +299,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits) string YulUtilFunctions::shiftRightFunctionDynamic() { - // Note that if this is extended with signed shifts, - // the opcodes SAR and SDIV behave differently with regards to rounding! - string const functionName = "shift_right_unsigned_dynamic"; return m_functionCollector.createFunction(functionName, [&]() { return @@ -321,6 +318,86 @@ string YulUtilFunctions::shiftRightFunctionDynamic() }); } +string YulUtilFunctions::shiftRightSignedFunctionDynamic() +{ + string const functionName = "shift_right_signed_dynamic"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (bits, value) -> result { + + result := sar(bits, value) + + let divisor := exp(2, bits) + let xor_mask := sub(0, slt(value, 0)) + result := xor(div(xor(value, xor_mask), divisor), xor_mask) + // combined version of + // switch slt(value, 0) + // case 0 { result := div(value, divisor) } + // default { result := not(div(not(value), divisor)) } + + } + )") + ("functionName", functionName) + ("hasShifts", m_evmVersion.hasBitwiseShifting()) + .render(); + }); +} + + +string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType) +{ + solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); + solAssert(_amountType.category() == Type::Category::Integer, ""); + string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (value, bits) -> result { + bits := (bits) + + if slt(bits, 0) { invalid() } + + result := ((bits, value)) + } + )") + ("functionName", functionName) + ("amountSigned", dynamic_cast(_amountType).isSigned()) + ("cleanAmount", cleanupFunction(_amountType)) + ("shift", shiftLeftFunctionDynamic()) + ("cleanup", cleanupFunction(_type)) + .render(); + }); +} + +string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType) +{ + solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); + solAssert(_amountType.category() == Type::Category::Integer, ""); + IntegerType const* integerType = dynamic_cast(&_type); + bool valueSigned = integerType && integerType->isSigned(); + + string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (value, bits) -> result { + bits := (bits) + + if slt(bits, 0) { invalid() } + + result := ((bits, (value))) + } + )") + ("functionName", functionName) + ("amountSigned", dynamic_cast(_amountType).isSigned()) + ("cleanAmount", cleanupFunction(_amountType)) + ("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic()) + ("cleanup", cleanupFunction(_type)) + .render(); + }); +} + string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes) { solAssert(_numBytes <= 32, ""); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 9f21a7d39..e72d6d293 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -81,6 +81,14 @@ public: std::string shiftLeftFunctionDynamic(); std::string shiftRightFunction(size_t _numBits); std::string shiftRightFunctionDynamic(); + std::string shiftRightSignedFunctionDynamic(); + + /// @returns the name of a function that performs a left shift and subsequent cleanup + /// and, if needed, prior cleanup. + /// If the amount to shift by is signed, a check for negativeness is performed. + /// signature: (value, amountToShift) -> result + std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType); + std::string typedShiftRightFunction(Type const& _type, Type const& _amountType); /// @returns the name of a function which replaces the /// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index d10ec3bd5..6bfe35395 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -254,29 +254,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) bool IRGeneratorForStatements::visit(Assignment const& _assignment) { _assignment.rightHandSide().accept(*this); - Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType( - &type(_assignment.leftHandSide()) - ); - IRVariable value = convert(_assignment.rightHandSide(), *intermediateType); + Token assignmentOperator = _assignment.assignmentOperator(); + Token binaryOperator = + assignmentOperator == Token::Assign ? + assignmentOperator : + TokenTraits::AssignmentToBinaryOp(assignmentOperator); + + Type const* rightIntermediateType = + TokenTraits::isShiftOp(binaryOperator) ? + type(_assignment.rightHandSide()).mobileType() : + type(_assignment.rightHandSide()).closestTemporaryType( + &type(_assignment.leftHandSide()) + ); + solAssert(rightIntermediateType, ""); + IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); _assignment.leftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); - if (_assignment.assignmentOperator() != Token::Assign) + if (assignmentOperator != Token::Assign) { - solAssert(type(_assignment.leftHandSide()) == *intermediateType, ""); - solAssert(intermediateType->isValueType(), "Compound operators only available for value types."); - + solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); + solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types."); IRVariable leftIntermediate = readFromLValue(*m_currentLValue); - m_code << value.name() << " := " << binaryOperation( - TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), - *intermediateType, - leftIntermediate.name(), - value.name() - ); + if (TokenTraits::isShiftOp(binaryOperator)) + { + solAssert(type(_assignment) == leftIntermediate.type(), ""); + solAssert(type(_assignment) == type(_assignment.leftHandSide()), ""); + define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value); + + writeToLValue(*m_currentLValue, IRVariable(_assignment)); + m_currentLValue.reset(); + return false; + } + else + { + solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, ""); + m_code << value.name() << " := " << binaryOperation( + binaryOperator, + *rightIntermediateType, + leftIntermediate.name(), + value.name() + ); + } } writeToLValue(*m_currentLValue, value); + m_currentLValue.reset(); if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) define(_assignment, value); @@ -541,6 +565,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) solAssert(false, "Unknown comparison operator."); define(_binOp) << expr << "\n"; } + else if (TokenTraits::isShiftOp(op)) + { + IRVariable left = convert(_binOp.leftExpression(), *commonType); + IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); + define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; + } else { string left = expressionAsType(_binOp.leftExpression(), *commonType); @@ -1921,6 +1951,10 @@ string IRGeneratorForStatements::binaryOperation( string const& _right ) { + solAssert( + !TokenTraits::isShiftOp(_operator), + "Have to use specific shift operation function for shifts." + ); if (IntegerType const* type = dynamic_cast(&_type)) { string fun; @@ -1964,6 +1998,31 @@ string IRGeneratorForStatements::binaryOperation( return {}; } +std::string IRGeneratorForStatements::shiftOperation( + langutil::Token _operator, + IRVariable const& _value, + IRVariable const& _amountToShift +) +{ + IntegerType const* amountType = dynamic_cast(&_amountToShift.type()); + solAssert(amountType, ""); + + solAssert(_operator == Token::SHL || _operator == Token::SAR, ""); + + return + Whiskers(R"( + (, ) + )") + ("shift", + _operator == Token::SHL ? + m_utils.typedShiftLeftFunction(_value.type(), *amountType) : + m_utils.typedShiftRightFunction(_value.type(), *amountType) + ) + ("value", _value.name()) + ("amount", _amountToShift.name()) + .render(); +} + void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp) { langutil::Token const op = _binOp.getOperator(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index f73c585b4..c51b2225d 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -147,6 +147,11 @@ private: std::string const& _right ); + /// @returns code to perform the given shift operation. + /// The operation itself will be performed in the type of the value, + /// while the amount to shift can have its own type. + std::string shiftOperation(langutil::Token _op, IRVariable const& _value, IRVariable const& _shiftAmount); + /// Assigns the value of @a _value to the lvalue @a _lvalue. void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value); /// @returns a fresh IR variable containing the value of the lvalue @a _lvalue. diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol index cc81c15e4..6789bc350 100644 --- a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol +++ b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol @@ -7,5 +7,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol index e5a4152b5..38a776b30 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol @@ -5,5 +5,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol index 0f36c10ee..1853814c6 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol @@ -5,5 +5,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_left.sol b/test/libsolidity/semanticTests/shifts/shift_left.sol index 15d2a972a..e72671ceb 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol index 06cb38606..fd5981996 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol @@ -5,6 +5,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol index 5cc15c1a8..2f470d500 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol @@ -5,6 +5,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256,uint8): 0x4266, 0x0 -> 0x4266 // f(uint256,uint8): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol index 99ff376d6..de7b4ec3e 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol @@ -6,6 +6,7 @@ contract C { return y << x; } } - +// ==== +// compileViaYul: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol index 0f35077b1..a08f13aef 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint32,uint32): 0x4266, 0x0 -> 0x4266 // f(uint32,uint32): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol index 3070314f8..af214a244 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint8,uint8): 0x66, 0x0 -> 0x66 // f(uint8,uint8): 0x66, 0x8 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol index 77c18c44b..611458246 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol @@ -8,6 +8,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int256,int256): 1, -1 -> FAILURE // g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol index e63a9a57e..623a715ec 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol @@ -10,6 +10,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int256,int256): 1, -1 -> FAILURE // g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_overflow.sol b/test/libsolidity/semanticTests/shifts/shift_overflow.sol index f1b4bca0a..54f5fcc2b 100644 --- a/test/libsolidity/semanticTests/shifts/shift_overflow.sol +++ b/test/libsolidity/semanticTests/shifts/shift_overflow.sol @@ -8,6 +8,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // leftU(uint8,uint8): 255, 8 -> 0 // leftU(uint8,uint8): 255, 1 -> 254 diff --git a/test/libsolidity/semanticTests/shifts/shift_right.sol b/test/libsolidity/semanticTests/shifts/shift_right.sol index d78d18aba..bfdb665d1 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol index cfee67301..80f25238c 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol @@ -5,6 +5,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol index ba819fbce..5b8494167 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol @@ -5,6 +5,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int256,int256): 0x4266, 0x0 -> 0x4266 // f(int256,int256): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol index 0c1949a59..4c705337c 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol @@ -18,6 +18,8 @@ contract C { return a >> b; } } +// ==== +// compileViaYul: also // ---- // f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe // f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol index 54ac9540f..18ea9972c 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol @@ -10,6 +10,8 @@ contract C { return a >> b; } } +// ==== +// compileViaYul: also // ---- // f(uint8,uint8): 0x00, 0x04 -> 0x0f // f(uint8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol index 73aae5bc9..2bac06e6e 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int256,int256): -4266, 0 -> -4266 // f(int256,int256): -4266, 1 -> -2133 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol index 7f3beb59f..4fecffda4 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol @@ -5,6 +5,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int256,int256): -4266, 0 -> -4266 // f(int256,int256): -4266, 1 -> -2133 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol index 24ab54123..883f004d0 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int16,int16): -4266, 0 -> -4266 // f(int16,int16): -4266, 1 -> -2133 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol index 7ff669dc9..4f689c015 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int32,int32): -4266, 0 -> -4266 // f(int32,int32): -4266, 1 -> -2133 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol index c6424f141..02f293e3c 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(int8,int8): -66, 0 -> -66 // f(int8,int8): -66, 1 -> -33 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol index 6e462e704..850a89be3 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol @@ -6,6 +6,8 @@ contract C { return a >> b; } } +// ==== +// compileViaYul: also // ---- // f(int16,int16): 0xff99, 0x00 -> FAILURE // f(int16,int16): 0xff99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol index 2466298f0..d797531ec 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol @@ -6,6 +6,8 @@ contract C { return a >> b; } } +// ==== +// compileViaYul: also // ---- // f(int32,int32): 0xffffff99, 0x00 -> FAILURE // f(int32,int32): 0xffffff99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol index 643bc5e62..ee485335d 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol @@ -6,6 +6,8 @@ contract C { return a >> b; } } +// ==== +// compileViaYul: also // ---- // f(int8,int8): 0x99, 0x00 -> FAILURE // f(int8,int8): 0x99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol index 03573d985..8cc6c4a98 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint32,uint32): 0x4266, 0x0 -> 0x4266 // f(uint32,uint32): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol index 8457b10e1..acfd99b86 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol @@ -4,6 +4,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint8,uint8): 0x66, 0x0 -> 0x66 // f(uint8,uint8): 0x66, 0x8 -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_signed_cleanup_amount.sol b/test/libsolidity/semanticTests/shifts/shift_signed_cleanup_amount.sol new file mode 100644 index 000000000..72b8f69c8 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_signed_cleanup_amount.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint256 a, int8 b) public returns (uint256) { + assembly { b := 0xff } + return a << b; + } + function g(uint256 a, int8 b) public returns (uint256) { + assembly { b := 0xff } + return a >> b; + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256,int8): 0x1234, 0x0 -> FAILURE +// g(uint256,int8): 0x1234, 0x0 -> FAILURE From 6fb112fa8e5946ea1a9f86ded3fe2b191fdbc7dd Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2020 01:49:46 +0200 Subject: [PATCH 18/65] Remove some unneeded headers. --- libevmasm/ExpressionClasses.cpp | 3 --- libevmasm/SimplificationRules.cpp | 3 --- libsolidity/analysis/ContractLevelChecker.cpp | 1 - libsolidity/analysis/ControlFlowGraph.cpp | 1 - libsolidity/analysis/OverrideChecker.cpp | 1 - libsolidity/codegen/ABIFunctions.cpp | 1 - libsolidity/codegen/ExpressionCompiler.cpp | 1 - libsolidity/codegen/MultiUseYulFunctionCollector.cpp | 2 -- libsolidity/codegen/YulUtilFunctions.cpp | 3 --- libsolidity/codegen/ir/IRGenerator.cpp | 3 --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 1 - libyul/AsmScopeFiller.cpp | 2 -- libyul/backends/wasm/WasmCodeTransform.cpp | 3 --- libyul/optimiser/ExpressionSplitter.cpp | 2 -- libyul/optimiser/FullInliner.cpp | 2 -- 15 files changed, 29 deletions(-) diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 62780b19f..04686ec94 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -26,9 +26,6 @@ #include #include -#include -#include - #include #include #include diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index 751153b05..8fb65d1d3 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -29,9 +29,6 @@ #include #include -#include -#include - #include #include diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 1327e89f3..79b055784 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -26,7 +26,6 @@ #include #include #include -#include using namespace std; diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp index 4cb096ae0..fb5fc1be0 100644 --- a/libsolidity/analysis/ControlFlowGraph.cpp +++ b/libsolidity/analysis/ControlFlowGraph.cpp @@ -18,7 +18,6 @@ #include #include -#include #include using namespace std; diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index fbff807f9..1f4fa10a2 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -27,7 +27,6 @@ #include #include -#include #include diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 8909651be..20e4e23db 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -28,7 +28,6 @@ #include #include -#include using namespace std; using namespace solidity; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 14b0d763b..a7ddb4b87 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -36,7 +36,6 @@ #include #include -#include #include #include diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp index e8994ac04..f49f3083d 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp @@ -23,8 +23,6 @@ #include -#include -#include using namespace std; using namespace solidity; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 4e0ebb929..f98d5770f 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -28,9 +28,6 @@ #include #include -#include -#include - using namespace std; using namespace solidity; using namespace solidity::util; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bac4fea66..bfecb9cf2 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -38,9 +38,6 @@ #include -#include -#include - #include using namespace std; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 6bfe35395..902b7ba55 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -42,7 +42,6 @@ #include #include -#include #include using namespace std; diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 62d00435f..90143ba4d 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -29,8 +29,6 @@ #include -#include - #include #include diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index cb1294d44..9c553b380 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -29,9 +29,6 @@ #include -#include -#include - using namespace std; using namespace solidity; using namespace solidity::yul; diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 8144de49a..30a5fd5e9 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -31,8 +31,6 @@ #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::yul; diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 32036184a..a0c80533d 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -34,8 +34,6 @@ #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::yul; From 7e1835af71560338fe1a2cf13c816b8cf0a38af1 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Thu, 7 May 2020 03:52:53 +0200 Subject: [PATCH 19/65] Fix error reporting source code format --- libsolidity/analysis/ContractLevelChecker.cpp | 4 +++- libsolidity/analysis/SyntaxChecker.cpp | 3 ++- libsolidity/analysis/TypeChecker.cpp | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 1327e89f3..8f7b69a4a 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -220,7 +220,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c for (auto declaration: _contract.annotation().unimplementedDeclarations) ssl.append("Missing implementation: ", declaration->location()); m_errorReporter.typeError( - 3656_error,_contract.location(), ssl, + 3656_error, + _contract.location(), + ssl, "Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."); diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c3267c37f..058244cac 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -291,7 +291,8 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) for (FunctionDefinition const* function: _contract.definedFunctions()) if (function->name() == contractName) m_errorReporter.syntaxError( - 5796_error,function->location(), + 5796_error, + function->location(), "Functions are not allowed to have the same name as the contract. " "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." ); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7277714b8..e8806da1f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -493,7 +493,8 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) unsupportedTypes.emplace_back(param->toString()); if (!unsupportedTypes.empty()) m_errorReporter.typeError( - 2763_error,_variable.location(), + 2763_error, + _variable.location(), "The following types are only supported for getters in ABIEncoderV2: " + joinHumanReadable(unsupportedTypes) + ". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature." @@ -3125,8 +3126,7 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss if (_expression.annotation().isLValue) return; - return m_errorReporter.typeError( - 1123_error,_expression.location(), [&]() { + return m_errorReporter.typeError(1123_error, _expression.location(), [&]() { if (_expression.annotation().isConstant) return "Cannot assign to a constant variable."; From 5e9502bfae0900e5696b5c6b760c4f7ccd9aabda Mon Sep 17 00:00:00 2001 From: a3d4 Date: Thu, 7 May 2020 05:33:08 +0200 Subject: [PATCH 20/65] Fix ABI internal compiler error caused by an internal constructor --- Changelog.md | 1 + libsolidity/interface/ABI.cpp | 5 +++-- test/libsolidity/ABIJson/internal_constructor.sol | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/ABIJson/internal_constructor.sol diff --git a/Changelog.md b/Changelog.md index 0b3749be9..98a0ca408 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: Bugfixes: + * ABI: Skip ``private`` or ``internal`` constructors. diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 56e2ecbde..c00c2f83c 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) ); abi.emplace(std::move(method)); } - if (_contractDef.constructor()) + FunctionDefinition const* constructor = _contractDef.constructor(); + if (constructor && constructor->visibility() >= Visibility::Public) { - FunctionType constrType(*_contractDef.constructor()); + FunctionType constrType(*constructor); FunctionType const* externalFunctionType = constrType.interfaceFunctionType(); solAssert(!!externalFunctionType, ""); Json::Value method; diff --git a/test/libsolidity/ABIJson/internal_constructor.sol b/test/libsolidity/ABIJson/internal_constructor.sol new file mode 100644 index 000000000..7f8ea5ed2 --- /dev/null +++ b/test/libsolidity/ABIJson/internal_constructor.sol @@ -0,0 +1,9 @@ +// bug #8712 +contract B { + uint immutable x; + constructor(function() internal returns(uint) fp) internal { + x = fp(); } +} +// ---- +// :B +// [] From ff084c28099a2758d4f780a13289f21a830eb319 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 7 May 2020 11:22:56 +0200 Subject: [PATCH 21/65] Make error correction script user interaction case insensitive --- scripts/correct_error_ids.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/correct_error_ids.py b/scripts/correct_error_ids.py index d1f1cbf57..8717dbf23 100644 --- a/scripts/correct_error_ids.py +++ b/scripts/correct_error_ids.py @@ -126,9 +126,9 @@ def main(): f"Please commit current changes first, and review the results when the script finishes.\n\n" f"Do you want to start [Y/N]? " ) - while len(answer) == 0 or answer not in "YN": + while len(answer) == 0 or answer not in "YNyn": answer = input("[Y/N]? ") - if answer != "Y": + if answer not in "yY": return source_file_names = find_source_files(cwd) From e986fde0c191bc665c73ef3ff5777e02a0651fac Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2020 01:55:30 +0200 Subject: [PATCH 22/65] Introduce non-reversed version of tupleEncoder. --- libsolidity/codegen/ABIFunctions.cpp | 20 ++++++++--- libsolidity/codegen/ABIFunctions.h | 34 +++++++++++++++---- libsolidity/codegen/CompilerUtils.cpp | 4 +-- libsolidity/codegen/YulUtilFunctions.cpp | 5 ++- libsolidity/codegen/ir/IRGenerator.cpp | 11 +++--- .../codegen/ir/IRGeneratorForStatements.cpp | 5 ++- .../yul_string_format_ascii/output.json | 2 +- .../output.json | 2 +- .../output.json | 2 +- .../yul_string_format_ascii_long/output.json | 2 +- .../yul_string_format_hex/output.json | 2 +- ...ta_array_dynamic_static_short_reencode.sol | 2 ++ .../pass_function_types_externally.sol | 2 ++ 13 files changed, 63 insertions(+), 30 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 8909651be..80d626038 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -38,7 +38,8 @@ using namespace solidity::frontend; string ABIFunctions::tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, - bool _encodeAsLibraryTypes + bool _encodeAsLibraryTypes, + bool _reversed ) { EncodingOptions options; @@ -54,6 +55,8 @@ string ABIFunctions::tupleEncoder( for (auto const& t: _targetTypes) functionName += t->identifier() + "_"; functionName += options.toFunctionNameSuffix(); + if (_reversed) + functionName += "_reversed"; return createFunction(functionName, [&]() { // Note that the values are in reverse due to the difference in calling semantics. @@ -94,7 +97,10 @@ string ABIFunctions::tupleEncoder( stackPos += sizeOnStack; } solAssert(headPos == headSize_, ""); - string valueParams = suffixedVariableNameList("value", stackPos, 0); + string valueParams = + _reversed ? + suffixedVariableNameList("value", stackPos, 0) : + suffixedVariableNameList("value", 0, stackPos); templ("valueParams", valueParams.empty() ? "" : ", " + valueParams); templ("encodeElements", encodeElements); @@ -104,7 +110,8 @@ string ABIFunctions::tupleEncoder( string ABIFunctions::tupleEncoderPacked( TypePointers const& _givenTypes, - TypePointers const& _targetTypes + TypePointers const& _targetTypes, + bool _reversed ) { EncodingOptions options; @@ -120,6 +127,8 @@ string ABIFunctions::tupleEncoderPacked( for (auto const& t: _targetTypes) functionName += t->identifier() + "_"; functionName += options.toFunctionNameSuffix(); + if (_reversed) + functionName += "_reversed"; return createFunction(functionName, [&]() { solAssert(!_givenTypes.empty(), ""); @@ -158,7 +167,10 @@ string ABIFunctions::tupleEncoderPacked( encodeElements += elementTempl.render(); stackPos += sizeOnStack; } - string valueParams = suffixedVariableNameList("value", stackPos, 0); + string valueParams = + _reversed ? + suffixedVariableNameList("value", stackPos, 0) : + suffixedVariableNameList("value", 0, stackPos); templ("valueParams", valueParams.empty() ? "" : ", " + valueParams); templ("encodeElements", encodeElements); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e760917ac..a66b1284e 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -67,31 +67,53 @@ public: /// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// into memory, converting the types to @a _targetTypes on the fly. - /// Parameters are: ... , i.e. - /// the layout on the stack is ... with + /// Parameters are: ... , i.e. + /// the layout on the stack is ... with /// the top of the stack on the right. /// The values represent stack slots. If a type occupies more or less than one /// stack slot, it takes exactly that number of values. /// Returns a pointer to the end of the area written in memory. /// Does not allocate memory (does not change the free memory pointer), but writes /// to memory starting at $headStart and an unrestricted amount after that. + /// If @reversed is true, the order of the variables after is reversed. std::string tupleEncoder( TypePointers const& _givenTypes, TypePointers const& _targetTypes, - bool _encodeAsLibraryTypes = false + bool _encodeAsLibraryTypes = false, + bool _reversed = false ); + /// Specialization of tupleEncoder to _reversed = true + std::string tupleEncoderReversed( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _encodeAsLibraryTypes = false + ) { + return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true); + } + /// @returns name of an assembly function to encode values of @a _givenTypes /// with packed encoding into memory, converting the types to @a _targetTypes on the fly. - /// Parameters are: ... , i.e. - /// the layout on the stack is ... with + /// Parameters are: ... , i.e. + /// the layout on the stack is ... with /// the top of the stack on the right. /// The values represent stack slots. If a type occupies more or less than one /// stack slot, it takes exactly that number of values. /// Returns a pointer to the end of the area written in memory. /// Does not allocate memory (does not change the free memory pointer), but writes /// to memory starting at memPos and an unrestricted amount after that. - std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes); + /// If @reversed is true, the order of the variables after is reversed. + std::string tupleEncoderPacked( + TypePointers const& _givenTypes, + TypePointers const& _targetTypes, + bool _reversed = false + ); + + /// Specialization of tupleEncoderPacked to _reversed = true + std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes) + { + return tupleEncoderPacked(_givenTypes, _targetTypes, true); + } /// @returns name of an assembly function to ABI-decode values of @a _types /// into memory. If @a _fromMemory is true, decodes from memory instead of diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 3d0117707..156cee565 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2( string encoderName = _padToWordBoundaries ? - m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : - m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); + m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : + m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes); m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); } diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 4e0ebb929..f0c2ae0e3 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1125,13 +1125,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT return m_functionCollector.createFunction(functionName, [&]() { if (_mappingType.keyType()->isDynamicallySized()) return Whiskers(R"( - function (slot ) -> dataSlot { - dataSlot := (slot ) + function (slot , ) -> dataSlot { + dataSlot := ( , slot) } )") ("functionName", functionName) ("key", _keyType.sizeOnStack() > 0 ? "key" : "") - ("comma", _keyType.sizeOnStack() > 0 ? "," : "") ("hash", packedHashFunction( {&_keyType, TypeProvider::uint256()}, {_mappingType.keyType(), TypeProvider::uint256()} diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bac4fea66..2a86eafb1 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -482,10 +482,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) { // - (4, calldatasize()) - () + let := (4, calldatasize()) + let := () let memPos := (0) - let memEnd := (memPos ) + let memEnd := (memPos , ) return(memPos, sub(memEnd, memPos)) } @@ -507,13 +507,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) unsigned paramVars = make_shared(type->parameterTypes())->sizeOnStack(); unsigned retVars = make_shared(type->returnParameterTypes())->sizeOnStack(); - templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "; - templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := "; ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes()); templ["params"] = suffixedVariableNameList("param_", 0, paramVars); - templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0); + templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars); if (FunctionDefinition const* funDef = dynamic_cast(&type->declaration())) templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef); @@ -524,7 +522,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) templ["allocate"] = m_utils.allocationFunction(); templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false); - templ["comma"] = retVars == 0 ? "" : ", "; } t("cases", functions); if (FunctionDefinition const* fallback = _contract.fallbackFunction()) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 6bfe35395..20023865e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -742,8 +742,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { string vars = IRVariable(arg).commaSeparatedList(); if (!vars.empty()) - // In reverse because abi_encode expects it like that. - nonIndexedArgs = ", " + move(vars) + nonIndexedArgs; + nonIndexedArgs += ", " + move(vars); nonIndexedArgTypes.push_back(arg.annotation().type); nonIndexedParamTypes.push_back(paramTypes[i]); } @@ -1023,7 +1022,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("object", m_context.creationObjectName(*contract)); t("abiEncode", - m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false) + m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) ); t("constructorParams", constructorParams); t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 215146d1d..83511d5d6 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -38,7 +38,7 @@ object \"C_10\" { abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_9() let memPos := allocateMemory(0) - let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) + let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 7b2291843..69c2091dc 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -38,7 +38,7 @@ object \"C_10\" { abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_9() let memPos := allocateMemory(0) - let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0) + let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index ef647f39e..8742ef645 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -38,7 +38,7 @@ object \"C_10\" { abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_9() let memPos := allocateMemory(0) - let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) + let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 8243cd93e..4e6b565d0 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -38,7 +38,7 @@ object \"C_10\" { abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_9() let memPos := allocateMemory(0) - let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) + let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) } diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 70f2fc3de..f98417556 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -38,7 +38,7 @@ object \"C_10\" { abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_9() let memPos := allocateMemory(0) - let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) + let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol index d5802d1b1..c777b12fd 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol @@ -7,6 +7,8 @@ contract C { return this.f(x); } } +// ==== +// compileViaYul: also // ---- // g(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 42 // g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 42 diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol index ebef9a254..2cb6428f7 100644 --- a/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol @@ -16,6 +16,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256): 7 -> 8 // f2(uint256): 7 -> 8 From 50e59f10084176c9e797617fcd3c178f12af89ab Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 6 May 2020 18:26:54 +0200 Subject: [PATCH 23/65] trigger error when runtimeCode is called on contracts with immutables --- Changelog.md | 2 +- libsolidity/analysis/TypeChecker.cpp | 16 +++++++++++++--- .../syntaxTests/immutable/creationCode.sol | 9 +++++++++ .../syntaxTests/immutable/runtimeCode.sol | 11 +++++++++++ .../immutable/runtimeCodeInheritance.sol | 13 +++++++++++++ 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/immutable/creationCode.sol create mode 100644 test/libsolidity/syntaxTests/immutable/runtimeCode.sol create mode 100644 test/libsolidity/syntaxTests/immutable/runtimeCodeInheritance.sol diff --git a/Changelog.md b/Changelog.md index 98a0ca408..d17bbadf8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ Compiler Features: Bugfixes: * ABI: Skip ``private`` or ``internal`` constructors. - + * Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7277714b8..71b3f3a3c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2663,9 +2663,19 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) )) { annotation.isPure = true; - m_scope->annotation().contractDependencies.insert( - &dynamic_cast(*magicType->typeArgument()).contractDefinition() - ); + ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); + m_scope->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + + if ( + memberName == "runtimeCode" && + !accessedContractType.immutableVariables().empty() + ) + m_errorReporter.typeError( + 9274_error, + _memberAccess.location(), + "\"runtimeCode\" is not available for contracts containing immutable variables." + ); + if (contractDependenciesAreCyclic(*m_scope)) m_errorReporter.typeError( 4224_error, diff --git a/test/libsolidity/syntaxTests/immutable/creationCode.sol b/test/libsolidity/syntaxTests/immutable/creationCode.sol new file mode 100644 index 000000000..aa747ba3e --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/creationCode.sol @@ -0,0 +1,9 @@ +contract A { + address public immutable user = address(0x0); +} + +contract Test { + function test() public pure returns(bytes memory) { + return type(A).creationCode; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/runtimeCode.sol b/test/libsolidity/syntaxTests/immutable/runtimeCode.sol new file mode 100644 index 000000000..7db3245dc --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/runtimeCode.sol @@ -0,0 +1,11 @@ +contract A { + address public immutable user = address(0x0); +} + +contract Test { + function test() public pure returns(bytes memory) { + return type(A).runtimeCode; + } +} +// ---- +// TypeError: (153-172): "runtimeCode" is not available for contracts containing immutable variables. diff --git a/test/libsolidity/syntaxTests/immutable/runtimeCodeInheritance.sol b/test/libsolidity/syntaxTests/immutable/runtimeCodeInheritance.sol new file mode 100644 index 000000000..aec4acd2e --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/runtimeCodeInheritance.sol @@ -0,0 +1,13 @@ +contract Base { + address public immutable user = address(0x0); +} + +contract Derived is Base {} + +contract Test { + function test() public pure returns(bytes memory) { + return type(Derived).runtimeCode; + } +} +// ---- +// TypeError: (185-210): "runtimeCode" is not available for contracts containing immutable variables. From eb4926cb48be1a2b13d2822f2f6baa0c1b6a46ff Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 5 May 2020 16:53:30 +0200 Subject: [PATCH 24/65] run addMissingFunctions only once --- Changelog.md | 1 + libsolidity/codegen/Compiler.cpp | 3 ++ libsolidity/codegen/CompilerContext.cpp | 3 ++ libsolidity/codegen/CompilerContext.h | 3 ++ libsolidity/codegen/ContractCompiler.cpp | 5 ++-- .../missing_functions_duplicate_bug.sol | 28 +++++++++++++++++++ 6 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol diff --git a/Changelog.md b/Changelog.md index d17bbadf8..086e80ee9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Compiler Features: Bugfixes: * ABI: Skip ``private`` or ``internal`` constructors. * Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables. + * Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice. diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 78198fff0..f343f2377 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -49,6 +49,9 @@ void Compiler::compileContract( m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); m_context.optimise(m_optimiserSettings); + + solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called."); + solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called."); } std::shared_ptr Compiler::runtimeAssemblyPtr() const diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 2d81e833b..9431ecec6 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions() pair> CompilerContext::requestedYulFunctions() { + solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once."); + m_requestedYulFunctionsRan = true; + set empty; swap(empty, m_externallyUsedYulFunctions); return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)}; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 0720a8f3b..51d3afe1c 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -167,6 +167,7 @@ public: /// Clears the internal list, i.e. calling it again will result in an /// empty return value. std::pair> requestedYulFunctions(); + bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; } /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; @@ -389,6 +390,8 @@ private: YulUtilFunctions m_yulUtilFunctions; /// The queue of low-level functions to generate. std::queue>> m_lowLevelFunctionGenerationQueue; + /// Flag to check that requestedYulFunctions() was called exactly once + bool m_requestedYulFunctionsRan = false; }; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d095c1eac..319d7a7ad 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -103,8 +103,6 @@ void ContractCompiler::compileContract( // and adds the function to the compilation queue. Additionally internal functions, // which are referenced directly or indirectly will be added. appendFunctionSelector(_contract); - // This processes the above populated queue until it is empty. - appendMissingFunctions(); } size_t ContractCompiler::compileConstructor( @@ -215,6 +213,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract) solAssert(!!m_runtimeCompiler, ""); solAssert(_contract.isLibrary(), "Tried to deploy contract as library."); + appendMissingFunctions(); + m_runtimeCompiler->appendMissingFunctions(); + CompilerContext::LocationSetter locationSetter(m_context, _contract); solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); diff --git a/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol b/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol new file mode 100644 index 000000000..c233cd69c --- /dev/null +++ b/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol @@ -0,0 +1,28 @@ +pragma solidity ^0.6.0; + +pragma experimental ABIEncoderV2; + +contract Ownable { + address private _owner; + + modifier onlyOwner() { + require(msg.sender == _owner, "Ownable: caller is not the owner"); + _; + } + + function renounceOwnership() public onlyOwner { } +} + +library VoteTiming { + function init(uint phaseLength) internal pure { + require(true, ""); + } +} + +contract Voting is Ownable { + constructor() public { + VoteTiming.init(1); + } +} +// ---- +// Warning: (324-340): Unused function parameter. Remove or comment out the variable name to silence this warning. From 3e9fa7e388628400ec6a0e2c71bc05db93bf3a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 7 May 2020 17:16:50 +0200 Subject: [PATCH 25/65] yulopti: Print errors to stderr rather than stdout --- test/tools/yulopti.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index de074e642..c61b24c3e 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -61,7 +61,7 @@ class YulOpti public: void printErrors() { - SourceReferenceFormatter formatter(cout); + SourceReferenceFormatter formatter(cerr); for (auto const& error: m_errors) formatter.printErrorInformation(*error); @@ -74,7 +74,7 @@ public: m_ast = yul::Parser(errorReporter, m_dialect).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { - cout << "Error parsing source." << endl; + cerr << "Error parsing source." << endl; printErrors(); return false; } @@ -86,7 +86,7 @@ public: ); if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) { - cout << "Error analyzing source." << endl; + cerr << "Error analyzing source." << endl; printErrors(); return false; } @@ -182,7 +182,7 @@ public: break; } default: - cout << "Unknown option." << endl; + cerr << "Unknown option." << endl; } source = AsmPrinter{m_dialect}(*m_ast); } From d3d152087086d4d9a29668f646d9efede23b2328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 7 May 2020 17:19:37 +0200 Subject: [PATCH 26/65] yulopti: Add an error message to assetion triggered when an optimizer step abbreviation conflicts with yulopti controls --- test/tools/yulopti.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index c61b24c3e..b6fd3e2ad 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -119,7 +119,15 @@ public: for (auto const& optionAndDescription: _extraOptions) { - yulAssert(_optimizationSteps.count(optionAndDescription.first) == 0, ""); + yulAssert( + _optimizationSteps.count(optionAndDescription.first) == 0, + "ERROR: Conflict between yulopti controls and Yul optimizer step abbreviations.\n" + "Character '" + string(1, optionAndDescription.first) + "' is assigned to both " + + optionAndDescription.second + " and " + _optimizationSteps.at(optionAndDescription.first) + " step.\n" + "This is most likely caused by someone adding a new step abbreviation to " + "OptimiserSuite::stepNameToAbbreviationMap() and not realizing that it's used by yulopti.\n" + "Please update the code to use a different character and recompile yulopti." + ); printPair(optionAndDescription); } From 156bfadad3747235f000363dbb3821e30b4d2021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 7 May 2020 17:27:24 +0200 Subject: [PATCH 27/65] yulopti: Fix conflict between 'l' in yulopti and OptimiserSuite and make future conflicts less likely - OptimizerSuite uses letters so switching to punctuation marks in yulopti should help. --- test/tools/yulopti.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index b6fd3e2ad..9f5d56085 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -154,9 +154,9 @@ public: } map const& abbreviationMap = OptimiserSuite::stepAbbreviationToNameMap(); map const& extraOptions = { - {'q', "quit"}, - {'l', "VarNameCleaner"}, - {'p', "StackCompressor"}, + {'#', "quit"}, + {',', "VarNameCleaner"}, + {';', "StackCompressor"}, }; printUsageBanner(abbreviationMap, extraOptions, 4); @@ -175,14 +175,14 @@ public: } else switch (option) { - case 'q': + case '#': return; - case 'l': + case ',': VarNameCleaner::run(context, *m_ast); // VarNameCleaner destroys the unique names guarantee of the disambiguator. disambiguated = false; break; - case 'p': + case ';': { Object obj; obj.code = m_ast; From f38cf85482b9a5bd8aedc5b92ed55f129302e7f6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2020 17:29:42 +0200 Subject: [PATCH 28/65] Use vector of slots. --- libsolidity/codegen/ir/IRGenerator.cpp | 54 +++++++++---------- libsolidity/codegen/ir/IRGenerator.h | 5 +- .../codegen/ir/IRGeneratorForStatements.cpp | 44 +++++++-------- libsolidity/codegen/ir/IRVariable.h | 3 +- .../standard_ir_requested/output.json | 2 +- .../yul_string_format_ascii/output.json | 2 +- .../output.json | 2 +- .../output.json | 2 +- .../yul_string_format_ascii_long/output.json | 2 +- .../yul_string_format_hex/output.json | 2 +- .../constructor_arguments_internal.sol | 2 + 11 files changed, 57 insertions(+), 63 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bfd8ce2d1..3211fb4df 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -169,24 +169,24 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) string functionName = m_context.functionName(_function); return m_context.functionCollector().createFunction(functionName, [&]() { Whiskers t(R"( - function () { + function () -> { } )"); t("functionName", functionName); - string params; + vector params; for (auto const& varDecl: _function.parameters()) - params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); - t("params", params); - string retParams; + params += m_context.addLocalVariable(*varDecl).stackSlots(); + t("params", joinHumanReadable(params)); + vector retParams; string retInit; for (auto const& varDecl: _function.returnParameters()) { - retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); + retParams += m_context.addLocalVariable(*varDecl).stackSlots(); retInit += generateInitialAssignment(*varDecl); } - t("returns", retParams.empty() ? "" : " -> " + retParams); + t("retParams", joinHumanReadable(retParams)); t("initReturnVariables", retInit); t("body", generate(_function.body())); return t.render(); @@ -300,17 +300,17 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec return generator.code(); } -pair> IRGenerator::evaluateConstructorArguments( +pair>> IRGenerator::evaluateConstructorArguments( ContractDefinition const& _contract ) { - map constructorParams; + map> constructorParams; vector>const *>> baseConstructorArguments; for (ASTPointer const& base: _contract.baseContracts()) if (FunctionDefinition const* baseConstructor = dynamic_cast( base->name().annotation().referencedDeclaration - )->constructor(); baseConstructor && base->arguments()) + )->constructor(); baseConstructor && base->arguments()) baseConstructorArguments.emplace_back( dynamic_cast(baseConstructor->scope()), base->arguments() @@ -338,11 +338,11 @@ pair> IRGenerator::evaluateConstr { vector params; for (size_t i = 0; i < arguments->size(); ++i) - params.emplace_back(generator.evaluateExpression( - *(arguments->at(i)), - *(baseContract->constructor()->parameters()[i]->type()) - ).commaSeparatedList()); - constructorParams[baseContract] = joinHumanReadable(params); + params += generator.evaluateExpression( + *(arguments->at(i)), + *(baseContract->constructor()->parameters()[i]->type()) + ).stackSlots(); + constructorParams[baseContract] = std::move(params); } } @@ -363,16 +363,16 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract) void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract) { auto listAllParams = [&]( - map const& baseParams) -> string + map> const& baseParams) -> vector { vector params; for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) if (baseParams.count(contract)) - params.emplace_back(baseParams.at(contract)); - return joinHumanReadable(params); + params += baseParams.at(contract); + return params; }; - map baseConstructorParams; + map> baseConstructorParams; for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i) { ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i]; @@ -387,16 +387,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra } )"); - string params; + vector params; if (contract->constructor()) for (ASTPointer const& varDecl: contract->constructor()->parameters()) - params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); - t("params", params); - string baseParamsString = listAllParams(baseConstructorParams); - t("baseParams", baseParamsString); - t("comma", !params.empty() && !baseParamsString.empty() ? ", " : ""); + params += m_context.addLocalVariable(*varDecl).stackSlots(); + t("params", joinHumanReadable(params)); + vector baseParams = listAllParams(baseConstructorParams); + t("baseParams", joinHumanReadable(baseParams)); + t("comma", !params.empty() && !baseParams.empty() ? ", " : ""); t("functionName", implicitConstructorName(*contract)); - pair> evaluatedArgs = evaluateConstructorArguments(*contract); + pair>> evaluatedArgs = evaluateConstructorArguments(*contract); baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end()); t("evalBaseArguments", evaluatedArgs.first); if (i < _contract.annotation().linearizedBaseContracts.size() - 1) @@ -404,7 +404,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra t("hasNextConstructor", true); ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1]; t("nextConstructor", implicitConstructorName(*nextContract)); - t("nextParams", listAllParams(baseConstructorParams)); + t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams))); } else t("hasNextConstructor", false); diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index a4298a902..642cfcdfb 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -81,9 +81,8 @@ private: /// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of /// @a _contract /// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params - std::pair> evaluateConstructorArguments( - ContractDefinition const& _contract - ); + std::pair>> + evaluateConstructorArguments(ContractDefinition const& _contract); /// Initializes state variables of /// @a _contract diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 10761fd35..7415a0977 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -637,13 +637,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; case FunctionType::Kind::Internal: { - vector args; - for (size_t i = 0; i < arguments.size(); ++i) - if (functionType->takesArbitraryParameters()) - args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList()); - else - args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList()); - optional functionDef; if (auto memberAccess = dynamic_cast(&_functionCall.expression())) { @@ -681,6 +674,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(functionDef.has_value(), ""); solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), ""); + vector args; + for (size_t i = 0; i < arguments.size(); ++i) + if (functionType->takesArbitraryParameters()) + args += IRVariable(*arguments[i]).stackSlots(); + else + args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); + if (functionDef.value() != nullptr) define(_functionCall) << m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) << @@ -716,7 +716,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); vector indexedArgs; - string nonIndexedArgs; + vector nonIndexedArgs; TypePointers nonIndexedArgTypes; TypePointers nonIndexedParamTypes; if (!event.isAnonymous()) @@ -739,9 +739,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } else { - string vars = IRVariable(arg).commaSeparatedList(); - if (!vars.empty()) - nonIndexedArgs += ", " + move(vars); + nonIndexedArgs += IRVariable(arg).stackSlots(); nonIndexedArgTypes.push_back(arg.annotation().type); nonIndexedParamTypes.push_back(paramTypes[i]); } @@ -756,7 +754,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) templ("end", m_context.newYulVariable()); templ("freeMemory", freeMemory()); templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes)); - templ("nonIndexedArgs", nonIndexedArgs); + templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs)); templ("log", "log" + to_string(indexedArgs.size())); templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) { return _arg.commaSeparatedList(); @@ -813,8 +811,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); templ( "argumentVars", - (type(*arguments.front()).sizeOnStack() > 0 ? ", " : "") + - IRVariable{*arguments.front()}.commaSeparatedList() + joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots()) ); templ("encode", m_context.abiFunctions().tupleEncoder( {&type(*arguments.front())}, @@ -991,11 +988,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) ); TypePointers argumentTypes; - string constructorParams; + vector constructorParams; for (ASTPointer const& arg: arguments) { argumentTypes.push_back(arg->annotation().type); - constructorParams += ", " + IRVariable{*arg}.commaSeparatedList(); + constructorParams += IRVariable{*arg}.stackSlots(); } ContractDefinition const* contract = @@ -1023,7 +1020,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) t("abiEncode", m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) ); - t("constructorParams", constructorParams); + t("constructorParams", joinHumanReadablePrefixed(constructorParams)); t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); t("saltSet", functionType->saltSet()); if (functionType->saltSet()) @@ -1398,14 +1395,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) solAssert(keyType.sizeOnStack() <= 1, ""); string slot = m_context.newYulVariable(); - Whiskers templ("let := ( )\n"); + Whiskers templ("let := (,)\n"); templ("slot", slot); templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType)); templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList()); - if (keyType.sizeOnStack() == 0) - templ("key", ""); - else - templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList()); + templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList()); m_code << templ.render(); setLValue(_indexAccess, IRLValue{ *_indexAccess.annotation().type, @@ -1678,10 +1672,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall( for (auto const& arg: _arguments) { argumentTypes.emplace_back(&type(*arg)); - if (IRVariable(*arg).type().sizeOnStack() > 0) - argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList()); + argumentStrings += IRVariable(*arg).stackSlots(); } - string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings)); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); @@ -1796,7 +1788,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( } else templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); - templ("argumentString", argumentString); + templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); // Output data will replace input data, unless we have ECRecover (then, output // area will be 32 bytes just before input area). diff --git a/libsolidity/codegen/ir/IRVariable.h b/libsolidity/codegen/ir/IRVariable.h index ba4b1b93a..dbf37704c 100644 --- a/libsolidity/codegen/ir/IRVariable.h +++ b/libsolidity/codegen/ir/IRVariable.h @@ -69,10 +69,11 @@ public: /// The returned IRVariable is itself typed with the type of the stack slot as defined /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. IRVariable part(std::string const& _slot) const; -private: + /// @returns a vector containing the names of the stack slots of the variable. std::vector stackSlots() const; +private: /// @returns a name consisting of the base name appended with an underscore and @æ _suffix, /// unless @a _suffix is empty, in which case the base name itself is returned. std::string suffixedName(std::string const& _suffix) const; diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 69b95a683..f63075090 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -65,7 +65,7 @@ object \"C_6\" { mstore(64, newFreePtr) } - function fun_f_5() { + function fun_f_5() { } diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 83511d5d6..0609704c5 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -107,7 +107,7 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4_mpos { + function fun_f_9() -> vloc__4_mpos { let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 69c2091dc..1bc00b338 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -79,7 +79,7 @@ object \"C_10\" { converted := 0x6162636162630000000000000000000000000000000000000000000000000000 } - function fun_f_9() -> vloc__4 { + function fun_f_9() -> vloc__4 { let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() vloc__4 := zero_value_for_type_t_bytes32_1 diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 8742ef645..fdef77781 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -83,7 +83,7 @@ object \"C_10\" { converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) } - function fun_f_9() -> vloc__4 { + function fun_f_9() -> vloc__4 { let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() vloc__4 := zero_value_for_type_t_bytes4_1 diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 4e6b565d0..2a845796d 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -111,7 +111,7 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4_mpos { + function fun_f_9() -> vloc__4_mpos { let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index f98417556..784baf2f4 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -83,7 +83,7 @@ object \"C_10\" { converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) } - function fun_f_9() -> vloc__4 { + function fun_f_9() -> vloc__4 { let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() vloc__4 := zero_value_for_type_t_bytes4_1 diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol index 756478e34..b29498083 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol @@ -33,6 +33,8 @@ contract Main { } } +// ==== +// compileViaYul: also // ---- // getFlag() -> true // getName() -> "abc" From 75ca072ae6861ce7446a502ef984672f8a040c16 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2020 18:18:40 +0200 Subject: [PATCH 29/65] Assertion about getters for non-value type immutables. --- libsolidity/codegen/ir/IRGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bfd8ce2d1..e5d14d57e 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -203,7 +203,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) if (auto const* mappingType = dynamic_cast(type)) return m_context.functionCollector().createFunction(functionName, [&]() { - solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), ""); pair slot_offset = m_context.storageLocationOfVariable(_varDecl); solAssert(slot_offset.second == 0, ""); FunctionType funType(_varDecl); From a0e291bd06c7db80dd29d18b01319caf8f3e66d2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 29 Apr 2020 11:29:49 +0200 Subject: [PATCH 30/65] Implement Yul IR generation for abi.encode* --- .../codegen/ir/IRGeneratorForStatements.cpp | 89 +++++++++++++++++++ libsolidity/codegen/ir/IRVariable.cpp | 2 +- .../semanticTests/abiEncoderV1/abi_encode.sol | 2 + .../abiEncoderV1/abi_encode_rational.sol | 2 + .../abi_encode_empty_string_v2.sol | 2 + .../abiEncoderV2/abi_encode_rational_v2.sol | 2 + .../abiEncoderV2/calldata_array_dynamic.sol | 1 + .../calldata_array_dynamic_index_access.sol | 1 + .../calldata_array_multi_dynamic.sol | 1 + .../abiEncoderV2/calldata_array_static.sol | 1 + .../calldata_array_static_index_access.sol | 1 + .../calldata_array_struct_dynamic.sol | 1 + .../calldata_array_two_dynamic.sol | 1 + .../calldata_array_two_static.sol | 1 + .../abiEncoderV2/calldata_struct_dynamic.sol | 1 + .../abiEncoderV2/calldata_struct_simple.sol | 1 + .../semanticTests/array/reusing_memory.sol | 2 + .../keccak256_multiple_arguments.sol | 2 + ...ltiple_arguments_with_numeric_literals.sol | 2 + ...ultiple_arguments_with_string_literals.sol | 2 + .../tryCatch/invalid_error_encoding.sol | 1 + 21 files changed, 117 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 7415a0977..05cfcc2db 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -781,6 +781,94 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked; + solAssert(functionType->padArguments() != isPacked, ""); + bool const hasSelectorOrSignature = + functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || + functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature; + + TypePointers argumentTypes; + TypePointers targetTypes; + vector argumentVars; + for (size_t i = 0; i < arguments.size(); ++i) + { + // ignore selector + if (hasSelectorOrSignature && i == 0) + continue; + argumentTypes.emplace_back(&type(*arguments[i])); + targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked)); + argumentVars += IRVariable(*arguments[i]).stackSlots(); + } + + string selector; + if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature) + { + // hash the signature + Type const& selectorType = type(*arguments.front()); + if (auto const* stringType = dynamic_cast(&selectorType)) + { + FixedHash<4> hash(keccak256(stringType->value())); + selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + } + else + { + // Used to reset the free memory pointer later. + string freeMemoryPre = m_context.newYulVariable(); + m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n"; + IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory()); + IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32)); + + define(hashVariable) << + "keccak256(" << + m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) << + "(" << + array.commaSeparatedList() << + "), " << + m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) << + "(" << + array.commaSeparatedList() << + "))\n"; + IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4)); + define(selectorVariable, hashVariable); + m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n"; + } + } + else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector) + selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name(); + + Whiskers templ(R"( + let := () + let := add(, 0x20) + + mstore(, ) + := add(, 4) + + let := () + mstore(, sub(, add(, 0x20))) + mstore(, ()) + )"); + templ("data", IRVariable(_functionCall).part("mpos").name()); + templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); + templ("mpos", m_context.newYulVariable()); + templ("mend", m_context.newYulVariable()); + templ("selector", selector); + templ("encode", + isPacked ? + m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) : + m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false) + ); + templ("arguments", joinHumanReadablePrefixed(argumentVars)); + templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer)); + templ("roundUp", m_utils.roundUpFunction()); + + m_code << templ.render(); + break; + } case FunctionType::Kind::Revert: { solAssert(arguments.size() == parameterTypes.size(), ""); @@ -843,6 +931,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(arguments.size() == 1, ""); ArrayType const* arrayType = TypeProvider::bytesMemory(); + auto array = convert(*arguments[0], *arrayType); define(_functionCall) << diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index dac1087f0..d92a5fb53 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -89,7 +89,7 @@ string IRVariable::name() const { solAssert(m_type.sizeOnStack() == 1, ""); auto const& [itemName, type] = m_type.stackItems().front(); - solAssert(!type, ""); + solAssert(!type, "Expected null type for name " + itemName); return suffixedName(itemName); } diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol index ac17cf80e..61c6658ac 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol @@ -28,6 +28,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f0() -> 0x20, 0x0 // f1() -> 0x20, 0x40, 0x1, 0x2 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol index 704fd54dc..133645cff 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol @@ -5,5 +5,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol index 373334ee7..405d34c5a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol @@ -9,5 +9,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol index 55047880a..1074a1d04 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol @@ -8,5 +8,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol index b2076b370..c47af87f4 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[]): 32, 3, 23, 42, 87 -> 32, 160, 32, 3, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol index f2224c803..92067f8db 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[]): 32, 3, 42, 23, 87 -> 32, 160, 32, 3, 42, 23, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol index f981a6e46..f50f59d1f 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol @@ -21,6 +21,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 13, 17, 23, 4, 27, 31, 37, 41 -> 32, 416, 32, 2, 64, 192, 3, 13, 17, 23, 4, 27, 31, 37, 41 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol index 9c6386e31..3a6fd9151 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol @@ -15,6 +15,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol index 4bf181db3..d23dfecfb 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol @@ -15,6 +15,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3]): 23, 42, 87 -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol index 02322d504..ce2f7a390 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol @@ -10,6 +10,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256[])[]): 32, 1, 32, 32, 3, 17, 42, 23 -> 32, 256, 32, 1, 32, 32, 3, 17, 42, 23 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol index 16ba40630..51674081e 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[],uint256[],bool): 0x60, 0xE0, true, 3, 23, 42, 87, 2, 51, 72 -> 32, 160, 0x20, 3, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol index a4d1af66d..45a89c483 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[3],uint256[2],bool): 23, 42, 87, 51, 72, true -> 32, 96, 23, 42, 87 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol index 29ff15692..0ae57dbed 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256[])): 0x20, 0x20, 3, 42, 23, 17 -> 32, 192, 0x20, 0x20, 3, 42, 23, 17 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol index f369321c3..6d747fdf0 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f((uint256)): 3 -> 32, 32, 3 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index b2876eeb3..994a31db9 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -22,5 +22,7 @@ contract Main { return map[a]; } } +// ==== +// compileViaYul: also // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol index 972aee839..4a31c6715 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol @@ -3,5 +3,7 @@ contract c { d = keccak256(abi.encodePacked(a, b, c)); } } +// ==== +// compileViaYul: also // ---- // foo(uint256,uint256,uint256): 0xa, 0xc, 0xd -> 0xbc740a98aae5923e8f04c9aa798c9ee82f69e319997699f2782c40828db9fd81 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol index 01397f55f..958975966 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol @@ -3,5 +3,7 @@ contract c { d = keccak256(abi.encodePacked(a, b, uint8(145))); } } +// ==== +// compileViaYul: also // ---- // foo(uint256,uint16): 0xa, 0xc -> 0x88acd45f75907e7c560318bc1a5249850a0999c4896717b1167d05d116e6dbad diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol index b157178fb..c4e0b7d09 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol @@ -7,6 +7,8 @@ contract c { d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); } } +// ==== +// compileViaYul: also // ---- // foo() -> 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d // bar(uint256,uint16): 0xa, 0xc -> 0x6990f36476dc412b1c4baa48e2d9f4aa4bb313f61fda367c8fdbbb2232dc6146 diff --git a/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol b/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol index 2bfc23191..1b89213cf 100644 --- a/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol +++ b/test/libsolidity/semanticTests/tryCatch/invalid_error_encoding.sol @@ -149,6 +149,7 @@ contract C { } // ==== // EVMVersion: >=byzantium +// compileViaYul: also // ---- // f1() -> 2 // f1a() -> 2 From 704b334cf69139ba067e54c62a6e45da212a3d18 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Wed, 6 May 2020 19:27:22 +0530 Subject: [PATCH 31/65] Updated external test repos --- test/externalTests/colony.sh | 4 ++-- test/externalTests/gnosis.sh | 5 ++--- test/externalTests/zeppelin.sh | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/externalTests/colony.sh b/test/externalTests/colony.sh index 66231eb99..559703ae7 100755 --- a/test/externalTests/colony.sh +++ b/test/externalTests/colony.sh @@ -34,12 +34,12 @@ function colony_test FORCE_ABIv2=false CONFIG="truffle.js" - truffle_setup https://github.com/erak/colonyNetwork.git develop_060 + truffle_setup https://github.com/solidity-external-tests/colonyNetwork.git develop_060 run_install install_fn cd lib rm -Rf dappsys - git clone https://github.com/erak/dappsys-monolithic.git -b master_060 dappsys + git clone https://github.com/solidity-external-tests/dappsys-monolithic.git -b master_060 dappsys cd .. truffle_run_test compile_fn test_fn diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index b2bb657cb..b78877211 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -33,10 +33,10 @@ function gnosis_safe_test OPTIMIZER_LEVEL=1 CONFIG="truffle.js" - truffle_setup https://github.com/erak/safe-contracts.git development_060 + truffle_setup https://github.com/solidity-external-tests/safe-contracts.git development_060 force_truffle_version - sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:erak/mock-contract#master_060|g' package.json + sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_060|g' package.json run_install install_fn replace_libsolc_call @@ -45,4 +45,3 @@ function gnosis_safe_test } external_test Gnosis-Safe gnosis_safe_test - diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 49b6c3e42..7c82cd281 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -33,7 +33,7 @@ function zeppelin_test OPTIMIZER_LEVEL=1 CONFIG="truffle-config.js" - truffle_setup https://github.com/erak/openzeppelin-contracts.git master_060 + truffle_setup https://github.com/OpenZeppelin/openzeppelin-contracts.git master run_install install_fn truffle_run_test compile_fn test_fn From f56fd5bde8c5b0e3ed7d2d13ce2e6ccac5064da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 May 2020 19:40:54 +0200 Subject: [PATCH 32/65] cmdlineTests.sh: Allow using input.yul instead of input.sol in command-line tests --- test/cmdlineTests.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 077aa5c19..07e249492 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -245,7 +245,18 @@ printTask "Running general commandline tests..." stdoutExpectationFile="${tdir}/output.json" args="--standard-json "$(cat ${tdir}/args 2>/dev/null || true) else - inputFile="${tdir}input.sol" + if [[ -e "${tdir}input.yul" && -e "${tdir}input.sol" ]] + then + printError "Ambiguous input. Found both input.sol and input.yul." + exit 1 + fi + + if [ -e "${tdir}input.yul" ] + then + inputFile="${tdir}input.yul" + else + inputFile="${tdir}input.sol" + fi stdin="" stdout="$(cat ${tdir}/output 2>/dev/null || true)" stdoutExpectationFile="${tdir}/output" From d932c58b56cb03a4427f89c1236ee39ff7cebc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 May 2020 15:54:17 +0200 Subject: [PATCH 33/65] CommandLineInterface: Replace hard-coded option names with constants where available --- solc/CommandLineInterface.cpp | 50 ++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 537b2096b..0166a5df6 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -699,7 +699,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da string pathName = (p / _fileName).string(); if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) { - serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl; + serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl; m_error = true; return; } @@ -719,10 +719,10 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv) g_hasOutput = false; // Declare the supported options. - po::options_description desc(R"(solc, the Solidity commandline compiler. + po::options_description desc((R"(solc, the Solidity commandline compiler. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you -are welcome to redistribute it under certain conditions. See 'solc --license' +are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"(' for details. Usage: solc [options] [input_file...] @@ -732,9 +732,9 @@ at standard output or in files in the output directory, if specified. Imports are automatically read from the filesystem, but it is also possible to remap paths using the context:prefix=path syntax. Example: -solc --bin -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol +solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol -Allowed options)", +Allowed options)").c_str(), po::options_description::m_default_line_length, po::options_description::m_default_line_length - 23 ); @@ -780,20 +780,27 @@ Allowed options)", ) ( 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 --standard-json or the one produced by --combined-json ast,compact-format" + ("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_argAssemble.c_str(), - "Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly." + ("Switch to assembly mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "and assumes input is assembly.").c_str() ) ( g_argYul.c_str(), - "Switch to Yul mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is Yul." + ("Switch to Yul mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "and assumes input is Yul.").c_str() ) ( g_argStrictAssembly.c_str(), - "Switch to strict assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is strict assembly." + ("Switch to strict assembly mode, ignoring all options except " + "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "and assumes input is strict assembly.").c_str() ) ( g_strYulDialect.c_str(), @@ -807,8 +814,8 @@ Allowed options)", ) ( g_argLink.c_str(), - "Switch to linker mode, ignoring all options apart from --libraries " - "and modify binaries in place." + ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " " + "and modify binaries in place.").c_str() ) ( g_argMetadataHash.c_str(), @@ -835,7 +842,7 @@ Allowed options)", "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 --optimize to enable Yul optimizer.") + (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(), @@ -933,7 +940,7 @@ Allowed options)", for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as(), boost::is_any_of(","))) if (!g_combinedJsonArgs.count(item)) { - serr() << "Invalid option to --combined-json: " << item << endl; + serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl; return false; } } @@ -1047,7 +1054,7 @@ bool CommandLineInterface::processInput() std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); if (!versionOption) { - serr() << "Invalid option for --evm-version: " << versionOptionStr << endl; + serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl; return false; } m_evmVersion = *versionOption; @@ -1064,12 +1071,12 @@ bool CommandLineInterface::processInput() bool optimize = m_args.count(g_argOptimize); if (m_args.count(g_strOptimizeYul)) { - serr() << "--optimize-yul is invalid in assembly mode. Use --optimize instead." << endl; + serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl; return false; } if (m_args.count(g_strNoOptimizeYul)) { - serr() << "--no-optimize-yul is invalid in assembly mode. Optimization is disabled by default and can be enabled with --optimize." << endl; + serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; return false; } if (m_args.count(g_argMachine)) @@ -1083,7 +1090,7 @@ bool CommandLineInterface::processInput() targetMachine = Machine::Ewasm; else { - serr() << "Invalid option for --machine: " << machine << endl; + serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl; return false; } } @@ -1099,13 +1106,14 @@ bool CommandLineInterface::processInput() inputLanguage = Input::Ewasm; if (targetMachine != Machine::Ewasm) { - serr() << "If you select Ewasm as --yul-dialect, --machine has to be Ewasm as well." << endl; + serr() << "If you select Ewasm as --" << g_strYulDialect << ", "; + serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl; return false; } } else { - serr() << "Invalid option for --yul-dialect: " << dialect << endl; + serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl; return false; } } @@ -1142,7 +1150,7 @@ bool CommandLineInterface::processInput() m_metadataHash = CompilerStack::MetadataHash::None; else { - serr() << "Invalid option for --metadata-hash: " << hashStr << endl; + serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl; return false; } } From 5d4b9022f0795763bf4daa1df200de550024cbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 May 2020 22:03:32 +0200 Subject: [PATCH 34/65] yul-phaser README: --no-optimize-yul options is not really necessary --- tools/yulPhaser/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/yulPhaser/README.md b/tools/yulPhaser/README.md index abc2a0e47..45a7ec0ec 100644 --- a/tools/yulPhaser/README.md +++ b/tools/yulPhaser/README.md @@ -66,14 +66,11 @@ tools/yul-phaser *.yul \ `yul-phaser` can process the intermediate representation produced by `solc`: ``` bash -solc/solc \ - --ir \ - --no-optimize-yul \ - --output-dir +solc/solc --ir --output-dir ``` After running this command you'll find one or more .yul files in the output directory. -These files contain whole Yul objects rather than just raw Yul programs but `yul-phaser` is prepared to handle them. +These files contain whole Yul objects rather than just raw Yul programs but `yul-phaser` is prepared to handle them too. ### How to choose good parameters Choosing good parameters for a genetic algorithm is not a trivial task but phaser's defaults are generally enough to find a sequence that gives results comparable or better than one hand-crafted by an experienced developer for a given set of programs. From 6a582278309a963767ed31fea5ac8e6de108aa3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 May 2020 18:20:14 +0200 Subject: [PATCH 35/65] CommandLineInterface: Make --yul-optimizations work in strict assembly mode --- Changelog.md | 1 + docs/yul.rst | 3 +- solc/CommandLineInterface.cpp | 46 +++++++--- solc/CommandLineInterface.h | 7 +- .../standard_yul_optimiserSteps/input.json | 26 ++++++ .../standard_yul_optimiserSteps/output.json | 30 +++++++ .../strict_asm_optimizer_steps/args | 1 + .../strict_asm_optimizer_steps/err | 1 + .../strict_asm_optimizer_steps/input.yul | 27 ++++++ .../strict_asm_optimizer_steps/output | 88 +++++++++++++++++++ 10 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 test/cmdlineTests/standard_yul_optimiserSteps/input.json create mode 100644 test/cmdlineTests/standard_yul_optimiserSteps/output.json create mode 100644 test/cmdlineTests/strict_asm_optimizer_steps/args create mode 100644 test/cmdlineTests/strict_asm_optimizer_steps/err create mode 100644 test/cmdlineTests/strict_asm_optimizer_steps/input.yul create mode 100644 test/cmdlineTests/strict_asm_optimizer_steps/output diff --git a/Changelog.md b/Changelog.md index 086e80ee9..714f9b526 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: + * Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode. diff --git a/docs/yul.rst b/docs/yul.rst index c200558b0..3c7e0ba4d 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1041,8 +1041,7 @@ Optimization step sequence -------------------------- By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly. -You can override this sequence and supply your own using the `--yul-optimizations` option when compiling -in Solidity mode: +You can override this sequence and supply your own using the `--yul-optimizations` option: .. code-block:: sh diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 0166a5df6..1342ef1f6 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -787,19 +787,19 @@ Allowed options)").c_str(), ( g_argAssemble.c_str(), ("Switch to assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is assembly.").c_str() ) ( g_argYul.c_str(), ("Switch to Yul mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is Yul.").c_str() ) ( g_argStrictAssembly.c_str(), ("Switch to strict assembly mode, ignoring all options except " - "--" + g_argMachine + ", --" + g_strYulDialect + " and --" + g_argOptimize + " " + "--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " " "and assumes input is strict assembly.").c_str() ) ( @@ -1079,6 +1079,29 @@ bool CommandLineInterface::processInput() serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl; return false; } + + optional yulOptimiserSteps; + if (m_args.count(g_strYulOptimizations)) + { + if (!optimize) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + yulOptimiserSteps = m_args[g_strYulOptimizations].as(); + } + if (m_args.count(g_argMachine)) { string machine = m_args[g_argMachine].as(); @@ -1130,7 +1153,7 @@ bool CommandLineInterface::processInput() "Warning: Yul is still experimental. Please use the output with care." << endl; - return assemble(inputLanguage, targetMachine, optimize); + return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps); } if (m_args.count(g_argLink)) { @@ -1542,18 +1565,21 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _ bool CommandLineInterface::assemble( yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, - bool _optimize + bool _optimize, + optional _yulOptimiserSteps ) { + solAssert(_optimize || !_yulOptimiserSteps.has_value(), ""); + bool successful = true; map assemblyStacks; for (auto const& src: m_sourceCodes) { - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack( - m_evmVersion, - _language, - _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal() - ); + OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); + if (_yulOptimiserSteps.has_value()) + settings.yulOptimiserSteps = _yulOptimiserSteps.value(); + + auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings); try { if (!stack.parseAndAnalyze(src.first, src.second)) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index f6972cf04..446ea8224 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -56,7 +56,12 @@ private: /// @returns the full object with library placeholder hints in hex. static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); - bool assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize); + bool assemble( + yul::AssemblyStack::Language _language, + yul::AssemblyStack::Machine _targetMachine, + bool _optimize, + std::optional _yulOptimiserSteps = std::nullopt + ); void outputCompilationResults(); diff --git a/test/cmdlineTests/standard_yul_optimiserSteps/input.json b/test/cmdlineTests/standard_yul_optimiserSteps/input.json new file mode 100644 index 000000000..1015496f9 --- /dev/null +++ b/test/cmdlineTests/standard_yul_optimiserSteps/input.json @@ -0,0 +1,26 @@ +{ + "language": "Yul", + "sources": + { + "A": + { + "content": "{ let x := mload(0) sstore(add(x, 0), 0) }" + } + }, + "settings": + { + "optimizer": { + "enabled": true, + "details": { + "yul": true, + "yulDetails": { + "optimizerSteps": "dhfoDgvulfnTUtnIf" + } + } + }, + "outputSelection": + { + "*": { "*": ["*"], "": [ "*" ] } + } + } +} diff --git a/test/cmdlineTests/standard_yul_optimiserSteps/output.json b/test/cmdlineTests/standard_yul_optimiserSteps/output.json new file mode 100644 index 000000000..383ad8557 --- /dev/null +++ b/test/cmdlineTests/standard_yul_optimiserSteps/output.json @@ -0,0 +1,30 @@ +{"contracts":{"A":{"object":{"evm":{"assembly":" /* \"A\":17:18 */ + 0x00 + /* \"A\":11:19 */ + mload + /* \"A\":38:39 */ + 0x00 + /* \"A\":34:35 */ + 0x00 + /* \"A\":31:32 */ + dup3 + /* \"A\":27:36 */ + add + /* \"A\":20:40 */ + sstore + pop +","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"object\" { + code { + let x := mload(0) + sstore(add(x, 0), 0) + } +} +","irOptimized":"object \"object\" { + code { + { + let x := mload(0) + sstore(add(x, 0), 0) + } + } +} +"}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]} diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/args b/test/cmdlineTests/strict_asm_optimizer_steps/args new file mode 100644 index 000000000..ade406ee4 --- /dev/null +++ b/test/cmdlineTests/strict_asm_optimizer_steps/args @@ -0,0 +1 @@ +--strict-assembly --optimize --yul-optimizations dhfoDgvulfnTUtnIf diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/err b/test/cmdlineTests/strict_asm_optimizer_steps/err new file mode 100644 index 000000000..014a1178f --- /dev/null +++ b/test/cmdlineTests/strict_asm_optimizer_steps/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/input.yul b/test/cmdlineTests/strict_asm_optimizer_steps/input.yul new file mode 100644 index 000000000..b1c756df3 --- /dev/null +++ b/test/cmdlineTests/strict_asm_optimizer_steps/input.yul @@ -0,0 +1,27 @@ +object "C_6" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + codecopy(0, dataoffset("C_6_deployed"), datasize("C_6_deployed")) + return(0, datasize("C_6_deployed")) + } + object "C_6_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + pop(selector) + } + pop(iszero(calldatasize())) + revert(0, 0) + } + + function shift_right_224_unsigned(value) -> newValue + { + newValue := shr(224, value) + } + } + } +} diff --git a/test/cmdlineTests/strict_asm_optimizer_steps/output b/test/cmdlineTests/strict_asm_optimizer_steps/output new file mode 100644 index 000000000..369be348c --- /dev/null +++ b/test/cmdlineTests/strict_asm_optimizer_steps/output @@ -0,0 +1,88 @@ + +======= strict_asm_optimizer_steps/input.yul (EVM) ======= + +Pretty printed source: +object "C_6" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + codecopy(0, dataoffset("C_6_deployed"), datasize("C_6_deployed")) + return(0, datasize("C_6_deployed")) + } + } + object "C_6_deployed" { + code { + { + mstore(64, 128) + pop(iszero(lt(calldatasize(), 4))) + revert(0, 0) + } + } + } +} + + +Binary representation: +60806040523415600f5760006000fd5b6010601d60003960106000f3fe608060405260043610155060006000fd + +Text representation: + /* "strict_asm_optimizer_steps/input.yul":45:48 */ + 0x80 + /* "strict_asm_optimizer_steps/input.yul":41:43 */ + 0x40 + /* "strict_asm_optimizer_steps/input.yul":34:49 */ + mstore + /* "strict_asm_optimizer_steps/input.yul":61:72 */ + callvalue + /* "strict_asm_optimizer_steps/input.yul":58:60 */ + iszero + tag_1 + jumpi + /* "strict_asm_optimizer_steps/input.yul":85:86 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":82:83 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":75:87 */ + revert + /* "strict_asm_optimizer_steps/input.yul":58:60 */ +tag_1: + /* "strict_asm_optimizer_steps/input.yul":98:163 */ + dataSize(sub_0) + dataOffset(sub_0) + /* "strict_asm_optimizer_steps/input.yul":107:108 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":98:163 */ + codecopy + /* "strict_asm_optimizer_steps/input.yul":172:207 */ + dataSize(sub_0) + /* "strict_asm_optimizer_steps/input.yul":179:180 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":172:207 */ + return +stop + +sub_0: assembly { + /* "strict_asm_optimizer_steps/input.yul":298:301 */ + 0x80 + /* "strict_asm_optimizer_steps/input.yul":294:296 */ + 0x40 + /* "strict_asm_optimizer_steps/input.yul":287:302 */ + mstore + /* "strict_asm_optimizer_steps/input.yul":348:349 */ + 0x04 + /* "strict_asm_optimizer_steps/input.yul":332:346 */ + calldatasize + /* "strict_asm_optimizer_steps/input.yul":329:350 */ + lt + /* "strict_asm_optimizer_steps/input.yul":322:351 */ + iszero + /* "strict_asm_optimizer_steps/input.yul":319:321 */ + pop + /* "strict_asm_optimizer_steps/input.yul":570:571 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":567:568 */ + 0x00 + /* "strict_asm_optimizer_steps/input.yul":560:572 */ + revert +} From dc5612467f94bfa94b48caf7fc2f09154cdaf7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 4 May 2020 21:25:57 +0200 Subject: [PATCH 36/65] docs/yul: Use backtick syntax correct for RST in the docs for --yul-optimizations --- docs/yul.rst | 62 ++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/yul.rst b/docs/yul.rst index 3c7e0ba4d..f2e09d22e 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1041,13 +1041,13 @@ Optimization step sequence -------------------------- By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly. -You can override this sequence and supply your own using the `--yul-optimizations` option: +You can override this sequence and supply your own using the ``--yul-optimizations`` option: .. code-block:: sh solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' -By enclosing part of the sequence in square brackets (`[]`) you tell the optimizer to repeatedly +By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly apply that part until it no longer improves the size of the resulting assembly. You can use brackets multiple times in a single sequence but they cannot be nested. @@ -1056,37 +1056,37 @@ The following optimization steps are available: ============ =============================== Abbreviation Full name ============ =============================== -f `BlockFlattener` -l `CircularReferencesPruner` -c `CommonSubexpressionEliminator` -C `ConditionalSimplifier` -U `ConditionalUnsimplifier` -n `ControlFlowSimplifier` -D `DeadCodeEliminator` -v `EquivalentFunctionCombiner` -e `ExpressionInliner` -j `ExpressionJoiner` -s `ExpressionSimplifier` -x `ExpressionSplitter` -I `ForLoopConditionIntoBody` -O `ForLoopConditionOutOfBody` -o `ForLoopInitRewriter` -i `FullInliner` -g `FunctionGrouper` -h `FunctionHoister` -T `LiteralRematerialiser` -L `LoadResolver` -M `LoopInvariantCodeMotion` -r `RedundantAssignEliminator` -m `Rematerialiser` -V `SSAReverser` -a `SSATransform` -t `StructuralSimplifier` -u `UnusedPruner` -d `VarDeclInitializer` +``f`` ``BlockFlattener`` +``l`` ``CircularReferencesPruner`` +``c`` ``CommonSubexpressionEliminator`` +``C`` ``ConditionalSimplifier`` +``U`` ``ConditionalUnsimplifier`` +``n`` ``ControlFlowSimplifier`` +``D`` ``DeadCodeEliminator`` +``v`` ``EquivalentFunctionCombiner`` +``e`` ``ExpressionInliner`` +``j`` ``ExpressionJoiner`` +``s`` ``ExpressionSimplifier`` +``x`` ``ExpressionSplitter`` +``I`` ``ForLoopConditionIntoBody`` +``O`` ``ForLoopConditionOutOfBody`` +``o`` ``ForLoopInitRewriter`` +``i`` ``FullInliner`` +``g`` ``FunctionGrouper`` +``h`` ``FunctionHoister`` +``T`` ``LiteralRematerialiser`` +``L`` ``LoadResolver`` +``M`` ``LoopInvariantCodeMotion`` +``r`` ``RedundantAssignEliminator`` +``m`` ``Rematerialiser`` +``V`` ``SSAReverser`` +``a`` ``SSATransform`` +``t`` ``StructuralSimplifier`` +``u`` ``UnusedPruner`` +``d`` ``VarDeclInitializer`` ============ =============================== -Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`. +Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. For this reason the Yul optimizer always applies them before applying any steps supplied by the user. From e8aec6de54bbebdd78c81e6100bc70c3d05ff624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 May 2020 20:59:04 +0200 Subject: [PATCH 37/65] More information about --yul-optimizations option in READMEs and docs --- docs/yul.rst | 3 +++ libyul/optimiser/README.md | 12 ++++++++++++ tools/yulPhaser/README.md | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/yul.rst b/docs/yul.rst index f2e09d22e..2e8bc4a10 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1047,6 +1047,9 @@ You can override this sequence and supply your own using the ``--yul-optimizatio solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' +The order of steps is significant and affects the quality of the output. +Moreover, applying a step may uncover new optimization opportunities for others that were already +applied so repeating steps is often beneficial. By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly apply that part until it no longer improves the size of the resulting assembly. You can use brackets multiple times in a single sequence but they cannot be nested. diff --git a/libyul/optimiser/README.md b/libyul/optimiser/README.md index 58cc006b4..4bd3d6a36 100644 --- a/libyul/optimiser/README.md +++ b/libyul/optimiser/README.md @@ -4,6 +4,7 @@ planned state of the optimiser. Table of Contents: +- [Selecting optimisations](#selecting-optimisations) - [Preprocessing](#preprocessing) - [Pseudo-SSA Transformation](#pseudo-ssa-transformation) - [Tools](#tools) @@ -33,6 +34,17 @@ the following transformation steps are the main components: - [Redundant Assign Eliminator](#redundant-assign-eliminator) - [Full Function Inliner](#full-function-inliner) +## Selecting optimisations + +By default the optimiser applies its predefined sequence of optimisation steps to the generated assembly. +You can override this sequence and supply your own using the `--yul-optimizations` option: + +``` bash +solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' +``` + +There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence). + ## Preprocessing The preprocessing components perform transformations to get the program diff --git a/tools/yulPhaser/README.md b/tools/yulPhaser/README.md index 45a7ec0ec..3bc72b537 100644 --- a/tools/yulPhaser/README.md +++ b/tools/yulPhaser/README.md @@ -9,7 +9,7 @@ The input is a set of one or more [Yul](/docs/yul.rst) programs and each sequenc Optimised programs are given numeric scores according to the selected metric. Optimisation step sequences are presented in an abbreviated form - as strings of letters where each character represents one step. -The abbreviations are defined in [`OptimiserSuite::stepNameToAbbreviationMap()`](/libyul/optimiser/Suite.cpp#L388-L423). +There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence). ### How to use it The application has sensible defaults for most parameters. @@ -72,6 +72,13 @@ solc/solc --ir --output-dir After running this command you'll find one or more .yul files in the output directory. These files contain whole Yul objects rather than just raw Yul programs but `yul-phaser` is prepared to handle them too. +#### Using optimisation step sequences with the compiler +You can tell Yul optimiser to use a specific sequence for your code by passing `--yul-optimizations` option to `solc`: + +``` bash +solc/solc --optimize --ir-optimized --yul-optimizations +``` + ### How to choose good parameters Choosing good parameters for a genetic algorithm is not a trivial task but phaser's defaults are generally enough to find a sequence that gives results comparable or better than one hand-crafted by an experienced developer for a given set of programs. The difficult part is providing a fairly representative set of input files. From 6965f199fd11cada51c5a97ceb72cc5e14534a17 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 9 May 2020 00:24:58 +0200 Subject: [PATCH 38/65] Groundwork. Prepare for automatic tagging of errors in parser-based classes [Not compilable until the next commit] --- liblangutil/ParserBase.cpp | 20 ++++++++++---------- liblangutil/ParserBase.h | 11 ++++++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index c3a15794b..d57db0994 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -143,27 +143,27 @@ void ParserBase::decreaseRecursionDepth() m_recursionDepth--; } -void ParserBase::parserWarning(string const& _description) +void ParserBase::parserWarning(ErrorId _error, string const& _description) { - m_errorReporter.warning(6635_error, currentLocation(), _description); + m_errorReporter.warning(_error, currentLocation(), _description); } -void ParserBase::parserError(SourceLocation const& _location, string const& _description) +void ParserBase::parserError(ErrorId _error, SourceLocation const& _location, string const& _description) { - m_errorReporter.parserError(2314_error, _location, _description); + m_errorReporter.parserError(_error, _location, _description); } -void ParserBase::parserError(string const& _description) +void ParserBase::parserError(ErrorId _error, string const& _description) { - parserError(currentLocation(), _description); + parserError(_error, currentLocation(), _description); } -void ParserBase::fatalParserError(string const& _description) +void ParserBase::fatalParserError(ErrorId _error, string const& _description) { - fatalParserError(currentLocation(), _description); + fatalParserError(_error, currentLocation(), _description); } -void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description) +void ParserBase::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description) { - m_errorReporter.fatalParserError(1957_error, _location, _description); + m_errorReporter.fatalParserError(_error, _location, _description); } diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index 575a049e2..8a8073696 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -32,6 +32,7 @@ namespace solidity::langutil class ErrorReporter; class Scanner; +struct ErrorId; class ParserBase { @@ -88,17 +89,17 @@ protected: /// Creates a @ref ParserError and annotates it with the current position and the /// given @a _description. - void parserError(std::string const& _description); - void parserError(SourceLocation const& _location, std::string const& _description); + void parserError(ErrorId _error, std::string const& _description); + void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description); /// Creates a @ref ParserWarning and annotates it with the current position and the /// given @a _description. - void parserWarning(std::string const& _description); + void parserWarning(ErrorId _error, std::string const& _description); /// Creates a @ref ParserError and annotates it with the current position and the /// given @a _description. Throws the FatalError. - void fatalParserError(std::string const& _description); - void fatalParserError(SourceLocation const& _location, std::string const& _description); + void fatalParserError(ErrorId _error, std::string const& _description); + void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description); std::shared_ptr m_scanner; /// The reference to the list of errors and warning to add errors/warnings during parsing From c3e519a151c0533962d0025c0640c6f4d5eee1e1 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 9 May 2020 01:28:55 +0200 Subject: [PATCH 39/65] Add unique IDs to error reporting calls --- liblangutil/ParserBase.cpp | 14 +++---- libsolidity/parsing/Parser.cpp | 76 ++++++++++++++++++---------------- libyul/AsmParser.cpp | 21 +++++----- libyul/ObjectParser.cpp | 12 +++--- 4 files changed, 65 insertions(+), 58 deletions(-) diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index d57db0994..f0ebefd93 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -77,9 +77,9 @@ void ParserBase::expectToken(Token _value, bool _advance) { string const expectedToken = ParserBase::tokenName(_value); if (m_parserErrorRecovery) - parserError("Expected " + expectedToken + " but got " + tokenName(tok)); + parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok)); else - fatalParserError("Expected " + expectedToken + " but got " + tokenName(tok)); + fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok)); // Do not advance so that recovery can sync or make use of the current token. // This is especially useful if the expected token // is the only one that is missing and is at the end of a construct. @@ -108,21 +108,21 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN // rollback to where the token started, and raise exception to be caught at a higher level. m_scanner->setPosition(startPosition); m_inParserRecovery = true; - fatalParserError(errorLoc, msg); + fatalParserError(1957_error, errorLoc, msg); } else { if (m_inParserRecovery) - parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); + parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); else - parserError(errorLoc, msg + "Recovered at next " + expectedToken); + parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken); m_inParserRecovery = false; } } else if (m_inParserRecovery) { string expectedToken = ParserBase::tokenName(_value); - parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); + parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); m_inParserRecovery = false; } @@ -134,7 +134,7 @@ void ParserBase::increaseRecursionDepth() { m_recursionDepth++; if (m_recursionDepth >= 1200) - fatalParserError("Maximum recursion depth reached during parsing."); + fatalParserError(7319_error, "Maximum recursion depth reached during parsing."); } void ParserBase::decreaseRecursionDepth() diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d24b0e69f..f726e6dc9 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -103,7 +103,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) nodes.push_back(parseEnumDefinition()); break; default: - fatalParserError(string("Expected pragma, import directive or contract/interface/library/struct/enum definition.")); + fatalParserError(7858_error, string("Expected pragma, import directive or contract/interface/library/struct/enum definition.")); } } solAssert(m_recursionDepth == 0, ""); @@ -163,7 +163,7 @@ ASTPointer Parser::parsePragmaDirective() { Token token = m_scanner->currentToken(); if (token == Token::Illegal) - parserError("Token incompatible with Solidity parser as part of pragma directive."); + parserError(6281_error, "Token incompatible with Solidity parser as part of pragma directive."); else { string literal = m_scanner->currentLiteral(); @@ -241,18 +241,18 @@ ASTPointer Parser::parseImportDirective() unitAlias = expectIdentifierToken(); } else - fatalParserError("Expected string literal (path), \"*\" or alias list."); + fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list."); // "from" is not a keyword but parsed as an identifier because of backwards // compatibility and because it is a really common word. if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from") - fatalParserError("Expected \"from\"."); + fatalParserError(8208_error, "Expected \"from\"."); m_scanner->next(); if (m_scanner->currentToken() != Token::StringLiteral) - fatalParserError("Expected import path."); + fatalParserError(6845_error, "Expected import path."); path = getLiteralAndAdvance(); } if (path->empty()) - fatalParserError("Import path cannot be empty."); + fatalParserError(6326_error, "Import path cannot be empty."); nodeFactory.markEndPosition(); expectToken(Token::Semicolon); return nodeFactory.createNode(path, unitAlias, move(symbolAliases)); @@ -279,7 +279,7 @@ std::pair Parser::parseContractKind() kind = ContractKind::Library; break; default: - parserError("Expected keyword \"contract\", \"interface\" or \"library\"."); + parserError(3515_error, "Expected keyword \"contract\", \"interface\" or \"library\"."); return std::make_pair(ContractKind::Contract, abstract); } m_scanner->next(); @@ -344,7 +344,7 @@ ASTPointer Parser::parseContractDefinition() else if (currentTokenValue == Token::Using) subNodes.push_back(parseUsingDirective()); else - fatalParserError(string("Function, variable, struct or modifier declaration expected.")); + fatalParserError(9182_error, string("Function, variable, struct or modifier declaration expected.")); } } catch (FatalError const&) @@ -463,6 +463,7 @@ StateMutability Parser::parseStateMutability() case Token::Constant: stateMutability = StateMutability::View; parserError( + 7698_error, "The state mutability modifier \"constant\" was removed in version 0.5.0. " "Use \"view\" or \"pure\" instead." ); @@ -495,7 +496,8 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari // Detect this and return early. if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal)) break; - parserError(string( + parserError( + 9439_error,string( "Visibility already specified as \"" + Declaration::visibilityToString(result.visibility) + "\"." @@ -509,7 +511,8 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari { if (result.stateMutability != StateMutability::NonPayable) { - parserError(string( + parserError( + 9680_error,string( "State mutability already specified as \"" + stateMutabilityToString(result.stateMutability) + "\"." @@ -522,14 +525,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else if (!_isStateVariable && token == Token::Override) { if (result.overrides) - parserError("Override already specified."); + parserError(1827_error, "Override already specified."); result.overrides = parseOverrideSpecifier(); } else if (!_isStateVariable && token == Token::Virtual) { if (result.isVirtual) - parserError("Virtual already specified."); + parserError(6879_error, "Virtual already specified."); result.isVirtual = true; m_scanner->next(); @@ -577,9 +580,9 @@ ASTPointer Parser::parseFunctionDefinition() "the \"function\" keyword to define it." }; if (m_scanner->currentToken() == Token::Constructor) - parserError(message); + parserError(3323_error, message); else - parserWarning(message); + parserWarning(3445_error, message); m_scanner->next(); } else @@ -660,10 +663,10 @@ ASTPointer Parser::parseEnumDefinition() break; expectToken(Token::Comma); if (m_scanner->currentToken() != Token::Identifier) - fatalParserError(string("Expected identifier after ','")); + fatalParserError(1612_error, string("Expected identifier after ','")); } if (members.empty()) - parserError({"enum with no members is not allowed."}); + parserError(3147_error, {"enum with no members is not allowed."}); nodeFactory.markEndPosition(); expectToken(Token::RBrace); @@ -690,6 +693,7 @@ ASTPointer Parser::parseVariableDeclaration( if (dynamic_cast(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace) fatalParserError( + 2915_error, "Expected a state variable declaration. If you intended this as a fallback function " "or a function to handle plain ether transactions, use the \"fallback\" keyword " "or the \"receive\" keyword instead." @@ -710,7 +714,8 @@ ASTPointer Parser::parseVariableDeclaration( nodeFactory.markEndPosition(); if (visibility != Visibility::Default) { - parserError(string( + parserError( + 4110_error,string( "Visibility already specified as \"" + Declaration::visibilityToString(visibility) + "\"." @@ -723,7 +728,7 @@ ASTPointer Parser::parseVariableDeclaration( else if (_options.isStateVariable && token == Token::Override) { if (overrides) - parserError("Override already specified."); + parserError(9125_error, "Override already specified."); overrides = parseOverrideSpecifier(); } @@ -735,6 +740,7 @@ ASTPointer Parser::parseVariableDeclaration( { if (mutability != VariableDeclaration::Mutability::Mutable) parserError( + 3109_error, string("Mutability already set to ") + (mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"") ); @@ -746,9 +752,9 @@ ASTPointer Parser::parseVariableDeclaration( else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) - parserError(string("Location already specified.")); + parserError(3548_error, string("Location already specified.")); else if (!type) - parserError(string("Location specifier needs explicit type name.")); + parserError(7439_error, string("Location specifier needs explicit type name.")); else { switch (token) @@ -837,13 +843,13 @@ ASTPointer Parser::parseModifierDefinition() if (m_scanner->currentToken() == Token::Override) { if (overrides) - parserError("Override already specified."); + parserError(9102_error, "Override already specified."); overrides = parseOverrideSpecifier(); } else if (m_scanner->currentToken() == Token::Virtual) { if (isVirtual) - parserError("Virtual already specified."); + parserError(2662_error, "Virtual already specified."); isVirtual = true; m_scanner->next(); @@ -991,7 +997,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) } else { - parserError("State mutability can only be specified for address types."); + parserError(9106_error, "State mutability can only be specified for address types."); m_scanner->next(); } } @@ -1000,7 +1006,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) else if (token == Token::Var) { if (!_allowVar) - parserError(string("Expected explicit type name.")); + parserError(7059_error, string("Expected explicit type name.")); m_scanner->next(); } else if (token == Token::Function) @@ -1010,7 +1016,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) else if (token == Token::Identifier) type = parseUserDefinedTypeName(); else - fatalParserError(string("Expected type name")); + fatalParserError(3546_error, string("Expected type name")); if (type) // Parse "[...]" postfixes for arrays. @@ -1053,7 +1059,7 @@ ASTPointer Parser::parseMapping() m_scanner->next(); } else - fatalParserError(string("Expected elementary type name or identifier for mapping key type")); + fatalParserError(1005_error, string("Expected elementary type name or identifier for mapping key type")); expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); @@ -1079,7 +1085,7 @@ ASTPointer Parser::parseParameterList( while (m_scanner->currentToken() != Token::RParen) { if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) - fatalParserError("Unexpected trailing comma in parameter list."); + fatalParserError(7591_error, "Unexpected trailing comma in parameter list."); expectToken(Token::Comma); parameters.push_back(parseVariableDeclaration(options)); } @@ -1214,7 +1220,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con if (m_scanner->currentToken() == Token::StringLiteral) { if (m_scanner->currentLiteral() != "evmasm") - fatalParserError("Only \"evmasm\" supported."); + fatalParserError(4531_error, "Only \"evmasm\" supported."); // This can be used in the future to set the dialect. m_scanner->next(); } @@ -1377,7 +1383,7 @@ ASTPointer Parser::parseEmitStatement(ASTPointer const ASTNodeFactory eventCallNodeFactory(*this); if (m_scanner->currentToken() != Token::Identifier) - fatalParserError("Expected event name or path."); + fatalParserError(5620_error, "Expected event name or path."); IndexAccessedPath iap; while (true) @@ -1845,7 +1851,7 @@ ASTPointer Parser::parsePrimaryExpression() nodeFactory.markEndPosition(); m_scanner->next(); if (m_scanner->currentToken() == Token::Illegal) - fatalParserError(to_string(m_scanner->currentError())); + fatalParserError(5428_error, to_string(m_scanner->currentError())); expression = nodeFactory.createNode(token, make_shared(literal)); break; } @@ -1876,7 +1882,7 @@ ASTPointer Parser::parsePrimaryExpression() if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken) components.push_back(parseExpression()); else if (isArray) - parserError("Expected expression (inline array elements cannot be omitted)."); + parserError(4799_error, "Expected expression (inline array elements cannot be omitted)."); else components.push_back(ASTPointer()); @@ -1891,7 +1897,7 @@ ASTPointer Parser::parsePrimaryExpression() break; } case Token::Illegal: - fatalParserError(to_string(m_scanner->currentError())); + fatalParserError(8936_error, to_string(m_scanner->currentError())); break; default: if (TokenTraits::isElementaryTypeName(token)) @@ -1907,7 +1913,7 @@ ASTPointer Parser::parsePrimaryExpression() m_scanner->next(); } else - fatalParserError(string("Expected primary expression.")); + fatalParserError(6933_error, string("Expected primary expression.")); break; } return expression; @@ -1965,7 +1971,7 @@ pair>, vector>> Parser::pars m_scanner->peekNextToken() == Token::RBrace ) { - parserError("Unexpected trailing comma."); + parserError(2074_error, "Unexpected trailing comma."); m_scanner->next(); } @@ -2084,7 +2090,7 @@ ASTPointer Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces for (auto const& lengthExpression: _iap.indices) { if (lengthExpression.end) - parserError(lengthExpression.location, "Expected array length expression."); + parserError(5464_error, lengthExpression.location, "Expected array length expression."); nodeFactory.setLocation(lengthExpression.location); type = nodeFactory.createNode(type, lengthExpression.start); } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 13b96839b..19574446d 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -122,11 +122,11 @@ Statement Parser::parseStatement() if (currentToken() == Token::Default) _switch.cases.emplace_back(parseCase()); if (currentToken() == Token::Default) - fatalParserError("Only one default case allowed."); + fatalParserError(6931_error, "Only one default case allowed."); else if (currentToken() == Token::Case) - fatalParserError("Case not allowed after default case."); + fatalParserError(4904_error, "Case not allowed after default case."); if (_switch.cases.empty()) - fatalParserError("Switch statement without any cases."); + fatalParserError(2418_error, "Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; return Statement{move(_switch)}; } @@ -184,6 +184,7 @@ Statement Parser::parseStatement() auto const token = currentToken() == Token::Comma ? "," : ":="; fatalParserError( + 2856_error, std::string("Variable name must precede \"") + token + "\"" + @@ -194,7 +195,7 @@ Statement Parser::parseStatement() auto const& identifier = std::get(elementary); if (m_dialect.builtin(identifier.name)) - fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\"."); + fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\"."); variableNames.emplace_back(identifier); @@ -218,7 +219,7 @@ Statement Parser::parseStatement() return Statement{std::move(assignment)}; } default: - fatalParserError("Call or assignment expected."); + fatalParserError(6913_error, "Call or assignment expected."); break; } @@ -250,7 +251,7 @@ Case Parser::parseCase() advance(); ElementaryOperation literal = parseElementaryOperation(); if (!holds_alternative(literal)) - fatalParserError("Literal expected."); + fatalParserError(4805_error, "Literal expected."); _case.value = make_unique(std::get(std::move(literal))); } else @@ -353,7 +354,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() break; case Token::Number: if (!isValidNumberLiteral(currentLiteral())) - fatalParserError("Invalid number literal."); + fatalParserError(4828_error, "Invalid number literal."); kind = LiteralKind::Number; break; case Token::TrueLiteral: @@ -382,7 +383,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() break; } default: - fatalParserError("Literal or identifier expected."); + fatalParserError(1856_error, "Literal or identifier expected."); } return ret; } @@ -472,7 +473,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) else if (holds_alternative(_initialOp)) ret = std::move(std::get(_initialOp)); else - fatalParserError("Function name expected."); + fatalParserError(9980_error, "Function name expected."); expectToken(Token::LParen); if (currentToken() != Token::RParen) @@ -525,7 +526,7 @@ YulString Parser::expectAsmIdentifier() } if (m_dialect.builtin(name)) - fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name."); + fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name."); advance(); return name; } diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 74c339f18..7e50fa88e 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -66,7 +66,7 @@ shared_ptr ObjectParser::parseObject(Object* _containingObject) RecursionGuard guard(*this); if (currentToken() != Token::Identifier || currentLiteral() != "object") - fatalParserError("Expected keyword \"object\"."); + fatalParserError(4294_error, "Expected keyword \"object\"."); advance(); shared_ptr ret = make_shared(); @@ -83,7 +83,7 @@ shared_ptr ObjectParser::parseObject(Object* _containingObject) else if (currentToken() == Token::Identifier && currentLiteral() == "data") parseData(*ret); else - fatalParserError("Expected keyword \"data\" or \"object\" or \"}\"."); + fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\"."); } if (_containingObject) addNamedSubObject(*_containingObject, ret->name, ret); @@ -96,7 +96,7 @@ shared_ptr ObjectParser::parseObject(Object* _containingObject) shared_ptr ObjectParser::parseCode() { if (currentToken() != Token::Identifier || currentLiteral() != "code") - fatalParserError("Expected keyword \"code\"."); + fatalParserError(4846_error, "Expected keyword \"code\"."); advance(); return parseBlock(); @@ -133,11 +133,11 @@ YulString ObjectParser::parseUniqueName(Object const* _containingObject) expectToken(Token::StringLiteral, false); YulString name{currentLiteral()}; if (name.empty()) - parserError("Object name cannot be empty."); + parserError(3287_error, "Object name cannot be empty."); else if (_containingObject && _containingObject->name == name) - parserError("Object name cannot be the same as the name of the containing object."); + parserError(8311_error, "Object name cannot be the same as the name of the containing object."); else if (_containingObject && _containingObject->subIndexByName.count(name)) - parserError("Object name \"" + name.str() + "\" already exists inside the containing object."); + parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object."); advance(); return name; } From 0b09a7768977e0502cd98882ab1fb190fb1e3d59 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Sat, 9 May 2020 01:45:02 +0200 Subject: [PATCH 40/65] Removed minor parameter redundancy --- libsolidity/parsing/Parser.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index f726e6dc9..99b9a2cac 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -103,7 +103,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) nodes.push_back(parseEnumDefinition()); break; default: - fatalParserError(7858_error, string("Expected pragma, import directive or contract/interface/library/struct/enum definition.")); + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition."); } } solAssert(m_recursionDepth == 0, ""); @@ -344,7 +344,7 @@ ASTPointer Parser::parseContractDefinition() else if (currentTokenValue == Token::Using) subNodes.push_back(parseUsingDirective()); else - fatalParserError(9182_error, string("Function, variable, struct or modifier declaration expected.")); + fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected."); } } catch (FatalError const&) @@ -497,11 +497,11 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal)) break; parserError( - 9439_error,string( + 9439_error, "Visibility already specified as \"" + Declaration::visibilityToString(result.visibility) + "\"." - )); + ); m_scanner->next(); } else @@ -512,11 +512,11 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari if (result.stateMutability != StateMutability::NonPayable) { parserError( - 9680_error,string( + 9680_error, "State mutability already specified as \"" + stateMutabilityToString(result.stateMutability) + "\"." - )); + ); m_scanner->next(); } else @@ -663,10 +663,10 @@ ASTPointer Parser::parseEnumDefinition() break; expectToken(Token::Comma); if (m_scanner->currentToken() != Token::Identifier) - fatalParserError(1612_error, string("Expected identifier after ','")); + fatalParserError(1612_error, "Expected identifier after ','"); } if (members.empty()) - parserError(3147_error, {"enum with no members is not allowed."}); + parserError(3147_error, "enum with no members is not allowed."); nodeFactory.markEndPosition(); expectToken(Token::RBrace); @@ -715,11 +715,11 @@ ASTPointer Parser::parseVariableDeclaration( if (visibility != Visibility::Default) { parserError( - 4110_error,string( + 4110_error, "Visibility already specified as \"" + Declaration::visibilityToString(visibility) + "\"." - )); + ); m_scanner->next(); } else @@ -752,9 +752,9 @@ ASTPointer Parser::parseVariableDeclaration( else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) - parserError(3548_error, string("Location already specified.")); + parserError(3548_error, "Location already specified."); else if (!type) - parserError(7439_error, string("Location specifier needs explicit type name.")); + parserError(7439_error, "Location specifier needs explicit type name."); else { switch (token) @@ -1006,7 +1006,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) else if (token == Token::Var) { if (!_allowVar) - parserError(7059_error, string("Expected explicit type name.")); + parserError(7059_error, "Expected explicit type name."); m_scanner->next(); } else if (token == Token::Function) @@ -1016,7 +1016,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) else if (token == Token::Identifier) type = parseUserDefinedTypeName(); else - fatalParserError(3546_error, string("Expected type name")); + fatalParserError(3546_error, "Expected type name"); if (type) // Parse "[...]" postfixes for arrays. @@ -1059,7 +1059,7 @@ ASTPointer Parser::parseMapping() m_scanner->next(); } else - fatalParserError(1005_error, string("Expected elementary type name or identifier for mapping key type")); + fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); expectToken(Token::Arrow); bool const allowVar = false; ASTPointer valueType = parseTypeName(allowVar); @@ -1913,7 +1913,7 @@ ASTPointer Parser::parsePrimaryExpression() m_scanner->next(); } else - fatalParserError(6933_error, string("Expected primary expression.")); + fatalParserError(6933_error, "Expected primary expression."); break; } return expression; From 591a5fff07ed5cebc1cb236d74e2cc572d05cec1 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 11 May 2020 09:26:27 +0200 Subject: [PATCH 41/65] isoltest: WhiskersError and YulException treated differently when enforcing viaYul --- test/libsolidity/SemanticTest.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index ab454cb28..33d40c899 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -13,6 +13,8 @@ */ #include +#include +#include #include #include #include @@ -27,6 +29,7 @@ using namespace std; using namespace solidity; +using namespace solidity::yul; using namespace solidity::util; using namespace solidity::util::formatting; using namespace solidity::frontend::test; @@ -201,6 +204,16 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref return TestResult::Failure; } } + catch (WhiskersError const&) + { + // this is an error in Whiskers template, so should be thrown anyway + throw; + } + catch (YulException const&) + { + // this should be an error in yul compilation or translation + throw; + } catch (boost::exception const&) { if (compileViaYul && !m_runWithYul) From e54c4eecfc4e973625cb54d1c5b59c982ab30cbb Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Thu, 7 May 2020 21:54:37 +0530 Subject: [PATCH 42/65] implemented type(X).min and type(X).max for all integer types --- libsolidity/analysis/GlobalContext.cpp | 8 +- libsolidity/analysis/TypeChecker.cpp | 32 ++- libsolidity/analysis/ViewPureChecker.cpp | 2 + libsolidity/ast/TypeProvider.cpp | 8 +- libsolidity/ast/Types.cpp | 53 +++- libsolidity/ast/Types.h | 3 + libsolidity/codegen/ExpressionCompiler.cpp | 10 + .../codegen/ir/IRGeneratorForStatements.cpp | 20 +- .../semanticTests/integer/basic.sol | 24 ++ .../libsolidity/semanticTests/integer/int.sol | 245 ++++++++++++++++++ .../semanticTests/integer/uint.sol | 244 +++++++++++++++++ .../syntaxTests/metaTypes/typeRecursive.sol | 4 +- .../metaTypes/unsupportedArgForType.sol | 2 +- 13 files changed, 626 insertions(+), 29 deletions(-) create mode 100644 test/libsolidity/semanticTests/integer/basic.sol create mode 100644 test/libsolidity/semanticTests/integer/int.sol create mode 100644 test/libsolidity/semanticTests/integer/uint.sol diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index a0764edba..ddde7358e 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -100,11 +100,13 @@ inline vector> constructMagicVariable magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)), + // Accepts a MagicType that can be any contract type or an Integer type and returns a + // MagicType. The TypeChecker handles the correctness of the input and output types. magicVarDecl("type", TypeProvider::function( - strings{"address"} /* accepts any contract type, handled by the type checker */, - strings{} /* returns a MagicType, handled by the type checker */, + strings{}, + strings{}, FunctionType::Kind::MetaType, - false, + true, StateMutability::Pure )), }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b4030c0c4..b65d22b5e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -214,17 +214,28 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio return {}; } TypePointer firstArgType = type(*arguments.front()); - if ( - firstArgType->category() != Type::Category::TypeType || - dynamic_cast(*firstArgType).actualType()->category() != TypeType::Category::Contract - ) + + bool wrongType = false; + if (firstArgType->category() == Type::Category::TypeType) + { + TypeType const* typeTypePtr = dynamic_cast(firstArgType); + Type::Category typeCategory = typeTypePtr->actualType()->category(); + if ( + typeCategory != Type::Category::Contract && + typeCategory != Type::Category::Integer + ) + wrongType = true; + } + else + wrongType = true; + + if (wrongType) { m_errorReporter.typeError( arguments.front()->location(), - "Invalid type for argument in function call. " - "Contract type required, but " + - type(*arguments.front())->toString(true) + - " provided." + "Invalid type for argument in the function call. " + "A contract type or an integer type is required, but " + + type(*arguments.front())->toString(true) + " provided." ); return {}; } @@ -2600,6 +2611,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.isPure = true; else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId") annotation.isPure = true; + else if ( + magicType->kind() == MagicType::Kind::MetaType && + (memberName == "min" || memberName == "max") + ) + annotation.isPure = true; } return false; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 0d87fdbc2..87b9dcef6 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -357,6 +357,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::MetaType, "runtimeCode"}, {MagicType::Kind::MetaType, "name"}, {MagicType::Kind::MetaType, "interfaceId"}, + {MagicType::Kind::MetaType, "min"}, + {MagicType::Kind::MetaType, "max"}, }; set static const payableMembers{ {MagicType::Kind::Message, "value"} diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index 7dd895cbe..6fd778403 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind) MagicType const* TypeProvider::meta(Type const* _type) { - solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now."); + solAssert( + _type && ( + _type->category() == Type::Category::Contract || + _type->category() == Type::Category::Integer + ), + "Only contracts or integer types supported for now." + ); return createAndGet(_type); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7ee12d6dd..aa888d26b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -556,6 +556,22 @@ string IntegerType::toString(bool) const return prefix + util::toString(m_bits); } +u256 IntegerType::min() const +{ + if (isSigned()) + return s2u(s256(minValue())); + else + return u256(minValue()); +} + +u256 IntegerType::max() const +{ + if (isSigned()) + return s2u(s256(maxValue())); + else + return u256(maxValue()); +} + bigint IntegerType::minValue() const { if (isSigned()) @@ -3763,20 +3779,35 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const case Kind::MetaType: { solAssert( - m_typeArgument && m_typeArgument->category() == Type::Category::Contract, - "Only contracts supported for now" + m_typeArgument && ( + m_typeArgument->category() == Type::Category::Contract || + m_typeArgument->category() == Type::Category::Integer + ), + "Only contracts or integer types supported for now" ); - ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); - if (contract.canBeDeployed()) + + if (m_typeArgument->category() == Type::Category::Contract) + { + ContractDefinition const& contract = dynamic_cast(*m_typeArgument).contractDefinition(); + if (contract.canBeDeployed()) + return MemberList::MemberMap({ + {"creationCode", TypeProvider::array(DataLocation::Memory)}, + {"runtimeCode", TypeProvider::array(DataLocation::Memory)}, + {"name", TypeProvider::stringMemory()}, + }); + else + return MemberList::MemberMap({ + {"interfaceId", TypeProvider::fixedBytes(4)}, + }); + } + else if (m_typeArgument->category() == Type::Category::Integer) + { + IntegerType const* integerTypePointer = dynamic_cast(m_typeArgument); return MemberList::MemberMap({ - {"creationCode", TypeProvider::array(DataLocation::Memory)}, - {"runtimeCode", TypeProvider::array(DataLocation::Memory)}, - {"name", TypeProvider::stringMemory()}, - }); - else - return MemberList::MemberMap({ - {"interfaceId", TypeProvider::fixedBytes(4)}, + {"min", integerTypePointer}, + {"max", integerTypePointer}, }); + } } } solAssert(false, "Unknown kind of magic."); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 974bae4a3..d549d4f24 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -452,6 +452,9 @@ public: unsigned numBits() const { return m_bits; } bool isSigned() const { return m_modifier == Modifier::Signed; } + u256 min() const; + u256 max() const; + bigint minValue() const; bigint maxValue() const; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 14b0d763b..e33fa608a 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1591,6 +1591,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) result ^= fromBigEndian(function.first.ref()); m_context << (u256{result} << (256 - 32)); } + else if (member == "min" || member == "max") + { + MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); + IntegerType const* integerType = dynamic_cast(arg->typeArgument()); + + if (member == "min") + m_context << integerType->min(); + else + m_context << integerType->max(); + } else if ((set{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member)) { // no-op diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index f795e0e98..8e7d9b18b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -477,12 +477,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) return false; } + if (commonType->category() == Type::Category::RationalNumber) + { + define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n"; + return false; // skip sub-expressions + } + _binOp.leftExpression().accept(*this); _binOp.rightExpression().accept(*this); - if (commonType->category() == Type::Category::RationalNumber) - define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n"; - else if (TokenTraits::isCompareOp(op)) + if (TokenTraits::isCompareOp(op)) { if (auto type = dynamic_cast(commonType)) { @@ -1175,6 +1179,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) result ^= fromBigEndian(function.first.ref()); define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n"; } + else if (member == "min" || member == "max") + { + MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); + IntegerType const* integerType = dynamic_cast(arg->typeArgument()); + + if (member == "min") + define(_memberAccess) << formatNumber(integerType->min()) << "\n"; + else + define(_memberAccess) << formatNumber(integerType->max()) << "\n"; + } else if (set{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member)) { // no-op diff --git a/test/libsolidity/semanticTests/integer/basic.sol b/test/libsolidity/semanticTests/integer/basic.sol new file mode 100644 index 000000000..7c31e3888 --- /dev/null +++ b/test/libsolidity/semanticTests/integer/basic.sol @@ -0,0 +1,24 @@ +contract C { + function basic() public pure returns(bool) { + uint uint_min = type(uint).min; + require(uint_min == 0); + + uint uint_max = type(uint).max; + require(uint_max == 2**256 - 1); + require(uint_max == 115792089237316195423570985008687907853269984665640564039457584007913129639935); + + int int_min = type(int).min; + require(int_min == -2**255); + require(int_min == -57896044618658097711785492504343953926634992332820282019728792003956564819968); + + int int_max = type(int).max; + require(int_max == 2**255 -1); + require(int_max == 57896044618658097711785492504343953926634992332820282019728792003956564819967); + + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// basic() -> true diff --git a/test/libsolidity/semanticTests/integer/int.sol b/test/libsolidity/semanticTests/integer/int.sol new file mode 100644 index 000000000..79d685645 --- /dev/null +++ b/test/libsolidity/semanticTests/integer/int.sol @@ -0,0 +1,245 @@ +contract test { + + function intMinA() public pure returns (bool) { + + int8 int8_min = type(int8).min; + require(int8_min == -2**7); + + int16 int16_min = type(int16).min; + require(int16_min == -2**15); + + int24 int24_min = type(int24).min; + require(int24_min == -2**23); + + int32 int32_min = type(int32).min; + require(int32_min == -2**31); + + int40 int40_min = type(int40).min; + require(int40_min == -2**39); + + int48 int48_min = type(int48).min; + require(int48_min == -2**47); + + int56 int56_min = type(int56).min; + require(int56_min == -2**55); + + int64 int64_min = type(int64).min; + require(int64_min == -2**63); + + return true; + } + + function intMinB() public pure returns(bool) { + + int72 int72_min = type(int72).min; + require(int72_min == -2**71); + + int80 int80_min = type(int80).min; + require(int80_min == -2**79); + + int88 int88_min = type(int88).min; + require(int88_min == -2**87); + + int96 int96_min = type(int96).min; + require(int96_min == -2**95); + + int104 int104_min = type(int104).min; + require(int104_min == -2**103); + + int112 int112_min = type(int112).min; + require(int112_min == -2**111); + + int120 int120_min = type(int120).min; + require(int120_min == -2**119); + + int128 int128_min = type(int128).min; + require(int128_min == -2**127); + + return true; + } + + function intMinC() public pure returns (bool) { + + int136 int136_min = type(int136).min; + require(int136_min == -2**135); + + int144 int144_min = type(int144).min; + require(int144_min == -2**143); + + int152 int152_min = type(int152).min; + require(int152_min == -2**151); + + int160 int160_min = type(int160).min; + require(int160_min == -2**159); + + int168 int168_min = type(int168).min; + require(int168_min == -2**167); + + int176 int176_min = type(int176).min; + require(int176_min == -2**175); + + int184 int184_min = type(int184).min; + require(int184_min == -2**183); + + int192 int192_min = type(int192).min; + require(int192_min == -2**191); + + return true; + } + + function intMinD() public pure returns(bool) { + + int200 int200_min = type(int200).min; + require(int200_min == -2**199); + + int208 int208_min = type(int208).min; + require(int208_min == -2**207); + + int216 int216_min = type(int216).min; + require(int216_min == -2**215); + + int224 int224_min = type(int224).min; + require(int224_min == -2**223); + + int232 int232_min = type(int232).min; + require(int232_min == -2**231); + + int240 int240_min = type(int240).min; + require(int240_min == -2**239); + + int248 int248_min = type(int248).min; + require(int248_min == -2**247); + + int256 int256_min = type(int256).min; + require(int256_min == -2**255); + + return true; + } + + function intMaxA() public pure returns (bool) { + + int8 int8_max = type(int8).max; + require(int8_max == 2**7-1); + + int16 int16_max = type(int16).max; + require(int16_max == 2**15-1); + + int24 int24_max = type(int24).max; + require(int24_max == 2**23-1); + + int32 int32_max = type(int32).max; + require(int32_max == 2**31-1); + + int40 int40_max = type(int40).max; + require(int40_max == 2**39-1); + + int48 int48_max = type(int48).max; + require(int48_max == 2**47-1); + + int56 int56_max = type(int56).max; + require(int56_max == 2**55-1); + + int64 int64_max = type(int64).max; + require(int64_max == 2**63-1); + + return true; + } + + function intMaxB() public pure returns(bool) { + + int72 int72_max = type(int72).max; + require(int72_max == 2**71-1); + + int80 int80_max = type(int80).max; + require(int80_max == 2**79-1); + + int88 int88_max = type(int88).max; + require(int88_max == 2**87-1); + + int96 int96_max = type(int96).max; + require(int96_max == 2**95-1); + + int104 int104_max = type(int104).max; + require(int104_max == 2**103-1); + + int112 int112_max = type(int112).max; + require(int112_max == 2**111-1); + + int120 int120_max = type(int120).max; + require(int120_max == 2**119-1); + + int128 int128_max = type(int128).max; + require(int128_max == 2**127-1); + + return true; + } + + function intMaxC() public pure returns (bool) { + + int136 int136_max = type(int136).max; + require(int136_max == 2**135-1); + + int144 int144_max = type(int144).max; + require(int144_max == 2**143-1); + + int152 int152_max = type(int152).max; + require(int152_max == 2**151-1); + + int160 int160_max = type(int160).max; + require(int160_max == 2**159-1); + + int168 int168_max = type(int168).max; + require(int168_max == 2**167-1); + + int176 int176_max = type(int176).max; + require(int176_max == 2**175-1); + + int184 int184_max = type(int184).max; + require(int184_max == 2**183-1); + + int192 int192_max = type(int192).max; + require(int192_max == 2**191-1); + + return true; + } + + function intMaxD() public pure returns(bool) { + + int200 int200_max = type(int200).max; + require(int200_max == 2**199-1); + + int208 int208_max = type(int208).max; + require(int208_max == 2**207-1); + + int216 int216_max = type(int216).max; + require(int216_max == 2**215-1); + + int224 int224_max = type(int224).max; + require(int224_max == 2**223-1); + + int232 int232_max = type(int232).max; + require(int232_max == 2**231-1); + + int240 int240_max = type(int240).max; + require(int240_max == 2**239-1); + + int248 int248_max = type(int248).max; + require(int248_max == 2**247-1); + + int256 int256_max = type(int256).max; + require(int256_max == 2**255-1); + + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// intMinA() -> true +// intMinB() -> true +// intMinC() -> true +// intMinD() -> true +// intMaxA() -> true +// intMaxB() -> true +// intMaxC() -> true +// intMaxD() -> true diff --git a/test/libsolidity/semanticTests/integer/uint.sol b/test/libsolidity/semanticTests/integer/uint.sol new file mode 100644 index 000000000..a2f3746c2 --- /dev/null +++ b/test/libsolidity/semanticTests/integer/uint.sol @@ -0,0 +1,244 @@ +contract test { + + function uintMinA() public pure returns(bool) { + + uint8 uint8_min = type(uint8).min; + require(uint8_min == 0); + + uint16 uint16_min = type(uint16).min; + require(uint16_min == 0); + + uint24 uint24_min = type(uint24).min; + require(uint24_min == 0); + + uint32 uint32_min = type(uint32).min; + require(uint32_min == 0); + + uint40 uint40_min = type(uint40).min; + require(uint40_min == 0); + + uint48 uint48_min = type(uint48).min; + require(uint48_min == 0); + + uint56 uint56_min = type(uint56).min; + require(uint56_min == 0); + + uint64 uint64_min = type(uint64).min; + require(uint64_min == 0); + + return true; + } + + function uintMinB() public pure returns(bool) { + + uint72 uint72_min = type(uint72).min; + require(uint72_min == 0); + + uint80 uint80_min = type(uint80).min; + require(uint80_min == 0); + + uint88 uint88_min = type(uint88).min; + require(uint88_min == 0); + + uint96 uint96_min = type(uint96).min; + require(uint96_min == 0); + + uint104 uint104_min = type(uint104).min; + require(uint104_min == 0); + + uint112 uint112_min = type(uint112).min; + require(uint112_min == 0); + + uint120 uint120_min = type(uint120).min; + require(uint120_min == 0); + + uint128 uint128_min = type(uint128).min; + require(uint128_min == 0); + + return true; + } + + function uintMinC() public pure returns(bool) { + + uint136 uint136_min = type(uint136).min; + require(uint136_min == 0); + + uint144 uint144_min = type(uint144).min; + require(uint144_min == 0); + + uint152 uint152_min = type(uint152).min; + require(uint152_min == 0); + + uint160 uint160_min = type(uint160).min; + require(uint160_min == 0); + + uint168 uint168_min = type(uint168).min; + require(uint168_min == 0); + + uint176 uint176_min = type(uint176).min; + require(uint176_min == 0); + + uint184 uint184_min = type(uint184).min; + require(uint184_min == 0); + + uint192 uint192_min = type(uint192).min; + require(uint192_min == 0); + + return true; + } + + function uintMinD() public pure returns(bool) { + + uint200 uint200_min = type(uint200).min; + require(uint200_min == 0); + + uint208 uint208_min = type(uint208).min; + require(uint208_min == 0); + + uint216 uint216_min = type(uint216).min; + require(uint216_min == 0); + + uint224 uint224_min = type(uint224).min; + require(uint224_min == 0); + + uint232 uint232_min = type(uint232).min; + require(uint232_min == 0); + + uint240 uint240_min = type(uint240).min; + require(uint240_min == 0); + + uint248 uint248_min = type(uint248).min; + require(uint248_min == 0); + + uint256 uint256_min = type(uint256).min; + require(uint256_min == 0); + + return true; + } + + function uintMaxA() public pure returns (bool) { + + uint8 uint8_max = type(uint8).max; + require(uint8_max == 2**8-1); + + uint16 uint16_max = type(uint16).max; + require(uint16_max == 2**16-1); + + uint24 uint24_max = type(uint24).max; + require(uint24_max == 2**24-1); + + uint32 uint32_max = type(uint32).max; + require(uint32_max == 2**32-1); + + uint40 uint40_max = type(uint40).max; + require(uint40_max == 2**40-1); + + uint48 uint48_max = type(uint48).max; + require(uint48_max == 2**48-1); + + uint56 uint56_max = type(uint56).max; + require(uint56_max == 2**56-1); + + uint64 uint64_max = type(uint64).max; + require(uint64_max == 2**64-1); + + return true; + } + + function uintMaxB() public pure returns (bool) { + + uint72 uint72_max = type(uint72).max; + require(uint72_max == 2**72-1); + + uint80 uint80_max = type(uint80).max; + require(uint80_max == 2**80-1); + + uint88 uint88_max = type(uint88).max; + require(uint88_max == 2**88-1); + + uint96 uint96_max = type(uint96).max; + require(uint96_max == 2**96-1); + + uint104 uint104_max = type(uint104).max; + require(uint104_max == 2**104-1); + + uint112 uint112_max = type(uint112).max; + require(uint112_max == 2**112-1); + + uint120 uint120_max = type(uint120).max; + require(uint120_max == 2**120-1); + + uint128 uint128_max = type(uint128).max; + require(uint128_max == 2**128-1); + + return true; + } + + function uintMaxC() public pure returns (bool) { + + uint136 uint136_max = type(uint136).max; + require(uint136_max == 2**136-1); + + uint144 uint144_max = type(uint144).max; + require(uint144_max == 2**144-1); + + uint152 uint152_max = type(uint152).max; + require(uint152_max == 2**152-1); + + uint160 uint160_max = type(uint160).max; + require(uint160_max == 2**160-1); + + uint168 uint168_max = type(uint168).max; + require(uint168_max == 2**168-1); + + uint176 uint176_max = type(uint176).max; + require(uint176_max == 2**176-1); + + uint184 uint184_max = type(uint184).max; + require(uint184_max == 2**184-1); + + uint192 uint192_max = type(uint192).max; + require(uint192_max == 2**192-1); + + return true; + } + + function uintMaxD() public pure returns(bool) { + uint200 uint200_max = type(uint200).max; + require(uint200_max == 2**200-1); + + uint208 uint208_max = type(uint208).max; + require(uint208_max == 2**208-1); + + uint216 uint216_max = type(uint216).max; + require(uint216_max == 2**216-1); + + uint224 uint224_max = type(uint224).max; + require(uint224_max == 2**224-1); + + uint232 uint232_max = type(uint232).max; + require(uint232_max == 2**232-1); + + uint240 uint240_max = type(uint240).max; + require(uint240_max == 2**240-1); + + uint248 uint248_max = type(uint248).max; + require(uint248_max == 2**248-1); + + uint256 uint256_max = type(uint256).max; + require(uint256_max == 2**256-1); + + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// uintMinA() -> true +// uintMinB() -> true +// uintMinC() -> true +// uintMinD() -> true +// uintMaxA() -> true +// uintMaxB() -> true +// uintMaxC() -> true +// uintMaxD() -> true diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol index 0ce06786e..903338a43 100644 --- a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -4,5 +4,5 @@ contract Test { } } // ---- -// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided. -// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided. +// TypeError: (65-75): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract Test) provided. +// TypeError: (60-76): Invalid type for argument in the function call. A contract type or an integer type is required, but tuple() provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol index 5c27d42f2..94f498922 100644 --- a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol +++ b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol @@ -6,4 +6,4 @@ contract Test { } } // ---- -// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided. +// TypeError: (154-155): Invalid type for argument in the function call. A contract type or an integer type is required, but type(struct Test.S) provided. From 338e55be10982f2ea054f7c6aba9139d0e749852 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2020 10:44:20 +0100 Subject: [PATCH 43/65] Add header guard to two files --- libyul/AsmJsonConverter.h | 2 ++ tools/solidityUpgrade/SourceTransform.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libyul/AsmJsonConverter.h b/libyul/AsmJsonConverter.h index 360fa6a36..b350689fc 100644 --- a/libyul/AsmJsonConverter.h +++ b/libyul/AsmJsonConverter.h @@ -20,6 +20,8 @@ * Converts inline assembly AST to JSON format */ +#pragma once + #include #include #include diff --git a/tools/solidityUpgrade/SourceTransform.h b/tools/solidityUpgrade/SourceTransform.h index 2b42471c1..a33a1d97e 100644 --- a/tools/solidityUpgrade/SourceTransform.h +++ b/tools/solidityUpgrade/SourceTransform.h @@ -15,6 +15,8 @@ along with solidity. If not, see . */ +#pragma once + #include #include From e4e200f29f83f1c6d55036b76ce5d76dae00fed4 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Mon, 11 May 2020 14:13:51 +0530 Subject: [PATCH 44/65] Changelog and tests --- Changelog.md | 2 +- .../semanticTests/arithmetics/addmod_mulmod.sol | 2 ++ .../array/create_memory_array_too_large.sol | 2 ++ .../syntaxTests/metaTypes/contract_min.sol | 7 +++++++ .../syntaxTests/metaTypes/int_name.sol | 7 +++++++ .../syntaxTests/metaTypes/integer.sol | 12 ++++++++++++ .../syntaxTests/metaTypes/integer_err.sol | 15 +++++++++++++++ .../syntaxTests/metaTypes/integer_pure.sol | 16 ++++++++++++++++ 8 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/metaTypes/contract_min.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/int_name.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/integer.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/integer_err.sol create mode 100644 test/libsolidity/syntaxTests/metaTypes/integer_pure.sol diff --git a/Changelog.md b/Changelog.md index 0b3749be9..52dcb07da 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.6.8 (unreleased) Language Features: - + * Implemented ``type(X).min`` and ``type(X).max`` for every integer type ``X`` that returns the smallest and largest value representable by the type. Compiler Features: diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol index d1e9f5c20..ea2aa3d8f 100644 --- a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol @@ -8,5 +8,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // test() -> 0 diff --git a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol index c42f84862..541a037a2 100644 --- a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol +++ b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol @@ -19,6 +19,8 @@ contract C { y[0] = 23; return x[2]; }} +// ==== +// compileViaYul: also // ---- // f() -> FAILURE // g() -> FAILURE diff --git a/test/libsolidity/syntaxTests/metaTypes/contract_min.sol b/test/libsolidity/syntaxTests/metaTypes/contract_min.sol new file mode 100644 index 000000000..a922593b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/contract_min.sol @@ -0,0 +1,7 @@ +contract Min { + function contractMin() public { + type(Min).min; + } +} +// ---- +// TypeError: (50-63): Member "min" not found or not visible after argument-dependent lookup in type(contract Min). diff --git a/test/libsolidity/syntaxTests/metaTypes/int_name.sol b/test/libsolidity/syntaxTests/metaTypes/int_name.sol new file mode 100644 index 000000000..b6c4f49a5 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/int_name.sol @@ -0,0 +1,7 @@ +contract test { + function intName() public { + type(int).name; + } +} +// ---- +// TypeError: (47-61): Member "name" not found or not visible after argument-dependent lookup in type(int256). diff --git a/test/libsolidity/syntaxTests/metaTypes/integer.sol b/test/libsolidity/syntaxTests/metaTypes/integer.sol new file mode 100644 index 000000000..31bd069ec --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/integer.sol @@ -0,0 +1,12 @@ +contract Test { + function basic() public pure { + uint uintMax = type(uint).max; + uintMax; + int intMax = type(int).max; + intMax; + uint uintMin = type(uint).min; + uintMin; + int intMin = type(int).min; + intMin; + } +} diff --git a/test/libsolidity/syntaxTests/metaTypes/integer_err.sol b/test/libsolidity/syntaxTests/metaTypes/integer_err.sol new file mode 100644 index 000000000..5a0610638 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/integer_err.sol @@ -0,0 +1,15 @@ +contract Test { + function assignment() public { + uint8 uint8Min = type(int).min; + uint uintMin = type(int).min; + + if (type(int).min == 2**256 - 1) { + uintMin; + } + + } +} +// ---- +// TypeError: (59-89): Type int256 is not implicitly convertible to expected type uint8. +// TypeError: (99-127): Type int256 is not implicitly convertible to expected type uint256. +// TypeError: (142-169): Operator == not compatible with types int256 and int_const 1157...(70 digits omitted)...9935 diff --git a/test/libsolidity/syntaxTests/metaTypes/integer_pure.sol b/test/libsolidity/syntaxTests/metaTypes/integer_pure.sol new file mode 100644 index 000000000..2eb115e0f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/integer_pure.sol @@ -0,0 +1,16 @@ +contract test { + + function viewAssignment() public view { + int min = type(int).min; + min; + } + + function assignment() public { + int max = type(int).max; + max; + } + +} +// ---- +// Warning: (21-112): Function state mutability can be restricted to pure +// Warning: (118-200): Function state mutability can be restricted to pure From c31a93b3f24e17ee8676de3e76b0cdab9cb7cc01 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 26 Apr 2020 00:35:28 +0100 Subject: [PATCH 45/65] Remove boost::filesystem where it is not needed A two uses in CommonIO remain for the compiler (plus testing/tools use it extensively) --- libsolidity/CMakeLists.txt | 2 +- libsolidity/formal/CHCSmtLib2Interface.cpp | 1 - libsolidity/formal/SMTLib2Interface.cpp | 1 - libsolutil/CommonIO.h | 1 - test/CMakeLists.txt | 2 +- tools/CMakeLists.txt | 2 +- 6 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index fe1bbad7a..5db82b368 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) endif() add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS}) -target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system) +target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::system) if (${Z3_FOUND}) target_link_libraries(solidity PUBLIC z3::libz3) diff --git a/libsolidity/formal/CHCSmtLib2Interface.cpp b/libsolidity/formal/CHCSmtLib2Interface.cpp index 1b58b791e..4b52b7d5a 100644 --- a/libsolidity/formal/CHCSmtLib2Interface.cpp +++ b/libsolidity/formal/CHCSmtLib2Interface.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 76527baeb..339a0a8ad 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include diff --git a/libsolutil/CommonIO.h b/libsolutil/CommonIO.h index be763c42f..38c8f564a 100644 --- a/libsolutil/CommonIO.h +++ b/libsolutil/CommonIO.h @@ -24,7 +24,6 @@ #pragma once #include -#include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d63d9aec5..ff545faf3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -187,7 +187,7 @@ add_executable(soltest ${sources} ${libsolidity_util_sources} ${yul_phaser_sources} ) -target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc) +target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::filesystem Boost::program_options Boost::unit_test_framework evmc) # Special compilation flag for Visual Studio (version 2019 at least affected) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 241a80f46..f6b87c9f6 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -42,6 +42,6 @@ add_executable(yul-phaser yulPhaser/SimulationRNG.h yulPhaser/SimulationRNG.cpp ) -target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options) +target_link_libraries(yul-phaser PRIVATE solidity Boost::filesystem Boost::program_options) install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}") From ad1d2558eb9835eebd984b5e5e80f63a758a2104 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2020 11:36:11 +0100 Subject: [PATCH 46/65] Drop Boost::system from libsolidity --- libsolidity/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 5db82b368..96ccff9e4 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) endif() add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS}) -target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::system) +target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost) if (${Z3_FOUND}) target_link_libraries(solidity PUBLIC z3::libz3) From 091abcea8cbe42d0e3082d276b8ffae99c745e24 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Mon, 11 May 2020 16:19:55 +0530 Subject: [PATCH 47/65] Updated docs for type(X).min and type(X).max --- docs/cheatsheet.rst | 2 ++ docs/types/value-types.rst | 3 +++ docs/units-and-global-variables.rst | 16 ++++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 5d6169a48..e8ecb0af6 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -122,6 +122,8 @@ Global Variables - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. - ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information`. +- ``type(X).min`` (``X``): the minimum value representable by the integer type ``X``, see :ref:`Type Information`. +- ``type(X).max`` (``X``): the maximum value representable by the integer type ``X``, see :ref:`Type Information`. .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 0301a8ad5..65cbd5504 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -40,6 +40,9 @@ Operators: * Shift operators: ``<<`` (left shift), ``>>`` (right shift) * Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) +For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to +access the minimum and maximum value representable by the type. + .. warning:: Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 54eed07a9..22c6fc12d 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -294,10 +294,11 @@ Furthermore, all functions of the current contract are callable directly includi Type Information ---------------- -The expression ``type(X)`` can be used to retrieve information about the -type ``X``. Currently, there is limited support for this feature, but -it might be expanded in the future. The following properties are -available for a contract type ``C``: +The expression ``type(X)`` can be used to retrieve information about the type +``X``. Currently, there is limited support for this feature (``X`` can be either +a contract or an integer type) but it might be expanded in the future. + +The following properties are available for a contract type ``C``: ``type(C).name`` The name of the contract. @@ -328,3 +329,10 @@ for an interface type ``I``: interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all function selectors defined within the interface itself - excluding all inherited functions. +The following properties are available for an integer type ``I``: + +``type(I).min`` + The smallest value representable by type ``I``. + +``type(I).max`` + The largest value representable by type ``I``. From 76dfda7c1c03e728d96a29e2a4db5dc2a56bf7a8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2020 12:38:49 +0100 Subject: [PATCH 48/65] Replace boost:variant with std::variant in StandardCompiler --- libsolidity/interface/StandardCompiler.cpp | 18 +++++++++--------- libsolidity/interface/StandardCompiler.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 15989dcf3..65b948bb2 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include @@ -491,7 +491,7 @@ std::optional checkOutputSelection(Json::Value const& _outputSelect } /// Validates the optimizer settings and returns them in a parsed object. /// On error returns the json-formatted error message. -boost::variant parseOptimizerSettings(Json::Value const& _jsonInput) +std::variant parseOptimizerSettings(Json::Value const& _jsonInput) { if (auto result = checkOptimizerKeys(_jsonInput)) return *result; @@ -552,7 +552,7 @@ boost::variant parseOptimizerSettings(Json::Valu } -boost::variant StandardCompiler::parseInput(Json::Value const& _input) +std::variant StandardCompiler::parseInput(Json::Value const& _input) { InputsAndSettings ret; @@ -740,10 +740,10 @@ boost::variant StandardCompile if (settings.isMember("optimizer")) { auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]); - if (optimiserSettings.type() == typeid(Json::Value)) - return boost::get(std::move(optimiserSettings)); // was an error + if (std::holds_alternative(optimiserSettings)) + return std::get(std::move(optimiserSettings)); // was an error else - ret.optimiserSettings = boost::get(std::move(optimiserSettings)); + ret.optimiserSettings = std::get(std::move(optimiserSettings)); } Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); @@ -1155,9 +1155,9 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept try { auto parsed = parseInput(_input); - if (parsed.type() == typeid(Json::Value)) - return boost::get(std::move(parsed)); - InputsAndSettings settings = boost::get(std::move(parsed)); + if (std::holds_alternative(parsed)) + return std::get(std::move(parsed)); + InputsAndSettings settings = std::get(std::move(parsed)); if (settings.language == "Solidity") return compileSolidity(std::move(settings)); else if (settings.language == "Yul") diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 885417d8b..d1a1489c3 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -24,9 +24,9 @@ #include -#include #include #include +#include namespace solidity::frontend { @@ -73,7 +73,7 @@ private: /// Parses the input json (and potentially invokes the read callback) and either returns /// it in condensed form or an error as a json object. - boost::variant parseInput(Json::Value const& _input); + std::variant parseInput(Json::Value const& _input); Json::Value compileSolidity(InputsAndSettings _inputsAndSettings); Json::Value compileYul(InputsAndSettings _inputsAndSettings); From 81397dc2c51e2a077a72009025ef736dc374de59 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2020 13:15:53 +0100 Subject: [PATCH 49/65] Remove "using boost*;" statements --- libyul/Utilities.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp index 8789381b7..a797258eb 100644 --- a/libyul/Utilities.cpp +++ b/libyul/Utilities.cpp @@ -38,9 +38,6 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::util; -using boost::split; -using boost::is_any_of; - string solidity::yul::reindent(string const& _code) { int constexpr indentationWidth = 4; @@ -55,7 +52,7 @@ string solidity::yul::reindent(string const& _code) }; vector lines; - split(lines, _code, is_any_of("\n")); + boost::split(lines, _code, boost::is_any_of("\n")); for (string& line: lines) boost::trim(line); From fe431320033d341c782297f38e5e1fb42c791ead Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Mon, 11 May 2020 14:59:06 +0200 Subject: [PATCH 50/65] Fix clang v10 compilation errors --- libsolidity/analysis/GlobalContext.cpp | 2 +- libsolidity/analysis/ImmutableValidator.cpp | 2 +- libsolidity/codegen/ContractCompiler.cpp | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index a0764edba..d08f11979 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -123,7 +123,7 @@ vector GlobalContext::declarations() const { vector declarations; declarations.reserve(m_magicVariables.size()); - for (ASTPointer const& variable: m_magicVariables) + for (ASTPointer const& variable: m_magicVariables) declarations.push_back(variable.get()); return declarations; } diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 0fad0a66d..e4fcfadcf 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -43,7 +43,7 @@ void ImmutableValidator::analyze() visitCallableIfNew(*contract->constructor()); for (ContractDefinition const* contract: linearizedContracts) - for (std::shared_ptr const inheritSpec: contract->baseContracts()) + for (std::shared_ptr const& inheritSpec: contract->baseContracts()) if (auto args = inheritSpec->arguments()) ASTNode::listAccept(*args, *this); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 319d7a7ad..678921824 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -587,13 +587,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) if (!_function.isConstructor()) // adding 1 for return address. m_context.adjustStackOffset(parametersSize + 1); - for (ASTPointer const& variable: _function.parameters()) + for (ASTPointer const& variable: _function.parameters()) { m_context.addVariable(*variable, parametersSize); parametersSize -= variable->annotation().type->sizeOnStack(); } - for (ASTPointer const& variable: _function.returnParameters()) + for (ASTPointer const& variable: _function.returnParameters()) appendStackVariableInitialisation(*variable); if (_function.isConstructor()) @@ -650,7 +650,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) if (stackLayout[i] != i) solAssert(false, "Invalid stack layout on cleanup."); - for (ASTPointer const& variable: _function.parameters() + _function.returnParameters()) + for (ASTPointer const& variable: _function.parameters() + _function.returnParameters()) m_context.removeVariable(*variable); m_context.adjustStackOffset(-(int)c_returnValuesSize); From c0bf5292367b26bb014fa7cde4524c1dd4997ad0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 May 2020 12:50:31 +0200 Subject: [PATCH 51/65] Support bare calls. --- .../codegen/ir/IRGeneratorForStatements.cpp | 193 +++++++++++------- .../codegen/ir/IRGeneratorForStatements.h | 7 + libsolidity/codegen/ir/IRVariable.cpp | 2 +- .../abiEncoderV1/abi_encode_call.sol | 2 + .../builtinFunctions/sha256_empty.sol | 2 - ...vm_exceptions_in_constructor_call_fail.sol | 2 + .../delegatecall_return_value.sol | 1 + ...elegatecall_return_value_pre_byzantium.sol | 1 + ...iccall_for_view_and_pure_pre_byzantium.sol | 1 + 9 files changed, 136 insertions(+), 75 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index ef971b1f4..7faf0d43c 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -706,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: + appendExternalFunctionCall(_functionCall, arguments); + break; case FunctionType::Kind::BareCall: case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareStaticCall: - appendExternalFunctionCall(_functionCall, arguments); + appendBareCall(_functionCall, arguments); break; case FunctionType::Kind::BareCallCode: solAssert(false, "Callcode has been removed."); @@ -1755,18 +1757,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall( ) { FunctionType const& funType = dynamic_cast(type(_functionCall.expression())); - solAssert( - funType.takesArbitraryParameters() || - _arguments.size() == funType.parameterTypes().size(), "" - ); - solUnimplementedAssert(!funType.bound(), ""); + solAssert(!funType.takesArbitraryParameters(), ""); + solAssert(_arguments.size() == funType.parameterTypes().size(), ""); + solAssert(!funType.isBareCall(), ""); FunctionType::Kind const funKind = funType.kind(); - solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); - solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); + solAssert( + funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall, + "Can only be used for regular external calls." + ); - bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; - bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); + solUnimplementedAssert(!funType.bound(), ""); + + bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall; + bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall(); ReturnInfo const returnInfo{m_context.evmVersion(), funType}; @@ -1778,7 +1782,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall( argumentStrings += IRVariable(*arg).stackSlots(); } - solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); if (!m_context.evmVersion().canOverchargeGasForCall()) { @@ -1790,33 +1793,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall( m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n"; } - ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); - Whiskers templ(R"( - - if iszero(extcodesize(
)) { revert(0, 0) } - + if iszero(extcodesize(
)) { revert(0, 0) } // storage for arguments and returned data let := - - - mstore(, ()) - - let := ( - - - - add(, 4) - - - ) + mstore(, ()) + let := (add(, 4) ) let := (,
, , , sub(, ), , ) if iszero() { () } - let + let if { // copy dynamic return data out @@ -1827,12 +1816,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall( mstore(, add(, ())) // decode return parameters from external try-call into retVars - := (, add(, )) + := (, add(, )) } )"); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); - templ("bareCall", funType.isBareCall()); if (_functionCall.annotation().tryCall) templ("success", m_context.trySuccessConditionVariable(_functionCall)); else @@ -1840,17 +1828,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("freeMemory", freeMemory()); templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); - if (!funType.isBareCall()) - templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); - - if (funKind == FunctionType::Kind::ECRecover) - templ("address", "1"); - else if (funKind == FunctionType::Kind::SHA256) - templ("address", "2"); - else if (funKind == FunctionType::Kind::RIPEMD160) - templ("address", "3"); - else - templ("address", IRVariable(_functionCall.expression()).part("address").name()); + templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); + templ("address", IRVariable(_functionCall.expression()).part("address").name()); // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. // This ensures it can catch badly formatted input from external calls. @@ -1863,50 +1842,27 @@ void IRGeneratorForStatements::appendExternalFunctionCall( string const retVars = IRVariable(_functionCall).commaSeparatedList(); templ("retVars", retVars); - templ("hasRetVars", !retVars.empty()); solAssert(retVars.empty() == returnInfo.returnTypes.empty(), ""); templ("roundUp", m_utils.roundUpFunction()); - templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true)); + templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true)); templ("dynamicReturnSize", returnInfo.dynamicReturnSize); templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); templ("noTryCall", !_functionCall.annotation().tryCall); - // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. - // Move arguments to memory, will not update the free memory pointer (but will update the memory - // pointer on the stack). - bool encodeInPlace = funType.takesArbitraryParameters() || funType.isBareCall(); - if (funType.kind() == FunctionType::Kind::ECRecover) - // This would be the only combination of padding and in-place encoding, - // but all parameters of ecrecover are value types anyway. - encodeInPlace = false; bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; - solUnimplementedAssert(encodeInPlace == !funType.padArguments(), ""); - if (encodeInPlace) - { - solUnimplementedAssert(!encodeForLibraryCall, ""); - templ("encodeArgs", abi.tupleEncoderPacked(argumentTypes, funType.parameterTypes())); - } - else - templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); + solAssert(funType.padArguments(), ""); + templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); - // Output data will replace input data, unless we have ECRecover (then, output - // area will be 32 bytes just before input area). - solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); - solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall"); templ("hasValue", !isDelegateCall && !useStaticCall); templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); - // Check that the target contract exists (has code) for non-low-level calls. - bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall); - templ("checkExistence", checkExistence); - if (funType.gasSet()) templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); else if (m_context.evmVersion().canOverchargeGasForCall()) @@ -1919,8 +1875,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall( u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; if (funType.valueSet()) gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; - if (!checkExistence) - gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); } // Order is important here, STATICCALL might overlap with DELEGATECALL. @@ -1933,8 +1887,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall( templ("forwardingRevert", m_utils.forwardingRevertFunction()); - solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, ""); - solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); + m_code << templ.render(); +} + +void IRGeneratorForStatements::appendBareCall( + FunctionCall const& _functionCall, + vector> const& _arguments +) +{ + FunctionType const& funType = dynamic_cast(type(_functionCall.expression())); + solAssert( + !funType.bound() && + !funType.takesArbitraryParameters() && + _arguments.size() == 1 && + funType.parameterTypes().size() == 1, "" + ); + FunctionType::Kind const funKind = funType.kind(); + + solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); + solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); + solAssert( + funKind == FunctionType::Kind::BareCall || + funKind == FunctionType::Kind::BareDelegateCall || + funKind == FunctionType::Kind::BareStaticCall, "" + ); + + solAssert(!_functionCall.annotation().tryCall, ""); + Whiskers templ(R"( + + let := mload() + let := sub(( , ), ) + + let := add(, 0x20) + let := mload() + + + let := (,
, , , , 0, 0) + + let := () + + )"); + + templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); + templ("pos", m_context.newYulVariable()); + templ("length", m_context.newYulVariable()); + + templ("arg", IRVariable(*_arguments.front()).commaSeparatedList()); + Type const& argType = type(*_arguments.front()); + if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory()) + templ("needsEncoding", false); + else + { + templ("needsEncoding", true); + ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); + templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()})); + } + + templ("success", IRVariable(_functionCall).tupleComponent(0).name()); + if (IRVariable(_functionCall).tupleComponent(1).type().category() == Type::Category::InaccessibleDynamic) + templ("returndataVar", ""); + else + { + templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).part("mpos").name()); + templ("extractReturndataFunction", m_utils.extractReturndataFunction()); + } + + templ("address", IRVariable(_functionCall.expression()).part("address").name()); + + if (funKind == FunctionType::Kind::BareCall) + { + templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); + templ("call", "call"); + } + else + { + solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall."); + templ("value", ""); + if (funKind == FunctionType::Kind::BareStaticCall) + templ("call", "staticcall"); + else + templ("call", "delegatecall"); + } + + if (funType.gasSet()) + templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); + else if (m_context.evmVersion().canOverchargeGasForCall()) + // Send all gas (requires tangerine whistle EVM) + templ("gas", "gas()"); + else + { + // send all gas except the amount needed to execute "SUB" and "CALL" + // @todo this retains too much gas for now, needs to be fine-tuned. + u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; + if (funType.valueSet()) + gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; + gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know + templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); + } m_code << templ.render(); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index c51b2225d..42d355ddd 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -106,6 +106,13 @@ private: std::vector> const& _arguments ); + /// Appends code for .call / .delegatecall / .staticcall. + /// All involved expressions have already been visited. + void appendBareCall( + FunctionCall const& _functionCall, + std::vector> const& _arguments + ); + /// @returns code that evaluates to the first unused memory slot (which does not have to /// be empty). static std::string freeMemory(); diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index d92a5fb53..1c5ed42bf 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -54,7 +54,7 @@ IRVariable IRVariable::part(string const& _name) const solAssert(itemName.empty() || itemType, ""); return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type}; } - solAssert(false, "Invalid stack item name."); + solAssert(false, "Invalid stack item name: " + _name); } vector IRVariable::stackSlots() const diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol index d73db5da6..d11914fce 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol @@ -22,5 +22,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol index ededa2fab..bb9fc3616 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol @@ -3,7 +3,5 @@ contract C { return sha256(""); } } -// ==== -// compileViaYul: also // ---- // f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol index 2192dabf3..4517c9702 100644 --- a/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol +++ b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol @@ -14,6 +14,8 @@ contract B { } } +// ==== +// compileViaYul: also // ---- // testIt() -> // test() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol index ffa22b8a2..0a80a9f5c 100644 --- a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol @@ -22,6 +22,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >=byzantium // ---- // get() -> 0x00 diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol index 498449d17..00f1f8a77 100644 --- a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol @@ -24,6 +24,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: 0x00 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol index a193e8752..e9157e34c 100644 --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol @@ -32,6 +32,7 @@ contract D { } } // ==== +// compileViaYul: also // EVMVersion: 0x1 From 875415a1325844e42373b358c941b8cd1c0e8a83 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2020 12:05:39 +0100 Subject: [PATCH 52/65] Replace boost/algorithm/cxx11 with C++11 features --- libsolidity/analysis/SyntaxChecker.cpp | 7 +++++-- libsolidity/analysis/TypeChecker.cpp | 7 +++++-- libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/interface/StandardCompiler.cpp | 1 - libyul/optimiser/ControlFlowSimplifier.cpp | 6 +++--- libyul/optimiser/StructuralSimplifier.cpp | 1 - libyul/optimiser/UnusedPruner.cpp | 7 +++---- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 058244cac..86204e88e 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -335,7 +334,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) { // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") - if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr)) + if (std::all_of( + _statement.declarations().begin(), + _statement.declarations().end(), + [](ASTPointer const& declaration) { return declaration == nullptr; } + )) m_errorReporter.syntaxError( 3299_error, _statement.location(), diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b9c85f545..f38161593 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -34,7 +34,6 @@ #include #include -#include #include #include @@ -1058,7 +1057,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) // No initial value is only permitted for single variables with specified type. if (_statement.declarations().size() != 1 || !_statement.declarations().front()) { - if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr)) + if (std::all_of( + _statement.declarations().begin(), + _statement.declarations().end(), + [](ASTPointer const& declaration) { return declaration == nullptr; } + )) { // The syntax checker has already generated an error for this case (empty LHS tuple). solAssert(m_errorReporter.hasErrors(), ""); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 519eea81a..da561050b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -67,7 +67,7 @@ #include -#include +#include #include using namespace std; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 65b948bb2..0abc867e8 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -31,7 +31,6 @@ #include #include -#include #include #include diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 1640fa1b0..1d6b09615 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -25,7 +25,6 @@ #include #include -#include using namespace std; using namespace solidity; @@ -60,8 +59,9 @@ void removeEmptyDefaultFromSwitch(Switch& _switchStmt) void removeEmptyCasesFromSwitch(Switch& _switchStmt) { - bool hasDefault = boost::algorithm::any_of( - _switchStmt.cases, + bool hasDefault = std::any_of( + _switchStmt.cases.begin(), + _switchStmt.cases.end(), [](Case const& _case) { return !_case.value; } ); diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 836169701..0d5da95c1 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -22,7 +22,6 @@ #include #include -#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 9dc7cedc3..6ed8df47c 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -29,8 +29,6 @@ #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::yul; @@ -85,8 +83,9 @@ void UnusedPruner::operator()(Block& _block) // movable or it returns a single value. In the latter case, we // replace `let a := f()` by `pop(f())` (in pure Yul, this will be // `drop(f())`). - if (boost::algorithm::none_of( - varDecl.variables, + if (std::none_of( + varDecl.variables.begin(), + varDecl.variables.end(), [=](TypedName const& _typedName) { return used(_typedName.name); } )) { From c0c5127ed050a0a8012d8a5325bbff4ac5333e7f Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Mon, 11 May 2020 19:05:41 +0530 Subject: [PATCH 53/65] Changed a potentially misleading name --- docs/cheatsheet.rst | 4 ++-- docs/units-and-global-variables.rst | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index e8ecb0af6..5e673f7b0 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -122,8 +122,8 @@ Global Variables - ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information`. - ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information`. - ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information`. -- ``type(X).min`` (``X``): the minimum value representable by the integer type ``X``, see :ref:`Type Information`. -- ``type(X).max`` (``X``): the maximum value representable by the integer type ``X``, see :ref:`Type Information`. +- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information`. +- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information`. .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 22c6fc12d..d936ef246 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -329,10 +329,10 @@ for an interface type ``I``: interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all function selectors defined within the interface itself - excluding all inherited functions. -The following properties are available for an integer type ``I``: +The following properties are available for an integer type ``T``: -``type(I).min`` - The smallest value representable by type ``I``. +``type(T).min`` + The smallest value representable by type ``T``. -``type(I).max`` - The largest value representable by type ``I``. +``type(T).max`` + The largest value representable by type ``T``. From 79b217dfb3b96015d77f5dabcac0bf5a0330bc54 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 6 May 2020 17:21:44 +0200 Subject: [PATCH 54/65] Callvalue checks for implicit constructors. --- Changelog.md | 4 ++ docs/bugs.json | 8 +++ docs/bugs_by_version.json | 54 ++++++++++++++++++- libsolidity/codegen/ContractCompiler.cpp | 7 ++- .../constructor/callvalue_check.sol | 40 ++++++++++++++ .../constructor/no_callvalue_check.sol | 21 ++++++++ .../constructor/nonpayable_new.sol | 23 ++++++++ .../syntaxTests/constructor/payable_new.sol | 16 ++++++ 8 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/semanticTests/constructor/callvalue_check.sol create mode 100644 test/libsolidity/semanticTests/constructor/no_callvalue_check.sol create mode 100644 test/libsolidity/syntaxTests/constructor/nonpayable_new.sol create mode 100644 test/libsolidity/syntaxTests/constructor/payable_new.sol diff --git a/Changelog.md b/Changelog.md index 086e80ee9..c3a1d3855 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ ### 0.6.8 (unreleased) +Important Bugfixes: + * Add missing callvalue check to the creation code of a contract that does not define a constructor but has a base that does define a constructor. + + Language Features: diff --git a/docs/bugs.json b/docs/bugs.json index 56bae3aaf..c251fa114 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,12 @@ [ + { + "name": "ImplicitConstructorCallvalueCheck", + "summary": "The creation code of a contract that does not define a constructor but has a base that does define a constructor did not revert for calls with non-zero value.", + "description": "Starting from Solidity 0.4.5 the creation code of contracts without explicit payable constructor is supposed to contain a callvalue check that results in contract creation reverting, if non-zero value is passed. However, this check was missing in case no explicit constructor was defined in a contract at all, but the contract has a base that does define a constructor. In these cases it is possible to send value in a contract creation transaction or using inline assembly without revert, even though the creation code is supposed to be non-payable.", + "introduced": "0.4.5", + "fixed": "0.6.8", + "severity": "very low" + }, { "name": "TupleAssignmentMultiStackSlotComponents", "summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 4c27e90d9..0398e4cd7 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -415,6 +415,7 @@ }, "0.4.10": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -433,6 +434,7 @@ }, "0.4.11": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -450,6 +452,7 @@ }, "0.4.12": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -466,6 +469,7 @@ }, "0.4.13": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -482,6 +486,7 @@ }, "0.4.14": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -497,6 +502,7 @@ }, "0.4.15": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -511,6 +517,7 @@ }, "0.4.16": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -527,6 +534,7 @@ }, "0.4.17": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -544,6 +552,7 @@ }, "0.4.18": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -560,6 +569,7 @@ }, "0.4.19": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -596,6 +606,7 @@ }, "0.4.20": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -613,6 +624,7 @@ }, "0.4.21": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -630,6 +642,7 @@ }, "0.4.22": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -647,6 +660,7 @@ }, "0.4.23": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -663,6 +677,7 @@ }, "0.4.24": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -679,6 +694,7 @@ }, "0.4.25": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -693,6 +709,7 @@ }, "0.4.26": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -739,6 +756,7 @@ }, "0.4.5": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -758,6 +776,7 @@ }, "0.4.6": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -776,6 +795,7 @@ }, "0.4.7": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -794,6 +814,7 @@ }, "0.4.8": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -812,6 +833,7 @@ }, "0.4.9": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -830,6 +852,7 @@ }, "0.5.0": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -844,6 +867,7 @@ }, "0.5.1": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -858,6 +882,7 @@ }, "0.5.10": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -868,6 +893,7 @@ }, "0.5.11": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -877,6 +903,7 @@ }, "0.5.12": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -886,6 +913,7 @@ }, "0.5.13": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -895,6 +923,7 @@ }, "0.5.14": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -905,6 +934,7 @@ }, "0.5.15": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -914,6 +944,7 @@ }, "0.5.16": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden" @@ -922,6 +953,7 @@ }, "0.5.17": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow" ], @@ -929,6 +961,7 @@ }, "0.5.2": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -943,6 +976,7 @@ }, "0.5.3": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -957,6 +991,7 @@ }, "0.5.4": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -971,6 +1006,7 @@ }, "0.5.5": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -987,6 +1023,7 @@ }, "0.5.6": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -1003,6 +1040,7 @@ }, "0.5.7": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -1017,6 +1055,7 @@ }, "0.5.8": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -1030,6 +1069,7 @@ }, "0.5.9": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -1042,6 +1082,7 @@ }, "0.6.0": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "YulOptimizerRedundantAssignmentBreakContinue" @@ -1050,6 +1091,7 @@ }, "0.6.1": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow" ], @@ -1057,6 +1099,7 @@ }, "0.6.2": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow" ], @@ -1064,6 +1107,7 @@ }, "0.6.3": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow" ], @@ -1071,6 +1115,7 @@ }, "0.6.4": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow" ], @@ -1078,16 +1123,21 @@ }, "0.6.5": { "bugs": [ + "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents" ], "released": "2020-04-06" }, "0.6.6": { - "bugs": [], + "bugs": [ + "ImplicitConstructorCallvalueCheck" + ], "released": "2020-04-09" }, "0.6.7": { - "bugs": [], + "bugs": [ + "ImplicitConstructorCallvalueCheck" + ], "released": "2020-05-04" } } \ No newline at end of file diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 319d7a7ad..7d07841f8 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -157,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c if (FunctionDefinition const* constructor = _contract.constructor()) appendConstructor(*constructor); - else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract())) - appendBaseConstructor(*c); else + { + // Implicit constructors are always non-payable. appendCallValueCheck(); + if (auto c = _contract.nextConstructor(m_context.mostDerivedContract())) + appendBaseConstructor(*c); + } } size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract) diff --git a/test/libsolidity/semanticTests/constructor/callvalue_check.sol b/test/libsolidity/semanticTests/constructor/callvalue_check.sol new file mode 100644 index 000000000..7a327e695 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/callvalue_check.sol @@ -0,0 +1,40 @@ +contract A1 { constructor() public {} } +contract B1 is A1 {} + +contract A2 { constructor() public payable {} } +contract B2 is A2 {} + +contract B3 {} + +contract B4 { constructor() public {} } + +contract C { + function createWithValue(bytes memory c, uint256 value) public payable returns (bool) { + uint256 y = 0; + assembly { y := create(value, add(c, 0x20), mload(c)) } + return y != 0; + } + function f(uint256 value) public payable returns (bool) { + return createWithValue(type(B1).creationCode, value); + } + function g(uint256 value) public payable returns (bool) { + return createWithValue(type(B2).creationCode, value); + } + function h(uint256 value) public payable returns (bool) { + return createWithValue(type(B3).creationCode, value); + } + function i(uint256 value) public payable returns (bool) { + return createWithValue(type(B4).creationCode, value); + } +} +// ==== +// EVMVersion: >homestead +// ---- +// f(uint256), 2000 ether: 0 -> true +// f(uint256), 2000 ether: 100 -> false +// g(uint256), 2000 ether: 0 -> true +// g(uint256), 2000 ether: 100 -> false +// h(uint256), 2000 ether: 0 -> true +// h(uint256), 2000 ether: 100 -> false +// i(uint256), 2000 ether: 0 -> true +// i(uint256), 2000 ether: 100 -> false diff --git a/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol new file mode 100644 index 000000000..9744aa3ce --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol @@ -0,0 +1,21 @@ +contract A1 {} +contract B1 is A1 { constructor() public payable {} } + +contract A2 { constructor() public {} } +contract B2 is A2 { constructor() public payable {} } + +contract B3 { constructor() public payable {} } + +contract C { + function f() public payable returns (bool) { + // Make sure none of these revert. + new B1{value: 10}(); + new B2{value: 10}(); + new B3{value: 10}(); + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// f(), 2000 ether -> true diff --git a/test/libsolidity/syntaxTests/constructor/nonpayable_new.sol b/test/libsolidity/syntaxTests/constructor/nonpayable_new.sol new file mode 100644 index 000000000..c89508743 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/nonpayable_new.sol @@ -0,0 +1,23 @@ +contract A1 { constructor() public {} } +contract B1 is A1 {} + +contract A2 { constructor() public payable {} } +contract B2 is A2 {} + +contract B3 {} + +contract B4 { constructor() public {} } + +contract C { + function f() public payable { + new B1{value: 10}(); + new B2{value: 10}(); + new B3{value: 10}(); + new B4{value: 10}(); + } +} +// ---- +// TypeError: (235-252): Cannot set option "value", since the constructor of contract B1 is not payable. +// TypeError: (258-275): Cannot set option "value", since the constructor of contract B2 is not payable. +// TypeError: (281-298): Cannot set option "value", since the constructor of contract B3 is not payable. +// TypeError: (304-321): Cannot set option "value", since the constructor of contract B4 is not payable. diff --git a/test/libsolidity/syntaxTests/constructor/payable_new.sol b/test/libsolidity/syntaxTests/constructor/payable_new.sol new file mode 100644 index 000000000..fb8e9ef53 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/payable_new.sol @@ -0,0 +1,16 @@ +contract A1 {} +contract B1 is A1 { constructor() public payable {} } + +contract A2 { constructor() public {} } +contract B2 is A2 { constructor() public payable {} } + +contract B3 { constructor() public payable {} } + +contract C { + function f() public payable { + new B1{value: 10}(); + new B2{value: 10}(); + new B3{value: 10}(); + } +} +// ---- From c1ed5bbb0ff078ff5cfe7287a603b1385e6d6ee9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 11 May 2020 16:53:45 +0200 Subject: [PATCH 55/65] Update Dockerfiles and CI scripts to Ubuntu 20.04 and simplify them. --- .circleci/README.md | 10 +- .circleci/config.yml | 78 ++++++------ .circleci/docker/Dockerfile.ubuntu1904.clang | 113 ------------------ ...rfile.ubuntu1904 => Dockerfile.ubuntu2004} | 44 +------ .circleci/docker/Dockerfile.ubuntu2004.clang | 61 ++++++++++ libsolidity/analysis/ViewPureChecker.cpp | 2 +- libyul/AsmAnalysis.cpp | 2 +- libyul/backends/evm/EVMCodeTransform.cpp | 10 +- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 2 +- .../optimiser/RedundantAssignEliminator.cpp | 2 +- libyul/optimiser/UnusedPruner.cpp | 2 +- scripts/docs.sh | 2 +- 12 files changed, 121 insertions(+), 207 deletions(-) delete mode 100644 .circleci/docker/Dockerfile.ubuntu1904.clang rename .circleci/docker/{Dockerfile.ubuntu1904 => Dockerfile.ubuntu2004} (52%) create mode 100644 .circleci/docker/Dockerfile.ubuntu2004.clang diff --git a/.circleci/README.md b/.circleci/README.md index b48adba24..338e858d9 100644 --- a/.circleci/README.md +++ b/.circleci/README.md @@ -7,15 +7,15 @@ The docker images are build locally on the developer machine: ```sh cd .circleci/docker/ -docker build -t ethereum/solidity-buildpack-deps:ubuntu1904- -f Dockerfile.ubuntu1904 . -docker push ethereum/solidity-buildpack-deps:ubuntu1904- +docker build -t ethereum/solidity-buildpack-deps:ubuntu2004- -f Dockerfile.ubuntu2004 . +docker push ethereum/solidity-buildpack-deps:ubuntu2004- ``` -The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `-docker-image-rev` (e.g., `ubuntu-1904-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ. +The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `-docker-image-rev` (e.g., `ubuntu-2004-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ. Once the docker image has been built and pushed to Dockerhub, you can find it at: - https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904- + https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu2004- where the image tag reflects the target OS and revision to build Solidity and run its tests on. @@ -24,7 +24,7 @@ where the image tag reflects the target OS and revision to build Solidity and ru ```sh cd solidity # Mounts your local solidity directory in docker container for testing -docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904- /bin/bash +docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu2004- /bin/bash cd /src/solidity ``` diff --git a/.circleci/config.yml b/.circleci/config.yml index e1161e432..5ef9ea07e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,12 +10,12 @@ parameters: ubuntu-1804-docker-image-rev: type: string default: "4" - ubuntu-1904-docker-image-rev: + ubuntu-2004-docker-image-rev: type: string - default: "4" - ubuntu-1904-clang-docker-image-rev: + default: "1" + ubuntu-2004-clang-docker-image-rev: type: string - default: "5" + default: "1" ubuntu-1604-clang-ossfuzz-docker-image-rev: type: string default: "2" @@ -137,9 +137,9 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - test_ubuntu1904_clang: &test_ubuntu1904_clang + - test_ubuntu2004_clang: &test_ubuntu2004_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >> steps: - checkout - attach_workspace: @@ -148,9 +148,9 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - test_ubuntu1904: &test_ubuntu1904 + - test_ubuntu2004: &test_ubuntu2004 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> steps: - checkout - attach_workspace: @@ -160,7 +160,7 @@ defaults: - store_artifacts: *artifacts_test_results - test_asan: &test_asan - <<: *test_ubuntu1904 + <<: *test_ubuntu2004 steps: - checkout - attach_workspace: @@ -179,7 +179,7 @@ defaults: tags: only: /.*/ - - workflow_ubuntu1904: &workflow_ubuntu1904 + - workflow_ubuntu2004: &workflow_ubuntu2004 <<: *workflow_trigger_on_tags requires: - b_ubu @@ -189,17 +189,17 @@ defaults: requires: - b_ubu_ossfuzz - - workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang + - workflow_ubuntu2004_clang: &workflow_ubuntu2004_clang <<: *workflow_trigger_on_tags requires: - b_ubu_clang - - workflow_ubuntu1904_release: &workflow_ubuntu1904_release + - workflow_ubuntu2004_release: &workflow_ubuntu2004_release <<: *workflow_trigger_on_tags requires: - b_ubu_release - - workflow_ubuntu1904_codecov: &workflow_ubuntu1904_codecov + - workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov <<: *workflow_trigger_on_tags requires: - b_ubu_codecov @@ -209,7 +209,7 @@ defaults: requires: - b_osx - - workflow_ubuntu1904_asan: &workflow_ubuntu1904_asan + - workflow_ubuntu2004_asan: &workflow_ubuntu2004_asan <<: *workflow_trigger_on_tags requires: - b_ubu_asan @@ -359,16 +359,16 @@ jobs: chk_docs_pragma_min_version: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> environment: TERM: xterm steps: - checkout - run: *run_docs_pragma_min_version - b_ubu_clang: &build_ubuntu1904_clang + b_ubu_clang: &build_ubuntu2004_clang docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >> environment: CC: clang CXX: clang++ @@ -378,9 +378,9 @@ jobs: - store_artifacts: *artifacts_solc - persist_to_workspace: *artifacts_executables - b_ubu: &build_ubuntu1904 + b_ubu: &build_ubuntu2004 docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> steps: - checkout - run: *run_build @@ -388,8 +388,8 @@ jobs: - store_artifacts: *artifacts_tools - persist_to_workspace: *artifacts_executables - b_ubu_release: &build_ubuntu1904_release - <<: *build_ubuntu1904 + b_ubu_release: &build_ubuntu2004_release + <<: *build_ubuntu2004 environment: FORCE_RELEASE: ON @@ -406,7 +406,7 @@ jobs: - persist_to_workspace: *artifacts_executables b_ubu_codecov: - <<: *build_ubuntu1904 + <<: *build_ubuntu2004 environment: COVERAGE: ON CMAKE_BUILD_TYPE: Debug @@ -416,7 +416,7 @@ jobs: - persist_to_workspace: *artifacts_build_dir t_ubu_codecov: - <<: *test_ubuntu1904 + <<: *test_ubuntu2004 environment: EVM: constantinople OPTIMIZE: 1 @@ -439,7 +439,7 @@ jobs: # Builds in C++20 mode and uses debug build in order to speed up. # Do *NOT* store any artifacts or workspace as we don't run tests on this build. b_ubu_cxx20: - <<: *build_ubuntu1904 + <<: *build_ubuntu2004 environment: CMAKE_BUILD_TYPE: Debug CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF @@ -591,7 +591,7 @@ jobs: # x64 ASAN build, for testing for memory related bugs b_ubu_asan: &b_ubu_asan - <<: *build_ubuntu1904 + <<: *build_ubuntu2004 environment: CMAKE_OPTIONS: -DSANITIZE=address CMAKE_BUILD_TYPE: Release @@ -603,7 +603,7 @@ jobs: b_docs: docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> steps: - checkout - run: *setup_prerelease_commit_hash @@ -615,11 +615,11 @@ jobs: destination: docs-html t_ubu_soltest: &t_ubu_soltest - <<: *test_ubuntu1904 + <<: *test_ubuntu2004 t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> environment: EVM: constantinople SOLTEST_FLAGS: --enforce-via-yul @@ -635,7 +635,7 @@ jobs: t_ubu_clang_soltest: &t_ubu_clang_soltest - <<: *test_ubuntu1904_clang + <<: *test_ubuntu2004_clang environment: EVM: constantinople OPTIMIZE: 0 @@ -645,7 +645,7 @@ jobs: t_ubu_cli: &t_ubu_cli docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + - image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >> environment: TERM: xterm steps: @@ -833,21 +833,21 @@ workflows: # Ubuntu build and tests - b_ubu: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags - - t_ubu_cli: *workflow_ubuntu1904 - - t_ubu_soltest: *workflow_ubuntu1904 - - t_ubu_soltest_enforce_yul: *workflow_ubuntu1904 + - t_ubu_cli: *workflow_ubuntu2004 + - t_ubu_soltest: *workflow_ubuntu2004 + - t_ubu_soltest_enforce_yul: *workflow_ubuntu2004 - b_ubu_clang: *workflow_trigger_on_tags - - t_ubu_clang_soltest: *workflow_ubuntu1904_clang + - t_ubu_clang_soltest: *workflow_ubuntu2004_clang # Ubuntu fake release build and tests - b_ubu_release: *workflow_trigger_on_tags - - t_ubu_release_cli: *workflow_ubuntu1904_release - - t_ubu_release_soltest: *workflow_ubuntu1904_release + - t_ubu_release_cli: *workflow_ubuntu2004_release + - t_ubu_release_soltest: *workflow_ubuntu2004_release # ASan build and tests - b_ubu_asan: *workflow_trigger_on_tags - - t_ubu_asan_constantinople: *workflow_ubuntu1904_asan - - t_ubu_asan_cli: *workflow_ubuntu1904_asan + - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan + - t_ubu_asan_cli: *workflow_ubuntu2004_asan # Emscripten build and selected tests - b_ems: *workflow_trigger_on_tags @@ -874,4 +874,4 @@ workflows: # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags - - t_ubu_codecov: *workflow_ubuntu1904_codecov + - t_ubu_codecov: *workflow_ubuntu2004_codecov diff --git a/.circleci/docker/Dockerfile.ubuntu1904.clang b/.circleci/docker/Dockerfile.ubuntu1904.clang deleted file mode 100644 index cf790c84e..000000000 --- a/.circleci/docker/Dockerfile.ubuntu1904.clang +++ /dev/null @@ -1,113 +0,0 @@ -# vim:syntax=dockerfile -#------------------------------------------------------------------------------ -# Dockerfile for building and testing Solidity Compiler on CI -# Target: Ubuntu 19.04 (Disco Dingo) Clang variant -# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps -# -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016-2019 solidity contributors. -#------------------------------------------------------------------------------ -FROM buildpack-deps:disco AS base - -ARG DEBIAN_FRONTEND=noninteractive - -RUN set -ex; \ - dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ - echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ - apt-get update; \ - apt-get install -qqy --no-install-recommends \ - build-essential \ - software-properties-common \ - cmake ninja-build \ - clang++-8 llvm-8-dev \ - libjsoncpp-dev \ - libleveldb1d \ - ; \ - apt-get install -qy python-pip python-sphinx; \ - update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \ - update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 1; \ - update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 1; \ - pip install codecov; \ - rm -rf /var/lib/apt/lists/* - -FROM base AS libraries - -ENV CC clang -ENV CXX clang++ - -# Boost -RUN git clone -b boost-1.69.0 https://github.com/boostorg/boost.git \ - /usr/src/boost; \ - cd /usr/src/boost; \ - git submodule update --init --recursive; \ - ./bootstrap.sh --with-toolset=clang --prefix=/usr; \ - ./b2 toolset=clang headers; \ - ./b2 toolset=clang variant=release \ - system filesystem unit_test_framework program_options \ - install -j $(($(nproc)/2)); \ - rm -rf /usr/src/boost - -# Z3 -RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \ - /usr/src/z3; \ - cd /usr/src/z3; \ - python scripts/mk_make.py --prefix=/usr ; \ - cd build; \ - make -j; \ - make install; \ - rm -rf /usr/src/z3; - -# OSSFUZZ: libprotobuf-mutator -RUN set -ex; \ - git clone https://github.com/google/libprotobuf-mutator.git \ - /usr/src/libprotobuf-mutator; \ - cd /usr/src/libprotobuf-mutator; \ - git checkout 3521f47a2828da9ace403e4ecc4aece1a84feb36; \ - mkdir build; \ - cd build; \ - cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \ - -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - cp -vpr external.protobuf/bin/* /usr/bin/; \ - cp -vpr external.protobuf/include/* /usr/include/; \ - cp -vpr external.protobuf/lib/* /usr/lib/; \ - ninja install/strip; \ - rm -rf /usr/src/libprotobuf-mutator - -# EVMONE -RUN set -ex; \ - cd /usr/src; \ - git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ - cd evmone; \ - mkdir build; \ - cd build; \ - # isoltest links against the evmone shared library - cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ - ninja; \ - ninja install/strip; \ - # abiv2_proto_ossfuzz links against the evmone standalone static library - cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \ - ninja; \ - ninja install/strip; \ - rm -rf /usr/src/evmone - -FROM base -COPY --from=libraries /usr/lib /usr/lib -COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu2004 similarity index 52% rename from .circleci/docker/Dockerfile.ubuntu1904 rename to .circleci/docker/Dockerfile.ubuntu2004 index 903b6945b..d39025809 100644 --- a/.circleci/docker/Dockerfile.ubuntu1904 +++ b/.circleci/docker/Dockerfile.ubuntu2004 @@ -21,59 +21,26 @@ # # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ -FROM buildpack-deps:disco AS base +FROM buildpack-deps:focal AS base ARG DEBIAN_FRONTEND=noninteractive RUN set -ex; \ - dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ - echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ apt-get update; \ apt-get install -qqy --no-install-recommends \ build-essential \ software-properties-common \ - cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \ + cmake ninja-build \ libboost-filesystem-dev libboost-test-dev libboost-system-dev \ libboost-program-options-dev \ - libjsoncpp-dev \ - llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \ + libcvc4-dev z3 libz3-dev \ ; \ - apt-get install -qy python-pip python-sphinx; \ - update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \ - pip install codecov; \ + apt-get install -qy python3-pip python3-sphinx; \ + pip3 install codecov; \ rm -rf /var/lib/apt/lists/* FROM base AS libraries -# OSSFUZZ: libprotobuf-mutator -RUN set -ex; \ - git clone https://github.com/google/libprotobuf-mutator.git \ - /usr/src/libprotobuf-mutator; \ - cd /usr/src/libprotobuf-mutator; \ - git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \ - mkdir build; \ - cd build; \ - cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \ - -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - cp -vpr external.protobuf/bin/* /usr/bin/; \ - cp -vpr external.protobuf/include/* /usr/include/; \ - cp -vpr external.protobuf/lib/* /usr/lib/; \ - ninja install/strip; \ - rm -rf /usr/src/libprotobuf-mutator - -# OSSFUZZ: libfuzzer -RUN set -ex; \ - cd /var/tmp; \ - svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer; \ - mkdir -p build-libfuzzer; \ - cd build-libfuzzer; \ - clang++-8 -O1 -stdlib=libstdc++ -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer; \ - ar r /usr/lib/libFuzzingEngine.a *.o; \ - rm -rf /var/lib/libfuzzer - # EVMONE RUN set -ex; \ cd /usr/src; \ @@ -81,7 +48,6 @@ RUN set -ex; \ cd evmone; \ mkdir build; \ cd build; \ - # isoltest links against the evmone shared library cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ ninja; \ ninja install/strip; \ diff --git a/.circleci/docker/Dockerfile.ubuntu2004.clang b/.circleci/docker/Dockerfile.ubuntu2004.clang new file mode 100644 index 000000000..f436c34b7 --- /dev/null +++ b/.circleci/docker/Dockerfile.ubuntu2004.clang @@ -0,0 +1,61 @@ +# vim:syntax=dockerfile +#------------------------------------------------------------------------------ +# Dockerfile for building and testing Solidity Compiler on CI +# Target: Ubuntu 19.04 (Disco Dingo) Clang variant +# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps +# +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016-2019 solidity contributors. +#------------------------------------------------------------------------------ +FROM buildpack-deps:focal AS base + +ARG DEBIAN_FRONTEND=noninteractive + +RUN set -ex; \ + apt-get update; \ + apt-get install -qqy --no-install-recommends \ + build-essential \ + software-properties-common \ + cmake ninja-build \ + libboost-filesystem-dev libboost-test-dev libboost-system-dev \ + libboost-program-options-dev \ + clang llvm-dev \ + z3 libz3-dev \ + ; \ + rm -rf /var/lib/apt/lists/* + +FROM base AS libraries + +ENV CC clang +ENV CXX clang++ + +# EVMONE +RUN set -ex; \ + cd /usr/src; \ + git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + cd evmone; \ + mkdir build; \ + cd build; \ + cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ + ninja; \ + ninja install/strip; \ + rm -rf /usr/src/evmone + +FROM base +COPY --from=libraries /usr/lib /usr/lib +COPY --from=libraries /usr/bin /usr/bin +COPY --from=libraries /usr/include /usr/include diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index dcd6f6a9c..230e0a8ba 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -230,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) { AssemblyViewPureChecker{ _inlineAssembly.dialect(), - [=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); } + [&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); } }(_inlineAssembly.operations()); } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 5c6b7c927..2edd4dc38 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -527,7 +527,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation // Similarly we assume bitwise shifting and create2 go together. yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - auto errorForVM = [=](string const& vmKindMessage) { + auto errorForVM = [&](string const& vmKindMessage) { typeError( _location, "The \"" + diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index b9c5cb0ee..9e8d4d662 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -85,11 +85,11 @@ void VariableReferenceCounter::operator()(Block const& _block) void VariableReferenceCounter::increaseRefIfFound(YulString _variableName) { m_scope->lookup(_variableName, GenericVisitor{ - [=](Scope::Variable const& _var) + [&](Scope::Variable const& _var) { ++m_context.variableReferences[&_var]; }, - [=](Scope::Function const&) { } + [](Scope::Function const&) { } }); } @@ -272,7 +272,7 @@ void CodeTransform::operator()(FunctionCall const& _call) Scope::Function* function = nullptr; yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{ - [=](Scope::Variable&) { yulAssert(false, "Expected function name."); }, + [](Scope::Variable&) { yulAssert(false, "Expected function name."); }, [&](Scope::Function& _function) { function = &_function; } }), "Function name not found."); yulAssert(function, ""); @@ -296,7 +296,7 @@ void CodeTransform::operator()(Identifier const& _identifier) // First search internals, then externals. yulAssert(m_scope, ""); if (m_scope->lookup(_identifier.name, GenericVisitor{ - [=](Scope::Variable& _var) + [&](Scope::Variable& _var) { // TODO: opportunity for optimization: Do not DUP if this is the last reference // to the top most element of the stack @@ -307,7 +307,7 @@ void CodeTransform::operator()(Identifier const& _identifier) m_assembly.appendConstant(u256(0)); decreaseReference(_identifier.name, _var); }, - [=](Scope::Function&) + [](Scope::Function&) { yulAssert(false, "Function not removed during desugaring."); } diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index dc69d913a..ebcdd961b 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -1232,7 +1232,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) FunctionHoister::run(context, ast); FunctionGrouper::run(context, ast); - MainFunction{}(ast); + MainFunction::run(context, ast); ForLoopConditionIntoBody::run(context, ast); ExpressionSplitter::run(context, ast); WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser); diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index 236a27fb0..5d1d0557b 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -306,7 +306,7 @@ void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEli void AssignmentRemover::operator()(Block& _block) { - boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool { + boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool { return holds_alternative(_statement) && m_toRemove.count(&std::get(_statement)); }); diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 6ed8df47c..a0e0f535d 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -86,7 +86,7 @@ void UnusedPruner::operator()(Block& _block) if (std::none_of( varDecl.variables.begin(), varDecl.variables.end(), - [=](TypedName const& _typedName) { return used(_typedName.name); } + [&](TypedName const& _typedName) { return used(_typedName.name); } )) { if (!varDecl.value) diff --git a/scripts/docs.sh b/scripts/docs.sh index 2c08a82bc..91f619d89 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -28,6 +28,6 @@ set -e cd docs -pip install -r requirements.txt +pip3 install -r requirements.txt sphinx-build -nW -b html -d _build/doctrees . _build/html cd .. From c2f5a66a79def58aa56e38d25122a92f1d782a6d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 6 May 2020 17:40:06 +0200 Subject: [PATCH 56/65] Implement .creationCode for Sol->Yul. --- .../codegen/ir/IRGeneratorForStatements.cpp | 15 ++++++++++- .../constructor/callvalue_check.sol | 1 + .../various/code_access_create.sol | 2 ++ .../various/code_access_runtime.sol | 27 +++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/semanticTests/various/code_access_runtime.sol diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 7faf0d43c..553f618cd 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1304,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(false, "Blockhash has been removed."); else if (member == "creationCode" || member == "runtimeCode") { - solUnimplementedAssert(false, ""); + solUnimplementedAssert(member != "runtimeCode", ""); + TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + m_context.subObjectsCreated().insert(&contract); + m_code << Whiskers(R"( + let := datasize("") + let := (add(, 32)) + mstore(, ) + datacopy(add(, 32), dataoffset(""), ) + )") + ("allocationFunction", m_utils.allocationFunction()) + ("size", m_context.newYulVariable()) + ("objectName", m_context.creationObjectName(contract)) + ("result", IRVariable(_memberAccess).commaSeparatedList()).render(); } else if (member == "name") { diff --git a/test/libsolidity/semanticTests/constructor/callvalue_check.sol b/test/libsolidity/semanticTests/constructor/callvalue_check.sol index 7a327e695..c096382d0 100644 --- a/test/libsolidity/semanticTests/constructor/callvalue_check.sol +++ b/test/libsolidity/semanticTests/constructor/callvalue_check.sol @@ -29,6 +29,7 @@ contract C { } // ==== // EVMVersion: >homestead +// compileViaYul: also // ---- // f(uint256), 2000 ether: 0 -> true // f(uint256), 2000 ether: 100 -> false diff --git a/test/libsolidity/semanticTests/various/code_access_create.sol b/test/libsolidity/semanticTests/various/code_access_create.sol index 3fbcf6132..9029e59ec 100644 --- a/test/libsolidity/semanticTests/various/code_access_create.sol +++ b/test/libsolidity/semanticTests/various/code_access_create.sol @@ -22,5 +22,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // test() -> 7 diff --git a/test/libsolidity/semanticTests/various/code_access_runtime.sol b/test/libsolidity/semanticTests/various/code_access_runtime.sol new file mode 100644 index 000000000..cc1401c6f --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_runtime.sol @@ -0,0 +1,27 @@ +contract D { + uint256 x; + + constructor() public { + x = 7; + } + + function f() public view returns (uint256) { + return x; + } +} + + +contract C { + function test() public returns (uint256) { + D d = new D(); + bytes32 hash; + assembly { hash := extcodehash(d) } + assert(hash == keccak256(type(D).runtimeCode)); + return 42; + } +} + +// ==== +// EVMVersion: >=constantinople +// ---- +// test() -> 42 From 00946f3ea09321c2db7799897b183ba48a7cc597 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 20 Apr 2020 14:06:16 +0200 Subject: [PATCH 57/65] ossfuzz: Add sol proto fuzzer skeleton Co-Authored-By: Leonardo --- test/tools/ossfuzz/protoToSol.cpp | 227 ++++++++++++++++++++++++++++++ test/tools/ossfuzz/protoToSol.h | 185 ++++++++++++++++++++++++ test/tools/ossfuzz/solProto.proto | 111 +++++++++++++++ 3 files changed, 523 insertions(+) create mode 100644 test/tools/ossfuzz/protoToSol.cpp create mode 100644 test/tools/ossfuzz/protoToSol.h create mode 100644 test/tools/ossfuzz/solProto.proto diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp new file mode 100644 index 000000000..9551ee3ec --- /dev/null +++ b/test/tools/ossfuzz/protoToSol.cpp @@ -0,0 +1,227 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include + +using namespace solidity::test::solprotofuzzer; +using namespace solidity::util; +using namespace std; + +string ProtoConverter::protoToSolidity(Program const& _p) +{ + // Create random number generator with fuzzer supplied + // seed. + m_randomGen = make_shared(_p.seed()); + return visit(_p); +} + +pair ProtoConverter::generateTestCase(TestContract const& _testContract) +{ + ostringstream testCode; + string usingLibDecl; + switch (_testContract.type()) + { + case TestContract::LIBRARY: + { + m_libraryTest = true; + auto testTuple = pseudoRandomLibraryTest(); + m_libraryName = get<0>(testTuple); + usingLibDecl = Whiskers(R"( + using for uint;)") + ("libraryName", get<0>(testTuple)) + .render(); + testCode << Whiskers(R"( + uint x; + if (x.() != ) + return 1;)") + ("testFunction", get<1>(testTuple)) + ("expectedOutput", get<2>(testTuple)) + .render(); + break; + } + case TestContract::CONTRACT: + { + unsigned errorCode = 1; + unsigned contractVarIndex = 0; + for (auto const& testTuple: m_contractTests) + { + // Do this to avoid stack too deep errors + // We require uint as a return var, so we + // cannot have more than 16 variables without + // running into stack too deep errors + if (contractVarIndex >= s_maxVars) + break; + string contractName = testTuple.first; + string contractVarName = "tc" + to_string(contractVarIndex); + testCode << Whiskers(R"( + = new ();)") + ("contractName", contractName) + ("contractVarName", contractVarName) + .render(); + for (auto const& t: testTuple.second) + { + testCode << Whiskers(R"( + if (.() != ) + return ;)") + ("contractVarName", contractVarName) + ("testFunction", t.first) + ("expectedOutput", t.second) + ("errorCode", to_string(errorCode)) + .render(); + errorCode++; + } + contractVarIndex++; + } + break; + } + } + // Expected return value when all tests pass + testCode << Whiskers(R"( + return 0;)") + .render(); + return pair(usingLibDecl, testCode.str()); +} + +string ProtoConverter::visit(TestContract const& _testContract) +{ + string testCode; + string usingLibDecl; + m_libraryTest = false; + + // Simply return valid uint (zero) if there are + // no tests. + if (emptyLibraryTests() || emptyContractTests()) + testCode = Whiskers(R"( + return 0;)") + .render(); + else + tie(usingLibDecl, testCode) = generateTestCase(_testContract); + + return Whiskers(R"( +contract C { + function test() public returns (uint) + { + } +} +)") + ("isLibrary", m_libraryTest) + ("usingDecl", usingLibDecl) + ("testCode", testCode) + .render(); +} + +bool ProtoConverter::libraryTest() const +{ + return m_libraryTest; +} + +string ProtoConverter::libraryName() const +{ + return m_libraryName; +} + +string ProtoConverter::visit(Program const& _p) +{ + ostringstream program; + ostringstream contracts; + + for (auto &contract: _p.contracts()) + contracts << visit(contract); + + program << Whiskers(R"( +pragma solidity >=0.0; + + + + +)") + ("contracts", contracts.str()) + ("testContract", visit(_p.test())) + .render(); + return program.str(); +} + +string ProtoConverter::visit(ContractType const& _contractType) +{ + switch (_contractType.contract_type_oneof_case()) + { + case ContractType::kC: + return visit(_contractType.c()); + case ContractType::kL: + return visit(_contractType.l()); + case ContractType::kI: + return visit(_contractType.i()); + case ContractType::CONTRACT_TYPE_ONEOF_NOT_SET: + return ""; + } +} + +string ProtoConverter::visit(Contract const& _contract) +{ + openProgramScope(&_contract); + return ""; +} + +string ProtoConverter::visit(Interface const& _interface) +{ + openProgramScope(&_interface); + return ""; +} + +string ProtoConverter::visit(Library const& _library) +{ + openProgramScope(&_library); + return ""; +} + +tuple ProtoConverter::pseudoRandomLibraryTest() +{ + solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found"); + unsigned index = randomNumber() % m_libraryTests.size(); + return m_libraryTests[index]; +} + +void ProtoConverter::openProgramScope(CIL _program) +{ + string programNamePrefix; + if (holds_alternative(_program)) + programNamePrefix = "C"; + else if (holds_alternative(_program)) + programNamePrefix = "I"; + else + programNamePrefix = "L"; + string programName = programNamePrefix + to_string(m_programNumericSuffix++); + m_programNameMap.emplace(_program, programName); +} + +string ProtoConverter::programName(CIL _program) +{ + solAssert(m_programNameMap.count(_program), "Sol proto fuzzer: Unregistered program"); + return m_programNameMap[_program]; +} + +unsigned ProtoConverter::randomNumber() +{ + solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator"); + return m_randomGen->operator()(); +} diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h new file mode 100644 index 000000000..6a534d71c --- /dev/null +++ b/test/tools/ossfuzz/protoToSol.h @@ -0,0 +1,185 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + +#include + +#include +#include +#include +#include + +namespace solidity::test::solprotofuzzer +{ +/// Random number generator that is seeded with a fuzzer +/// supplied unsigned integer. +struct SolRandomNumGenerator +{ + using RandomEngine = std::minstd_rand; + + explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {} + + /// @returns a pseudo random unsigned integer + unsigned operator()() + { + return m_random(); + } + + RandomEngine m_random; +}; + +/* There are two types of tests created by the converter: + * - library test + * - contract test + * + * The template for library test is the following: + * + * // Library generated from fuzzer protobuf specification + * library L0 { + * function f0(uint) public view returns (uint) { + * return 31337; + * } + * function f1(uint) public pure returns (uint) { + * return 455; + * } + * } + * library L1 { + * function f0(uint) external view returns (uint) { + * return 607; + * } + * } + * + * // Test entry point + * contract C { + * // Uses a single pseudo randomly chosen library + * // and calls a pseudo randomly chosen function + * // returning a non-zero error code on failure or + * // a zero uint when test passes. + * using L0 for uint; + * function test() public pure returns (uint) { + * uint x; + * if (x.f1() != 455) + * return 1; + * return 0; + * } + * } + * + * The template for contract test is the following + * // Contracts generated from fuzzer protobuf specification + * contract C0B { + * function f0() public pure virtual returns (uint) + * { + * return 42; + * } + * } + * contract C0 is C0B { + * function f0() public pure override returns (uint) + * { + * return 1337; + * } + * } + * + * // Test entry point + * contract C { + * // Invokes one or more contract functions returning + * // a non-zero error code for failure, a zero uint + * // when all tests pass + * function test() public pure returns (uint) + * { + * C0 tc0 = new C0(); + * if (tc0.f0() != 1337) + * return 1; + * C0B tc1 = new C0B(); + * if (tc1.f0() != 42) + * return 2; + * // Expected return value if all tests pass + * return 0; + * } + * } + */ +class ProtoConverter +{ +public: + ProtoConverter() {} + ProtoConverter(ProtoConverter const&) = delete; + ProtoConverter(ProtoConverter&&) = delete; + std::string protoToSolidity(Program const&); + /// @returns true if test calls a library function, false + /// otherwise + bool libraryTest() const; + /// @returns name of the library under test + std::string libraryName() const; +private: + /// Variant type that points to one of contract, interface, library protobuf messages + using CIL = std::variant; + /// Protobuf message visitors that accept a const reference to a protobuf message + /// type and return its solidity translation. + std::string visit(Program const&); + std::string visit(TestContract const&); + std::string visit(ContractType const&); + std::string visit(Interface const& _interface); + std::string visit(Library const& _library); + std::string visit(Contract const& _contract); + /// @returns a string pair containing a library declaration (relevant for library + /// tests only) and a solidity test case + std::pair generateTestCase(TestContract const& _testContract); + /// @returns name of a program i.e., contract, library or interface + std::string programName(CIL _program); + /// @returns a tuple containing the names of the library and function under + /// test, and its expected output. + std::tuple pseudoRandomLibraryTest(); + /// Performs bookkeeping for a fuzzer-supplied program + void openProgramScope(CIL _program); + /// @returns a deterministic pseudo random unsigned integer + unsigned randomNumber(); + /// @returns true if fuzzer supplied Library protobuf message + /// contains zero functions, false otherwise. + static bool emptyLibrary(Library const& _library) + { + return _library.funcdef_size() == 0; + } + /// @returns true if there are no valid library test cases, false + /// otherwise. + bool emptyLibraryTests() + { + return m_libraryTests.size() == 0; + } + /// @returns true if there are no valid contract test cases, false + /// otherwise. + bool emptyContractTests() + { + return m_contractTests.size() == 0; + } + /// Numeric suffix that is part of program names e.g., "0" in "C0" + unsigned m_programNumericSuffix = 0; + /// Flag that states whether library call is tested (true) or not (false). + bool m_libraryTest = false; + /// A smart pointer to fuzzer driven random number generator + std::shared_ptr m_randomGen; + /// Maps protobuf program to its string name + std::map m_programNameMap; + /// List of tuples containing library name, function and its expected output + std::vector> m_libraryTests; + /// Maps contract name to a map of function names and their expected output + std::map> m_contractTests; + /// Name of the library under test, relevant if m_libraryTest is set + std::string m_libraryName; + /// Maximum number of local variables in test function to avoid stack too deep + /// errors + static unsigned constexpr s_maxVars = 15; +}; +} diff --git a/test/tools/ossfuzz/solProto.proto b/test/tools/ossfuzz/solProto.proto new file mode 100644 index 000000000..f0444e0d7 --- /dev/null +++ b/test/tools/ossfuzz/solProto.proto @@ -0,0 +1,111 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +syntax = "proto2"; + +message InterfaceFunction { + enum StateMutability { + PURE = 0; + VIEW = 1; + PAYABLE = 2; + NONPAYABLE = 3; + } + required StateMutability mut = 1; +} + +message LibraryFunction { + // Library functions cannot be payable + enum StateMutability { + PURE = 0; + VIEW = 1; + NONPAYABLE = 2; + } + enum Visibility { + PUBLIC = 0; + EXTERNAL = 1; + INTERNAL = 2; + PRIVATE = 3; + } + required Visibility vis = 1; + required StateMutability mut = 2; +} + +message ContractFunction { + enum StateMutability { + PURE = 0; + VIEW = 1; + PAYABLE = 2; + NONPAYABLE = 3; + } + enum Visibility { + PUBLIC = 0; + EXTERNAL = 1; + INTERNAL = 2; + PRIVATE = 3; + } + required Visibility vis = 1; + required StateMutability mut = 2; + required bool virtualfunc = 3; +} + +message Library { + repeated LibraryFunction funcdef = 1; +} + +message Interface { + repeated InterfaceFunction funcdef = 1; + repeated Interface bases = 2; +} + +message Contract { + repeated ContractFunction funcdef = 1; + required bool abstract = 2; + repeated ContractOrInterface bases = 3; +} + +message ContractOrInterface { + oneof contract_or_interface_oneof { + Contract c = 1; + Interface i = 2; + } +} + +message ContractType { + oneof contract_type_oneof { + Contract c = 1; + Library l = 2; + Interface i = 3; + } +} + +message TestContract { + enum Type { + LIBRARY = 0; + CONTRACT = 1; + } + required Type type = 1; +} + +message Program { + repeated ContractType contracts = 1; + required TestContract test = 2; + // Seed is an unsigned integer that initializes + // a pseudo random number generator. + required uint64 seed = 3; +} + +package solidity.test.solprotofuzzer; \ No newline at end of file From 596ac018f53c287f55c6bbd5f5af41156dd1da6f Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 20 Apr 2020 16:08:31 +0200 Subject: [PATCH 58/65] Fuzzer: Add test harness for solidity inheritance protobuf fuzzer Co-Authored-By: Leonardo --- .circleci/config.yml | 2 + test/tools/ossfuzz/CMakeLists.txt | 21 ++- test/tools/ossfuzz/protoToSol.cpp | 107 ++++++------ test/tools/ossfuzz/protoToSol.h | 4 +- test/tools/ossfuzz/solProto.proto | 2 +- test/tools/ossfuzz/solProtoFuzzer.cpp | 234 ++++++++++++++++++++++++++ 6 files changed, 313 insertions(+), 57 deletions(-) create mode 100644 test/tools/ossfuzz/solProtoFuzzer.cpp diff --git a/.circleci/config.yml b/.circleci/config.yml index e1161e432..f2b58bf47 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,6 +50,7 @@ defaults: cd build protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz + protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4 @@ -97,6 +98,7 @@ defaults: - test/tools/ossfuzz/strictasm_opt_ossfuzz - test/tools/ossfuzz/yul_proto_diff_ossfuzz - test/tools/ossfuzz/yul_proto_ossfuzz + - test/tools/ossfuzz/sol_proto_ossfuzz # test result output directory - artifacts_test_results: &artifacts_test_results diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 83543b9f0..ccd71a983 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -10,7 +10,7 @@ add_dependencies(ossfuzz if (OSSFUZZ) add_custom_target(ossfuzz_proto) - add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz) + add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_proto_ossfuzz) add_custom_target(ossfuzz_abiv2) add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz) @@ -78,6 +78,25 @@ if (OSSFUZZ) protobuf.a ) set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + + add_executable(sol_proto_ossfuzz + solProtoFuzzer.cpp + protoToSol.cpp + solProto.pb.cc + abiV2FuzzerCommon.cpp + ../../EVMHost.cpp + ) + target_include_directories(sol_proto_ossfuzz PRIVATE + /usr/include/libprotobuf-mutator + ) + target_link_libraries(sol_proto_ossfuzz PRIVATE solidity libsolc + evmc + evmone-standalone + protobuf-mutator-libfuzzer.a + protobuf-mutator.a + protobuf.a + ) + set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) else() add_library(solc_opt_ossfuzz solc_opt_ossfuzz.cpp diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp index 9551ee3ec..aefbb41b9 100644 --- a/test/tools/ossfuzz/protoToSol.cpp +++ b/test/tools/ossfuzz/protoToSol.cpp @@ -46,17 +46,22 @@ pair ProtoConverter::generateTestCase(TestContract const& _testC m_libraryTest = true; auto testTuple = pseudoRandomLibraryTest(); m_libraryName = get<0>(testTuple); - usingLibDecl = Whiskers(R"( - using for uint;)") - ("libraryName", get<0>(testTuple)) - .render(); - testCode << Whiskers(R"( - uint x; - if (x.() != ) - return 1;)") - ("testFunction", get<1>(testTuple)) - ("expectedOutput", get<2>(testTuple)) - .render(); + Whiskers u(R"(using for uint;)"); + u("ind", "\t"); + u("libraryName", get<0>(testTuple)); + usingLibDecl = u.render(); + Whiskers test(R"()"); + test("endl", "\n"); + test("ind", "\t\t"); + test("varDecl", "uint x;"); + Whiskers ifStmt(R"(if ()return 1;)"); + Whiskers ifCond(R"(x.() != )"); + ifCond("testFunction", get<1>(testTuple)); + ifCond("expectedOutput", get<2>(testTuple)); + ifStmt("cond", ifCond.render()); + ifStmt("endl", "\n"); + ifStmt("ind", "\t\t\t"); + test("ifStmt", ifStmt.render()); break; } case TestContract::CONTRACT: @@ -73,21 +78,28 @@ pair ProtoConverter::generateTestCase(TestContract const& _testC break; string contractName = testTuple.first; string contractVarName = "tc" + to_string(contractVarIndex); - testCode << Whiskers(R"( - = new ();)") - ("contractName", contractName) - ("contractVarName", contractVarName) - .render(); + Whiskers init(R"( = new ();)"); + init("endl", "\n"); + init("ind", "\t\t"); + init("contractName", contractName); + init("contractVarName", contractVarName); + testCode << init.render(); for (auto const& t: testTuple.second) { - testCode << Whiskers(R"( - if (.() != ) - return ;)") - ("contractVarName", contractVarName) - ("testFunction", t.first) - ("expectedOutput", t.second) - ("errorCode", to_string(errorCode)) - .render(); + Whiskers tc(R"()"); + tc("endl", "\n"); + tc("ind", "\t\t"); + Whiskers ifStmt(R"(if ()return ;)"); + Whiskers ifCond(R"(.() != )"); + ifCond("contractVarName", contractVarName); + ifCond("testFunction", t.first); + ifCond("expectedOutput", t.second); + ifStmt("endl", "\n"); + ifStmt("cond", ifCond.render()); + ifStmt("ind", "\t\t\t"); + ifStmt("errorCode", to_string(errorCode)); + tc("ifStmt", ifStmt.render()); + testCode << tc.render(); errorCode++; } contractVarIndex++; @@ -96,10 +108,8 @@ pair ProtoConverter::generateTestCase(TestContract const& _testC } } // Expected return value when all tests pass - testCode << Whiskers(R"( - return 0;)") - .render(); - return pair(usingLibDecl, testCode.str()); + testCode << Whiskers(R"(return 0;)")("endl", "\n")("ind", "\t\t").render(); + return {usingLibDecl, testCode.str()}; } string ProtoConverter::visit(TestContract const& _testContract) @@ -111,23 +121,20 @@ string ProtoConverter::visit(TestContract const& _testContract) // Simply return valid uint (zero) if there are // no tests. if (emptyLibraryTests() || emptyContractTests()) - testCode = Whiskers(R"( - return 0;)") - .render(); + testCode = Whiskers(R"(return 0;)")("endl", "\n")("ind", "\t\t").render(); else tie(usingLibDecl, testCode) = generateTestCase(_testContract); - return Whiskers(R"( -contract C { - function test() public returns (uint) - { - } -} -)") - ("isLibrary", m_libraryTest) - ("usingDecl", usingLibDecl) - ("testCode", testCode) - .render(); + Whiskers c(R"(contract C {})"); + c("endl", "\n"); + c("isLibrary", m_libraryTest); + c("usingDecl", usingLibDecl); + Whiskers f("function test() public returns (uint){}"); + f("ind", "\t"); + f("endl", "\n"); + f("testCode", testCode); + c("function", f.render()); + return c.render(); } bool ProtoConverter::libraryTest() const @@ -148,17 +155,11 @@ string ProtoConverter::visit(Program const& _p) for (auto &contract: _p.contracts()) contracts << visit(contract); - program << Whiskers(R"( -pragma solidity >=0.0; - - - - -)") - ("contracts", contracts.str()) - ("testContract", visit(_p.test())) - .render(); - return program.str(); + Whiskers p(R"(pragma solidity >=0.0;)"); + p("endl", "\n"); + p("contracts", contracts.str()); + p("test", visit(_p.test())); + return p.render(); } string ProtoConverter::visit(ContractType const& _contractType) diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h index 6a534d71c..de2220752 100644 --- a/test/tools/ossfuzz/protoToSol.h +++ b/test/tools/ossfuzz/protoToSol.h @@ -156,13 +156,13 @@ private: /// otherwise. bool emptyLibraryTests() { - return m_libraryTests.size() == 0; + return m_libraryTests.empty(); } /// @returns true if there are no valid contract test cases, false /// otherwise. bool emptyContractTests() { - return m_contractTests.size() == 0; + return m_contractTests.empty(); } /// Numeric suffix that is part of program names e.g., "0" in "C0" unsigned m_programNumericSuffix = 0; diff --git a/test/tools/ossfuzz/solProto.proto b/test/tools/ossfuzz/solProto.proto index f0444e0d7..86e938c7c 100644 --- a/test/tools/ossfuzz/solProto.proto +++ b/test/tools/ossfuzz/solProto.proto @@ -108,4 +108,4 @@ message Program { required uint64 seed = 3; } -package solidity.test.solprotofuzzer; \ No newline at end of file +package solidity.test.solprotofuzzer; diff --git a/test/tools/ossfuzz/solProtoFuzzer.cpp b/test/tools/ossfuzz/solProtoFuzzer.cpp new file mode 100644 index 000000000..80facb3e1 --- /dev/null +++ b/test/tools/ossfuzz/solProtoFuzzer.cpp @@ -0,0 +1,234 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include +#include + +#include +#include + +#include + +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; + +using namespace solidity::test::abiv2fuzzer; +using namespace solidity::test::solprotofuzzer; +using namespace solidity; +using namespace solidity::test; +using namespace solidity::util; +using namespace std; + +namespace +{ +/// Test function returns a uint256 value +static size_t const expectedOutputLength = 32; +/// Expected output value is decimal 0 +static uint8_t const expectedOutput[expectedOutputLength] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/// Compares the contents of the memory address pointed to +/// by `_result` of `_length` bytes to the expected output. +/// Returns true if `_result` matches expected output, false +/// otherwise. +bool isOutputExpected(evmc::result const& _run) +{ + if (_run.output_size != expectedOutputLength) + return false; + + return memcmp(_run.output_data, expectedOutput, expectedOutputLength) == 0; +} + +/// Accepts a reference to a user-specified input and returns an +/// evmc_message with all of its fields zero initialized except +/// gas and input fields. +/// The gas field is set to the maximum permissible value so that we +/// don't run into out of gas errors. The input field is copied from +/// user input. +evmc_message initializeMessage(bytes const& _input) +{ + // Zero initialize all message fields + evmc_message msg = {}; + // Gas available (value of type int64_t) is set to its maximum + // value. + msg.gas = std::numeric_limits::max(); + msg.input_data = _input.data(); + msg.input_size = _input.size(); + return msg; +} + +/// Accepts host context implementation, and keccak256 hash of the function +/// to be called at a specified address in the simulated blockchain as +/// input and returns the result of the execution of the called function. +evmc::result executeContract( + EVMHost& _hostContext, + bytes const& _functionHash, + evmc_address _deployedAddress +) +{ + evmc_message message = initializeMessage(_functionHash); + message.destination = _deployedAddress; + message.kind = EVMC_CALL; + return _hostContext.call(message); +} + +/// Accepts a reference to host context implementation and byte code +/// as input and deploys it on the simulated blockchain. Returns the +/// result of deployment. +evmc::result deployContract(EVMHost& _hostContext, bytes const& _code) +{ + evmc_message message = initializeMessage(_code); + message.kind = EVMC_CREATE; + return _hostContext.call(message); +} + +std::pair compileContract( + std::string _sourceCode, + std::string _contractName, + std::map const& _libraryAddresses = {}, + frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal() +) +{ + try + { + // Compile contract generated by the proto fuzzer + SolidityCompilationFramework solCompilationFramework; + return std::make_pair( + solCompilationFramework.compileContract(_sourceCode, _contractName, _libraryAddresses, _optimization), + solCompilationFramework.getMethodIdentifiers() + ); + } + // Ignore stack too deep errors during compilation + catch (evmasm::StackTooDeepException const&) + { + return std::make_pair(bytes{}, Json::Value(0)); + } +} + +evmc::result deployAndExecute(EVMHost& _hostContext, bytes _byteCode, std::string _hexEncodedInput) +{ + // Deploy contract and signal failure if deploy failed + evmc::result createResult = deployContract(_hostContext, _byteCode); + solAssert( + createResult.status_code == EVMC_SUCCESS, + "Proto solc fuzzer: Contract creation failed" + ); + + // Execute test function and signal failure if EVM reverted or + // did not return expected output on successful execution. + evmc::result callResult = executeContract( + _hostContext, + fromHex(_hexEncodedInput), + createResult.create_address + ); + + // We don't care about EVM One failures other than EVMC_REVERT + solAssert(callResult.status_code != EVMC_REVERT, "Proto solc fuzzer: EVM One reverted"); + return callResult; +} + +evmc::result compileDeployAndExecute( + std::string _sourceCode, + std::string _contractName, + std::string _methodName, + frontend::OptimiserSettings _optimization, + std::string _libraryName = {} +) +{ + bytes libraryBytecode; + Json::Value libIds; + // We target the default EVM which is the latest + langutil::EVMVersion version = {}; + EVMHost hostContext(version, evmone); + std::map _libraryAddressMap; + + // First deploy library + if (!_libraryName.empty()) + { + tie(libraryBytecode, libIds) = compileContract( + _sourceCode, + _libraryName, + {}, + _optimization + ); + // Deploy contract and signal failure if deploy failed + evmc::result createResult = deployContract(hostContext, libraryBytecode); + solAssert( + createResult.status_code == EVMC_SUCCESS, + "Proto solc fuzzer: Library deployment failed" + ); + _libraryAddressMap[_libraryName] = EVMHost::convertFromEVMC(createResult.create_address); + } + + auto [bytecode, ids] = compileContract( + _sourceCode, + _contractName, + _libraryAddressMap, + _optimization + ); + + return deployAndExecute( + hostContext, + bytecode, + ids[_methodName].asString() + ); +} +} + +DEFINE_PROTO_FUZZER(Program const& _input) +{ + ProtoConverter converter; + string sol_source = converter.protoToSolidity(_input); + + if (char const* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) + { + // With libFuzzer binary run this to generate a YUL source file x.yul: + // PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input + ofstream of(dump_path); + of.write(sol_source.data(), sol_source.size()); + } + + if (char const* dump_path = getenv("SOL_DEBUG_FILE")) + { + sol_source.clear(); + // With libFuzzer binary run this to generate a YUL source file x.yul: + // PROTO_FUZZER_LOAD_PATH=x.yul ./a.out proto-input + ifstream ifstr(dump_path); + sol_source = { + std::istreambuf_iterator(ifstr), + std::istreambuf_iterator() + }; + std::cout << sol_source << std::endl; + } + + auto minimalResult = compileDeployAndExecute( + sol_source, + ":C", + "test()", + frontend::OptimiserSettings::minimal(), + converter.libraryTest() ? converter.libraryName() : "" + ); + bool successState = minimalResult.status_code == EVMC_SUCCESS; + if (successState) + solAssert( + isOutputExpected(minimalResult), + "Proto solc fuzzer: Output incorrect" + ); +} From 1d5350e32f04e991dbfc8fca402cbc8c7930e85d Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 12 May 2020 02:26:02 +0200 Subject: [PATCH 59/65] Add error IDs to OverrideChecker --- libsolidity/analysis/OverrideChecker.cpp | 35 ++++++++++++++---------- libsolidity/analysis/OverrideChecker.h | 4 +++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index 1f4fa10a2..4cf2ff8f4 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -506,12 +506,13 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr ); if (!_overriding.overrides()) - overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier."); + overrideError(_overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier."); if (_super.isVariable()) overrideError( _super, _overriding, + 1452_error, "Cannot override public state variable.", "Overriding " + _overriding.astNodeName() + " is here:" ); @@ -519,6 +520,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr overrideError( _super, _overriding, + 4334_error, "Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?", "Overriding " + _overriding.astNodeName() + " is here:" ); @@ -526,7 +528,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr if (_overriding.isVariable()) { if (_super.visibility() != Visibility::External) - overrideError(_overriding, _super, "Public state variables can only override functions with external visibility."); + overrideError(_overriding, _super, 5225_error, "Public state variables can only override functions with external visibility."); solAssert(_overriding.visibility() == Visibility::External, ""); } else if (_overriding.visibility() != _super.visibility()) @@ -537,7 +539,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr _super.visibility() == Visibility::External && _overriding.visibility() == Visibility::Public )) - overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " visibility differs."); + overrideError(_overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs."); } if (_super.isFunction()) @@ -548,7 +550,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!"); if (!functionType->hasEqualReturnTypes(*superType)) - overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " return types differ."); + overrideError(_overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ."); // This is only relevant for a function overriding a function. if (_overriding.isFunction()) @@ -557,6 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr overrideError( _overriding, _super, + 2837_error, "Overriding function changes state mutability from \"" + stateMutabilityToString(_super.stateMutability()) + "\" to \"" + @@ -568,6 +571,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr overrideError( _overriding, _super, + 4593_error, "Overriding an implemented function with an unimplemented function is not allowed." ); } @@ -577,6 +581,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr void OverrideChecker::overrideListError( OverrideProxy const& _item, set _secondary, + ErrorId _error, string const& _message1, string const& _message2 ) @@ -594,7 +599,7 @@ void OverrideChecker::overrideListError( contractSingularPlural = "contracts "; m_errorReporter.typeError( - 5883_error, + _error, _item.overrides() ? _item.overrides()->location() : _item.location(), ssl, _message1 + @@ -605,10 +610,10 @@ void OverrideChecker::overrideListError( ); } -void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg) +void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg) { m_errorReporter.typeError( - 9456_error, + _error, _overriding.location(), SecondarySourceLocation().append(_secondaryMsg, _super.location()), _message @@ -616,10 +621,10 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration } -void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg) +void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg) { m_errorReporter.typeError( - 1452_error, + _error, _overriding.location(), SecondarySourceLocation().append(_secondaryMsg, _super.location()), _message @@ -773,11 +778,11 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign 4520_error, list[i]->location(), ssl, - "Duplicate contract \"" + - joinHumanReadable(list[i]->namePath(), ".") + - "\" found in override list of \"" + - _item.name() + - "\"." + "Duplicate contract \"" + + joinHumanReadable(list[i]->namePath(), ".") + + "\" found in override list of \"" + + _item.name() + + "\"." ); } } @@ -810,6 +815,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign overrideListError( _item, missingContracts, + 4327_error, _item.astNodeNameCapitalized() + " needs to specify overridden ", "" ); @@ -819,6 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign overrideListError( _item, surplusContracts, + 2353_error, "Invalid ", "specified in override list: " ); diff --git a/libsolidity/analysis/OverrideChecker.h b/libsolidity/analysis/OverrideChecker.h index 9d7776028..d47b89f3d 100644 --- a/libsolidity/analysis/OverrideChecker.h +++ b/libsolidity/analysis/OverrideChecker.h @@ -32,6 +32,7 @@ namespace solidity::langutil { class ErrorReporter; +struct ErrorId; struct SourceLocation; } @@ -160,18 +161,21 @@ private: void overrideListError( OverrideProxy const& _item, std::set _secondary, + langutil::ErrorId _error, std::string const& _message1, std::string const& _message2 ); void overrideError( Declaration const& _overriding, Declaration const& _super, + langutil::ErrorId _error, std::string const& _message, std::string const& _secondaryMsg = "Overridden function is here:" ); void overrideError( OverrideProxy const& _overriding, OverrideProxy const& _super, + langutil::ErrorId _error, std::string const& _message, std::string const& _secondaryMsg = "Overridden function is here:" ); From bf7ab8d27731a497884a9b9e6477d1a958ff25c7 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Tue, 12 May 2020 13:00:29 +0530 Subject: [PATCH 60/65] Error ids only walk into a given set of directories --- scripts/correct_error_ids.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/scripts/correct_error_ids.py b/scripts/correct_error_ids.py index 8717dbf23..9fd3547f0 100644 --- a/scripts/correct_error_ids.py +++ b/scripts/correct_error_ids.py @@ -104,16 +104,14 @@ def find_source_files(top_dir): """Builds the list of .h and .cpp files in top_dir directory""" source_file_names = [] - black_set = { ".circleci", ".git", ".github", "build", "cmake", "CMakeFiles", "deps", "docs" } + dirs = ['libevmasm', 'liblangutil', 'libsolc', 'libsolidity', 'libsolutil', 'libyul', 'solc'] - for root, _, file_names in os.walk(top_dir, onerror=lambda e: exit(f"Walk error: {e}")): - path_elements = set(root.split(os.sep)) - if not black_set.isdisjoint(path_elements): - continue - for file_name in file_names: - _, ext = path.splitext(file_name) - if ext in [".h", ".cpp"]: - source_file_names.append(path.join(root, file_name)) + for dir in dirs: + for root, _, file_names in os.walk(os.path.join(top_dir, dir), onerror=lambda e: exit(f"Walk error: {e}")): + for file_name in file_names: + _, ext = path.splitext(file_name) + if ext in [".h", ".cpp"]: + source_file_names.append(path.join(root, file_name)) return source_file_names From 7cae074b8aa8a4098e51047d4d88a83224b10818 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 12 May 2020 02:29:54 +0200 Subject: [PATCH 61/65] Add error IDs to BMC --- libsolidity/formal/BMC.cpp | 40 +++++++++++++++++++++++--------------- libsolidity/formal/BMC.h | 9 +++++---- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index e8fc331fa..952eb1c50 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -21,8 +21,6 @@ #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::util; @@ -594,8 +592,7 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target) *_target.expression, _target.constraints, _target.value, - _target.callStack, - "Condition is always $VALUE." + _target.callStack ); } @@ -613,6 +610,8 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const& _target.callStack, _target.modelExpressions, _target.expression->location(), + 4144_error, + 8312_error, "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")", "", &_target.value @@ -633,6 +632,8 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _ _target.callStack, _target.modelExpressions, _target.expression->location(), + 2661_error, + 8065_error, "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")", "", &_target.value @@ -647,6 +648,8 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target) _target.callStack, _target.modelExpressions, _target.expression->location(), + 3046_error, + 5272_error, "Division by zero", "", &_target.value @@ -661,6 +664,8 @@ void BMC::checkBalance(BMCVerificationTarget& _target) _target.callStack, _target.modelExpressions, _target.expression->location(), + 1236_error, + 4010_error, "Insufficient funds", "address(this).balance" ); @@ -675,6 +680,8 @@ void BMC::checkAssert(BMCVerificationTarget& _target) _target.callStack, _target.modelExpressions, _target.expression->location(), + 4661_error, + 7812_error, "Assertion violation" ); } @@ -705,9 +712,11 @@ void BMC::addVerificationTarget( void BMC::checkCondition( smt::Expression _condition, - vector const& callStack, + vector const& _callStack, pair, vector> const& _modelExpressions, SourceLocation const& _location, + ErrorId _errorHappens, + ErrorId _errorMightHappen, string const& _description, string const& _additionalValueName, smt::Expression const* _additionalValue @@ -719,7 +728,7 @@ void BMC::checkCondition( vector expressionsToEvaluate; vector expressionNames; tie(expressionsToEvaluate, expressionNames) = _modelExpressions; - if (callStack.size()) + if (_callStack.size()) if (_additionalValue) { expressionsToEvaluate.emplace_back(*_additionalValue); @@ -750,7 +759,7 @@ void BMC::checkCondition( { std::ostringstream message; message << _description << " happens here"; - if (callStack.size()) + if (_callStack.size()) { std::ostringstream modelMessage; modelMessage << " for:\n"; @@ -763,11 +772,11 @@ void BMC::checkCondition( for (auto const& eval: sortedModel) modelMessage << " " << eval.first << " = " << eval.second << "\n"; m_errorReporter.warning( - 4334_error, + _errorHappens, _location, message.str(), SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) - .append(SMTEncoder::callStackMessage(callStack)) + .append(SMTEncoder::callStackMessage(_callStack)) .append(move(secondaryLocation)) ); } @@ -781,7 +790,7 @@ void BMC::checkCondition( case smt::CheckResult::UNSATISFIABLE: break; case smt::CheckResult::UNKNOWN: - m_errorReporter.warning(5225_error, _location, _description + " might happen here.", secondaryLocation); + m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation); break; case smt::CheckResult::CONFLICTING: m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); @@ -798,8 +807,7 @@ void BMC::checkBooleanNotConstant( Expression const& _condition, smt::Expression const& _constraints, smt::Expression const& _value, - vector const& _callStack, - string const& _description + vector const& _callStack ) { // Do not check for const-ness if this is a constant. @@ -832,22 +840,22 @@ void BMC::checkBooleanNotConstant( m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack)); else { - string value; + string description; if (positiveResult == smt::CheckResult::SATISFIABLE) { solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, ""); - value = "true"; + description = "Condition is always true."; } else { solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, ""); solAssert(negatedResult == smt::CheckResult::SATISFIABLE, ""); - value = "false"; + description = "Condition is always false."; } m_errorReporter.warning( 6838_error, _condition.location(), - boost::algorithm::replace_all_copy(_description, "$VALUE", value), + description, SMTEncoder::callStackMessage(_callStack) ); } diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index f4b427ede..8b30f073d 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -44,6 +44,7 @@ using solidity::util::h256; namespace solidity::langutil { class ErrorReporter; +struct ErrorId; struct SourceLocation; } @@ -144,22 +145,22 @@ private: /// Check that a condition can be satisfied. void checkCondition( smt::Expression _condition, - std::vector const& callStack, + std::vector const& _callStack, std::pair, std::vector> const& _modelExpressions, langutil::SourceLocation const& _location, + langutil::ErrorId _errorHappens, + langutil::ErrorId _errorMightHappen, std::string const& _description, std::string const& _additionalValueName = "", smt::Expression const* _additionalValue = nullptr ); /// Checks that a boolean condition is not constant. Do not warn if the expression /// is a literal constant. - /// @param _description the warning string, $VALUE will be replaced by the constant value. void checkBooleanNotConstant( Expression const& _condition, smt::Expression const& _constraints, smt::Expression const& _value, - std::vector const& _callStack, - std::string const& _description + std::vector const& _callStack ); std::pair> checkSatisfiableAndGenerateModel(std::vector const& _expressionsToEvaluate); From 02eee54f38e0abafc7b86df7ba9c626398ba1914 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 12 May 2020 11:42:29 +0200 Subject: [PATCH 62/65] Add error IDs to ContractLevelChecker --- libsolidity/analysis/ContractLevelChecker.cpp | 31 +++++++++++++------ libsolidity/analysis/ContractLevelChecker.h | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 1cff26c26..cb8694537 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -112,7 +112,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co functions[function->name()].push_back(function); } - findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); + findDuplicateDefinitions(functions); } void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract) @@ -123,11 +123,11 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr for (EventDefinition const* event: _contract.events()) events[event->name()].push_back(event); - findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); + findDuplicateDefinitions(events); } template -void ContractLevelChecker::findDuplicateDefinitions(map> const& _definitions, string _message) +void ContractLevelChecker::findDuplicateDefinitions(map> const& _definitions) { for (auto const& it: _definitions) { @@ -146,13 +146,27 @@ void ContractLevelChecker::findDuplicateDefinitions(map> const if (ssl.infos.size() > 0) { - ssl.limitSize(_message); + ErrorId error; + string message; + if constexpr (is_same_v) + { + error = 1686_error; + message = "Function with same name and arguments defined twice."; + } + else + { + static_assert(is_same_v, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\""); + error = 5883_error; + message = "Event with same name and arguments defined twice."; + } + + ssl.limitSize(message); m_errorReporter.declarationError( - 1686_error, + error, overloads[i]->location(), ssl, - _message + message ); } } @@ -222,9 +236,8 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c 3656_error, _contract.location(), ssl, - "Contract \"" + _contract.annotation().canonicalName - + "\" should be marked as abstract."); - + "Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract." + ); } } diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h index 736b3d3d7..e40cb8b0d 100644 --- a/libsolidity/analysis/ContractLevelChecker.h +++ b/libsolidity/analysis/ContractLevelChecker.h @@ -60,7 +60,7 @@ private: void checkDuplicateFunctions(ContractDefinition const& _contract); void checkDuplicateEvents(ContractDefinition const& _contract); template - void findDuplicateDefinitions(std::map> const& _definitions, std::string _message); + void findDuplicateDefinitions(std::map> const& _definitions); /// Checks for unimplemented functions and modifiers. void checkAbstractDefinitions(ContractDefinition const& _contract); /// Checks that the base constructor arguments are properly provided. From c29d76f8f22cc36808bf986d497b48589726020e Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Tue, 12 May 2020 15:21:13 +0530 Subject: [PATCH 63/65] Test for function with value setting --- .../syntaxTests/conversion/function_cast_value_set.sol | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/libsolidity/syntaxTests/conversion/function_cast_value_set.sol diff --git a/test/libsolidity/syntaxTests/conversion/function_cast_value_set.sol b/test/libsolidity/syntaxTests/conversion/function_cast_value_set.sol new file mode 100644 index 000000000..163724d35 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_cast_value_set.sol @@ -0,0 +1,7 @@ +contract C { + function f() public payable { + function() external payable x = this.f{value: 7}; + } +} +// ---- +// TypeError: (46-94): Type function () payable external is not implicitly convertible to expected type function () payable external. From 095e17f7f922e2b029884ab5584cbdfe7bfdd952 Mon Sep 17 00:00:00 2001 From: Harikrishnan Mulackal Date: Tue, 12 May 2020 19:00:42 +0530 Subject: [PATCH 64/65] Bug that ignored return tag when no other devdoc tags were present --- Changelog.md | 2 +- libsolidity/interface/Natspec.cpp | 14 ++++++-------- test/libsolidity/SolidityNatspecJSON.cpp | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7c613ff1f..338eb5631 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,7 +17,7 @@ Bugfixes: * ABI: Skip ``private`` or ``internal`` constructors. * Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables. * Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice. - + * Natspec: Fixed a bug that ignored ``@return`` tag when no other developer-documentation tags were present. ### 0.6.7 (2020-05-04) diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index b78e46081..5e51696ec 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -99,16 +99,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) if (auto fun = dynamic_cast(&it.second->declaration())) { Json::Value method(devDocumentation(fun->annotation().docTags)); + // add the function, only if we have any documentation to add + Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun); + + if (!jsonReturn.empty()) + method["returns"] = jsonReturn; + if (!method.empty()) - { - // add the function, only if we have any documentation to add - Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun); - - if (!jsonReturn.empty()) - method["returns"] = jsonReturn; - methods[it.second->externalSignature()] = method; - } } } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 13bd1f3b0..69ae832ad 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -355,6 +355,27 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions) checkNatspec(sourceCode, "test", natspec, false); } +BOOST_AUTO_TEST_CASE(dev_return_no_params) +{ + char const* sourceCode = R"( + contract test { + /// @return d The result of the multiplication + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } + } + )"; + + char const* natspec = R"ABCDEF( + { + "methods": { + "mul(uint256,uint256)": { + "returns": { "d": "The result of the multiplication" + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, false); +} + BOOST_AUTO_TEST_CASE(dev_return) { char const* sourceCode = R"( From 7da453014dd3c0ae14abd9ba89a974e20a9f17c9 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 5 May 2020 01:03:41 +0200 Subject: [PATCH 65/65] Fix source location of yul variable declarations without value. --- Changelog.md | 1 + libyul/backends/evm/EVMCodeTransform.cpp | 4 ++-- test/cmdlineTests/optimizer_user_yul/output | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 338eb5631..996904b27 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,6 +18,7 @@ Bugfixes: * Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables. * Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice. * Natspec: Fixed a bug that ignored ``@return`` tag when no other developer-documentation tags were present. + * Yul assembler: Fix source location of variable declarations without value. ### 0.6.7 (2020-05-04) diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 9e8d4d662..a4e19998f 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -184,11 +184,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) } else { + m_assembly.setSourceLocation(_varDecl.location); int variablesLeft = numVariables; while (variablesLeft--) m_assembly.appendConstant(u256(0)); } + m_assembly.setSourceLocation(_varDecl.location); bool atTopOfStack = true; for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex) { @@ -203,7 +205,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) if (atTopOfStack) { m_context->variableStackHeights.erase(&var); - m_assembly.setSourceLocation(_varDecl.location); m_assembly.appendInstruction(evmasm::Instruction::POP); } else @@ -216,7 +217,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) int slot = *m_unusedStackSlots.begin(); m_unusedStackSlots.erase(m_unusedStackSlots.begin()); m_context->variableStackHeights[&var] = slot; - m_assembly.setSourceLocation(_varDecl.location); if (int heightDiff = variableHeightDiff(var, varName, true)) m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1)); m_assembly.appendInstruction(evmasm::Instruction::POP); diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output index b95f2e0fa..8eef7b4f6 100644 --- a/test/cmdlineTests/optimizer_user_yul/output +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -5,7 +5,7 @@ EVM assembly: mstore(0x40, 0x80) /* "optimizer_user_yul/input.sol":72:77 int a */ 0x00 - /* "optimizer_user_yul/input.sol":38:487 constructor() public payable... */ + /* "optimizer_user_yul/input.sol":152:161 let x,y,z */ dup1 0x00 dup1